From f3236f26fa4988f599474e811c3c1f0d0a17561d Mon Sep 17 00:00:00 2001 From: Jikoo Date: Tue, 13 Feb 2024 12:40:34 -0500 Subject: [PATCH 1/6] Temporary MyWorlds compat Will later be replaced by more robust support for arbitrary non-vanilla NBT tags --- .../com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java index 23ed6bb9..6da363a1 100644 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java @@ -92,6 +92,14 @@ private void revertSpecialValues(@NotNull CompoundTag newData, @Nullable Compoun newData.put("RootVehicle", oldData.getCompound("RootVehicle")); } + // TODO: Better-support arbitrary non-vanilla data + // Blacklist vanilla root tags, copy all others + // Should do this BEFORE calling #setExtraData to not clobber additional info from server impl that we should load properly + CompoundTag myWorlds = getTag(oldData, "MyWorlds", CompoundTag.class); + if (myWorlds != null) { + newData.put("MyWorlds", myWorlds); + } + // Revert automatic updates to play timestamps. copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class); copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class); From 7c003734c407166cda765a1367b9a71e1ccf17b7 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Thu, 15 Feb 2024 17:47:16 -0500 Subject: [PATCH 2/6] Improve support for arbitrary tags Rather than expanding tag preservation, preserve all tags that are not known tags that may not be clobbered by the save process. This has the side benefit of more easily preserving vehicles and the previously-missed shoulder entities. Session timestamp tags still need to be reverted. --- .../openinv/internal/v1_20_R3/OpenPlayer.java | 79 ++++++++++++------- 1 file changed, 50 insertions(+), 29 deletions(-) diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java index 6da363a1..fbf0d2aa 100644 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java @@ -20,21 +20,52 @@ 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_R3.CraftServer; import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Set; public class OpenPlayer extends CraftPlayer { + private static final Set RESET_TAGS = Set.of( + // net.minecraft.world.Entity#saveWithoutId(CompoundTag) + "CustomName", + "CustomNameVisible", + "Silent", + "NoGravity", + "Glowing", + "TicksFrozen", + "HasVisualFire", + "Tags", + "Passengers", + // net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) + // Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle + // SpawnX, SpawnY, etc. in #RESET_TAG_PREFIXES via Spawn + "warden_spawn_tracker", + "enteredNetherPosition", + // net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag) + // Intentional omissions to prevent pet loss: ShoulderEntityLeft, ShoulderEntityRight + "LastDeathLocation", + // net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag) + // SleepingX etc. in #RESET_TAG_PREFIXES + "active_effects", + "Brain" + ); + private static final Set RESET_TAG_PREFIXES = Set.of( + "Bukkit", // Flags for re-enabling Bukkit API features + "Spawn", // SpawnX etc. only set if respawnPosition not null + "Sleeping" // SleepingX etc. only set if sleeping pos is set + ); + public OpenPlayer(CraftServer server, ServerPlayer entity) { super(server, entity); } @@ -51,12 +82,13 @@ public void saveData() { try { PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; - CompoundTag playerData = player.saveWithoutId(new CompoundTag()); + CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player); + CompoundTag playerData = getWritableTag(oldData); + playerData = player.saveWithoutId(playerData); setExtraData(playerData); - if (!isOnline()) { - // Preserve certain data when offline. - CompoundTag oldData = worldNBTStorage.load(player); + if (oldData != null) { + // Revert certain special data values when offline. revertSpecialValues(playerData, oldData); } @@ -71,35 +103,24 @@ public void saveData() { } } - private void revertSpecialValues(@NotNull CompoundTag newData, @Nullable CompoundTag oldData) { + @Contract("null -> new") + private @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) { if (oldData == null) { - return; + return new CompoundTag(); } - // 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")); - } + // Copy old data. This is a deep clone, so operating on it should be safe. + oldData = oldData.copy(); - // TODO: Better-support arbitrary non-vanilla data - // Blacklist vanilla root tags, copy all others - // Should do this BEFORE calling #setExtraData to not clobber additional info from server impl that we should load properly - CompoundTag myWorlds = getTag(oldData, "MyWorlds", CompoundTag.class); - if (myWorlds != null) { - newData.put("MyWorlds", myWorlds); - } + // Remove vanilla/server data that is not written every time. + oldData.getAllKeys() + .removeIf(key -> RESET_TAGS.contains(key) + || RESET_TAG_PREFIXES.stream().anyMatch(key::startsWith)); + + return oldData; + } + private void revertSpecialValues(@NotNull CompoundTag newData, @NotNull CompoundTag oldData) { // Revert automatic updates to play timestamps. copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class); copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class); From 92ef1de5585c23b4158f3481468205b4fc8c2f96 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Thu, 15 Feb 2024 17:50:00 -0500 Subject: [PATCH 3/6] Fix timestamps updating for vanilla players Affected only vanilla players who had never logged in on a Craftbukkit-based server --- .../com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java index fbf0d2aa..43225a92 100644 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java @@ -136,8 +136,8 @@ private void copyValue( 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) { + // New container being null means the server implementation doesn't store this data. + if (newContainer == null) { return; } @@ -146,9 +146,12 @@ private void copyValue( } private @Nullable T getTag( - @NotNull CompoundTag container, + @Nullable CompoundTag container, @NotNull String key, @NotNull Class dataType) { + if (container == null) { + return null; + } Tag value = container.get(key); if (value == null || !dataType.isAssignableFrom(value.getClass())) { return null; From 76d6aa622cfa3472afdab28726ff39d9c4c18361 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 16 Feb 2024 10:04:13 -0500 Subject: [PATCH 4/6] Be less lazy with tag exclusion Using a prefix does save a couple lines of text, but also means that non-vanilla tags like "Spawner" etc. would be deleted. Anyone using a namespace prefixed with "Bukkit" should also be using a Bukkit-supported save method, so that should always be safe. Other server implementation tags that I've viewed ("Spigot"- and "Paper"-prefixed) are written every time. --- .../openinv/internal/v1_20_R3/OpenPlayer.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java index 43225a92..8b314a09 100644 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java @@ -49,22 +49,24 @@ public class OpenPlayer extends CraftPlayer { "Passengers", // net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) // Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle - // SpawnX, SpawnY, etc. in #RESET_TAG_PREFIXES via Spawn "warden_spawn_tracker", "enteredNetherPosition", + "SpawnX", + "SpawnY", + "SpawnZ", + "SpawnForced", + "SpawnAngle", + "SpawnDimension", // net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag) // Intentional omissions to prevent pet loss: ShoulderEntityLeft, ShoulderEntityRight "LastDeathLocation", // net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag) - // SleepingX etc. in #RESET_TAG_PREFIXES "active_effects", + "SleepingX", + "SleepingY", + "SleepingZ", "Brain" ); - private static final Set RESET_TAG_PREFIXES = Set.of( - "Bukkit", // Flags for re-enabling Bukkit API features - "Spawn", // SpawnX etc. only set if respawnPosition not null - "Sleeping" // SleepingX etc. only set if sleeping pos is set - ); public OpenPlayer(CraftServer server, ServerPlayer entity) { super(server, entity); @@ -114,8 +116,7 @@ public void saveData() { // Remove vanilla/server data that is not written every time. oldData.getAllKeys() - .removeIf(key -> RESET_TAGS.contains(key) - || RESET_TAG_PREFIXES.stream().anyMatch(key::startsWith)); + .removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit")); return oldData; } From 79328d86029ada3827e22889aafbd51e775e6173 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 16 Feb 2024 10:21:46 -0500 Subject: [PATCH 5/6] Do reset shoulder entities Shoulder entities are actually loaded and kept in memory as a CompoundTag. As they aren't read into the world unless evicted from the shoulder, they should be saved properly, so preserving them would cause entity duplication. --- .../java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java index 8b314a09..6cf45719 100644 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java @@ -58,7 +58,8 @@ public class OpenPlayer extends CraftPlayer { "SpawnAngle", "SpawnDimension", // net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag) - // Intentional omissions to prevent pet loss: ShoulderEntityLeft, ShoulderEntityRight + "ShoulderEntityLeft", + "ShoulderEntityRight", "LastDeathLocation", // net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag) "active_effects", From 35076d1f7cd7ac447b1426fb0dd56055b30220eb Mon Sep 17 00:00:00 2001 From: Jikoo Date: Fri, 16 Feb 2024 15:06:04 -0500 Subject: [PATCH 6/6] Backport changes Adds backwards compatibility for deleting migrated active effects tag. In the future, these backwards compatibilities will be maintained only for the current supported versions, i.e. when 1.21 comes out, the 1.19 module will be dropped, and 1.21 will not contain 1.19 backwards compatibility. --- .../openinv/internal/v1_19_R3/OpenPlayer.java | 83 ++++++++++++------ .../openinv/internal/v1_20_R2/OpenPlayer.java | 84 +++++++++++++------ .../openinv/internal/v1_20_R3/OpenPlayer.java | 1 + 3 files changed, 118 insertions(+), 50 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 347d9cb9..34c0891f 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 @@ -19,7 +19,6 @@ 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; @@ -27,13 +26,48 @@ 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.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; +import java.util.Set; public class OpenPlayer extends CraftPlayer { + private static final Set RESET_TAGS = Set.of( + // net.minecraft.world.Entity#saveWithoutId(CompoundTag) + "CustomName", + "CustomNameVisible", + "Silent", + "NoGravity", + "Glowing", + "TicksFrozen", + "HasVisualFire", + "Tags", + "Passengers", + // net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) + // Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle + "warden_spawn_tracker", + "enteredNetherPosition", + "SpawnX", + "SpawnY", + "SpawnZ", + "SpawnForced", + "SpawnAngle", + "SpawnDimension", + // net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag) + "ShoulderEntityLeft", + "ShoulderEntityRight", + "LastDeathLocation", + // net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag) + "ActiveEffects", + "SleepingX", + "SleepingY", + "SleepingZ", + "Brain" + ); + public OpenPlayer(CraftServer server, ServerPlayer entity) { super(server, entity); } @@ -56,12 +90,13 @@ public void saveData() { try { PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; - CompoundTag playerData = player.saveWithoutId(new CompoundTag()); + CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player); + CompoundTag playerData = getWritableTag(oldData); + playerData = player.saveWithoutId(playerData); setExtraData(playerData); - if (!isOnline()) { - // Preserve certain data when offline. - CompoundTag oldData = worldNBTStorage.load(player); + if (oldData != null) { + // Revert certain special data values when offline. revertSpecialValues(playerData, oldData); } @@ -75,27 +110,22 @@ public void saveData() { } } - private void revertSpecialValues(@NotNull CompoundTag newData, @Nullable CompoundTag oldData) { + @Contract("null -> new") + private @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) { if (oldData == null) { - return; + return new CompoundTag(); } - // 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")); - } + // Copy old data. This is a deep clone, so operating on it should be safe. + oldData = oldData.copy(); + + // Remove vanilla/server data that is not written every time. + oldData.getAllKeys().removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit")); + return oldData; + } + + private void revertSpecialValues(@NotNull CompoundTag newData, @NotNull CompoundTag oldData) { // Revert automatic updates to play timestamps. copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class); copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class); @@ -111,8 +141,8 @@ private void copyValue( 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) { + // New container being null means the server implementation doesn't store this data. + if (newContainer == null) { return; } @@ -121,9 +151,12 @@ private void copyValue( } private @Nullable T getTag( - @NotNull CompoundTag container, + @Nullable CompoundTag container, @NotNull String key, @NotNull Class dataType) { + if (container == null) { + return null; + } Tag value = container.get(key); if (value == null || !dataType.isAssignableFrom(value.getClass())) { return null; 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 4388d823..33677e0d 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,20 +20,55 @@ 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_R2.CraftServer; import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; +import java.util.Set; public class OpenPlayer extends CraftPlayer { + private static final Set RESET_TAGS = Set.of( + // net.minecraft.world.Entity#saveWithoutId(CompoundTag) + "CustomName", + "CustomNameVisible", + "Silent", + "NoGravity", + "Glowing", + "TicksFrozen", + "HasVisualFire", + "Tags", + "Passengers", + // net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag) + // Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle + "warden_spawn_tracker", + "enteredNetherPosition", + "SpawnX", + "SpawnY", + "SpawnZ", + "SpawnForced", + "SpawnAngle", + "SpawnDimension", + // net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag) + "ShoulderEntityLeft", + "ShoulderEntityRight", + "LastDeathLocation", + // net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag) + "ActiveEffects", // Backwards compat: Renamed from 1.19 + "active_effects", + "SleepingX", + "SleepingY", + "SleepingZ", + "Brain" + ); + public OpenPlayer(CraftServer server, ServerPlayer entity) { super(server, entity); } @@ -50,12 +85,13 @@ public void saveData() { try { PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo; - CompoundTag playerData = player.saveWithoutId(new CompoundTag()); + CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player); + CompoundTag playerData = getWritableTag(oldData); + playerData = player.saveWithoutId(playerData); setExtraData(playerData); - if (!isOnline()) { - // Preserve certain data when offline. - CompoundTag oldData = worldNBTStorage.load(player); + if (oldData != null) { + // Revert certain special data values when offline. revertSpecialValues(playerData, oldData); } @@ -69,27 +105,22 @@ public void saveData() { } } - private void revertSpecialValues(@NotNull CompoundTag newData, @Nullable CompoundTag oldData) { + @Contract("null -> new") + private @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) { if (oldData == null) { - return; + return new CompoundTag(); } - // 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")); - } + // Copy old data. This is a deep clone, so operating on it should be safe. + oldData = oldData.copy(); + + // Remove vanilla/server data that is not written every time. + oldData.getAllKeys().removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit")); + return oldData; + } + + private void revertSpecialValues(@NotNull CompoundTag newData, @NotNull CompoundTag oldData) { // Revert automatic updates to play timestamps. copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class); copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class); @@ -105,8 +136,8 @@ private void copyValue( 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) { + // New container being null means the server implementation doesn't store this data. + if (newContainer == null) { return; } @@ -115,9 +146,12 @@ private void copyValue( } private @Nullable T getTag( - @NotNull CompoundTag container, + @Nullable CompoundTag container, @NotNull String key, @NotNull Class dataType) { + if (container == null) { + return null; + } Tag value = container.get(key); if (value == null || !dataType.isAssignableFrom(value.getClass())) { return null; diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java index 6cf45719..08017f9f 100644 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/OpenPlayer.java @@ -62,6 +62,7 @@ public class OpenPlayer extends CraftPlayer { "ShoulderEntityRight", "LastDeathLocation", // net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag) + "ActiveEffects", // Backwards compat: Renamed from 1.19 "active_effects", "SleepingX", "SleepingY",