diff --git a/CHANGELOG.md b/CHANGELOG.md index 716bf31..9eaebce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # 2.1.0 * Improve config ([#5](https://github.com/BlossomMods/BlossomBack/issues/5)) +* Make back/lastdeath optionally persist across server restarts ([#3](https://github.com/BlossomMods/BlossomBack/issues/3)) # 2.0.3 diff --git a/README.md b/README.md index d53ebee..67557f1 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,15 @@ BlossomBack is a Minecraft Fabric mod in the Blossom-series mods that provides / This mod's config file can be found at `config/BlossomMods/BlossomBack.json`, after running the server with the mod at least once. -`back`: [TeleportProps](#teleportprops) - properties of /back, used for /lastdeath if `lastDeath` is set to `null` -`lastDeath`: [TeleportProps](#teleportprops) or null - properties of /lastdeath +`back`: [TeleportProps](#teleportprops) - properties of /back +`lastDeath`: [TeleportProps](#teleportprops) - properties of /lastdeath +`separateCooldowns`: boolean - whether /back and /lastdeath use separate cooldown timers +`persistBack`: boolean - whether /back should persist across server restarts +`persistLastDeath`: boolean - whether /lastdeah should persist across server restarts ### TeleportProps +`enabled`: boolean - whether this is enabled `teleportation`: [TeleportationConfig](https://github.com/BlossomMods/BlossomLib/blob/main/README.md#teleportationconfig) - teleportation settings `standStill`: int - (seconds), how long the player has to stand still before being teleported @@ -36,7 +40,7 @@ teleport again - `/back` - teleport player to position before previous teleport Permission: `blossom.back` (default: true) - `/lastdeath` - teleport player to position where they died - Permission: `blossom.last-death` (default: false) + Permission: `blossom.last-death` (default: true) ## Translation keys diff --git a/build.gradle b/build.gradle index 59d12dd..2500870 100644 --- a/build.gradle +++ b/build.gradle @@ -26,6 +26,8 @@ dependencies { mappings "net.fabricmc:yarn:${project.minecraft_version}+${project.yarn_mappings}:v2" modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}+${project.minecraft_version}" + modImplementation "dev.codedsakura.blossom:blossom-lib:${project.blossomlib_version}+${mc_version}" } diff --git a/gradle.properties b/gradle.properties index 18f109a..c67633e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,4 +15,5 @@ yarn_mappings=build.1 loader_version=0.14.21 # Dependencies -blossomlib_version=2.5.2 +fabric_version=0.83.0 +blossomlib_version=2.5.3 diff --git a/src/main/java/dev/codedsakura/blossom/back/BlossomBack.java b/src/main/java/dev/codedsakura/blossom/back/BlossomBack.java index 298878e..2e44999 100644 --- a/src/main/java/dev/codedsakura/blossom/back/BlossomBack.java +++ b/src/main/java/dev/codedsakura/blossom/back/BlossomBack.java @@ -3,6 +3,9 @@ import com.mojang.brigadier.Command; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; +import dev.codedsakura.blossom.back.data.PlayerDeathData; +import dev.codedsakura.blossom.back.data.PlayerTeleportData; +import dev.codedsakura.blossom.back.data.ServerWorldSerializer; import dev.codedsakura.blossom.lib.BlossomLib; import dev.codedsakura.blossom.lib.config.BlossomConfig; import dev.codedsakura.blossom.lib.config.ConfigManager; @@ -11,6 +14,7 @@ import dev.codedsakura.blossom.lib.text.TextUtils; import dev.codedsakura.blossom.lib.utils.CustomLogger; import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; import org.apache.logging.log4j.core.Logger; @@ -26,7 +30,11 @@ class BlossomLastDeath { public class BlossomBack implements ModInitializer { static BlossomBackConfig CONFIG = ConfigManager.register(BlossomBackConfig.class, "BlossomBack.json", newConfig -> CONFIG = newConfig); public static final Logger LOGGER = CustomLogger.createLogger("BlossomBack"); - public static HashMap deaths = new HashMap<>(); + public static HashMap DEATHS = new HashMap<>(); + + static PlayerTeleportData teleportData; + static PlayerDeathData deathData; + @Override public void onInitialize() { @@ -39,6 +47,13 @@ public void onInitialize() { ConfigManager.refresh(BlossomBackConfig.class); } + teleportData = new PlayerTeleportData(); + deathData = new PlayerDeathData(); + + ServerLifecycleEvents.SERVER_STARTING.register(server -> ServerWorldSerializer.server = server); + + TeleportUtils.addLastTeleportAddHook((player, destination, lastTeleportMap) -> BlossomBack.onPlayerTeleportHook()); + BlossomLib.addCommand(literal("back") .requires(Permissions.require("blossom.back", true) .and(p -> CONFIG.back.enabled)) @@ -53,7 +68,7 @@ public void onInitialize() { private int runBack(CommandContext ctx) throws CommandSyntaxException { ServerPlayerEntity player = ctx.getSource().getPlayerOrThrow(); - var destination = TeleportUtils.getLastTeleport(player.getUuid()); + var destination = teleportData.get(player.getUuid()); LOGGER.trace("back {} ({}) to {}", player.getEntityName(), player.getUuid(), destination); @@ -76,7 +91,7 @@ private int runBack(CommandContext ctx) throws CommandSynta private int runLastDeath(CommandContext ctx) throws CommandSyntaxException { ServerPlayerEntity player = ctx.getSource().getPlayerOrThrow(); - var destination = deaths.get(player.getUuid()); + var destination = deathData.get(player.getUuid()); LOGGER.trace("back (death) {} ({}) to {}", player.getEntityName(), player.getUuid(), destination); @@ -95,4 +110,22 @@ private int runLastDeath(CommandContext ctx) throws Command return Command.SINGLE_SUCCESS; } + + public static void onPlayerDeathHook() { + if (!CONFIG.persistLastDeath) { + return; + } + + LOGGER.trace("persisting deaths"); + deathData.updateData(); + } + + public static void onPlayerTeleportHook() { + if (!CONFIG.persistBack) { + return; + } + + LOGGER.trace("persisting teleports"); + teleportData.updateData(); + } } diff --git a/src/main/java/dev/codedsakura/blossom/back/data/PlayerDeathData.java b/src/main/java/dev/codedsakura/blossom/back/data/PlayerDeathData.java new file mode 100644 index 0000000..8e42490 --- /dev/null +++ b/src/main/java/dev/codedsakura/blossom/back/data/PlayerDeathData.java @@ -0,0 +1,55 @@ +package dev.codedsakura.blossom.back.data; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import dev.codedsakura.blossom.back.BlossomBack; +import dev.codedsakura.blossom.lib.data.DataController; +import dev.codedsakura.blossom.lib.teleport.TeleportUtils; +import net.minecraft.server.world.ServerWorld; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.HashMap; +import java.util.UUID; + +public class PlayerDeathData extends DataController> { + private final Gson GSON = new GsonBuilder() + .registerTypeAdapter(ServerWorld.class, new ServerWorldSerializer()) + .setPrettyPrinting() + .serializeNulls() + .disableHtmlEscaping() + .create(); + + @Override + public HashMap defaultData() { + return new HashMap<>(); + } + + @Override + public String getFilename() { + return "BlossomBack.deaths"; + } + + public void updateData() { + data.putAll(BlossomBack.DEATHS); + this.write(); + } + + public TeleportUtils.TeleportDestination get(UUID player) { + if (data.containsKey(player)) { + return data.get(player); + } + return BlossomBack.DEATHS.get(player); + } + + @Override + protected HashMap readJson(InputStreamReader reader) { + return GSON.fromJson(new BufferedReader(reader), getType()); + } + + @Override + protected void writeJson(OutputStreamWriter writer) { + GSON.toJson(data, writer); + } +} diff --git a/src/main/java/dev/codedsakura/blossom/back/data/PlayerTeleportData.java b/src/main/java/dev/codedsakura/blossom/back/data/PlayerTeleportData.java new file mode 100644 index 0000000..905f95d --- /dev/null +++ b/src/main/java/dev/codedsakura/blossom/back/data/PlayerTeleportData.java @@ -0,0 +1,54 @@ +package dev.codedsakura.blossom.back.data; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import dev.codedsakura.blossom.lib.data.DataController; +import dev.codedsakura.blossom.lib.teleport.TeleportUtils; +import net.minecraft.server.world.ServerWorld; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.HashMap; +import java.util.UUID; + +public class PlayerTeleportData extends DataController> { + private final Gson GSON = new GsonBuilder() + .registerTypeAdapter(ServerWorld.class, new ServerWorldSerializer()) + .setPrettyPrinting() + .serializeNulls() + .disableHtmlEscaping() + .create(); + + @Override + public HashMap defaultData() { + return new HashMap<>(); + } + + @Override + public String getFilename() { + return "BlossomBack.teleports"; + } + + public void updateData() { + data.putAll(TeleportUtils.getAllLastTeleports()); + this.write(); + } + + public TeleportUtils.TeleportDestination get(UUID player) { + if (data.containsKey(player)) { + return data.get(player); + } + return TeleportUtils.getLastTeleport(player); + } + + @Override + protected HashMap readJson(InputStreamReader reader) { + return GSON.fromJson(new BufferedReader(reader), getType()); + } + + @Override + protected void writeJson(OutputStreamWriter writer) { + GSON.toJson(data, writer); + } +} diff --git a/src/main/java/dev/codedsakura/blossom/back/data/ServerWorldSerializer.java b/src/main/java/dev/codedsakura/blossom/back/data/ServerWorldSerializer.java new file mode 100644 index 0000000..bba40ca --- /dev/null +++ b/src/main/java/dev/codedsakura/blossom/back/data/ServerWorldSerializer.java @@ -0,0 +1,24 @@ +package dev.codedsakura.blossom.back.data; + +import com.google.gson.*; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryKeys; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.Identifier; + +import java.lang.reflect.Type; + +public class ServerWorldSerializer implements JsonSerializer, JsonDeserializer { + public static MinecraftServer server; + + @Override + public ServerWorld deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + return server.getWorld(RegistryKey.of(RegistryKeys.WORLD, new Identifier(jsonElement.getAsString()))); + } + + @Override + public JsonElement serialize(ServerWorld serverWorld, Type type, JsonSerializationContext jsonSerializationContext) { + return new JsonPrimitive(serverWorld.getRegistryKey().getValue().toString()); + } +} diff --git a/src/main/java/dev/codedsakura/blossom/back/mixin/PlayerDeathMixin.java b/src/main/java/dev/codedsakura/blossom/back/mixin/PlayerDeathMixin.java index 3a3d930..87f9fc1 100644 --- a/src/main/java/dev/codedsakura/blossom/back/mixin/PlayerDeathMixin.java +++ b/src/main/java/dev/codedsakura/blossom/back/mixin/PlayerDeathMixin.java @@ -16,7 +16,8 @@ void onPlayerDeath(DamageSource damageSource, CallbackInfo ci) { ServerPlayerEntity self = (ServerPlayerEntity) (Object) this; TeleportDestination deathPoint = new TeleportDestination(self); - BlossomBack.deaths.put(self.getUuid(), deathPoint); + BlossomBack.DEATHS.put(self.getUuid(), deathPoint); + BlossomBack.onPlayerDeathHook(); BlossomBack.LOGGER.info("{} ({}) died at {}", self.getEntityName(), self.getUuid(), deathPoint); } }