diff --git a/src/main/java/net/minestom/server/instance/DynamicChunk.java b/src/main/java/net/minestom/server/instance/DynamicChunk.java index 01b5c8a53..c3291ff30 100644 --- a/src/main/java/net/minestom/server/instance/DynamicChunk.java +++ b/src/main/java/net/minestom/server/instance/DynamicChunk.java @@ -16,6 +16,7 @@ import net.minestom.server.utils.BlockPosition; import net.minestom.server.utils.binary.BinaryReader; import net.minestom.server.utils.binary.BinaryWriter; import net.minestom.server.utils.block.CustomBlockUtils; +import net.minestom.server.utils.cache.CachedObject; import net.minestom.server.utils.callback.OptionalCallback; import net.minestom.server.utils.chunk.ChunkCallback; import net.minestom.server.utils.chunk.ChunkUtils; @@ -26,7 +27,6 @@ import net.minestom.server.world.biomes.Biome; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.lang.ref.SoftReference; import java.util.Set; /** @@ -60,8 +60,7 @@ public class DynamicChunk extends Chunk { private long lastChangeTime; - private SoftReference cachedPacket = new SoftReference<>(null); - private long cachedPacketTime; + private CachedObject cachedPacket = new CachedObject<>(); public DynamicChunk(@NotNull Instance instance, @Nullable Biome[] biomes, int chunkX, int chunkZ, @NotNull PaletteStorage blockPalette, @NotNull PaletteStorage customBlockPalette) { @@ -387,22 +386,17 @@ public class DynamicChunk extends Chunk { @NotNull @Override protected ChunkDataPacket createFreshPacket() { - ChunkDataPacket packet = cachedPacket.get(); - if (packet != null && cachedPacketTime == getLastChangeTime()) { - return packet; - } - packet = new ChunkDataPacket(getIdentifier(), getLastChangeTime()); - packet.biomes = biomes; - packet.chunkX = chunkX; - packet.chunkZ = chunkZ; - packet.paletteStorage = blockPalette.clone(); - packet.customBlockPaletteStorage = customBlockPalette.clone(); - packet.blockEntities = blockEntities.clone(); - packet.blocksData = blocksData.clone(); - - this.cachedPacketTime = getLastChangeTime(); - this.cachedPacket = new SoftReference<>(packet); - return packet; + return cachedPacket.getUpdatedCache(() -> { + ChunkDataPacket chunkDataPacket = new ChunkDataPacket(getIdentifier(), getLastChangeTime()); + chunkDataPacket.biomes = biomes; + chunkDataPacket.chunkX = chunkX; + chunkDataPacket.chunkZ = chunkZ; + chunkDataPacket.paletteStorage = blockPalette.clone(); + chunkDataPacket.customBlockPaletteStorage = customBlockPalette.clone(); + chunkDataPacket.blockEntities = blockEntities.clone(); + chunkDataPacket.blocksData = blocksData.clone(); + return chunkDataPacket; + }, getLastChangeTime()); } @NotNull diff --git a/src/main/java/net/minestom/server/item/ItemMeta.java b/src/main/java/net/minestom/server/item/ItemMeta.java index 04d0023ba..49b892717 100644 --- a/src/main/java/net/minestom/server/item/ItemMeta.java +++ b/src/main/java/net/minestom/server/item/ItemMeta.java @@ -6,6 +6,7 @@ import net.minestom.server.instance.block.Block; import net.minestom.server.item.attribute.ItemAttribute; import net.minestom.server.utils.binary.BinaryWriter; import net.minestom.server.utils.binary.Writeable; +import net.minestom.server.utils.cache.CachedObject; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -33,8 +34,8 @@ public class ItemMeta implements Writeable { private final NBTCompound nbt; private final ItemMetaBuilder emptyBuilder; - private String cachedSNBT; - private ByteBuf cachedBuffer; + private CachedObject cachedSNBT = new CachedObject<>(); + private CachedObject cachedBuffer = new CachedObject<>(); protected ItemMeta(@NotNull ItemMetaBuilder metaBuilder) { this.damage = metaBuilder.damage; @@ -128,10 +129,7 @@ public class ItemMeta implements Writeable { } public @NotNull String toSNBT() { - if (cachedSNBT == null) { - this.cachedSNBT = nbt.toSNBT(); - } - return cachedSNBT; + return cachedSNBT.getCache(nbt::toSNBT); } @Override @@ -155,12 +153,12 @@ public class ItemMeta implements Writeable { @Override public synchronized void write(@NotNull BinaryWriter writer) { - if (cachedBuffer == null) { + final var buffer = cachedBuffer.getCache(() -> { BinaryWriter w = new BinaryWriter(); w.writeNBT("", nbt); - this.cachedBuffer = w.getBuffer(); - } - writer.write(cachedBuffer); - this.cachedBuffer.resetReaderIndex(); + return w.getBuffer(); + }); + writer.write(buffer); + buffer.resetReaderIndex(); } } diff --git a/src/main/java/net/minestom/server/utils/cache/CachedObject.java b/src/main/java/net/minestom/server/utils/cache/CachedObject.java new file mode 100644 index 000000000..5d52497fd --- /dev/null +++ b/src/main/java/net/minestom/server/utils/cache/CachedObject.java @@ -0,0 +1,69 @@ +package net.minestom.server.utils.cache; + +import com.google.common.annotations.Beta; +import org.jetbrains.annotations.NotNull; + +import java.lang.ref.SoftReference; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; + +/** + * Represents a single that can be collected by the garbage collector + * depending on memory demand. + * + * @param the object to cache + */ +@Beta +public class CachedObject extends SoftReference> { + + private long cacheTime; + + public CachedObject() { + super(new AtomicReference<>()); + } + + /** + * Retrieves the cache. + * + * @param supplier supplier for the value if absent + * @return the cache + */ + public @NotNull T getCache(@NotNull Supplier<@NotNull T> supplier) { + var referent = get(); + assert referent != null; + var value = referent.get(); + if (value == null) { + value = supplier.get(); + referent.set(value); + } + return value; + } + + /** + * Retrieves the cache, and ensure that the value is up-to-date. + * + * @param supplier supplier for the value if absent or outdated + * @param lastUpdate the required internal timestamp, supplier will be called otherwise + * @return the cache + */ + public @NotNull T getUpdatedCache(@NotNull Supplier<@NotNull T> supplier, long lastUpdate) { + var referent = get(); + assert referent != null; + var value = referent.get(); + if (value == null || cacheTime != lastUpdate) { + value = supplier.get(); + this.cacheTime = lastUpdate; + referent.set(value); + } + return value; + } + + /** + * @deprecated use {@link #getCache(Supplier)} + */ + @Override + @Deprecated + public AtomicReference get() { + return super.get(); + } +}