diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java index 994286d14..9c9d94e1e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java @@ -8,6 +8,7 @@ import com.jozufozu.flywheel.core.Contexts; import com.jozufozu.flywheel.core.shader.WorldProgram; import com.jozufozu.flywheel.event.BeginFrameEvent; import com.jozufozu.flywheel.event.RenderLayerEvent; +import com.jozufozu.flywheel.util.ChunkIter; import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.Minecraft; @@ -58,8 +59,9 @@ public class InstanceWorld { * Instantiate all the necessary instances to render the given world. */ public void loadAll(ClientLevel world) { - // FIXME: no more global blockEntity list - // world.blockEntityList.forEach(tileEntityInstanceManager::add); + ChunkIter.forEachChunk(world, chunk -> { + chunk.getBlockEntities().values().forEach(tileEntityInstanceManager::add); + }); world.entitiesForRendering() .forEach(entityInstanceManager::add); } diff --git a/src/main/java/com/jozufozu/flywheel/event/ForgeEvents.java b/src/main/java/com/jozufozu/flywheel/event/ForgeEvents.java index a4b454362..a61b81a63 100644 --- a/src/main/java/com/jozufozu/flywheel/event/ForgeEvents.java +++ b/src/main/java/com/jozufozu/flywheel/event/ForgeEvents.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.light.LightUpdater; +import com.jozufozu.flywheel.util.ChunkIter; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; @@ -47,6 +48,12 @@ public class ForgeEvents { } } + @SubscribeEvent + public static void unloadWorld(WorldEvent.Unload event) { + LevelAccessor world = event.getWorld(); + ChunkIter._unload(world); + } + @SubscribeEvent public static void tickLight(TickEvent.ClientTickEvent e) { if (e.phase == TickEvent.Phase.END && Backend.isGameActive()) diff --git a/src/main/java/com/jozufozu/flywheel/mixin/LeakChunkStorageArrayMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/LeakChunkStorageArrayMixin.java new file mode 100644 index 000000000..1a9bce762 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/mixin/LeakChunkStorageArrayMixin.java @@ -0,0 +1,39 @@ +package com.jozufozu.flywheel.mixin; + +import java.util.concurrent.atomic.AtomicReferenceArray; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.jozufozu.flywheel.util.ChunkIter; + +import net.minecraft.client.multiplayer.ClientChunkCache; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +/** + * In order to iterate over all loaded chunks, we do something absolutely foul. + * + *

+ * By stealing the reference to the backing array of the chunk storage when it is constructed, we gain 0 maintenance + * access to the full array of loaded chunks. + *

+ */ +@OnlyIn(Dist.CLIENT) +@Mixin(targets = "net.minecraft.client.multiplayer.ClientChunkCache$Storage") +public class LeakChunkStorageArrayMixin { + + @Shadow + @Final + AtomicReferenceArray chunks; + + @Inject(method = "", at = @At("TAIL")) + private void leakBackingArray(ClientChunkCache outer, int chunkRadius, CallbackInfo ci) { + ChunkIter._putStorageReference(outer.getLevel(), chunks); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/util/ChunkIter.java b/src/main/java/com/jozufozu/flywheel/util/ChunkIter.java new file mode 100644 index 000000000..eb106e5c6 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/util/ChunkIter.java @@ -0,0 +1,46 @@ +package com.jozufozu.flywheel.util; + +import java.util.WeakHashMap; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.function.Consumer; + +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +/** + * Helper class for iterating over all loaded chunks. + */ +@OnlyIn(Dist.CLIENT) +public class ChunkIter { + + private static final WeakHashMap> storages = new WeakHashMap<>(); + + /** + * Iterate over all loaded chunks in a level. + */ + public static void forEachChunk(BlockGetter level, Consumer consumer) { + AtomicReferenceArray storage = storages.get(level); + + if (storage == null) + return; + + for (int i = 0; i < storage.length(); i++) { + LevelChunk chunk = storage.get(i); + if (chunk != null) { + consumer.accept(chunk); + } + } + } + + // INTERNAL MAINTENENCE METHODS BELOW + + public static void _putStorageReference(BlockGetter level, AtomicReferenceArray storage) { + storages.put(level, storage); + } + + public static void _unload(BlockGetter world) { + storages.remove(world); + } +} diff --git a/src/main/resources/flywheel.mixins.json b/src/main/resources/flywheel.mixins.json index 803cf7b14..30a4c08bb 100644 --- a/src/main/resources/flywheel.mixins.json +++ b/src/main/resources/flywheel.mixins.json @@ -16,7 +16,8 @@ "light.NetworkLightUpdateMixin", "LevelRendererAccessor", "InstanceAddMixin", - "InstanceRemoveMixin" + "InstanceRemoveMixin", + "LeakChunkStorageArrayMixin" ], "injectors": { "defaultRequire": 1