diff --git a/gradle.properties b/gradle.properties index 45c6f4660..5ab61dd67 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ org.gradle.jvmargs = -Xmx3G org.gradle.daemon = false # mod version info -mod_version = 0.6.3 +mod_version = 0.6.4 mc_update_version = 1.18 minecraft_version = 1.18.2 forge_version = 40.0.15 diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstance.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstance.java index 6a3e67ba8..d039f402e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstance.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstance.java @@ -10,8 +10,6 @@ import com.jozufozu.flywheel.api.instance.TickableInstance; import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceManager; import com.jozufozu.flywheel.core.materials.FlatLit; import com.jozufozu.flywheel.light.LightListener; -import com.jozufozu.flywheel.light.LightProvider; -import com.jozufozu.flywheel.light.ListenerStatus; import com.jozufozu.flywheel.util.box.ImmutableBox; import net.minecraft.core.BlockPos; @@ -26,6 +24,7 @@ public abstract class AbstractInstance implements Instance, LightListener { protected final MaterialManager materialManager; public final Level world; + protected boolean removed = false; public AbstractInstance(MaterialManager materialManager, Level world) { this.materialManager = materialManager; @@ -39,10 +38,19 @@ public abstract class AbstractInstance implements Instance, LightListener { } + final void removeAndMark() { + if (removed) { + return; + } + + remove(); + removed = true; + } + /** * Free any acquired resources. */ - public abstract void remove(); + protected abstract void remove(); /** * Update instance data here. Good for when data doesn't change very often and when animations are GPU based. @@ -78,12 +86,12 @@ public abstract class AbstractInstance implements Instance, LightListener { } @Override - public ListenerStatus status() { - return ListenerStatus.OKAY; + public boolean isListenerInvalid() { + return removed; } @Override - public void onLightUpdate(LightProvider world, LightLayer type, ImmutableBox changed) { + public void onLightUpdate(LightLayer type, ImmutableBox changed) { updateLight(); } @@ -103,4 +111,5 @@ public abstract class AbstractInstance implements Instance, LightListener { models.forEach(model -> model.setBlockLight(block) .setSkyLight(sky)); } + } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java index 7f741cd8c..1f8310d1e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java @@ -260,7 +260,7 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift } public void invalidate() { - instances.values().forEach(AbstractInstance::remove); + instances.values().forEach(AbstractInstance::removeAndMark); instances.clear(); dynamicInstances.clear(); tickableInstances.clear(); @@ -314,7 +314,7 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift } protected void removeInternal(T obj, AbstractInstance instance) { - instance.remove(); + instance.removeAndMark(); instances.remove(obj); dynamicInstances.remove(obj); tickableInstances.remove(obj); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstance.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstance.java index 02e03435c..d7025fe57 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstance.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstance.java @@ -6,8 +6,7 @@ import com.jozufozu.flywheel.api.instance.TickableInstance; import com.jozufozu.flywheel.backend.instancing.AbstractInstance; import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceManager; import com.jozufozu.flywheel.light.LightListener; -import com.jozufozu.flywheel.light.LightProvider; -import com.jozufozu.flywheel.light.MovingListener; +import com.jozufozu.flywheel.light.TickingLightListener; import com.jozufozu.flywheel.util.box.GridAlignedBB; import com.mojang.math.Vector3f; @@ -34,7 +33,7 @@ import net.minecraft.world.phys.Vec3; * * @param The type of {@link Entity} your class is an instance of. */ -public abstract class EntityInstance extends AbstractInstance implements LightListener, MovingListener { +public abstract class EntityInstance extends AbstractInstance implements LightListener, TickingLightListener { protected final E entity; protected final GridAlignedBB bounds; @@ -51,7 +50,7 @@ public abstract class EntityInstance extends AbstractInstance } @Override - public boolean update(LightProvider provider) { + public boolean tickLightListener() { AABB boundsNow = entity.getBoundingBox(); if (bounds.sameAs(boundsNow)) return false; diff --git a/src/main/java/com/jozufozu/flywheel/light/BasicProvider.java b/src/main/java/com/jozufozu/flywheel/light/BasicProvider.java deleted file mode 100644 index fb5d3b189..000000000 --- a/src/main/java/com/jozufozu/flywheel/light/BasicProvider.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.jozufozu.flywheel.light; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.BlockAndTintGetter; -import net.minecraft.world.level.LightLayer; - -/** - * Wraps a world and minimally lowers the interface. - */ -public class BasicProvider implements LightProvider { - - private final BlockAndTintGetter reader; - private final BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); - - public BasicProvider(BlockAndTintGetter reader) { - this.reader = reader; - } - - @Override - public int getLight(LightLayer type, int x, int y, int z) { - return reader.getBrightness(type, pos.set(x, y, z)); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/light/GPULightVolume.java b/src/main/java/com/jozufozu/flywheel/light/GPULightVolume.java index fee086071..6064f5e8d 100644 --- a/src/main/java/com/jozufozu/flywheel/light/GPULightVolume.java +++ b/src/main/java/com/jozufozu/flywheel/light/GPULightVolume.java @@ -26,6 +26,7 @@ import com.jozufozu.flywheel.backend.gl.GlTextureUnit; import com.jozufozu.flywheel.util.box.GridAlignedBB; import com.jozufozu.flywheel.util.box.ImmutableBox; +import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.LightLayer; public class GPULightVolume extends LightVolume { @@ -36,8 +37,8 @@ public class GPULightVolume extends LightVolume { private final GlTextureUnit textureUnit = GlTextureUnit.T4; protected boolean bufferDirty; - public GPULightVolume(ImmutableBox sampleVolume) { - super(sampleVolume); + public GPULightVolume(BlockAndTintGetter level, ImmutableBox sampleVolume) { + super(level, sampleVolume); this.sampleVolume.assign(sampleVolume); glTexture = new GlTexture(GL_TEXTURE_3D); @@ -110,38 +111,24 @@ public class GPULightVolume extends LightVolume { glTexture.delete(); } - public void move(LightProvider world, ImmutableBox newSampleVolume) { + public void move(BlockAndTintGetter level, ImmutableBox newSampleVolume) { if (lightData == null) return; if (box.contains(newSampleVolume)) { sampleVolume.assign(newSampleVolume); - initialize(world); + initialize(); } else { - super.move(world, newSampleVolume); + super.move(level, newSampleVolume); } } - public void onLightUpdate(LightProvider world, LightLayer type, ImmutableBox changedVolume) { - super.onLightUpdate(world, type, changedVolume); - bufferDirty = true; - } - - public void onLightPacket(LightProvider world, int chunkX, int chunkZ) { - super.onLightPacket(world, chunkX, chunkZ); - bufferDirty = true; - } - - /** - * Completely (re)populate this volume with block and sky lighting data. - * This is expensive and should be avoided. - */ - public void initialize(LightProvider world) { - super.initialize(world); - bufferDirty = true; - } - @Override public ImmutableBox getVolume() { return sampleVolume; } + + @Override + protected void markDirty() { + this.bufferDirty = true; + } } diff --git a/src/main/java/com/jozufozu/flywheel/light/LightListener.java b/src/main/java/com/jozufozu/flywheel/light/LightListener.java index 396ae6bbd..55a1e8020 100644 --- a/src/main/java/com/jozufozu/flywheel/light/LightListener.java +++ b/src/main/java/com/jozufozu/flywheel/light/LightListener.java @@ -5,26 +5,37 @@ import com.jozufozu.flywheel.util.box.ImmutableBox; import net.minecraft.world.level.LightLayer; +/** + * Implementors of this interface may choose to subscribe to light updates by calling + * {@link LightUpdater#addListener(LightListener)}.

+ * + * It is the responsibility of the implementor to keep a reference to the level an object is contained in. + */ public interface LightListener { ImmutableBox getVolume(); - ListenerStatus status(); + /** + * Check the status of the light listener. + * @return {@code true} if the listener is invalid/removed/deleted, + * and should no longer receive updates. + */ + boolean isListenerInvalid(); /** * Called when a light updates in a chunk the implementor cares about. */ - void onLightUpdate(LightProvider world, LightLayer type, ImmutableBox changed); + void onLightUpdate(LightLayer type, ImmutableBox changed); /** * Called when the server sends light data to the client. * */ - default void onLightPacket(LightProvider world, int chunkX, int chunkZ) { + default void onLightPacket(int chunkX, int chunkZ) { GridAlignedBB changedVolume = GridAlignedBB.from(chunkX, chunkZ); - onLightUpdate(world, LightLayer.BLOCK, changedVolume); + onLightUpdate(LightLayer.BLOCK, changedVolume); - onLightUpdate(world, LightLayer.SKY, changedVolume); + onLightUpdate(LightLayer.SKY, changedVolume); } } diff --git a/src/main/java/com/jozufozu/flywheel/light/LightProvider.java b/src/main/java/com/jozufozu/flywheel/light/LightProvider.java deleted file mode 100644 index c95a04df7..000000000 --- a/src/main/java/com/jozufozu/flywheel/light/LightProvider.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.jozufozu.flywheel.light; - -import net.minecraft.client.renderer.LightTexture; -import net.minecraft.world.level.LightLayer; - -public interface LightProvider { - int getLight(LightLayer type, int x, int y, int z); - - default int getPackedLight(int x, int y, int z) { - return LightTexture.pack(getLight(LightLayer.BLOCK, x, y, z), getLight(LightLayer.SKY, x, y, z)); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/light/LightUpdater.java b/src/main/java/com/jozufozu/flywheel/light/LightUpdater.java index 4958fd682..9cce9fd44 100644 --- a/src/main/java/com/jozufozu/flywheel/light/LightUpdater.java +++ b/src/main/java/com/jozufozu/flywheel/light/LightUpdater.java @@ -39,19 +39,15 @@ public class LightUpdater { } } - private final LightProvider provider; + private final LevelAccessor level; - private final WeakHashSet movingListeners = new WeakHashSet<>(); + private final WeakHashSet tickingLightListeners = new WeakHashSet<>(); private final WeakContainmentMultiMap sections = new WeakContainmentMultiMap<>(); private final WeakContainmentMultiMap chunks = new WeakContainmentMultiMap<>(); - public LightUpdater(LevelAccessor world) { + public LightUpdater(LevelAccessor level) { taskEngine = Backend.getTaskEngine(); - provider = new BasicProvider(world); - } - - public LightProvider getProvider() { - return provider; + this.level = level; } public void tick() { @@ -60,9 +56,9 @@ public class LightUpdater { } private void tickSerial() { - for (MovingListener movingListener : movingListeners) { - if (movingListener.update(provider)) { - addListener(movingListener); + for (TickingLightListener tickingLightListener : tickingLightListeners) { + if (tickingLightListener.tickLightListener()) { + addListener(tickingLightListener); } } } @@ -71,8 +67,8 @@ public class LightUpdater { Queue listeners = new ConcurrentLinkedQueue<>(); taskEngine.group("LightUpdater") - .addTasks(movingListeners.stream(), listener -> { - if (listener.update(provider)) { + .addTasks(tickingLightListeners.stream(), listener -> { + if (listener.tickLightListener()) { listeners.add(listener); } }) @@ -86,8 +82,8 @@ public class LightUpdater { * @param listener The object that wants to receive light update notifications. */ public void addListener(LightListener listener) { - if (listener instanceof MovingListener) - movingListeners.add(((MovingListener) listener)); + if (listener instanceof TickingLightListener) + tickingLightListeners.add(((TickingLightListener) listener)); ImmutableBox box = listener.getVolume(); @@ -130,12 +126,12 @@ public class LightUpdater { if (set == null || set.isEmpty()) return; - set.removeIf(l -> l.status().shouldRemove()); + set.removeIf(LightListener::isListenerInvalid); ImmutableBox chunkBox = GridAlignedBB.from(SectionPos.of(sectionPos)); for (LightListener listener : set) { - listener.onLightUpdate(provider, type, chunkBox); + listener.onLightUpdate(type, chunkBox); } } @@ -151,10 +147,10 @@ public class LightUpdater { if (set == null || set.isEmpty()) return; - set.removeIf(l -> l.status().shouldRemove()); + set.removeIf(LightListener::isListenerInvalid); for (LightListener listener : set) { - listener.onLightPacket(provider, chunkX, chunkZ); + listener.onLightPacket(chunkX, chunkZ); } } diff --git a/src/main/java/com/jozufozu/flywheel/light/LightVolume.java b/src/main/java/com/jozufozu/flywheel/light/LightVolume.java index b5a830f86..e2fc15d13 100644 --- a/src/main/java/com/jozufozu/flywheel/light/LightVolume.java +++ b/src/main/java/com/jozufozu/flywheel/light/LightVolume.java @@ -8,14 +8,17 @@ import com.jozufozu.flywheel.util.box.GridAlignedBB; import com.jozufozu.flywheel.util.box.ImmutableBox; import net.minecraft.core.BlockPos; +import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.LightLayer; public class LightVolume implements ImmutableBox, LightListener { + protected final BlockAndTintGetter level; protected final GridAlignedBB box = new GridAlignedBB(); protected ByteBuffer lightData; - public LightVolume(ImmutableBox sampleVolume) { + public LightVolume(BlockAndTintGetter level, ImmutableBox sampleVolume) { + this.level = level; this.setBox(sampleVolume); this.lightData = MemoryUtil.memAlloc(this.box.volume() * 2); @@ -57,7 +60,7 @@ public class LightVolume implements ImmutableBox, LightListener { return box.getMaxZ(); } - public void move(LightProvider world, ImmutableBox newSampleVolume) { + public void move(BlockAndTintGetter level, ImmutableBox newSampleVolume) { if (lightData == null) return; setBox(newSampleVolume); @@ -65,51 +68,43 @@ public class LightVolume implements ImmutableBox, LightListener { if (neededCapacity > lightData.capacity()) { lightData = MemoryUtil.memRealloc(lightData, neededCapacity); } - initialize(world); + initialize(); } @Override - public void onLightUpdate(LightProvider world, LightLayer type, ImmutableBox changedVolume) { + public void onLightUpdate(LightLayer type, ImmutableBox changedVolume) { if (lightData == null) return; GridAlignedBB vol = changedVolume.copy(); if (!vol.intersects(getVolume())) return; vol.intersectAssign(getVolume()); // compute the region contained by us that has dirty lighting data. - if (type == LightLayer.BLOCK) copyBlock(world, vol); - else if (type == LightLayer.SKY) copySky(world, vol); + if (type == LightLayer.BLOCK) copyBlock(vol); + else if (type == LightLayer.SKY) copySky(vol); + markDirty(); } @Override - public void onLightPacket(LightProvider world, int chunkX, int chunkZ) { + public void onLightPacket(int chunkX, int chunkZ) { if (lightData == null) return; GridAlignedBB changedVolume = GridAlignedBB.from(chunkX, chunkZ); if (!changedVolume.intersects(getVolume())) return; changedVolume.intersectAssign(getVolume()); // compute the region contained by us that has dirty lighting data. - copyLight(world, changedVolume); + copyLight(changedVolume); + markDirty(); } /** * Completely (re)populate this volume with block and sky lighting data. * This is expensive and should be avoided. */ - public void initialize(LightProvider world) { + public void initialize() { if (lightData == null) return; - // the volume is indexed based on the greater bounding box - int shiftX = box.getMinX(); - int shiftY = box.getMinY(); - int shiftZ = box.getMinZ(); - - // ... but we only iterate over the (potentially) smaller sample volume - getVolume().forEachContained((x, y, z) -> { - int blockLight = world.getLight(LightLayer.BLOCK, x, y, z); - int skyLight = world.getLight(LightLayer.SKY, x, y, z); - - writeLight(x - shiftX, y - shiftY, z - shiftZ, blockLight, skyLight); - }); + copyLight(getVolume()); + markDirty(); } /** @@ -117,13 +112,15 @@ public class LightVolume implements ImmutableBox, LightListener { * * @param worldVolume the region in the world to copy data from. */ - public void copyBlock(LightProvider world, ImmutableBox worldVolume) { + public void copyBlock(ImmutableBox worldVolume) { + var pos = new BlockPos.MutableBlockPos(); + int xShift = box.getMinX(); int yShift = box.getMinY(); int zShift = box.getMinZ(); worldVolume.forEachContained((x, y, z) -> { - int light = world.getLight(LightLayer.BLOCK, x, y, z); + int light = this.level.getBrightness(LightLayer.BLOCK, pos.set(x, y, z)); writeBlock(x - xShift, y - yShift, z - zShift, light); }); @@ -134,13 +131,15 @@ public class LightVolume implements ImmutableBox, LightListener { * * @param worldVolume the region in the world to copy data from. */ - public void copySky(LightProvider world, ImmutableBox worldVolume) { + public void copySky(ImmutableBox worldVolume) { + var pos = new BlockPos.MutableBlockPos(); + int xShift = box.getMinX(); int yShift = box.getMinY(); int zShift = box.getMinZ(); worldVolume.forEachContained((x, y, z) -> { - int light = world.getLight(LightLayer.SKY, x, y, z); + int light = this.level.getBrightness(LightLayer.SKY, pos.set(x, y, z)); writeSky(x - xShift, y - yShift, z - zShift, light); }); @@ -151,7 +150,7 @@ public class LightVolume implements ImmutableBox, LightListener { * * @param worldVolume the region in the world to copy data from. */ - public void copyLight(LightProvider world, ImmutableBox worldVolume) { + public void copyLight(ImmutableBox worldVolume) { BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); int xShift = box.getMinX(); @@ -161,8 +160,8 @@ public class LightVolume implements ImmutableBox, LightListener { worldVolume.forEachContained((x, y, z) -> { pos.set(x, y, z); - int block = world.getLight(LightLayer.BLOCK, x, y, z); - int sky = world.getLight(LightLayer.SKY, x, y, z); + int block = this.level.getBrightness(LightLayer.BLOCK, pos); + int sky = this.level.getBrightness(LightLayer.SKY, pos); writeLight(x - xShift, y - yShift, z - zShift, block, sky); }); @@ -173,6 +172,10 @@ public class LightVolume implements ImmutableBox, LightListener { lightData = null; } + protected void markDirty() { + // noop + } + protected void writeLight(int x, int y, int z, int block, int sky) { byte b = (byte) ((block & 0xF) << 4); byte s = (byte) ((sky & 0xF) << 4); @@ -211,8 +214,8 @@ public class LightVolume implements ImmutableBox, LightListener { } @Override - public ListenerStatus status() { - return ListenerStatus.OKAY; + public boolean isListenerInvalid() { + return lightData == null; } } diff --git a/src/main/java/com/jozufozu/flywheel/light/ListenerStatus.java b/src/main/java/com/jozufozu/flywheel/light/ListenerStatus.java deleted file mode 100644 index e8ca36d30..000000000 --- a/src/main/java/com/jozufozu/flywheel/light/ListenerStatus.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.jozufozu.flywheel.light; - -public enum ListenerStatus { - OKAY, - REMOVE, - UPDATE, - ; - - public boolean isOk() { - return this == OKAY; - } - - public boolean shouldRemove() { - return this == REMOVE; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/light/MovingListener.java b/src/main/java/com/jozufozu/flywheel/light/MovingListener.java deleted file mode 100644 index 388d6fdd7..000000000 --- a/src/main/java/com/jozufozu/flywheel/light/MovingListener.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.jozufozu.flywheel.light; - -public interface MovingListener extends LightListener { - boolean update(LightProvider provider); -} diff --git a/src/main/java/com/jozufozu/flywheel/light/TickingLightListener.java b/src/main/java/com/jozufozu/flywheel/light/TickingLightListener.java new file mode 100644 index 000000000..cd3d00b7a --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/light/TickingLightListener.java @@ -0,0 +1,9 @@ +package com.jozufozu.flywheel.light; + +public interface TickingLightListener extends LightListener { + /** + * Called every tick for active listeners. + * @return {@code true} if the listener changed. + */ + boolean tickLightListener(); +} diff --git a/src/main/java/com/jozufozu/flywheel/light/package-info.java b/src/main/java/com/jozufozu/flywheel/light/package-info.java new file mode 100644 index 000000000..da21c0a32 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/light/package-info.java @@ -0,0 +1,7 @@ + +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.light; + +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.MethodsReturnNonnullByDefault;