diff --git a/src/main/java/net/minestom/server/tag/Tag.java b/src/main/java/net/minestom/server/tag/Tag.java index 6f9496c93..71196ead1 100644 --- a/src/main/java/net/minestom/server/tag/Tag.java +++ b/src/main/java/net/minestom/server/tag/Tag.java @@ -178,6 +178,12 @@ public class Tag { write(nbtCompound, (T) value); } + final boolean shareValue(@NotNull Tag other) { + // Verify if these 2 tags can share the same cached value + // Key/Default value/Path are ignored + return this == other || this.readFunction == other.readFunction; + } + public static @NotNull Tag Byte(@NotNull String key) { return tag(key, NBTByte::getValue, NBT::Byte); } diff --git a/src/main/java/net/minestom/server/tag/TagHandlerImpl.java b/src/main/java/net/minestom/server/tag/TagHandlerImpl.java index 6c14a5979..8b5ae0fd5 100644 --- a/src/main/java/net/minestom/server/tag/TagHandlerImpl.java +++ b/src/main/java/net/minestom/server/tag/TagHandlerImpl.java @@ -172,6 +172,12 @@ final class TagHandlerImpl implements TagHandler { this.tag = tag; this.value = value; } + + NBT updatedNbt() { + NBT nbt = this.nbt; + if (nbt == null) this.nbt = nbt = tag.writeFunction.apply(value); + return nbt; + } } private record Cache(Entry[] entries, NBTCompound compound) implements TagReadable { @@ -196,6 +202,10 @@ final class TagHandlerImpl implements TagHandler { } if (entry.value instanceof TagHandlerImpl handler) { entries = handler.entries; + } else if (entry.updatedNbt() instanceof NBTCompound compound) { + var tmp = new TagHandlerImpl(); + tmp.updateContent(compound); + entries = tmp.entries; } } } @@ -206,14 +216,13 @@ final class TagHandlerImpl implements TagHandler { if (value instanceof TagHandlerImpl) throw new IllegalStateException("Cannot read path-able tag " + tag.getKey()); final Tag entryTag = entry.tag; - if (entryTag == tag) { + if (entryTag.shareValue(tag)) { // Tag is the same, return the value //noinspection unchecked return (T) value; } // Value must be parsed from nbt if the tag is different - NBT nbt = entry.nbt; - if (nbt == null) entry.nbt = nbt = (NBT) entryTag.writeFunction.apply(value); + final NBT nbt = entry.updatedNbt(); try { return tag.readFunction.apply(nbt); } catch (ClassCastException e) { diff --git a/src/test/java/net/minestom/server/tag/TagEqualityTest.java b/src/test/java/net/minestom/server/tag/TagEqualityTest.java new file mode 100644 index 000000000..e8807e63d --- /dev/null +++ b/src/test/java/net/minestom/server/tag/TagEqualityTest.java @@ -0,0 +1,53 @@ +package net.minestom.server.tag; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test tags that can share cached values. + */ +public class TagEqualityTest { + + @Test + public void same() { + var tag = Tag.String("test"); + assertTrue(tag.shareValue(tag)); + } + + @Test + public void similar() { + var tag = Tag.String("test"); + var tag2 = Tag.String("test"); + assertTrue(tag.shareValue(tag2)); + } + + @Test + public void differentDefault() { + var tag = Tag.String("test").defaultValue("test2"); + var tag2 = Tag.String("test").defaultValue("test3"); + assertTrue(tag.shareValue(tag2)); + } + + @Test + public void differentType() { + var tag = Tag.String("test"); + var tag2 = Tag.Integer("test"); + assertFalse(tag.shareValue(tag2)); + } + + @Test + public void list() { + var tag = Tag.String("test").list(); + assertTrue(tag.shareValue(tag)); + } + + //@Test + public void similarList() { + // TODO make work + var tag = Tag.String("test").list(); + var tag2 = Tag.String("test").list(); + assertTrue(tag.shareValue(tag2)); + } +} diff --git a/src/test/java/net/minestom/server/tag/TagPathTest.java b/src/test/java/net/minestom/server/tag/TagPathTest.java index b4ab9c988..55bbd95e9 100644 --- a/src/test/java/net/minestom/server/tag/TagPathTest.java +++ b/src/test/java/net/minestom/server/tag/TagPathTest.java @@ -236,6 +236,7 @@ public class TagPathTest { } } """, handler.asCompound()); + assertEquals(5, handler.getTag(tag.path("struct"))); handler.setTag(tag, 5); assertEqualsSNBT("""