├── icon.ase ├── icon16.png ├── icon128.png ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── libs.versions.toml ├── src └── main │ ├── resources │ ├── assets │ │ └── pluralcraft │ │ │ └── icon.png │ ├── pluralcraft.mixins.json │ └── quilt.mod.json │ └── java │ └── im │ └── f24 │ └── pluralcraft │ ├── PluralPlayer.java │ ├── mixin │ ├── MinecraftServerMixin.java │ └── ServerPlayerMixin.java │ ├── PluralSystemManager.java │ ├── PluralCraft.java │ ├── Member.java │ ├── PluralSystem.java │ └── PluralCraftCommands.java ├── .gitignore ├── gradle.properties ├── settings.gradle ├── .editorconfig ├── LICENSE ├── README.md ├── gradlew.bat └── gradlew /icon.ase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xf24/pluralcraft/HEAD/icon.ase -------------------------------------------------------------------------------- /icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xf24/pluralcraft/HEAD/icon16.png -------------------------------------------------------------------------------- /icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xf24/pluralcraft/HEAD/icon128.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xf24/pluralcraft/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/assets/pluralcraft/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xf24/pluralcraft/HEAD/src/main/resources/assets/pluralcraft/icon.png -------------------------------------------------------------------------------- /src/main/resources/pluralcraft.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "im.f24.pluralcraft.mixin", 5 | "compatibilityLevel": "JAVA_17", 6 | "mixins": [ 7 | "ServerPlayerMixin", 8 | "MinecraftServerMixin" 9 | ], 10 | "injectors": { 11 | "defaultRequire": 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Gradle 2 | .gradle/ 3 | build/ 4 | out/ 5 | classes/ 6 | 7 | # Quilt Loom 8 | run/ 9 | 10 | # Eclipse 11 | *.launch 12 | 13 | # IntelliJ Idea 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # Visual Studio Code 20 | .settings/ 21 | .vscode/ 22 | bin/ 23 | .classpath 24 | .project 25 | 26 | # macOS 27 | *.DS_Store 28 | -------------------------------------------------------------------------------- /src/main/java/im/f24/pluralcraft/PluralPlayer.java: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (c) 2022 f24.im 3 | // 4 | // This file is part of PluralCraft, licensed under the MIT license. see the LICENSE file for more info. 5 | 6 | package im.f24.pluralcraft; 7 | 8 | public interface PluralPlayer { 9 | PluralSystem getSystem(); 10 | 11 | void createSystem(); 12 | } 13 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # Copyright (c) 2022 f24.im 3 | # 4 | # This file is part of PluralCraft, licensed under the MIT license. see the LICENSE file for more info. 5 | 6 | distributionBase=GRADLE_USER_HOME 7 | distributionPath=wrapper/dists 8 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip 9 | zipStoreBase=GRADLE_USER_HOME 10 | zipStorePath=wrapper/dists 11 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # Copyright (c) 2022 f24.im 3 | # 4 | # This file is part of PluralCraft, licensed under the MIT license. see the LICENSE file for more info. 5 | 6 | # Gradle Properties 7 | org.gradle.jvmargs = -Xmx1G 8 | org.gradle.parallel = true 9 | 10 | # Mod Properties 11 | version = 1.0.0-alpha 12 | maven_group = im.f24 13 | archives_base_name = pluralcraft 14 | 15 | # Dependencies are managed at gradle/libs.versions.toml 16 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (c) 2022 f24.im 3 | // 4 | // This file is part of PluralCraft, licensed under the MIT license. see the LICENSE file for more info. 5 | 6 | pluginManagement { 7 | repositories { 8 | maven { 9 | name = 'Quilt' 10 | url = 'https://maven.quiltmc.org/repository/release' 11 | } 12 | // Currently needed for Intermediary and other temporary dependencies 13 | maven { 14 | name = 'Fabric' 15 | url = 'https://maven.fabricmc.net/' 16 | } 17 | gradlePluginPortal() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | tab_width = 4 8 | trim_trailing_whitespace = true 9 | 10 | [*.gradle] 11 | indent_style = tab 12 | 13 | [*.java] 14 | indent_style = tab 15 | 16 | [*.json] 17 | indent_style = space 18 | indent_size = 2 19 | 20 | [quilt.mod.json] 21 | indent_style = tab 22 | tab_width = 2 23 | 24 | [*.toml] 25 | indent_style = tab 26 | tab_width = 2 27 | 28 | [*.properties] 29 | indent_style = space 30 | indent_size = 2 31 | 32 | [.editorconfig] 33 | indent_style = space 34 | indent_size = 4 35 | -------------------------------------------------------------------------------- /src/main/java/im/f24/pluralcraft/mixin/MinecraftServerMixin.java: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (c) 2022 f24.im 3 | // 4 | // This file is part of PluralCraft, licensed under the MIT license. see the LICENSE file for more info. 5 | 6 | package im.f24.pluralcraft.mixin; 7 | 8 | import im.f24.pluralcraft.PluralSystemManager; 9 | import net.minecraft.server.MinecraftServer; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 14 | 15 | @Mixin(MinecraftServer.class) 16 | public class MinecraftServerMixin { 17 | 18 | 19 | @Inject(method = "Lnet/minecraft/server/MinecraftServer;saveEverything(ZZZ)Z", at = @At("RETURN")) 20 | public void pluralcraft$saveEverything(boolean suppressLogs, boolean flush, boolean force, CallbackInfoReturnable cir) { 21 | PluralSystemManager.save((MinecraftServer) (Object)this); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/resources/quilt.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema_version": 1, 3 | "quilt_loader": { 4 | "group": "im.f24", 5 | "id": "pluralcraft", 6 | "version": "${version}", 7 | "metadata": { 8 | "name": "Plural Craft", 9 | "description": "A Server Side minecraft mod for plural systems.", 10 | "contributors": { 11 | "0xf24": "foxes", 12 | "mars" : "icon" 13 | }, 14 | "contact": { 15 | "issues": "https://github.com/0xf24/pluralcraft/issues", 16 | "sources": "https://github.com/0xf24/pluralcraft" 17 | }, 18 | "license" : "MIT", 19 | "icon": "assets/pluralcraft/icon.png" 20 | }, 21 | "intermediate_mappings": "net.fabricmc:intermediary", 22 | "entrypoints": { 23 | "init": "im.f24.pluralcraft.PluralCraft" 24 | }, 25 | "depends": [ 26 | { 27 | "id": "quilt_loader", 28 | "versions": ">=0.16.0-" 29 | }, 30 | { 31 | "id": "quilted_fabric_api", 32 | "versions": ">=2.0.0-" 33 | }, 34 | { 35 | "id": "minecraft", 36 | "versions": ">=1.19" 37 | } 38 | ] 39 | }, 40 | "mixin": "pluralcraft.mixins.json" 41 | } 42 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | # The latest versions are available at https://lambdaurora.dev/tools/import_quilt.html 3 | minecraft = "1.19" 4 | quilt_mappings = "1.19+build.1" 5 | quilt_loader = "0.17.1-beta.1" 6 | 7 | quilted_fabric_api = "2.0.0-alpha.2+0.55.3-1.19" 8 | 9 | placeholder_api = "2.0.0-beta.4+1.19" 10 | 11 | [libraries] 12 | minecraft = { module = "com.mojang:minecraft", version.ref = "minecraft" } 13 | quilt_mappings = { module = "org.quiltmc:quilt-mappings", version.ref = "quilt_mappings" } 14 | quilt_loader = { module = "org.quiltmc:quilt-loader", version.ref = "quilt_loader" } 15 | 16 | quilted_fabric_api = { module = "org.quiltmc.quilted-fabric-api:quilted-fabric-api", version.ref = "quilted_fabric_api" } 17 | 18 | # placeholder_api = { module = "eu.pb4:placeholder-api", version.ref = "placeholder_api"} 19 | 20 | # If you have multiple similar dependencies, you can declare a dependency bundle and reference it on the build script with "libs.bundles.example". 21 | # [bundles] 22 | # example = ["example-a", "example-b", "example-c"] 23 | 24 | [plugins] 25 | quilt_loom = { id = "org.quiltmc.loom", version = "0.12.+" } 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright © 2022 f24.im 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # & PluralCraft 2 | 3 | A 1.19+ server side quilt mod for plural systems. 4 | 5 | ## todo 6 | 7 | - implement coloured names 8 | - investigate unsticking latch during preview 9 | - skin proxying? 10 | - nameplate changes 11 | - better usage instructions 12 | 13 | ## installation 14 | 15 | install the mod jar into the server. for preview support enable chat previews in the server.properties file 16 | ```properties 17 | previews-chat=true 18 | ``` 19 | 20 | ## Usage 21 | 22 | create a system by running `/pc create` 23 | 24 | - the list of current members can be viewed with `/pc member` 25 | - members can be created with `/pc member new ` 26 | - members can be managed with `/pc member ` 27 | - members can be deleted with `delete` 28 | - members display names can be queried with `display` and set with `display ` 29 | - members proxies can be listed with `proxy`, added with `proxy add `, and removed with `proxy remove ` 30 | - the current fronter can be set with `/pc front [fronter]` where no fronter prints the current fronter and `clear` clears the fronter 31 | - autoproxy can be configured with `/pc autoproxy ` 32 | 33 | 34 | made with ❤️ by foxes 35 | -------------------------------------------------------------------------------- /src/main/java/im/f24/pluralcraft/PluralSystemManager.java: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (c) 2022 f24.im 3 | // 4 | // This file is part of PluralCraft, licensed under the MIT license. see the LICENSE file for more info. 5 | 6 | package im.f24.pluralcraft; 7 | 8 | import net.minecraft.nbt.CompoundTag; 9 | import net.minecraft.nbt.NbtIo; 10 | import net.minecraft.server.MinecraftServer; 11 | 12 | import java.io.*; 13 | import java.util.HashMap; 14 | import java.util.UUID; 15 | 16 | public class PluralSystemManager { 17 | 18 | private static final HashMap systems = new HashMap<>(); 19 | 20 | public static void save(MinecraftServer minecraftServer) { 21 | CompoundTag tag = new CompoundTag(); 22 | 23 | CompoundTag list = new CompoundTag(); 24 | 25 | for (var entries : systems.entrySet()) { 26 | list.put(entries.getKey().toString(), PluralSystem.toNBT(entries.getValue())); 27 | } 28 | 29 | tag.put("systems", list); 30 | 31 | File file = minecraftServer.getFile("pluralcraft.nbt"); 32 | 33 | try { 34 | NbtIo.write(tag, file); 35 | } catch (IOException e) { 36 | e.printStackTrace(); 37 | } 38 | } 39 | 40 | public static void register(UUID id, PluralSystem system) { 41 | systems.put(id, system); 42 | } 43 | 44 | public static void load(MinecraftServer server) { 45 | systems.clear(); 46 | 47 | File file = server.getFile("pluralcraft.nbt"); 48 | 49 | try { 50 | CompoundTag tag = NbtIo.read(file); 51 | 52 | if (tag == null) return; 53 | 54 | CompoundTag systemsTag = tag.getCompound("systems"); 55 | 56 | for (String key : systemsTag.getAllKeys()) { 57 | systems.put(UUID.fromString(key), PluralSystem.fromNBT(systemsTag.getCompound(key))); 58 | } 59 | 60 | } catch (Exception ignored) { 61 | 62 | } 63 | } 64 | 65 | public static PluralSystem get(UUID uuid) { 66 | return systems.get(uuid); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/im/f24/pluralcraft/PluralCraft.java: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (c) 2022 f24.im 3 | // 4 | // This file is part of PluralCraft, licensed under the MIT license. see the LICENSE file for more info. 5 | 6 | package im.f24.pluralcraft; 7 | 8 | import net.fabricmc.fabric.api.message.v1.ServerMessageDecoratorEvent; 9 | import net.fabricmc.fabric.api.message.v1.ServerMessageEvents; 10 | import net.minecraft.resources.ResourceLocation; 11 | import org.quiltmc.loader.api.ModContainer; 12 | import org.quiltmc.qsl.base.api.entrypoint.ModInitializer; 13 | import org.quiltmc.qsl.command.api.CommandRegistrationCallback; 14 | import org.quiltmc.qsl.lifecycle.api.event.ServerLifecycleEvents; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | import java.util.concurrent.CompletableFuture; 19 | 20 | public class PluralCraft implements ModInitializer { 21 | public static final Logger LOGGER = LoggerFactory.getLogger("PluralCraft"); 22 | public static final String MODID = "pluralcraft"; 23 | 24 | public static final ResourceLocation PROXY_PHASE = new ResourceLocation(MODID, "proxy"); 25 | 26 | @Override 27 | public void onInitialize(ModContainer mod) { 28 | LOGGER.info("pluralcraft " + mod.metadata().version() + "loaded"); 29 | 30 | CommandRegistrationCallback.EVENT.register(PluralCraftCommands::register); 31 | 32 | ServerLifecycleEvents.STARTING.register(PluralSystemManager::load); 33 | ServerLifecycleEvents.STOPPING.register(PluralSystemManager::save); 34 | 35 | ServerMessageDecoratorEvent.EVENT.addPhaseOrdering(PROXY_PHASE, ServerMessageDecoratorEvent.CONTENT_PHASE); 36 | 37 | ServerMessageDecoratorEvent.EVENT.register(PROXY_PHASE, (sender, message) -> { 38 | if (sender instanceof PluralPlayer pluralPlayer) { 39 | PluralSystem system = pluralPlayer.getSystem(); 40 | 41 | if (system != null) { 42 | return CompletableFuture.completedFuture(system.proxy(message)); 43 | } 44 | } 45 | return CompletableFuture.completedFuture(message); 46 | }); 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/im/f24/pluralcraft/mixin/ServerPlayerMixin.java: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (c) 2022-2022 f24.im 3 | // 4 | // This file is part of PluralCraft, licensed under the MIT license. see the LICENSE file for more info. 5 | 6 | package im.f24.pluralcraft.mixin; 7 | 8 | import com.mojang.authlib.GameProfile; 9 | import im.f24.pluralcraft.PluralPlayer; 10 | import im.f24.pluralcraft.PluralSystem; 11 | import im.f24.pluralcraft.PluralSystemManager; 12 | import net.minecraft.core.BlockPos; 13 | import net.minecraft.server.MinecraftServer; 14 | import net.minecraft.server.level.ServerLevel; 15 | import net.minecraft.server.level.ServerPlayer; 16 | import net.minecraft.world.entity.player.Player; 17 | import net.minecraft.world.entity.player.ProfilePublicKey; 18 | import net.minecraft.world.level.Level; 19 | import org.jetbrains.annotations.Nullable; 20 | import org.spongepowered.asm.mixin.Mixin; 21 | import org.spongepowered.asm.mixin.Unique; 22 | import org.spongepowered.asm.mixin.injection.At; 23 | import org.spongepowered.asm.mixin.injection.Inject; 24 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 25 | 26 | @Mixin(ServerPlayer.class) 27 | public abstract class ServerPlayerMixin extends Player implements PluralPlayer { 28 | 29 | private ServerPlayerMixin(Level level, BlockPos blockPos, float f, GameProfile gameProfile, @Nullable ProfilePublicKey profilePublicKey) { 30 | super(level, blockPos, f, gameProfile, profilePublicKey); 31 | } 32 | 33 | @Nullable 34 | @Unique 35 | private PluralSystem pluralCraft$system = null; 36 | 37 | @Inject( 38 | method = "(Lnet/minecraft/server/MinecraftServer;Lnet/minecraft/server/level/ServerLevel;Lcom/mojang/authlib/GameProfile;Lnet/minecraft/world/entity/player/ProfilePublicKey;)V", 39 | at = @At("RETURN") 40 | ) 41 | public void init(MinecraftServer minecraftServer, ServerLevel serverLevel, GameProfile gameProfile, ProfilePublicKey profilePublicKey, CallbackInfo ci) { 42 | pluralCraft$system = PluralSystemManager.get(this.uuid); 43 | } 44 | 45 | 46 | @Override 47 | public PluralSystem getSystem() { 48 | return pluralCraft$system; 49 | } 50 | 51 | @Override 52 | public void createSystem() { 53 | pluralCraft$system = new PluralSystem(); 54 | PluralSystemManager.register(this.uuid, pluralCraft$system); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /src/main/java/im/f24/pluralcraft/Member.java: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (c) 2022 f24.im 3 | // 4 | // This file is part of PluralCraft, licensed under the MIT license. see the LICENSE file for more info. 5 | 6 | package im.f24.pluralcraft; 7 | 8 | import net.minecraft.nbt.CompoundTag; 9 | import net.minecraft.nbt.ListTag; 10 | import net.minecraft.nbt.Tag; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | import java.util.ArrayList; 14 | import java.util.regex.Matcher; 15 | import java.util.regex.Pattern; 16 | 17 | public class Member { 18 | 19 | public final String name; 20 | private String displayName = ""; 21 | 22 | public final ArrayList proxyTags; 23 | 24 | 25 | public Member(String name) { 26 | this.name = name; 27 | this.proxyTags = new ArrayList<>(); 28 | } 29 | 30 | public String getDisplayName() { 31 | return displayName.isEmpty()? name : displayName; 32 | } 33 | 34 | 35 | public void setDisplayName(String displayName) { 36 | this.displayName = displayName; 37 | } 38 | 39 | 40 | public static Member fromNBT(String name, CompoundTag tag) { 41 | 42 | Member member = new Member(name); 43 | 44 | member.displayName = tag.getString("displayName"); 45 | 46 | var list = tag.getList("proxies", Tag.TAG_COMPOUND); 47 | 48 | for (int i = 0; i < list.size(); i++) { 49 | 50 | CompoundTag proxycompound = list.getCompound(i); 51 | 52 | ProxyTag proxyTag = new ProxyTag(proxycompound.getString("prefix"), proxycompound.getString("suffix")); 53 | 54 | member.proxyTags.add(proxyTag); 55 | } 56 | 57 | return member; 58 | } 59 | 60 | public static CompoundTag toNBT(Member member) { 61 | var compound = new CompoundTag(); 62 | 63 | compound.putString("displayName", member.displayName); 64 | 65 | var list = new ListTag(); 66 | 67 | for (ProxyTag tag : member.proxyTags) { 68 | if (!tag.prefix.isEmpty() || !tag.suffix.isEmpty()) { 69 | var proxycomp = new CompoundTag(); 70 | proxycomp.putString("prefix", tag.prefix); 71 | proxycomp.putString("suffix", tag.suffix); 72 | 73 | list.add(proxycomp); 74 | } 75 | } 76 | 77 | compound.put("proxies", list); 78 | 79 | return compound; 80 | } 81 | 82 | public static class ProxyTag { 83 | 84 | public final String prefix; 85 | public final String suffix; 86 | 87 | public final @Nullable Pattern regex; 88 | 89 | public ProxyTag(String prefix, String suffix) { 90 | this.prefix = prefix; 91 | this.suffix = suffix; 92 | 93 | if (prefix.isEmpty()) { 94 | if (suffix.isEmpty()) { 95 | regex = null; 96 | } else { 97 | regex = Pattern.compile("(?.+)(%s)$".formatted(suffix)); 98 | } 99 | } else { 100 | if (suffix.isEmpty()) { 101 | regex = Pattern.compile("^(%s)(?.+)".formatted(prefix)); 102 | } else { 103 | regex = Pattern.compile("^(%s)(?.+)(%s)$".formatted(prefix, suffix)); 104 | } 105 | } 106 | } 107 | 108 | public String raw() { 109 | return prefix + "text" + suffix; 110 | } 111 | 112 | private static final Pattern PARSER = Pattern.compile("^(?.*)(text)(?.*)$"); 113 | 114 | @Nullable 115 | public static ProxyTag parse(String tag) { 116 | Matcher matcher = PARSER.matcher(tag); 117 | 118 | if (matcher.matches()) { 119 | String prefix = matcher.group("prefix"); 120 | String suffix = matcher.group("suffix"); 121 | 122 | if (!prefix.isEmpty() || !suffix.isEmpty()) { 123 | return new ProxyTag(prefix, suffix); 124 | } 125 | } 126 | 127 | return null; 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/im/f24/pluralcraft/PluralSystem.java: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (c) 2022 f24.im 3 | // 4 | // This file is part of PluralCraft, licensed under the MIT license. see the LICENSE file for more info. 5 | 6 | package im.f24.pluralcraft; 7 | 8 | import net.minecraft.nbt.CompoundTag; 9 | import net.minecraft.network.chat.Component; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Iterator; 14 | import java.util.stream.Stream; 15 | 16 | public class PluralSystem implements Iterable { 17 | 18 | private ArrayList members = new ArrayList<>(); 19 | 20 | @Nullable 21 | private Member fronter; 22 | 23 | @Nullable 24 | private Member lastSender; 25 | 26 | public @Nullable Member getFronter() { 27 | return fronter; 28 | } 29 | 30 | public void setFronter(@Nullable Member member) { 31 | this.fronter = member; 32 | } 33 | 34 | private AutoProxyType autoProxy = AutoProxyType.OFF; 35 | 36 | 37 | public AutoProxyType getAutoProxy() { 38 | return autoProxy; 39 | } 40 | 41 | public void setAutoProxy(AutoProxyType type) { 42 | this.autoProxy = type; 43 | } 44 | 45 | public String formatStringAs(Member member, String message) { 46 | return String.format("%s %s", member.getDisplayName(), message); 47 | } 48 | 49 | public Component proxy(Component message) { 50 | String contents = message.getString(); 51 | 52 | if (contents.startsWith("\\\\")) { 53 | lastSender = null; 54 | return message; 55 | } 56 | 57 | if (contents.startsWith("\\")) { 58 | return message; 59 | } 60 | 61 | for (var member : members) { 62 | var tags = member.proxyTags; 63 | 64 | for (Member.ProxyTag tag : tags) { 65 | if (tag.regex != null) { 66 | var matcher = tag.regex.matcher(contents); 67 | 68 | if (matcher.matches()) { 69 | lastSender = member; 70 | 71 | return Component.literal(formatStringAs(member, matcher.group("content"))); 72 | } 73 | } 74 | } 75 | } 76 | 77 | return switch (autoProxy) { 78 | case LATCH -> { 79 | if (lastSender != null) { 80 | yield Component.literal(formatStringAs(lastSender, contents)); 81 | } 82 | yield message; 83 | } 84 | case FRONT -> { 85 | if (fronter != null) { 86 | yield Component.literal(formatStringAs(fronter, contents)); 87 | } 88 | yield message; 89 | } 90 | case OFF -> { 91 | lastSender = null; 92 | yield message; 93 | } 94 | }; 95 | } 96 | 97 | public static PluralSystem fromNBT(CompoundTag tag) { 98 | PluralSystem system = new PluralSystem(); 99 | 100 | CompoundTag members = tag.getCompound("members"); 101 | 102 | for (String key : members.getAllKeys()) { 103 | CompoundTag member = members.getCompound(key); 104 | 105 | if (member != null) system.members.add(Member.fromNBT(key, member)); 106 | } 107 | 108 | system.autoProxy = AutoProxyType.fromString(tag.getString("ap")); 109 | 110 | return system; 111 | } 112 | 113 | public static CompoundTag toNBT(PluralSystem system) { 114 | CompoundTag tag = new CompoundTag(); 115 | 116 | CompoundTag members = new CompoundTag(); 117 | 118 | for (Member member : system.members) { 119 | members.put(member.name, Member.toNBT(member)); 120 | } 121 | 122 | tag.putString("ap", system.autoProxy.toString()); 123 | 124 | tag.put("members", members); 125 | 126 | return tag; 127 | } 128 | 129 | public void createMember(String name) { 130 | Member member = new Member(name); 131 | members.add(member); 132 | } 133 | 134 | public Member getMember(String name) { 135 | for (Member member : members) { 136 | if (member.name.equals(name)) return member; 137 | } 138 | 139 | return null; 140 | } 141 | 142 | public Iterator iterator() { 143 | return members.iterator(); 144 | } 145 | 146 | public boolean deleteMember(String name) { 147 | for (Member member : members) { 148 | if (member.name.equals(name)) { 149 | members.remove(member); 150 | return true; 151 | } 152 | } 153 | 154 | return false; 155 | } 156 | 157 | public Stream getMemberNames() { 158 | return members.stream().map(m -> m.name); 159 | } 160 | 161 | public enum AutoProxyType { 162 | LATCH, 163 | FRONT, 164 | OFF; 165 | public static AutoProxyType fromString(String name) { 166 | try { 167 | return valueOf(name); 168 | } catch (IllegalArgumentException e) { 169 | return OFF; 170 | } 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # SPDX-License-Identifier: MIT 4 | # Copyright (c) 2022 f24.im 5 | # 6 | # This file is part of PluralCraft, licensed under the MIT license. see the LICENSE file for more info. 7 | 8 | ############################################################################## 9 | # 10 | # Gradle start up script for POSIX generated by Gradle. 11 | # 12 | # Important for running: 13 | # 14 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 15 | # noncompliant, but you have some other compliant shell such as ksh or 16 | # bash, then to run this script, type that shell name before the whole 17 | # command line, like: 18 | # 19 | # ksh Gradle 20 | # 21 | # Busybox and similar reduced shells will NOT work, because this script 22 | # requires all of these POSIX shell features: 23 | # * functions; 24 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 25 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 26 | # * compound commands having a testable exit status, especially «case»; 27 | # * various built-in commands including «command», «set», and «ulimit». 28 | # 29 | # Important for patching: 30 | # 31 | # (2) This script targets any POSIX shell, so it avoids extensions provided 32 | # by Bash, Ksh, etc; in particular arrays are avoided. 33 | # 34 | # The "traditional" practice of packing multiple parameters into a 35 | # space-separated string is a well documented source of bugs and security 36 | # problems, so this is (mostly) avoided, by progressively accumulating 37 | # options in "$@", and eventually passing that to Java. 38 | # 39 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 40 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 41 | # see the in-line comments for details. 42 | # 43 | # There are tweaks for specific operating systems such as AIX, CygWin, 44 | # Darwin, MinGW, and NonStop. 45 | # 46 | # (3) This script is generated from the Groovy template 47 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 48 | # within the Gradle project. 49 | # 50 | # You can find Gradle at https://github.com/gradle/gradle/. 51 | # 52 | ############################################################################## 53 | 54 | # Attempt to set APP_HOME 55 | 56 | # Resolve links: $0 may be a link 57 | app_path=$0 58 | 59 | # Need this for daisy-chained symlinks. 60 | while 61 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 62 | [ -h "$app_path" ] 63 | do 64 | ls=$( ls -ld "$app_path" ) 65 | link=${ls#*' -> '} 66 | case $link in #( 67 | /*) app_path=$link ;; #( 68 | *) app_path=$APP_HOME$link ;; 69 | esac 70 | done 71 | 72 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 73 | 74 | APP_NAME="Gradle" 75 | APP_BASE_NAME=${0##*/} 76 | 77 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 78 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 79 | 80 | # Use the maximum available, or set MAX_FD != -1 to use that value. 81 | MAX_FD=maximum 82 | 83 | warn () { 84 | echo "$*" 85 | } >&2 86 | 87 | die () { 88 | echo 89 | echo "$*" 90 | echo 91 | exit 1 92 | } >&2 93 | 94 | # OS specific support (must be 'true' or 'false'). 95 | cygwin=false 96 | msys=false 97 | darwin=false 98 | nonstop=false 99 | case "$( uname )" in #( 100 | CYGWIN* ) cygwin=true ;; #( 101 | Darwin* ) darwin=true ;; #( 102 | MSYS* | MINGW* ) msys=true ;; #( 103 | NONSTOP* ) nonstop=true ;; 104 | esac 105 | 106 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 107 | 108 | 109 | # Determine the Java command to use to start the JVM. 110 | if [ -n "$JAVA_HOME" ] ; then 111 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 112 | # IBM's JDK on AIX uses strange locations for the executables 113 | JAVACMD=$JAVA_HOME/jre/sh/java 114 | else 115 | JAVACMD=$JAVA_HOME/bin/java 116 | fi 117 | if [ ! -x "$JAVACMD" ] ; then 118 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 119 | 120 | Please set the JAVA_HOME variable in your environment to match the 121 | location of your Java installation." 122 | fi 123 | else 124 | JAVACMD=java 125 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 126 | 127 | Please set the JAVA_HOME variable in your environment to match the 128 | location of your Java installation." 129 | fi 130 | 131 | # Increase the maximum file descriptors if we can. 132 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 133 | case $MAX_FD in #( 134 | max*) 135 | MAX_FD=$( ulimit -H -n ) || 136 | warn "Could not query maximum file descriptor limit" 137 | esac 138 | case $MAX_FD in #( 139 | '' | soft) :;; #( 140 | *) 141 | ulimit -n "$MAX_FD" || 142 | warn "Could not set maximum file descriptor limit to $MAX_FD" 143 | esac 144 | fi 145 | 146 | # Collect all arguments for the java command, stacking in reverse order: 147 | # * args from the command line 148 | # * the main class name 149 | # * -classpath 150 | # * -D...appname settings 151 | # * --module-path (only if needed) 152 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 153 | 154 | # For Cygwin or MSYS, switch paths to Windows format before running java 155 | if "$cygwin" || "$msys" ; then 156 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 157 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 158 | 159 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 160 | 161 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 162 | for arg do 163 | if 164 | case $arg in #( 165 | -*) false ;; # don't mess with options #( 166 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 167 | [ -e "$t" ] ;; #( 168 | *) false ;; 169 | esac 170 | then 171 | arg=$( cygpath --path --ignore --mixed "$arg" ) 172 | fi 173 | # Roll the args list around exactly as many times as the number of 174 | # args, so each arg winds up back in the position where it started, but 175 | # possibly modified. 176 | # 177 | # NB: a `for` loop captures its iteration list before it begins, so 178 | # changing the positional parameters here affects neither the number of 179 | # iterations, nor the values presented in `arg`. 180 | shift # remove old arg 181 | set -- "$@" "$arg" # push replacement arg 182 | done 183 | fi 184 | 185 | # Collect all arguments for the java command; 186 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 187 | # shell script including quotes and variable substitutions, so put them in 188 | # double quotes to make sure that they get re-expanded; and 189 | # * put everything else in single quotes, so that it's not re-expanded. 190 | 191 | set -- \ 192 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 193 | -classpath "$CLASSPATH" \ 194 | org.gradle.wrapper.GradleWrapperMain \ 195 | "$@" 196 | 197 | # Use "xargs" to parse quoted args. 198 | # 199 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 200 | # 201 | # In Bash we could simply go: 202 | # 203 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 204 | # set -- "${ARGS[@]}" "$@" 205 | # 206 | # but POSIX shell has neither arrays nor command substitution, so instead we 207 | # post-process each arg (as a line of input to sed) to backslash-escape any 208 | # character that might be a shell metacharacter, then use eval to reverse 209 | # that process (while maintaining the separation between arguments), and wrap 210 | # the whole thing up as a single "set" statement. 211 | # 212 | # This will of course break if any of these variables contains a newline or 213 | # an unmatched quote. 214 | # 215 | 216 | eval "set -- $( 217 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 218 | xargs -n1 | 219 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 220 | tr '\n' ' ' 221 | )" '"$@"' 222 | 223 | exec "$JAVACMD" "$@" 224 | -------------------------------------------------------------------------------- /src/main/java/im/f24/pluralcraft/PluralCraftCommands.java: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // Copyright (c) 2022 f24.im 3 | // 4 | // This file is part of PluralCraft, licensed under the MIT license. see the LICENSE file for more info. 5 | 6 | package im.f24.pluralcraft; 7 | 8 | 9 | import com.mojang.brigadier.Command; 10 | import com.mojang.brigadier.CommandDispatcher; 11 | import com.mojang.brigadier.arguments.StringArgumentType; 12 | import com.mojang.brigadier.context.CommandContext; 13 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 14 | import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; 15 | import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; 16 | import net.minecraft.commands.CommandBuildContext; 17 | import net.minecraft.commands.CommandSourceStack; 18 | import net.minecraft.commands.Commands; 19 | import net.minecraft.network.chat.Component; 20 | import net.minecraft.network.chat.contents.TranslatableContents; 21 | import org.quiltmc.qsl.command.api.EnumArgumentType; 22 | 23 | import static net.minecraft.commands.Commands.argument; 24 | import static net.minecraft.commands.Commands.literal; 25 | 26 | public class PluralCraftCommands { 27 | 28 | public static final SimpleCommandExceptionType NO_SYSTEM = new SimpleCommandExceptionType(Component.translatable("pc.commands.system.not_found")); 29 | public static final DynamicCommandExceptionType NO_MEMBER = new DynamicCommandExceptionType(name -> Component.translatable("pc.commands.member.not_found", name)); 30 | public static final DynamicCommandExceptionType PROXY_NOT_FOUND = new DynamicCommandExceptionType(proxy -> Component.translatable("pc.commands.member.proxy.not_found", proxy)); 31 | public static final DynamicCommandExceptionType INVALID_PROXY = new DynamicCommandExceptionType(proxy -> Component.translatable("pc.commands.member.proxy.invalid", proxy)); 32 | 33 | 34 | public static void register(CommandDispatcher dispatcher, CommandBuildContext ctx, Commands.CommandSelection commandSelection) { 35 | dispatcher.register( 36 | literal("pc") 37 | .then(literal("member") 38 | .executes(PluralCraftCommands::listMembers) 39 | .then(literal("new") 40 | .then(argument("member", StringArgumentType.word()) 41 | .executes(PluralCraftCommands::newMember) 42 | )) 43 | .then(argument("member", StringArgumentType.word()) 44 | .then(literal("delete") 45 | .executes(PluralCraftCommands::deleteMember) 46 | ) 47 | .then(literal("display") 48 | .executes(PluralCraftCommands::getDisplayName) 49 | .then(argument("display_name", StringArgumentType.greedyString()) 50 | .executes(PluralCraftCommands::setDisplayName) 51 | ) 52 | ) 53 | .then(literal("proxy") 54 | .executes(PluralCraftCommands::listProxies) 55 | .then(literal("add") 56 | .then(argument("tag", StringArgumentType.greedyString()) 57 | .executes(PluralCraftCommands::addProxy) 58 | )) 59 | .then(literal("remove") 60 | .then(argument("tag", StringArgumentType.greedyString()) 61 | .executes(PluralCraftCommands::removeProxy) 62 | )) 63 | ) 64 | ) 65 | ) 66 | .then(literal("create") 67 | .executes(PluralCraftCommands::createSystem) 68 | ) 69 | .then(literal("autoproxy") 70 | .then(argument("type", EnumArgumentType.enumConstant(PluralSystem.AutoProxyType.class)) 71 | .executes(PluralCraftCommands::autoProxy) 72 | )) 73 | .then(literal("front") 74 | .executes(PluralCraftCommands::getFront) 75 | .then( 76 | literal("clear") 77 | .executes(PluralCraftCommands::clearFront) 78 | ) 79 | .then(argument("member", StringArgumentType.word()) 80 | .executes(PluralCraftCommands::setFront) 81 | ) 82 | ) 83 | .executes(PluralCraftCommands::displaySystem) 84 | ); 85 | } 86 | 87 | private static int displaySystem(CommandContext ctx) throws CommandSyntaxException { 88 | if (ctx.getSource().getPlayer() instanceof PluralPlayer player) { 89 | PluralSystem system = player.getSystem(); 90 | 91 | var component = Component.literal("members:\n"); 92 | 93 | for (Member member : system) { 94 | component.append(member.getDisplayName()); 95 | } 96 | 97 | ctx.getSource().sendSuccess(component, false); 98 | } else { 99 | throw NO_SYSTEM.create(); 100 | } 101 | 102 | return Command.SINGLE_SUCCESS; 103 | } 104 | 105 | private static int setFront(CommandContext ctx) throws CommandSyntaxException { 106 | if (ctx.getSource().getPlayer() instanceof PluralPlayer player) { 107 | PluralSystem system = player.getSystem(); 108 | 109 | if(system != null) { 110 | var name = StringArgumentType.getString(ctx, "member"); 111 | 112 | Member fronter = system.getMember(name); 113 | 114 | if (fronter != null) { 115 | system.setFronter(fronter); 116 | 117 | ctx.getSource().sendSuccess(Component.translatable("fronter is %s".formatted(name)), false); 118 | } else { 119 | throw NO_MEMBER.create(name); 120 | } 121 | } else { 122 | throw NO_SYSTEM.create(); 123 | } 124 | } 125 | 126 | return Command.SINGLE_SUCCESS; 127 | } 128 | 129 | private static int clearFront(CommandContext ctx) throws CommandSyntaxException { 130 | if (ctx.getSource().getPlayer() instanceof PluralPlayer player) { 131 | PluralSystem system = player.getSystem(); 132 | 133 | if(system != null) { 134 | system.setFronter(null); 135 | ctx.getSource().sendSuccess(Component.literal("fronter cleared"), false); 136 | } else { 137 | throw NO_SYSTEM.create(); 138 | } 139 | } 140 | 141 | return Command.SINGLE_SUCCESS; 142 | } 143 | 144 | private static int getFront(CommandContext ctx) throws CommandSyntaxException { 145 | if (ctx.getSource().getPlayer() instanceof PluralPlayer player) { 146 | PluralSystem system = player.getSystem(); 147 | 148 | 149 | if(system != null) { 150 | Member fronter = system.getFronter(); 151 | 152 | if (fronter != null) { 153 | ctx.getSource().sendSuccess(Component.literal("fronter is %s".formatted(fronter.name)), false); 154 | } else { 155 | ctx.getSource().sendSuccess(Component.literal("no fronter"), false); 156 | } 157 | } else { 158 | throw NO_SYSTEM.create(); 159 | } 160 | } 161 | 162 | return Command.SINGLE_SUCCESS; 163 | 164 | } 165 | 166 | private static int autoProxy(CommandContext ctx) throws CommandSyntaxException { 167 | if (ctx.getSource().getPlayer() instanceof PluralPlayer player) { 168 | PluralSystem system = player.getSystem(); 169 | 170 | 171 | if(system != null) { 172 | var arg = EnumArgumentType.getEnumConstant(ctx, "type", PluralSystem.AutoProxyType.class); 173 | 174 | system.setAutoProxy(arg); 175 | ctx.getSource().sendSuccess(Component.literal("autoproxy is %s".formatted(arg.toString().toLowerCase())), false); 176 | } else { 177 | throw NO_SYSTEM.create(); 178 | } 179 | } 180 | 181 | return Command.SINGLE_SUCCESS; 182 | 183 | } 184 | 185 | private static int createSystem(CommandContext ctx) { 186 | 187 | if (ctx.getSource().getPlayer() instanceof PluralPlayer player) { 188 | PluralSystem system = player.getSystem(); 189 | 190 | if( system == null) { 191 | 192 | player.createSystem(); 193 | ctx.getSource().sendSuccess(Component.literal("created system"), false); 194 | 195 | } else { 196 | ctx.getSource().sendFailure(Component.literal("system already exists!")); 197 | } 198 | } 199 | 200 | return Command.SINGLE_SUCCESS; 201 | } 202 | 203 | private static int listProxies(CommandContext ctx) throws CommandSyntaxException { 204 | 205 | if (ctx.getSource().getPlayer() instanceof PluralPlayer player) { 206 | PluralSystem system = player.getSystem(); 207 | 208 | 209 | if(system != null) { 210 | var component = Component.literal("proxies:"); 211 | 212 | String name = StringArgumentType.getString(ctx, "member"); 213 | var member = system.getMember(name); 214 | 215 | if (member != null) { 216 | if (member.proxyTags.size() == 0) { 217 | component.append(Component.literal("\n ")); 218 | } else { 219 | for(Member.ProxyTag tag : member.proxyTags) { 220 | component.append(Component.literal("\n %stext%s".formatted(tag.prefix, tag.suffix))); 221 | } 222 | } 223 | 224 | } else { 225 | throw NO_MEMBER.create(name); 226 | } 227 | 228 | ctx.getSource().sendSuccess(component, false); 229 | } else { 230 | throw NO_SYSTEM.create(); 231 | } 232 | } 233 | 234 | return Command.SINGLE_SUCCESS; 235 | } 236 | 237 | private static int removeProxy(CommandContext ctx) throws CommandSyntaxException { 238 | 239 | if (ctx.getSource().getPlayer() instanceof PluralPlayer player) { 240 | PluralSystem system = player.getSystem(); 241 | 242 | 243 | if( system != null) { 244 | 245 | String name = StringArgumentType.getString(ctx, "member"); 246 | var member = system.getMember(name); 247 | 248 | var proxy = StringArgumentType.getString(ctx, "tag"); 249 | 250 | if (member != null) { 251 | for(Member.ProxyTag tag : member.proxyTags) { 252 | if (proxy.equals(tag.raw())) { 253 | member.proxyTags.remove(tag); 254 | ctx.getSource().sendSuccess(Component.literal("removed proxy %s".formatted(proxy)), false); 255 | return Command.SINGLE_SUCCESS; 256 | } 257 | } 258 | throw PROXY_NOT_FOUND.create(proxy); 259 | } else { 260 | throw NO_MEMBER.create(name); 261 | } 262 | } else { 263 | throw NO_SYSTEM.create(); 264 | } 265 | } 266 | 267 | return Command.SINGLE_SUCCESS; 268 | } 269 | 270 | private static int addProxy(CommandContext ctx) throws CommandSyntaxException { 271 | if (ctx.getSource().getPlayer() instanceof PluralPlayer player) { 272 | PluralSystem system = player.getSystem(); 273 | 274 | 275 | if( system != null) { 276 | String name = StringArgumentType.getString(ctx, "member"); 277 | var member = system.getMember(name); 278 | 279 | var proxy = StringArgumentType.getString(ctx, "tag"); 280 | 281 | if (member != null) { 282 | Member.ProxyTag tag = Member.ProxyTag.parse(proxy); 283 | 284 | if (tag != null) { 285 | ctx.getSource().sendSuccess(Component.literal("added proxy %s".formatted(proxy)), false); 286 | member.proxyTags.add(tag); 287 | } else { 288 | throw INVALID_PROXY.create(proxy); 289 | } 290 | } else { 291 | throw NO_MEMBER.create(name); 292 | } 293 | } else { 294 | throw NO_SYSTEM.create(); 295 | } 296 | } 297 | 298 | return Command.SINGLE_SUCCESS; 299 | 300 | } 301 | 302 | private static int listMembers(CommandContext ctx) throws CommandSyntaxException { 303 | 304 | if (ctx.getSource().getPlayer() instanceof PluralPlayer player) { 305 | PluralSystem system = player.getSystem(); 306 | 307 | if( system != null) { 308 | var component = Component.literal("members: "); 309 | 310 | for (Member member : system) { 311 | component.append(Component.literal("\n %s".formatted(member.getDisplayName()))); 312 | } 313 | 314 | ctx.getSource().getPlayer().sendSystemMessage(component); 315 | } else { 316 | System.out.println("NO SYSTEM"); 317 | throw NO_SYSTEM.create(); 318 | } 319 | } 320 | 321 | return Command.SINGLE_SUCCESS; 322 | } 323 | 324 | 325 | private static int setDisplayName(CommandContext ctx) throws CommandSyntaxException { 326 | 327 | if (ctx.getSource().getPlayer() instanceof PluralPlayer player) { 328 | PluralSystem system = player.getSystem(); 329 | 330 | if( system != null) { 331 | Member member = system.getMember(StringArgumentType.getString(ctx, "member")); 332 | 333 | var display = StringArgumentType.getString(ctx, "display_name"); 334 | 335 | member.setDisplayName(display); 336 | 337 | ctx.getSource().sendSuccess(Component.literal("set display name to %s".formatted(display)), false); 338 | } else { 339 | System.out.println("NO SYSTEM"); 340 | throw NO_SYSTEM.create(); 341 | } 342 | } 343 | 344 | return Command.SINGLE_SUCCESS; 345 | } 346 | 347 | private static int getDisplayName(CommandContext ctx) throws CommandSyntaxException { 348 | 349 | if (ctx.getSource().getPlayer() instanceof PluralPlayer player) { 350 | PluralSystem system = player.getSystem(); 351 | 352 | if( system != null) { 353 | String name = StringArgumentType.getString(ctx, "member"); 354 | var member = system.getMember(name); 355 | 356 | if (member != null) { 357 | ctx.getSource().sendSuccess(Component.literal(member.getDisplayName()), false); 358 | } else { 359 | throw NO_MEMBER.create(name); 360 | } 361 | } else { 362 | System.out.println("NO SYSTEM"); 363 | throw NO_SYSTEM.create(); 364 | } 365 | } 366 | 367 | return Command.SINGLE_SUCCESS; 368 | } 369 | 370 | private static int newMember(CommandContext ctx) throws CommandSyntaxException { 371 | 372 | if (ctx.getSource().getPlayer() instanceof PluralPlayer player) { 373 | PluralSystem system = player.getSystem(); 374 | 375 | if( system != null) { 376 | String name = StringArgumentType.getString(ctx, "member"); 377 | 378 | system.createMember(name); 379 | ctx.getSource().sendSuccess(Component.literal("created member %s".formatted(name)), false); 380 | } else { 381 | System.out.println("NO SYSTEM"); 382 | throw NO_SYSTEM.create(); 383 | } 384 | } 385 | 386 | return Command.SINGLE_SUCCESS; 387 | } 388 | 389 | private static int deleteMember(CommandContext ctx) throws CommandSyntaxException { 390 | 391 | if (ctx.getSource().getPlayer() instanceof PluralPlayer player) { 392 | PluralSystem system = player.getSystem(); 393 | 394 | if( system != null) { 395 | String name = StringArgumentType.getString(ctx, "member"); 396 | 397 | if (system.deleteMember(name)) { 398 | ctx.getSource().sendSuccess(Component.literal("deleted member %s".formatted(name)), false); 399 | } else { 400 | throw NO_MEMBER.create(name); 401 | } 402 | } else { 403 | System.out.println("NO SYSTEM"); 404 | throw NO_SYSTEM.create(); 405 | } 406 | } 407 | 408 | return Command.SINGLE_SUCCESS; 409 | } 410 | 411 | } 412 | --------------------------------------------------------------------------------