From 5adc8e3900afb8d855374b96bcb20f08aba90903 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Sun, 12 Nov 2023 11:49:00 -0500 Subject: [PATCH] Add more defensive code and backport --- .../openinv/internal/v1_19_R3/OpenPlayer.java | 83 +++++++++++++++++-- .../openinv/internal/v1_20_R1/OpenPlayer.java | 83 +++++++++++++++++-- .../openinv/internal/v1_20_R2/OpenPlayer.java | 10 ++- 3 files changed, 161 insertions(+), 15 deletions(-) diff --git a/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/OpenPlayer.java b/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/OpenPlayer.java index 33a360e7..a7b5a534 100644 --- a/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/OpenPlayer.java +++ b/internal/v1_19_R3/src/main/java/com/lishid/openinv/internal/v1_19_R3/OpenPlayer.java @@ -16,15 +16,21 @@ package com.lishid.openinv.internal.v1_19_R3; -import java.io.File; import net.minecraft.Util; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.nbt.NumericTag; +import net.minecraft.nbt.Tag; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.storage.PlayerDataStorage; import org.apache.logging.log4j.LogManager; import org.bukkit.craftbukkit.v1_19_R3.CraftServer; import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; public class OpenPlayer extends CraftPlayer { @@ -52,13 +58,9 @@ public void saveData() { setExtraData(playerData); if (!isOnline()) { - // Special case: save old vehicle data + // Preserve certain data when offline. CompoundTag oldData = worldNBTStorage.load(player); - - if (oldData != null && oldData.contains("RootVehicle", 10)) { - // See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound) - playerData.put("RootVehicle", oldData.getCompound("RootVehicle")); - } + revertSpecialValues(playerData, oldData); } File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir()); @@ -71,4 +73,71 @@ public void saveData() { } } + private void revertSpecialValues(@NotNull CompoundTag newData, @Nullable CompoundTag oldData) { + if (oldData == null) { + return; + } + + // Prevent vehicle deletion. + if (oldData.contains("RootVehicle", Tag.TAG_COMPOUND)) { + // See net.minecraft.server.players.PlayerList#save(ServerPlayer) + // See net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) + try { + Tag attach = oldData.get("Attach"); + if (attach != null) { + newData.putUUID("Attach", NbtUtils.loadUUID(attach)); + } + } catch (IllegalArgumentException ignored) { + // Likely will not re-mount successfully, but at least the mount will not be deleted. + } + newData.put("Entity", oldData.getCompound("Entity")); + newData.put("RootVehicle", oldData.getCompound("RootVehicle")); + } + + // Revert automatic updates to play timestamps. + copyValue(newData, oldData, "bukkit", "lastPlayed", NumericTag.class); + copyValue(newData, oldData, "Paper", "LastSeen", NumericTag.class); + copyValue(newData, oldData, "Paper", "LastLogin", NumericTag.class); + } + + private void copyValue( + @NotNull CompoundTag source, + @NotNull CompoundTag target, + @NotNull String container, + @NotNull String key, + @NotNull Class tagType) { + CompoundTag oldContainer = getTag(source, container, CompoundTag.class); + CompoundTag newContainer = getTag(target, container, CompoundTag.class); + + // Container being null means the server implementation doesn't store this data. + if (oldContainer == null || newContainer == null) { + return; + } + + // If old tag exists, copy it to new location, removing otherwise. + setTag(newContainer, key, getTag(oldContainer, key, tagType)); + } + + private @Nullable T getTag( + @NotNull CompoundTag container, + @NotNull String key, + @NotNull Class dataType) { + Tag value = container.get(key); + if (value == null || !dataType.isAssignableFrom(value.getClass())) { + return null; + } + return dataType.cast(value); + } + + private void setTag( + @NotNull CompoundTag container, + @NotNull String key, + @Nullable T data) { + if (data == null) { + container.remove(key); + } else { + container.put(key, data); + } + } + } diff --git a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/OpenPlayer.java b/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/OpenPlayer.java index 9ebd79b2..4d3f05d5 100644 --- a/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/OpenPlayer.java +++ b/internal/v1_20_R1/src/main/java/com/lishid/openinv/internal/v1_20_R1/OpenPlayer.java @@ -17,14 +17,20 @@ package com.lishid.openinv.internal.v1_20_R1; import com.mojang.logging.LogUtils; -import java.io.File; import net.minecraft.Util; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.nbt.NumericTag; +import net.minecraft.nbt.Tag; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.storage.PlayerDataStorage; import org.bukkit.craftbukkit.v1_20_R1.CraftServer; import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; public class OpenPlayer extends CraftPlayer { @@ -52,13 +58,9 @@ public void saveData() { setExtraData(playerData); if (!isOnline()) { - // Special case: save old vehicle data + // Preserve certain data when offline. CompoundTag oldData = worldNBTStorage.load(player); - - if (oldData != null && oldData.contains("RootVehicle", 10)) { - // See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound) - playerData.put("RootVehicle", oldData.getCompound("RootVehicle")); - } + revertSpecialValues(playerData, oldData); } File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir()); @@ -71,4 +73,71 @@ public void saveData() { } } + private void revertSpecialValues(@NotNull CompoundTag newData, @Nullable CompoundTag oldData) { + if (oldData == null) { + return; + } + + // Prevent vehicle deletion. + if (oldData.contains("RootVehicle", Tag.TAG_COMPOUND)) { + // See net.minecraft.server.players.PlayerList#save(ServerPlayer) + // See net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) + try { + Tag attach = oldData.get("Attach"); + if (attach != null) { + newData.putUUID("Attach", NbtUtils.loadUUID(attach)); + } + } catch (IllegalArgumentException ignored) { + // Likely will not re-mount successfully, but at least the mount will not be deleted. + } + newData.put("Entity", oldData.getCompound("Entity")); + newData.put("RootVehicle", oldData.getCompound("RootVehicle")); + } + + // Revert automatic updates to play timestamps. + copyValue(newData, oldData, "bukkit", "lastPlayed", NumericTag.class); + copyValue(newData, oldData, "Paper", "LastSeen", NumericTag.class); + copyValue(newData, oldData, "Paper", "LastLogin", NumericTag.class); + } + + private void copyValue( + @NotNull CompoundTag source, + @NotNull CompoundTag target, + @NotNull String container, + @NotNull String key, + @NotNull Class tagType) { + CompoundTag oldContainer = getTag(source, container, CompoundTag.class); + CompoundTag newContainer = getTag(target, container, CompoundTag.class); + + // Container being null means the server implementation doesn't store this data. + if (oldContainer == null || newContainer == null) { + return; + } + + // If old tag exists, copy it to new location, removing otherwise. + setTag(newContainer, key, getTag(oldContainer, key, tagType)); + } + + private @Nullable T getTag( + @NotNull CompoundTag container, + @NotNull String key, + @NotNull Class dataType) { + Tag value = container.get(key); + if (value == null || !dataType.isAssignableFrom(value.getClass())) { + return null; + } + return dataType.cast(value); + } + + private void setTag( + @NotNull CompoundTag container, + @NotNull String key, + @Nullable T data) { + if (data == null) { + container.remove(key); + } else { + container.put(key, data); + } + } + } diff --git a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/OpenPlayer.java b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/OpenPlayer.java index a269a18a..e54d9572 100644 --- a/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/OpenPlayer.java +++ b/internal/v1_20_R2/src/main/java/com/lishid/openinv/internal/v1_20_R2/OpenPlayer.java @@ -20,6 +20,7 @@ import net.minecraft.Util; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.NumericTag; import net.minecraft.nbt.Tag; import net.minecraft.server.level.ServerPlayer; @@ -81,7 +82,14 @@ private void revertSpecialValues(@NotNull CompoundTag newData, @Nullable Compoun if (oldData.contains("RootVehicle", Tag.TAG_COMPOUND)) { // See net.minecraft.server.players.PlayerList#save(ServerPlayer) // See net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) - newData.putUUID("Attach", oldData.getUUID("Attach")); + try { + Tag attach = oldData.get("Attach"); + if (attach != null) { + newData.putUUID("Attach", NbtUtils.loadUUID(attach)); + } + } catch (IllegalArgumentException ignored) { + // Likely will not re-mount successfully, but at least the mount will not be deleted. + } newData.put("Entity", oldData.getCompound("Entity")); newData.put("RootVehicle", oldData.getCompound("RootVehicle")); }