A Minecraft server-side mod that adds various teleportation related commands
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

WIP 1.4.0, Started implementation of config file

MrSnowy 5b11df65 b93c4258

+568 -311
+11
CHANGELOG.md
··· 4 4 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 5 5 and this project kind of adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 6 7 + ### WIP [1.4.0] 8 + #### Changed 9 + - The loading logic of the storage file 10 + 11 + #### Added 12 + - **a config file!** 13 + - The config file starts at **version 0**! 14 + - versioning to the storage file for better handling of migrations. 15 + - We start at **version 1**, all previous version are version 0. 16 + 17 + 7 18 ### [1.3.4] 8 19 - Updated Italian translations (Thanks to [Jump-333](https://github.com/Jump-333)!) 9 20 - Turned DeathLocation into a Hashmap
+17 -14
README.md
··· 4 4 5 5 Here is the [Changelog](https://github.com/MrSn0wy/TeleportCommands/blob/main/CHANGELOG.md) 6 6 7 - ### Currently available commands: 7 + ## Currently available commands: 8 8 9 9 - `/worldspawn [<Disable Safety>]` - Teleports you to the world spawn (in the overworld), if given true it will not do safety checks 10 10 - `/back [<Disable Safety>]` - Teleports you to the location where you last died, if given true it will not do safety checks ··· 32 32 33 33 <br> 34 34 35 - ### TODO: 36 - #### Planned commands: 35 + ## TODO: 36 + ### Planned commands: 37 37 - [ ] `/wild` - Teleports you to a random location in the Overworld 38 38 - [x] `/worldspawn` - Teleports you to the worldspawn 39 - - [ ] `/spawn <dimension>` - Teleports you to your spawnpoint in a dimension, defaults to your current dimension 39 + - [ ] `/spawn <dimension>` - Teleports you to your spawn point in a dimension, defaults to your current dimension 40 40 - [ ] `/previous` - Go to the last teleported location 41 41 42 - #### Improvements: 42 + ### Improvements: 43 43 - [ ] Look into changing the mod into the more safe and sane kotlin (I love java) 44 44 - [ ] Add game tests 45 45 - [ ] Find the easiest way to backport the mod to older version (help) ··· 56 56 - [x] Add Quilt support and NeoForge 57 57 58 58 59 - ### Want to help? 59 + ## Want to help? 60 60 1. You can create a translation file so other people can use the mod in their native language: [translations.md](./common/src/main/resources/assets/teleport_commands/lang/translations.md) 61 61 62 62 63 - ### How to build 64 - #### Getting the correct environment 63 + ## How to build 64 + ### Getting the correct environment 65 + #### Nixos 65 66 If you are on nixos you can simply go into the folder of where you cloned the repo, and run `nix develop .`. This will give you the environment I use (apart from the IDE) :3. 66 67 68 + #### Generic linux 67 69 On any other linux distro, just install the jetbrains jdk, or try openjdk21. 68 70 69 - On windows probably go to the openjdk website and install the 21 version? idk goodluck. 71 + #### Windows 72 + On windows probably go to the openjdk website and install the 21 version? Idk goodluck. 70 73 71 - #### Building 72 - Then on linux just do `./gradlew build` and to make it in a single mod jar `./gradlew mergeJars`. 74 + ### Building 75 + Then on linux just run `./gradlew build`. To make it into a single mod jar just run `./gradlew mergeJars`. 73 76 74 77 Or on windows, just do `.\gradlew.bat build` and `.\gradlew.bat mergeJars`. 75 78 Note that this isn't tested for windows, but I think that is how it works. 76 79 77 - #### Getting the jars 80 + ### Getting the jars 78 81 Then you can find your jars in `fabric/build/libs/` (for fabric), `neoforge/build/libs/` (for neoforge) or `merged/build/libs/` (if you made the merged jar file). 79 82 80 83 If you have any issues just make an issue or contact me on Discord `@mrsnowy_` 81 84 82 - ### Notes 83 - Colors: 85 + ## Random notes lol 86 + Text Colors: 84 87 - Green = When something succeeds and an action will happen 85 88 - Aqua = When something needs attention 86 89 - White = When something is done
+1 -1
common/build.gradle
··· 37 37 artifacts { 38 38 commonJava sourceSets.main.java.sourceDirectories.singleFile 39 39 commonResources sourceSets.main.resources.sourceDirectories.singleFile 40 - } 40 + }
+5 -181
common/src/main/java/dev/mrsnowy/teleport_commands/TeleportCommands.java
··· 5 5 import dev.mrsnowy.teleport_commands.storage.StorageManager; 6 6 import dev.mrsnowy.teleport_commands.commands.*; 7 7 import dev.mrsnowy.teleport_commands.storage.DeathLocationStorage; 8 + import dev.mrsnowy.teleport_commands.storage.ConfigManager; 8 9 import net.minecraft.commands.CommandSourceStack; 9 - import net.minecraft.commands.Commands; 10 10 import net.minecraft.server.MinecraftServer; 11 11 import net.minecraft.server.level.ServerPlayer; 12 12 import net.minecraft.world.level.storage.LevelResource; 13 - 13 + import net.minecraft.core.BlockPos; 14 14 import java.io.*; 15 - import java.nio.file.Files; 16 15 import java.nio.file.Path; 17 16 import java.nio.file.Paths; 18 - import java.nio.file.StandardOpenOption; 19 - 20 - import net.minecraft.core.BlockPos; 21 - 22 - import static dev.mrsnowy.teleport_commands.storage.StorageManager.*; 23 17 24 18 public class TeleportCommands { 25 19 public static String MOD_LOADER; ··· 36 30 CONFIG_DIR = Paths.get(System.getProperty("user.dir")).resolve("config"); // Construct the game directory path 37 31 SERVER = server; 38 32 39 - StorageManager.STORAGE = storageValidator(); // Initialize the storage file 33 + StorageManager.StorageInit(); // Initialize the storage file 34 + ConfigManager.ConfigInit(); 40 35 DeathLocationStorage.clearDeathLocations(); // Clear data of death locations. 41 - 42 36 } 43 37 44 38 // initialize commands, also allows me to easily disable any when there is a config ··· 48 42 tpa.register(dispatcher); 49 43 warp.register(dispatcher); 50 44 worldspawn.register(dispatcher); 45 + main.register(dispatcher); 51 46 } 52 47 53 48 // Runs when the playerDeath mixin calls it, updates the /back command position ··· 58 53 59 54 DeathLocationStorage.setDeathLocation(uuid, pos, world); 60 55 } 61 - 62 - // cleans and updates Storage to the newest "version". This is painful 63 - private static StorageClass storageValidator() { 64 - Constants.LOGGER.info("Cleaning and updating Storage!"); 65 - 66 - try { 67 - StorageInit(); 68 - 69 - long startFileSize = Files.size(StorageManager.STORAGE_FILE); 70 - 71 - FileReader reader = new FileReader(StorageManager.STORAGE_FILE.toString()); 72 - JsonElement jsonElement = JsonParser.parseReader(reader); 73 - 74 - if (jsonElement.isJsonObject()) { 75 - 76 - JsonObject mainJsonObject = jsonElement.getAsJsonObject(); 77 - JsonArray newWarpsArray = new JsonArray(); 78 - JsonArray newPlayersArray = new JsonArray(); 79 - 80 - // get the Warps list 81 - if (mainJsonObject.has("Warps") && mainJsonObject.get("Warps").isJsonArray()) { 82 - 83 - // Warps 84 - for (JsonElement warpElement : mainJsonObject.get("Warps").getAsJsonArray()) { 85 - 86 - // Warp 87 - if (warpElement.isJsonObject()) { 88 - JsonObject warp = warpElement.getAsJsonObject(); 89 - 90 - String warpName = warp.has("name") ? warp.get("name").getAsString() : ""; 91 - Integer warpX = warp.has("x") ? warp.get("x").getAsInt() : null; 92 - Integer warpY = warp.has("y") ? warp.get("y").getAsInt() : null; 93 - Integer warpZ = warp.has("z") ? warp.get("z").getAsInt() : null; 94 - String warpWorld = warp.has("world") ? warp.get("world").getAsString() : ""; 95 - 96 - // check if it is valid 97 - if (!warpName.isBlank() && !warpWorld.isBlank() && warpX != null && warpY != null && warpZ != null) { 98 - JsonObject newWarp = new JsonObject(); 99 - 100 - newWarp.addProperty("name", warpName); 101 - newWarp.addProperty("x", warpX); 102 - newWarp.addProperty("y", warpY); 103 - newWarp.addProperty("z", warpZ); 104 - newWarp.addProperty("world", warpWorld); 105 - 106 - newWarpsArray.add(newWarp); 107 - } 108 - } 109 - } 110 - } 111 - 112 - 113 - // get the Players list 114 - if (mainJsonObject.has("Players") && mainJsonObject.get("Players").isJsonArray()) { 115 - 116 - // players 117 - for (JsonElement playerElement : mainJsonObject.get("Players").getAsJsonArray()) { 118 - 119 - // player 120 - if (playerElement.isJsonObject()) { 121 - 122 - JsonObject player = playerElement.getAsJsonObject(); 123 - boolean hasInformation = false; 124 - 125 - String UUID = player.has("Player_UUID") 126 - ? player.get("Player_UUID").getAsString() : (player.has("UUID") 127 - ? player.get("UUID").getAsString() : null); 128 - 129 - String DefaultHome = player.has("DefaultHome") 130 - ? player.get("DefaultHome").getAsString() : ""; 131 - 132 - JsonArray homes = new JsonArray(); 133 - 134 - if (player.has("Homes") && player.get("Homes").isJsonArray() ) { 135 - JsonArray tempHomes = player.get("Homes").getAsJsonArray(); 136 - boolean defaultHomeFound = false; 137 - 138 - 139 - for (JsonElement homeElement : tempHomes) { 140 - if (homeElement.isJsonObject()) { 141 - JsonObject home = homeElement.getAsJsonObject(); 142 - 143 - String homeName = home.has("name") 144 - ? home.get("name").getAsString() : ""; 145 - 146 - // upgrade doubles to int 147 - Integer homeX = home.has("x") && home.get("x").isJsonPrimitive() && home.get("x").getAsJsonPrimitive().isNumber() 148 - ? (int) Math.floor(home.get("x").getAsDouble()) : null; 149 - 150 - Integer homeY = home.has("y") && home.get("y").isJsonPrimitive() && home.get("y").getAsJsonPrimitive().isNumber() 151 - ? (int) Math.floor(home.get("y").getAsDouble()) : null; 152 - 153 - Integer homeZ = home.has("z") && home.get("z").isJsonPrimitive() && home.get("z").getAsJsonPrimitive().isNumber() 154 - ? (int) Math.floor(home.get("z").getAsDouble()) : null; 155 - 156 - String homeWorld = home.has("world") 157 - ? home.get("world").getAsString() : ""; 158 - 159 - // check if it is valid 160 - if (!homeName.isBlank() && !homeWorld.isBlank() && homeX != null && homeY != null && homeZ != null) { 161 - 162 - // check if it is the default home 163 - if (!DefaultHome.isBlank() && homeName.equals(DefaultHome)) { 164 - defaultHomeFound = true; 165 - } 166 - 167 - JsonObject newHome = new JsonObject(); 168 - 169 - newHome.addProperty("name", homeName); 170 - newHome.addProperty("x", homeX); 171 - newHome.addProperty("y", homeY); 172 - newHome.addProperty("z", homeZ); 173 - newHome.addProperty("world", homeWorld); 174 - 175 - homes.add(newHome); 176 - hasInformation = true; 177 - } 178 - } 179 - } 180 - 181 - // clean DefaultHome if there is no home with the name 182 - if (!defaultHomeFound) { 183 - DefaultHome = ""; 184 - } 185 - } 186 - 187 - // if it isn't empty it gets added to the newPlayersArray 188 - if ((UUID != null && !UUID.isBlank()) && hasInformation) { 189 - 190 - JsonObject newPlayer = new JsonObject(); 191 - 192 - newPlayer.addProperty("UUID", UUID); 193 - newPlayer.addProperty("DefaultHome", DefaultHome); 194 - newPlayer.add("Homes", homes); 195 - 196 - newPlayersArray.add(newPlayer); 197 - } 198 - } 199 - } 200 - } 201 - 202 - // save the cleaned and updated file 203 - mainJsonObject.remove("Warps"); 204 - mainJsonObject.add("Warps", newWarpsArray); 205 - 206 - mainJsonObject.remove("Players"); 207 - mainJsonObject.add("Players", newPlayersArray); 208 - 209 - // save the cleaned database 210 - Gson gson = new GsonBuilder().create(); 211 - byte[] json = gson.toJson(mainJsonObject).getBytes(); 212 - Files.write(StorageManager.STORAGE_FILE, json, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING); 213 - 214 - // Only show amount cleaned when it isn't 0B lool 215 - int diff = Math.round(( startFileSize - Files.size(StorageManager.STORAGE_FILE) )); 216 - 217 - if (diff > 0) { 218 - Constants.LOGGER.info("Success! Cleaned: {}B", diff); 219 - } else { 220 - Constants.LOGGER.info("Success!"); 221 - } 222 - 223 - return gson.fromJson(mainJsonObject, StorageManager.StorageClass.class); 224 - } 225 - 226 - } catch (IOException e) { 227 - Constants.LOGGER.error("Error while cleaning the database!", e); 228 - } 229 - 230 - return null; 231 - } 232 56 }
+138 -76
common/src/main/java/dev/mrsnowy/teleport_commands/commands/main.java
··· 1 - //package dev.mrsnowy.teleport_commands.commands; 2 - // 3 - //import com.mojang.brigadier.arguments.BoolArgumentType; 4 - //import dev.mrsnowy.teleport_commands.TeleportCommands; 5 - //import dev.mrsnowy.teleport_commands.common.DeathLocation; 6 - //import dev.mrsnowy.teleport_commands.storage.DeathLocationStorage; 7 - //import net.minecraft.ChatFormatting; 8 - //import net.minecraft.commands.Commands; 9 - //import net.minecraft.core.BlockPos; 10 - //import net.minecraft.network.chat.ClickEvent; 11 - //import net.minecraft.network.chat.Component; 12 - //import net.minecraft.server.level.ServerLevel; 13 - //import net.minecraft.server.level.ServerPlayer; 14 - //import net.minecraft.world.phys.Vec3; 15 - // 16 - //import java.util.Optional; 17 - // 18 - //import static dev.mrsnowy.teleport_commands.utils.tools.*; 19 - //import static net.minecraft.commands.Commands.argument; 20 - // 21 - //// TODO! add option to reload registered commands! 22 - // 23 - //public class main { 24 - // 25 - // public static void register(Commands commandManager) { 26 - // 27 - // commandManager.getDispatcher().register(Commands.literal("teleportcommands") 28 - // .then(Commands.literal("help") 29 - // .requires(source -> source.getPlayer() != null) 30 - // .executes(context -> { 31 - // final ServerPlayer player = context.getSource().getPlayerOrException(); 32 - // 33 - // try { 34 - // printCommands(player); 35 - // 36 - // } catch (Exception e) { 37 - // TeleportCommands.LOGGER.error("Error while going back! => ", e); 38 - // player.displayClientMessage(getTranslatedText("commands.teleport_commands.common.error", player).withStyle(ChatFormatting.RED, ChatFormatting.BOLD), true); 39 - // return 1; 40 - // } 41 - // return 0; 42 - // })) 43 - // .then(argument("Disable Safety", BoolArgumentType.bool()) 44 - // .requires(source -> source.getPlayer() != null) 45 - // .executes(context -> { 46 - // final boolean safety = BoolArgumentType.getBool(context, "Disable Safety"); 47 - // final ServerPlayer player = context.getSource().getPlayerOrException(); 48 - // 49 - // try { 50 - //// ToDeathLocation(player, safety); 51 - // 52 - // } catch (Exception e) { 53 - // TeleportCommands.LOGGER.error("Error while going back! => ", e); 54 - // player.displayClientMessage(getTranslatedText("commands.teleport_commands.common.error", player).withStyle(ChatFormatting.RED, ChatFormatting.BOLD), true); 55 - // return 1; 56 - // } 57 - // return 0; 58 - // })) 59 - // ); 60 - // } 61 - // 62 - // 63 - // // ----- 64 - // 65 - // 66 - // // Gets the DeathLocation of the player and teleports the player to it 67 - // private static void printCommands(ServerPlayer player) throws Exception { 68 - // 69 - // player.displayClientMessage(Component.literal("Thank you for using Teleport Commands (V)!").withStyle(ChatFormatting.AQUA), false); 70 - // player.displayClientMessage(Component.literal("Teleport Commands is a server-side mod that adds various teleportation related commands").withStyle(ChatFormatting.AQUA), false); 71 - // 72 - // player.displayClientMessage(Component.literal("----").withStyle(ChatFormatting.AQUA), false); 73 - // 74 - // player.displayClientMessage(Component.literal("Usage:").withStyle(ChatFormatting.AQUA), false); 75 - // } 76 - //} 1 + package dev.mrsnowy.teleport_commands.commands; 2 + 3 + import com.mojang.brigadier.CommandDispatcher; 4 + import com.mojang.brigadier.arguments.StringArgumentType; 5 + import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; 6 + import com.mojang.brigadier.suggestion.SuggestionProvider; 7 + import dev.mrsnowy.teleport_commands.Constants; 8 + import dev.mrsnowy.teleport_commands.TeleportCommands; 9 + import dev.mrsnowy.teleport_commands.storage.ConfigManager; 10 + import net.minecraft.ChatFormatting; 11 + import net.minecraft.commands.CommandSourceStack; 12 + import net.minecraft.commands.Commands; 13 + import net.minecraft.commands.SharedSuggestionProvider; 14 + import net.minecraft.network.chat.Component; 15 + import net.minecraft.network.chat.MutableComponent; 16 + import net.minecraft.server.level.ServerPlayer; 17 + 18 + import java.util.Arrays; 19 + 20 + import static dev.mrsnowy.teleport_commands.utils.tools.*; 21 + 22 + public class main { 23 + 24 + // TODO! Make this automatically generate based on the commands we have 25 + private static final String[] available_commands = { 26 + "back", 27 + "home", 28 + "tpa", 29 + "warp", 30 + "worldspawn" 31 + }; 32 + 33 + // sum lists 34 + private static final String[] enabled_commands = available_commands; // TODO! get enabled commands 35 + private static final String[] disabled_commands = available_commands; // TODO! get disabled commands 36 + 37 + // Create sum suggestion providers 38 + private static final SuggestionProvider<CommandSourceStack> disabled_commands_suggester = (context, builder) -> SharedSuggestionProvider.suggest(disabled_commands, builder); 39 + private static final SuggestionProvider<CommandSourceStack> enabled_commands_suggester = (context, builder) -> SharedSuggestionProvider.suggest(enabled_commands, builder); 40 + 41 + 42 + public static void register(CommandDispatcher<CommandSourceStack> dispatcher) { 43 + 44 + dispatcher.register(Commands.literal("teleportcommands") 45 + .then(Commands.literal("reloadConfig") 46 + .executes(context -> { 47 + try { 48 + ConfigManager.ConfigLoader(); 49 + } catch (Exception e) { 50 + Constants.LOGGER.error("Failed to reload config!", e); 51 + throw new SimpleCommandExceptionType(Component.literal(e.toString())).create(); 52 + } 53 + return 0; 54 + })) 55 + .then(Commands.literal("disable") 56 + .then(Commands.argument("command", StringArgumentType.word()) 57 + .suggests(enabled_commands_suggester) 58 + .requires(source -> source.hasPermission(4)) // Require OP 59 + .requires(source -> source.getPlayer() != null) 60 + .executes(context -> { 61 + final ServerPlayer player = context.getSource().getPlayerOrException(); 62 + final String string = StringArgumentType.getString(context, "command"); 63 + 64 + // todo! maybe move this check outside for reusability? 65 + if (!Arrays.asList(enabled_commands).contains(string)) { 66 + // TODO! make this translatable 67 + throw new SimpleCommandExceptionType(Component.literal("\"" + string + "\" is not available as an command!").withStyle(ChatFormatting.RED, ChatFormatting.BOLD)).create(); 68 + } 69 + 70 + try { 71 + player.displayClientMessage(Component.literal("meow " + string), true); 72 + 73 + } catch (Exception e) { 74 + Constants.LOGGER.error("Error while disabling a command! => ", e); 75 + // TODO replace the error below with something normal? 76 + throw new SimpleCommandExceptionType(getTranslatedText("commands.teleport_commands.common.error", player).withStyle(ChatFormatting.RED, ChatFormatting.BOLD)).create(); 77 + } 78 + return 0; 79 + }) 80 + )) 81 + .then(Commands.literal("enable") 82 + .then(Commands.argument("command", StringArgumentType.word()) 83 + .suggests(disabled_commands_suggester) 84 + .requires(source -> source.hasPermission(4)) // Require OP 85 + .requires(source -> source.getPlayer() != null) 86 + .executes(context -> { 87 + final ServerPlayer player = context.getSource().getPlayerOrException(); 88 + final String string = StringArgumentType.getString(context, "command"); 89 + 90 + // todo! maybe move this check outside for reusability? 91 + if (!Arrays.asList(disabled_commands).contains(string)) { 92 + // TODO! make this translatable 93 + throw new SimpleCommandExceptionType(Component.literal("\"" + string + "\" is not available as an command!")).create(); 94 + } 95 + 96 + try { 97 + player.displayClientMessage(Component.literal("meow " + string), true); 98 + 99 + } catch (Exception e) { 100 + Constants.LOGGER.error("Error while disabling a command! => ", e); 101 + // TODO replace the error below with something normal? 102 + throw new SimpleCommandExceptionType(getTranslatedText("commands.teleport_commands.common.error", player).withStyle(ChatFormatting.RED, ChatFormatting.BOLD)).create(); 103 + } 104 + return 0; 105 + }) 106 + )) 107 + // Todo! Is this still needed? 108 + .then(Commands.literal("reload") 109 + .requires(source -> source.hasPermission(4)) // Require OP 110 + .executes(context -> { 111 + TeleportCommands.registerCommands(context.getSource().dispatcher()); 112 + return 0; 113 + })) 114 + 115 + .then(Commands.literal("help") 116 + .executes(context -> { 117 + context.getSource().sendSuccess(main::printCommands, false); 118 + return 0; 119 + })) 120 + ); 121 + } 122 + 123 + 124 + // ----- 125 + 126 + 127 + private static MutableComponent printCommands() { 128 + MutableComponent message = Component.empty(); 129 + 130 + message.append(Component.literal("Thank you for using Teleport Commands (V" + Constants.VERSION + ")!").withStyle(ChatFormatting.AQUA)); 131 + message.append(Component.literal("Teleport Commands is a server-side mod that adds various teleportation related commands").withStyle(ChatFormatting.AQUA)); 132 + 133 + message.append(Component.literal("----").withStyle(ChatFormatting.AQUA)); 134 + message.append(Component.literal("Usage:").withStyle(ChatFormatting.AQUA)); 135 + 136 + return message; 137 + } 138 + }
+9
common/src/main/java/dev/mrsnowy/teleport_commands/common/ModCommand.java
··· 1 + package dev.mrsnowy.teleport_commands.common; 2 + 3 + public enum ModCommand { 4 + back, 5 + home, 6 + tpa, 7 + warp, 8 + worldspawn, 9 + }
-1
common/src/main/java/dev/mrsnowy/teleport_commands/common/Player.java
··· 1 1 package dev.mrsnowy.teleport_commands.common; 2 2 3 3 import dev.mrsnowy.teleport_commands.storage.StorageManager; 4 - import net.minecraft.core.BlockPos; 5 4 6 5 import java.util.ArrayList; 7 6 import java.util.List;
+252
common/src/main/java/dev/mrsnowy/teleport_commands/storage/ConfigManager.java
··· 1 + package dev.mrsnowy.teleport_commands.storage; 2 + 3 + import com.google.gson.*; 4 + import dev.mrsnowy.teleport_commands.Constants; 5 + import dev.mrsnowy.teleport_commands.TeleportCommands; 6 + 7 + import java.io.FileReader; 8 + import java.nio.file.Files; 9 + import java.nio.file.Path; 10 + import java.nio.file.StandardOpenOption; 11 + 12 + public class ConfigManager { 13 + public static Path CONFIG_FILE; 14 + public static ConfigClass CONFIG; 15 + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); 16 + private static final int defaultVersion = new ConfigClass().getVersion(); 17 + 18 + public static void ConfigInit() { 19 + CONFIG_FILE = TeleportCommands.CONFIG_DIR.resolve("teleport_commands.json"); 20 + 21 + try { 22 + ConfigLoader(); 23 + 24 + } catch (Exception e) { 25 + // crashing is probably better here, otherwise the whole mod will be broken 26 + Constants.LOGGER.error("Error while initializing the config file! Exiting! => ", e); 27 + throw new RuntimeException("Error while initializing the config file! Exiting! => ", e); 28 + } 29 + } 30 + 31 + public static void ConfigLoader() throws Exception { 32 + if (!CONFIG_FILE.toFile().exists() || CONFIG_FILE.toFile().length() == 0) { 33 + Files.createDirectories(TeleportCommands.CONFIG_DIR); 34 + 35 + Constants.LOGGER.warn("Config file was not found or was empty! Initializing config"); 36 + CONFIG = new ConfigClass(); 37 + ConfigSaver(); 38 + Constants.LOGGER.info("Config created successfully!"); 39 + } 40 + 41 + ConfigMigrator(); 42 + 43 + FileReader reader = new FileReader(CONFIG_FILE.toFile()); 44 + CONFIG = GSON.fromJson(reader, ConfigClass.class); 45 + if (CONFIG == null) { 46 + Constants.LOGGER.warn("Config file was empty! Loading defaults..."); 47 + CONFIG = new ConfigClass(); 48 + ConfigSaver(); 49 + } 50 + 51 + ConfigSaver(); // Save it so any missing values get added to the file. 52 + Constants.LOGGER.info("Config loaded successfully!"); 53 + } 54 + 55 + /// This function checks what version the config file is and migrates it to the current version of the mod. 56 + public static void ConfigMigrator() throws Exception { 57 + FileReader reader = new FileReader(CONFIG_FILE.toFile()); 58 + JsonObject jsonObject = GSON.fromJson(reader, JsonObject.class); 59 + 60 + int version = jsonObject.has("version") ? jsonObject.get("version").getAsInt() : 0; 61 + 62 + if (version < defaultVersion) { 63 + // Constants.LOGGER.warn("Config file is v{}, migrating to v{}!", version, defaultVersion); 64 + // 65 + // // Save the storage :3 66 + // byte[] json = GSON.toJson(jsonObject, JsonArray.class).getBytes(); 67 + // Files.write(CONFIG_FILE, json, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE); 68 + // 69 + // Constants.LOGGER.info("Config file migrated to v{} successfully!", defaultVersion); 70 + } else if (version > defaultVersion) { 71 + String message = String.format("Teleport Commands: The config file's version is newer than the supported version, found v%s, expected <= v%s.\n" + 72 + "If you intentionally backported then you can attempt to downgrade the config file located at this location: \"%s\".\n", 73 + version, defaultVersion, CONFIG_FILE.toAbsolutePath()); 74 + 75 + throw new IllegalStateException(message); 76 + } 77 + } 78 + 79 + public static void ConfigSaver() throws Exception { 80 + // todo! maybe throttle saves? 81 + byte[] json = GSON.toJson( ConfigManager.CONFIG ).getBytes(); 82 + 83 + Files.write(CONFIG_FILE, json, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE); 84 + } 85 + 86 + public static class ConfigClass { 87 + private final int version = 0; 88 + public Teleporting teleporting = new Teleporting(); 89 + public Back back = new Back(); 90 + public Home home = new Home(); 91 + public Tpa tpa = new Tpa(); 92 + public Warp warp = new Warp(); 93 + public WorldSpawn worldSpawn = new WorldSpawn(); 94 + 95 + public int getVersion() { 96 + return version; 97 + } 98 + 99 + public static final class Teleporting { 100 + private int delay = 5; 101 + private boolean whileMoving = true; 102 + private boolean whileFighting = false; 103 + private int fightCooldown = 10; 104 + 105 + public int getDelay() { 106 + return delay; 107 + } 108 + 109 + public void setDelay(int delay) { 110 + this.delay = delay; 111 + } 112 + 113 + public boolean isWhileMoving() { 114 + return whileMoving; 115 + } 116 + 117 + public void setWhileMoving(boolean whileMoving) { 118 + this.whileMoving = whileMoving; 119 + } 120 + 121 + public boolean isWhileFighting() { 122 + return whileFighting; 123 + } 124 + 125 + public void setWhileFighting(boolean whileFighting) { 126 + this.whileFighting = whileFighting; 127 + } 128 + 129 + public int getFightCooldown() { 130 + return fightCooldown; 131 + } 132 + 133 + public void setFightCooldown(int fightCooldown) { 134 + this.fightCooldown = fightCooldown; 135 + } 136 + } 137 + 138 + public final class Back { 139 + private boolean enabled = true; 140 + private boolean deleteAfterTeleport = false; 141 + 142 + public boolean isEnabled() { 143 + return enabled; 144 + } 145 + 146 + public void setEnabled(boolean enabled) { 147 + this.enabled = enabled; 148 + } 149 + 150 + public boolean isDeleteAfterTeleport() { 151 + return deleteAfterTeleport; 152 + } 153 + 154 + public void setDeleteAfterTeleport(boolean deleteAfterTeleport) { 155 + this.deleteAfterTeleport = deleteAfterTeleport; 156 + } 157 + } 158 + 159 + public final class Home { 160 + private boolean enabled = true; 161 + private int playerMaximum = 20; 162 + private boolean deleteInvalid = false; 163 + 164 + public boolean isEnabled() { 165 + return enabled; 166 + } 167 + 168 + public void setEnabled(boolean enabled) { 169 + this.enabled = enabled; 170 + } 171 + 172 + public int getPlayerMaximum() { 173 + return playerMaximum; 174 + } 175 + 176 + public void setPlayerMaximum(int playerMaximum) { 177 + this.playerMaximum = playerMaximum; 178 + } 179 + 180 + public boolean isDeleteInvalid() { 181 + return deleteInvalid; 182 + } 183 + 184 + public void setDeleteInvalid(boolean deleteInvalid) { 185 + this.deleteInvalid = deleteInvalid; 186 + } 187 + } 188 + 189 + public final class Tpa { 190 + private boolean enabled = true; 191 + 192 + public boolean isEnabled() { 193 + return enabled; 194 + } 195 + 196 + public void setEnabled(boolean enabled) { 197 + this.enabled = enabled; 198 + } 199 + } 200 + 201 + public final class Warp { 202 + private boolean enabled = true; 203 + private boolean deleteInvalid = false; 204 + 205 + public boolean isEnabled() { 206 + return enabled; 207 + } 208 + 209 + public void setEnabled(boolean enabled) { 210 + this.enabled = enabled; 211 + } 212 + 213 + public boolean isDeleteInvalid() { 214 + return deleteInvalid; 215 + } 216 + 217 + public void setDeleteInvalid(boolean deleteInvalid) { 218 + this.deleteInvalid = deleteInvalid; 219 + } 220 + } 221 + 222 + public final class WorldSpawn { 223 + private boolean enabled = true; 224 + private String world_id = "minecraft:overworld"; 225 + 226 + public boolean isEnabled() { 227 + return enabled; 228 + } 229 + 230 + public void setEnabled(boolean enabled) { 231 + this.enabled = enabled; 232 + } 233 + 234 + public String getWorld_id() { 235 + return world_id; 236 + } 237 + 238 + public void setWorld_id(String world_id) { 239 + this.world_id = world_id; 240 + } 241 + } 242 + } 243 + 244 + // --- Ideas! --- 245 + // Make config options for disabling certain commands 246 + // Make config options for renaming certain commands 247 + // Make config option for changing required permission level for certain commands 248 + // Make config for setting max homes 249 + // Make config that adds a delay between teleports and when fighting. (in teleport function?) 250 + // Make config that automatically deletes namedLocations (warps/homes) with invalid world id's 251 + // Make config for setting the world_id for /worldspawn ? 252 + }
+123 -23
common/src/main/java/dev/mrsnowy/teleport_commands/storage/StorageManager.java
··· 1 1 package dev.mrsnowy.teleport_commands.storage; 2 2 3 - import com.google.gson.Gson; 4 - import com.google.gson.GsonBuilder; 3 + import com.google.gson.*; 5 4 import dev.mrsnowy.teleport_commands.Constants; 6 5 import dev.mrsnowy.teleport_commands.TeleportCommands; 7 6 import dev.mrsnowy.teleport_commands.common.NamedLocation; 8 7 import dev.mrsnowy.teleport_commands.common.Player; 9 8 10 - import java.io.File; 9 + import java.io.FileReader; 11 10 import java.nio.file.Files; 12 11 import java.nio.file.Path; 13 12 import java.nio.file.StandardOpenOption; ··· 22 21 public static Path STORAGE_FOLDER; 23 22 public static Path STORAGE_FILE; 24 23 public static StorageClass STORAGE; 24 + private static final Gson GSON = new GsonBuilder().create(); 25 + private static final int defaultVersion = new StorageClass().getVersion(); 25 26 27 + /// Initializes the StorageManager class and loads the storage from the filesystem. 26 28 public static void StorageInit() { 27 29 STORAGE_FOLDER = TeleportCommands.SAVE_DIR.resolve("TeleportCommands/"); 28 30 STORAGE_FILE = STORAGE_FOLDER.resolve("storage.json"); 29 31 30 32 try { 31 - // check if the folder exists and create it 32 - if (!Files.exists(STORAGE_FOLDER)) { 33 - Files.createDirectories(STORAGE_FOLDER); 34 - } 33 + StorageLoader(); 34 + 35 + } catch (Exception e) { 36 + // crashing is probably better here, otherwise the whole mod will be broken 37 + Constants.LOGGER.error("Error while initializing the storage file! Exiting! => ", e); 38 + throw new RuntimeException("Error while initializing the storage file! Exiting! => ", e); 39 + } 40 + } 41 + 42 + /// Loads the storage from the filesystem 43 + public static void StorageLoader() throws Exception { 44 + 45 + if (!STORAGE_FILE.toFile().exists() || STORAGE_FILE.toFile().length() == 0) { 46 + Constants.LOGGER.warn("Storage file was not found or was empty! Initializing storage"); 47 + 48 + Files.createDirectories(STORAGE_FOLDER); 49 + STORAGE = new StorageClass(); 50 + StorageSaver(); 51 + Constants.LOGGER.info("Storage created successfully!"); 52 + } 53 + 54 + StorageMigrator(); 55 + 56 + FileReader reader = new FileReader(STORAGE_FILE.toFile()); 57 + STORAGE = GSON.fromJson(reader, StorageClass.class); 58 + if (STORAGE == null) { 59 + Constants.LOGGER.warn("Storage file was empty! Initializing storage"); 60 + STORAGE = new StorageClass(); 61 + StorageSaver(); 62 + } 35 63 36 - // check if the file exists and create it 37 - if (!Files.exists(STORAGE_FILE)) { 38 - Files.createFile(STORAGE_FILE); 39 - } 64 + STORAGE.cleanup(); 65 + 66 + StorageSaver(); // Save it so any missing values get added to the file. 67 + Constants.LOGGER.info("Storage loaded successfully!"); 68 + } 40 69 41 - // create the basic storage if it is empty 42 - if (new File(String.valueOf(STORAGE_FILE)).length() == 0) { 43 - StorageManager.STORAGE = new StorageClass(); 44 - StorageSaver(); // todo! verify that it creates em correctly 70 + /// This function checks what version the storage file is and migrates it to the current version of the mod. 71 + public static void StorageMigrator() throws Exception { 72 + FileReader reader = new FileReader(STORAGE_FILE.toFile()); 73 + JsonObject jsonObject = GSON.fromJson(reader, JsonObject.class); 74 + 75 + int version = jsonObject.has("version") ? jsonObject.get("version").getAsInt() : 0; 76 + 77 + if (version < defaultVersion) { 78 + Constants.LOGGER.warn("Storage file is v{}, migrating to v{}!", version, defaultVersion); 79 + 80 + // In v1.1.0 "Player_UUID" got renamed to "UUID". Since the storage file didn't have a version yet, it is set to version 0. 81 + if (version == 0) { 82 + 83 + if (jsonObject.has("Players") && jsonObject.get("Players").isJsonArray()) { 84 + 85 + JsonArray players = jsonObject.get("Players").getAsJsonArray(); 86 + 87 + for (JsonElement playerElement : players) { 88 + JsonObject player = playerElement.getAsJsonObject(); 89 + 90 + String UUID = player.has("Player_UUID") 91 + ? player.get("Player_UUID").getAsString() : (player.has("UUID") 92 + ? player.get("UUID").getAsString() : null); 93 + 94 + if (UUID == null || UUID.isBlank()) { 95 + // remove it then, it's an invalid entry 0.0 96 + players.remove(player); // may return true or false for success, but idc 97 + 98 + } else { 99 + player.remove("Player_UUID"); 100 + player.addProperty("UUID", UUID); 101 + } 102 + } 103 + } 104 + 105 + jsonObject.addProperty("version", 1); 45 106 } 46 107 47 - } catch (Exception e) { 48 - Constants.LOGGER.error("Error while creating the storage file! Exiting! => ", e); 49 - // crashing is probably better here, otherwise the whole mod will be broken 50 - System.exit(1); 108 + // Save the storage :3 109 + byte[] json = GSON.toJson(jsonObject, JsonArray.class).getBytes(); 110 + Files.write(STORAGE_FILE, json, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE); 111 + 112 + Constants.LOGGER.info("Storage file migrated to v{} successfully!", defaultVersion); 113 + } else if (version > defaultVersion) { 114 + String message = String.format("Teleport Commands: The storage file's version is newer than the supported version, found v%s, expected <= v%s.\n" + 115 + "If you intentionally backported then you can attempt to downgrade the storage file located at this location: \"%s\".\n", 116 + version, defaultVersion, STORAGE_FILE.toAbsolutePath()); 117 + 118 + throw new IllegalStateException(message); 51 119 } 52 120 } 53 121 122 + /// Saves the storage to the filesystem 54 123 public static void StorageSaver() throws Exception { 55 124 // todo! maybe throttle saves? 56 - Gson gson = new GsonBuilder().create(); 57 - byte[] json = gson.toJson( StorageManager.STORAGE ).getBytes(); 125 + byte[] json = GSON.toJson( StorageManager.STORAGE ).getBytes(); 58 126 59 - Files.write(STORAGE_FILE, json, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING); 127 + Files.write(STORAGE_FILE, json, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE); 60 128 } 61 129 62 130 63 131 public static class StorageClass { 132 + private final int version = 1; 64 133 private final ArrayList<NamedLocation> Warps = new ArrayList<>(); 65 134 private final ArrayList<Player> Players = new ArrayList<>(); 66 135 67 - // ----- 136 + /// Cleans up any values in the storage class 137 + public void cleanup() throws Exception { 138 + for (Player player : Players) { 139 + // Remove players with invalid UUID's 140 + if (player.getUUID().isBlank()) { 141 + Players.remove(player); 142 + } 143 + 144 + List<NamedLocation> homes = player.getHomes(); 145 + 146 + // Delete any homes with an invalid world_id (if enabled in config) 147 + if (ConfigManager.CONFIG.home.isDeleteInvalid()) { 148 + homes.removeIf(home -> home.getWorld().isEmpty()); 149 + } 150 + 151 + // Remove players with no homes 152 + if (homes.isEmpty()) { 153 + Players.remove(player); 154 + } 155 + } 156 + 157 + // Delete any warps with an invalid world_id (if enabled in config) 158 + if (ConfigManager.CONFIG.warp.isDeleteInvalid()) { 159 + Warps.removeIf(warp -> warp.getWorld().isEmpty()); 160 + } 161 + 162 + StorageSaver(); 163 + } 164 + 165 + public int getVersion() { 166 + return version; 167 + } 68 168 69 169 // returns all warps 70 170 public List<NamedLocation> getWarps() {
-14
common/src/main/java/dev/mrsnowy/teleport_commands/storage/configManager.java
··· 1 - package dev.mrsnowy.teleport_commands.storage; 2 - 3 - public class configManager { 4 - // Currently nothing to see here... yet 5 - 6 - // --- Ideas! --- 7 - // Make config options for disabling certain commands 8 - // Make config options for renaming certain commands 9 - // Make config option for changing required permission level for certain commands 10 - // Make config for setting max homes 11 - // Make config that adds a delay between teleports and when fighting. (in teleport function?) 12 - // Make config that automatically deletes namedLocations (warps/homes) with invalid world id's 13 - // Make config for setting the world_id for /worldspawn ? 14 - }
+1
flake.nix
··· 22 22 default = pkgs.mkShell { 23 23 packages = with pkgs; [ 24 24 jetbrains.jdk-no-jcef # Jetbrains jdk 25 + just # for the justfile 25 26 flite # Make mc not complain 26 27 27 28 # Took these from https://github.com/NixOS/nixpkgs/blob/nixos-25.05/pkgs/by-name/pr/prismlauncher/package.nix#L123
+11 -1
justfile
··· 1 + # A justfile :3 2 + 3 + build: 4 + ./gradlew build 5 + 6 + clean: 7 + ./gradlew clean 8 + 9 + mergeJars: 10 + ./gradlew mergeJars 1 11 2 12 ide: 3 - nohup idea-ultimate ./ >/dev/null 2>&1 & 13 + nohup idea ./ >/dev/null 2>&1 &