···44The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
55and this project kind of adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6677+### WIP [1.4.0]
88+#### Changed
99+- The loading logic of the storage file
1010+1111+#### Added
1212+- **a config file!**
1313+ - The config file starts at **version 0**!
1414+- versioning to the storage file for better handling of migrations.
1515+ - We start at **version 1**, all previous version are version 0.
1616+1717+718### [1.3.4]
819- Updated Italian translations (Thanks to [Jump-333](https://github.com/Jump-333)!)
920- Turned DeathLocation into a Hashmap
+17-14
README.md
···4455Here is the [Changelog](https://github.com/MrSn0wy/TeleportCommands/blob/main/CHANGELOG.md)
6677-### Currently available commands:
77+## Currently available commands:
8899- `/worldspawn [<Disable Safety>]` - Teleports you to the world spawn (in the overworld), if given true it will not do safety checks
1010- `/back [<Disable Safety>]` - Teleports you to the location where you last died, if given true it will not do safety checks
···32323333<br>
34343535-### TODO:
3636-#### Planned commands:
3535+## TODO:
3636+### Planned commands:
3737- [ ] `/wild` - Teleports you to a random location in the Overworld
3838- [x] `/worldspawn` - Teleports you to the worldspawn
3939-- [ ] `/spawn <dimension>` - Teleports you to your spawnpoint in a dimension, defaults to your current dimension
3939+- [ ] `/spawn <dimension>` - Teleports you to your spawn point in a dimension, defaults to your current dimension
4040- [ ] `/previous` - Go to the last teleported location
41414242-#### Improvements:
4242+### Improvements:
4343- [ ] Look into changing the mod into the more safe and sane kotlin (I love java)
4444- [ ] Add game tests
4545- [ ] Find the easiest way to backport the mod to older version (help)
···5656- [x] Add Quilt support and NeoForge
575758585959-### Want to help?
5959+## Want to help?
60601. 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)
616162626363-### How to build
6464-#### Getting the correct environment
6363+## How to build
6464+### Getting the correct environment
6565+#### Nixos
6566If 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.
66676868+#### Generic linux
6769On any other linux distro, just install the jetbrains jdk, or try openjdk21.
68706969-On windows probably go to the openjdk website and install the 21 version? idk goodluck.
7171+#### Windows
7272+On windows probably go to the openjdk website and install the 21 version? Idk goodluck.
70737171-#### Building
7272-Then on linux just do `./gradlew build` and to make it in a single mod jar `./gradlew mergeJars`.
7474+### Building
7575+Then on linux just run `./gradlew build`. To make it into a single mod jar just run `./gradlew mergeJars`.
73767477Or on windows, just do `.\gradlew.bat build` and `.\gradlew.bat mergeJars`.
7578Note that this isn't tested for windows, but I think that is how it works.
76797777-#### Getting the jars
8080+### Getting the jars
7881Then 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).
79828083If you have any issues just make an issue or contact me on Discord `@mrsnowy_`
81848282-### Notes
8383-Colors:
8585+## Random notes lol
8686+Text Colors:
8487- Green = When something succeeds and an action will happen
8588- Aqua = When something needs attention
8689- White = When something is done
···11package dev.mrsnowy.teleport_commands.storage;
2233-import com.google.gson.Gson;
44-import com.google.gson.GsonBuilder;
33+import com.google.gson.*;
54import dev.mrsnowy.teleport_commands.Constants;
65import dev.mrsnowy.teleport_commands.TeleportCommands;
76import dev.mrsnowy.teleport_commands.common.NamedLocation;
87import dev.mrsnowy.teleport_commands.common.Player;
981010-import java.io.File;
99+import java.io.FileReader;
1110import java.nio.file.Files;
1211import java.nio.file.Path;
1312import java.nio.file.StandardOpenOption;
···2221 public static Path STORAGE_FOLDER;
2322 public static Path STORAGE_FILE;
2423 public static StorageClass STORAGE;
2424+ private static final Gson GSON = new GsonBuilder().create();
2525+ private static final int defaultVersion = new StorageClass().getVersion();
25262727+ /// Initializes the StorageManager class and loads the storage from the filesystem.
2628 public static void StorageInit() {
2729 STORAGE_FOLDER = TeleportCommands.SAVE_DIR.resolve("TeleportCommands/");
2830 STORAGE_FILE = STORAGE_FOLDER.resolve("storage.json");
29313032 try {
3131- // check if the folder exists and create it
3232- if (!Files.exists(STORAGE_FOLDER)) {
3333- Files.createDirectories(STORAGE_FOLDER);
3434- }
3333+ StorageLoader();
3434+3535+ } catch (Exception e) {
3636+ // crashing is probably better here, otherwise the whole mod will be broken
3737+ Constants.LOGGER.error("Error while initializing the storage file! Exiting! => ", e);
3838+ throw new RuntimeException("Error while initializing the storage file! Exiting! => ", e);
3939+ }
4040+ }
4141+4242+ /// Loads the storage from the filesystem
4343+ public static void StorageLoader() throws Exception {
4444+4545+ if (!STORAGE_FILE.toFile().exists() || STORAGE_FILE.toFile().length() == 0) {
4646+ Constants.LOGGER.warn("Storage file was not found or was empty! Initializing storage");
4747+4848+ Files.createDirectories(STORAGE_FOLDER);
4949+ STORAGE = new StorageClass();
5050+ StorageSaver();
5151+ Constants.LOGGER.info("Storage created successfully!");
5252+ }
5353+5454+ StorageMigrator();
5555+5656+ FileReader reader = new FileReader(STORAGE_FILE.toFile());
5757+ STORAGE = GSON.fromJson(reader, StorageClass.class);
5858+ if (STORAGE == null) {
5959+ Constants.LOGGER.warn("Storage file was empty! Initializing storage");
6060+ STORAGE = new StorageClass();
6161+ StorageSaver();
6262+ }
35633636- // check if the file exists and create it
3737- if (!Files.exists(STORAGE_FILE)) {
3838- Files.createFile(STORAGE_FILE);
3939- }
6464+ STORAGE.cleanup();
6565+6666+ StorageSaver(); // Save it so any missing values get added to the file.
6767+ Constants.LOGGER.info("Storage loaded successfully!");
6868+ }
40694141- // create the basic storage if it is empty
4242- if (new File(String.valueOf(STORAGE_FILE)).length() == 0) {
4343- StorageManager.STORAGE = new StorageClass();
4444- StorageSaver(); // todo! verify that it creates em correctly
7070+ /// This function checks what version the storage file is and migrates it to the current version of the mod.
7171+ public static void StorageMigrator() throws Exception {
7272+ FileReader reader = new FileReader(STORAGE_FILE.toFile());
7373+ JsonObject jsonObject = GSON.fromJson(reader, JsonObject.class);
7474+7575+ int version = jsonObject.has("version") ? jsonObject.get("version").getAsInt() : 0;
7676+7777+ if (version < defaultVersion) {
7878+ Constants.LOGGER.warn("Storage file is v{}, migrating to v{}!", version, defaultVersion);
7979+8080+ // 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.
8181+ if (version == 0) {
8282+8383+ if (jsonObject.has("Players") && jsonObject.get("Players").isJsonArray()) {
8484+8585+ JsonArray players = jsonObject.get("Players").getAsJsonArray();
8686+8787+ for (JsonElement playerElement : players) {
8888+ JsonObject player = playerElement.getAsJsonObject();
8989+9090+ String UUID = player.has("Player_UUID")
9191+ ? player.get("Player_UUID").getAsString() : (player.has("UUID")
9292+ ? player.get("UUID").getAsString() : null);
9393+9494+ if (UUID == null || UUID.isBlank()) {
9595+ // remove it then, it's an invalid entry 0.0
9696+ players.remove(player); // may return true or false for success, but idc
9797+9898+ } else {
9999+ player.remove("Player_UUID");
100100+ player.addProperty("UUID", UUID);
101101+ }
102102+ }
103103+ }
104104+105105+ jsonObject.addProperty("version", 1);
45106 }
461074747- } catch (Exception e) {
4848- Constants.LOGGER.error("Error while creating the storage file! Exiting! => ", e);
4949- // crashing is probably better here, otherwise the whole mod will be broken
5050- System.exit(1);
108108+ // Save the storage :3
109109+ byte[] json = GSON.toJson(jsonObject, JsonArray.class).getBytes();
110110+ Files.write(STORAGE_FILE, json, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
111111+112112+ Constants.LOGGER.info("Storage file migrated to v{} successfully!", defaultVersion);
113113+ } else if (version > defaultVersion) {
114114+ String message = String.format("Teleport Commands: The storage file's version is newer than the supported version, found v%s, expected <= v%s.\n" +
115115+ "If you intentionally backported then you can attempt to downgrade the storage file located at this location: \"%s\".\n",
116116+ version, defaultVersion, STORAGE_FILE.toAbsolutePath());
117117+118118+ throw new IllegalStateException(message);
51119 }
52120 }
53121122122+ /// Saves the storage to the filesystem
54123 public static void StorageSaver() throws Exception {
55124 // todo! maybe throttle saves?
5656- Gson gson = new GsonBuilder().create();
5757- byte[] json = gson.toJson( StorageManager.STORAGE ).getBytes();
125125+ byte[] json = GSON.toJson( StorageManager.STORAGE ).getBytes();
581265959- Files.write(STORAGE_FILE, json, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
127127+ Files.write(STORAGE_FILE, json, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
60128 }
611296213063131 public static class StorageClass {
132132+ private final int version = 1;
64133 private final ArrayList<NamedLocation> Warps = new ArrayList<>();
65134 private final ArrayList<Player> Players = new ArrayList<>();
661356767- // -----
136136+ /// Cleans up any values in the storage class
137137+ public void cleanup() throws Exception {
138138+ for (Player player : Players) {
139139+ // Remove players with invalid UUID's
140140+ if (player.getUUID().isBlank()) {
141141+ Players.remove(player);
142142+ }
143143+144144+ List<NamedLocation> homes = player.getHomes();
145145+146146+ // Delete any homes with an invalid world_id (if enabled in config)
147147+ if (ConfigManager.CONFIG.home.isDeleteInvalid()) {
148148+ homes.removeIf(home -> home.getWorld().isEmpty());
149149+ }
150150+151151+ // Remove players with no homes
152152+ if (homes.isEmpty()) {
153153+ Players.remove(player);
154154+ }
155155+ }
156156+157157+ // Delete any warps with an invalid world_id (if enabled in config)
158158+ if (ConfigManager.CONFIG.warp.isDeleteInvalid()) {
159159+ Warps.removeIf(warp -> warp.getWorld().isEmpty());
160160+ }
161161+162162+ StorageSaver();
163163+ }
164164+165165+ public int getVersion() {
166166+ return version;
167167+ }
6816869169 // returns all warps
70170 public List<NamedLocation> getWarps() {
···11-package dev.mrsnowy.teleport_commands.storage;
22-33-public class configManager {
44-// Currently nothing to see here... yet
55-66- // --- Ideas! ---
77- // Make config options for disabling certain commands
88- // Make config options for renaming certain commands
99- // Make config option for changing required permission level for certain commands
1010- // Make config for setting max homes
1111- // Make config that adds a delay between teleports and when fighting. (in teleport function?)
1212- // Make config that automatically deletes namedLocations (warps/homes) with invalid world id's
1313- // Make config for setting the world_id for /worldspawn ?
1414-}
+1
flake.nix
···2222 default = pkgs.mkShell {
2323 packages = with pkgs; [
2424 jetbrains.jdk-no-jcef # Jetbrains jdk
2525+ just # for the justfile
2526 flite # Make mc not complain
26272728 # Took these from https://github.com/NixOS/nixpkgs/blob/nixos-25.05/pkgs/by-name/pr/prismlauncher/package.nix#L123