diff --git a/src/main/java/com/jozufozu/flywheel/backend/Backend.java b/src/main/java/com/jozufozu/flywheel/backend/Backend.java index 9228aebea..fe4dc0322 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Backend.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Backend.java @@ -40,7 +40,6 @@ public class Backend { private boolean instancedArrays; private boolean enabled; - public boolean chunkCachingEnabled; private final List> contexts = new ArrayList<>(); private final Map> materialRegistry = new HashMap<>(); @@ -138,8 +137,6 @@ public class Backend { enabled = FlwConfig.get() .enabled() && !OptifineHandler.usingShaders(); - chunkCachingEnabled = FlwConfig.get() - .chunkCaching(); } public boolean canUseInstancing(@Nullable Level world) { diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java index c4867c513..f380bd2d0 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java @@ -29,7 +29,8 @@ public class InstancedRenderDispatcher { * @param te The tile whose instance you want to update. */ public static void enqueueUpdate(BlockEntity te) { - getTiles(te.getLevel()).queueUpdate(te); + if (te.hasLevel() && te.getLevel() instanceof ClientLevel) + getTiles(te.getLevel()).queueUpdate(te); } /** diff --git a/src/main/java/com/jozufozu/flywheel/config/BooleanConfig.java b/src/main/java/com/jozufozu/flywheel/config/BooleanConfig.java index 1702dc685..183941ac2 100644 --- a/src/main/java/com/jozufozu/flywheel/config/BooleanConfig.java +++ b/src/main/java/com/jozufozu/flywheel/config/BooleanConfig.java @@ -9,6 +9,7 @@ import com.jozufozu.flywheel.backend.OptifineHandler; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.TextComponent; @@ -18,7 +19,6 @@ import net.minecraftforge.api.distmarker.OnlyIn; public enum BooleanConfig { ENGINE(() -> BooleanConfig::enabled), NORMAL_OVERLAY(() -> BooleanConfig::normalOverlay), - CHUNK_CACHING(() -> BooleanConfig::chunkCaching), ; final Supplier> receiver; @@ -31,6 +31,27 @@ public enum BooleanConfig { return new SConfigureBooleanPacket(this, directive); } + /** + * Encode a variant of BooleanConfig. Symmetrical function to {@link #decode} + */ + public void encode(FriendlyByteBuf buffer) { + buffer.writeByte(this.ordinal()); + } + + /** + * Safely decode a variant of BooleanConfig. Symmetrical function to {@link #encode} + */ + public static BooleanConfig decode(FriendlyByteBuf buffer) { + byte t = buffer.readByte(); + BooleanConfig[] values = values(); + // Protects against version differences. + // Shouldn't ever happen but do a sanity check for safety. + if (t >= 0 && t < values.length) + return values[t]; + else + return null; + } + @OnlyIn(Dist.CLIENT) private static void enabled(BooleanDirective state) { LocalPlayer player = Minecraft.getInstance().player; @@ -72,25 +93,6 @@ public enum BooleanConfig { player.displayClientMessage(text, false); } - @OnlyIn(Dist.CLIENT) - private static void chunkCaching(BooleanDirective state) { - LocalPlayer player = Minecraft.getInstance().player; - if (player == null || state == null) return; - - if (state == BooleanDirective.DISPLAY) { - Component text = new TextComponent("Chunk caching is currently: ").append(boolToText(FlwConfig.get().chunkCaching())); - player.displayClientMessage(text, false); - return; - } - - FlwConfig.get().client.chunkCaching.set(state.get()); - - Component text = boolToText(FlwConfig.get().chunkCaching()).append(new TextComponent(" chunk caching").withStyle(ChatFormatting.WHITE)); - - player.displayClientMessage(text, false); - Backend.reloadWorldRenderers(); - } - private static MutableComponent boolToText(boolean b) { return b ? new TextComponent("enabled").withStyle(ChatFormatting.DARK_GREEN) : new TextComponent("disabled").withStyle(ChatFormatting.RED); } diff --git a/src/main/java/com/jozufozu/flywheel/config/BooleanDirective.java b/src/main/java/com/jozufozu/flywheel/config/BooleanDirective.java index d0ff5e62d..86dabad8f 100644 --- a/src/main/java/com/jozufozu/flywheel/config/BooleanDirective.java +++ b/src/main/java/com/jozufozu/flywheel/config/BooleanDirective.java @@ -1,5 +1,7 @@ package com.jozufozu.flywheel.config; +import net.minecraft.network.FriendlyByteBuf; + public enum BooleanDirective { TRUE(true), FALSE(false), @@ -16,7 +18,21 @@ public enum BooleanDirective { } public boolean get() { - if (this == DISPLAY) throw new IllegalStateException("Cannot get value from DISPLAY directive"); + if (this == DISPLAY) throw new IllegalStateException("DISPLAY directive has no value"); return b; } + + /** + * Encode a variant of BooleanDirective. Symmetrical function to {@link #decode} + */ + public void encode(FriendlyByteBuf buffer) { + buffer.writeByte(this.ordinal()); + } + + /** + * Safely decode a variant of BooleanDirective. Symmetrical function to {@link #encode} + */ + public static BooleanDirective decode(FriendlyByteBuf buffer) { + return values()[buffer.readByte()]; + } } diff --git a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java index 054683390..ef79eda16 100644 --- a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java +++ b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java @@ -17,7 +17,6 @@ public class FlwCommands { dispatcher.register(Commands.literal("flywheel") .then(new BooleanConfigCommand("backend", BooleanConfig.ENGINE).register()) .then(new BooleanConfigCommand("debugNormals", BooleanConfig.NORMAL_OVERLAY).register()) - .then(new BooleanConfigCommand("chunkCaching", BooleanConfig.CHUNK_CACHING).register()) ); } } diff --git a/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java b/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java index 359d7111e..0fd5a2673 100644 --- a/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java +++ b/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java @@ -34,17 +34,12 @@ public class FlwConfig { return client.debugNormals.get(); } - public boolean chunkCaching() { - return client.chunkCaching.get(); - } - public static void init() { } public static class ClientConfig { public final BooleanValue enabled; public final BooleanValue debugNormals; - public final BooleanValue chunkCaching; public ClientConfig(ForgeConfigSpec.Builder builder) { @@ -53,9 +48,6 @@ public class FlwConfig { debugNormals = builder.comment("Enable or disable a debug overlay that colors pixels by their normal") .define("debugNormals", false); - - chunkCaching = builder.comment("Cache chunk lookups to improve performance.") - .define("chunkCaching", true); } } } diff --git a/src/main/java/com/jozufozu/flywheel/config/SConfigureBooleanPacket.java b/src/main/java/com/jozufozu/flywheel/config/SConfigureBooleanPacket.java index 9d81f69ce..86f2562c5 100644 --- a/src/main/java/com/jozufozu/flywheel/config/SConfigureBooleanPacket.java +++ b/src/main/java/com/jozufozu/flywheel/config/SConfigureBooleanPacket.java @@ -20,18 +20,20 @@ public class SConfigureBooleanPacket { } public SConfigureBooleanPacket(FriendlyByteBuf buffer) { - target = BooleanConfig.values()[buffer.readByte()]; - directive = BooleanDirective.values()[buffer.readByte()]; + target = BooleanConfig.decode(buffer); + directive = BooleanDirective.decode(buffer); } public void encode(FriendlyByteBuf buffer) { - buffer.writeByte(target.ordinal()); - buffer.writeByte(directive.ordinal()); + target.encode(buffer); + directive.encode(buffer); } public void execute(Supplier ctx) { - target.receiver.get() - .accept(directive); + if (directive != null) { + target.receiver.get() + .accept(directive); + } ctx.get() .setPacketHandled(true); } diff --git a/src/main/java/com/jozufozu/flywheel/event/ForgeEvents.java b/src/main/java/com/jozufozu/flywheel/event/ForgeEvents.java index a61b81a63..5ca34fa40 100644 --- a/src/main/java/com/jozufozu/flywheel/event/ForgeEvents.java +++ b/src/main/java/com/jozufozu/flywheel/event/ForgeEvents.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; +import com.jozufozu.flywheel.util.WorldAttached; import com.jozufozu.flywheel.light.LightUpdater; import com.jozufozu.flywheel.util.ChunkIter; @@ -52,6 +53,7 @@ public class ForgeEvents { public static void unloadWorld(WorldEvent.Unload event) { LevelAccessor world = event.getWorld(); ChunkIter._unload(world); + WorldAttached.invalidateWorld(world); } @SubscribeEvent diff --git a/src/main/java/com/jozufozu/flywheel/util/ChunkIter.java b/src/main/java/com/jozufozu/flywheel/util/ChunkIter.java index eb106e5c6..3174368df 100644 --- a/src/main/java/com/jozufozu/flywheel/util/ChunkIter.java +++ b/src/main/java/com/jozufozu/flywheel/util/ChunkIter.java @@ -34,7 +34,7 @@ public class ChunkIter { } } - // INTERNAL MAINTENENCE METHODS BELOW + // INTERNAL MAINTENANCE METHODS BELOW public static void _putStorageReference(BlockGetter level, AtomicReferenceArray storage) { storages.put(level, storage); diff --git a/src/main/java/com/jozufozu/flywheel/util/WorldAttached.java b/src/main/java/com/jozufozu/flywheel/util/WorldAttached.java index 002ad3d86..408b54e51 100644 --- a/src/main/java/com/jozufozu/flywheel/util/WorldAttached.java +++ b/src/main/java/com/jozufozu/flywheel/util/WorldAttached.java @@ -1,6 +1,10 @@ package com.jozufozu.flywheel.util; +import java.lang.ref.WeakReference; +import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -12,12 +16,30 @@ import net.minecraft.world.level.LevelAccessor; public class WorldAttached { + // weak references to prevent leaking hashmaps when a WorldAttached is GC'd during runtime + static List>> allMaps = new ArrayList<>(); private final Map attached; private final Function factory; public WorldAttached(Function factory) { this.factory = factory; attached = new HashMap<>(); + allMaps.add(new WeakReference<>(attached)); + } + + public static void invalidateWorld(LevelAccessor world) { + Iterator>> i = allMaps.iterator(); + while (i.hasNext()) { + Map map = i.next() + .get(); + if (map == null) { + // If the map has been GC'd, remove the weak reference + i.remove(); + } else { + // Prevent leaks + map.remove(world); + } + } } @Nonnull