diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/AbstractInstancer.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/AbstractInstancer.java index 717e3640d..57e9fe712 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/AbstractInstancer.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/AbstractInstancer.java @@ -11,10 +11,10 @@ import dev.engine_room.flywheel.api.instance.Instancer; import dev.engine_room.flywheel.backend.engine.embed.Environment; import dev.engine_room.flywheel.backend.util.AtomicBitSet; -public abstract class AbstractInstancer implements Instancer { +public abstract class AbstractInstancer implements Instancer, InstanceHandleImpl.State { public final InstanceType type; public final Environment environment; - private final Supplier> recreate; + private final InstanceHandleImpl.Hidden hidden; // Lock for all instances, only needs to be used in methods that may run on the TaskExecutor. protected final Object lock = new Object(); @@ -27,20 +27,45 @@ public abstract class AbstractInstancer implements Instancer protected AbstractInstancer(InstancerKey key, Supplier> recreate) { this.type = key.type(); this.environment = key.environment(); - this.recreate = recreate; + this.hidden = new InstanceHandleImpl.Hidden<>(recreate); + } + + @Override + public InstanceHandleImpl.State setChanged(int index) { + notifyDirty(index); + return this; + } + + @Override + public InstanceHandleImpl.State setDeleted(int index) { + notifyRemoval(index); + return InstanceHandleImpl.Deleted.instance(); + } + + @Override + public InstanceHandleImpl.State setVisible(int index, I instance, boolean visible) { + if (visible) { + return this; + } + + notifyRemoval(index); + + return hidden; + } + + @Override + public InstanceHandleImpl.Status status() { + return InstanceHandleImpl.Status.VISIBLE; } @Override public I createInstance() { - synchronized (lock) { - var i = instances.size(); - var handle = new InstanceHandleImpl(); - handle.instancer = this; - handle.recreate = recreate; - handle.index = i; - I instance = type.create(handle); - handle.instance = instance; + var handle = new InstanceHandleImpl<>(this); + I instance = type.create(handle); + handle.instance = instance; + synchronized (lock) { + handle.index = instances.size(); addLocked(instance, handle); return instance; } @@ -62,7 +87,8 @@ public abstract class AbstractInstancer implements Instancer // Should InstanceType have an isInstance method? var handle = (InstanceHandleImpl) instanceHandle; - if (handle.instancer == this && handle.visible) { + // Should you be allowed to steal deleted instances? + if (handle.state == this || handle.state.status() == InstanceHandleImpl.Status.DELETED) { return; } @@ -76,15 +102,18 @@ public abstract class AbstractInstancer implements Instancer handle.setDeleted(); // Add the instance to this instancer. - handle.instancer = this; - handle.recreate = recreate; - - if (handle.visible) { + switch (handle.state.status()) { + case VISIBLE: + handle.state = this; // Only lock now that we'll be mutating our state. synchronized (lock) { handle.index = instances.size(); addLocked(instance, handle); } + break; + case HIDDEN: + handle.state = hidden; + break; } } @@ -187,8 +216,9 @@ public abstract class AbstractInstancer implements Instancer // clearing it here would cause significant visual artifacts and instance leaks. // At the same time, we need to clear handles we own to prevent // instances from changing/deleting positions in this instancer that no longer exist. - if (handle.instancer == this) { + if (handle.state == this || handle.state == hidden) { handle.clear(); + handle.state = InstanceHandleImpl.Deleted.instance(); } } instances.clear(); diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java index 06886d0fe..20e27976c 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java @@ -115,8 +115,11 @@ public abstract class DrawManager> { continue; } - AbstractInstancer abstractInstancer = impl.instancer; + InstanceHandleImpl.State abstractInstancer = impl.state; + // AbstractInstancer directly implement HandleState, so this check is valid. if (!clazz.isInstance(abstractInstancer)) { + // This rejects instances that were created by a different engine, + // and also instances that are hidden or deleted. continue; } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstanceHandleImpl.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstanceHandleImpl.java index 5641cb96c..32e311963 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstanceHandleImpl.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstanceHandleImpl.java @@ -8,49 +8,110 @@ import dev.engine_room.flywheel.api.instance.Instance; import dev.engine_room.flywheel.api.instance.InstanceHandle; public class InstanceHandleImpl implements InstanceHandle { - @UnknownNullability - public AbstractInstancer instancer; + public State state; @UnknownNullability public I instance; - @UnknownNullability - public Supplier> recreate; - public boolean visible = true; public int index; + public InstanceHandleImpl(State state) { + this.state = state; + } + @Override public void setChanged() { - instancer.notifyDirty(index); + state = state.setChanged(index); } @Override public void setDeleted() { - instancer.notifyRemoval(index); + state = state.setDeleted(index); // invalidate ourselves clear(); } @Override public void setVisible(boolean visible) { - if (this.visible == visible) { - return; - } - - this.visible = visible; - - if (visible) { - recreate.get().stealInstance(instance); - } else { - instancer.notifyRemoval(index); - clear(); - } + state = state.setVisible(index, instance, visible); } @Override public boolean isVisible() { - return visible; + return state.status() == Status.VISIBLE; } public void clear() { index = -1; } + + public enum Status { + HIDDEN, + DELETED, + VISIBLE + } + + public interface State { + State setChanged(int index); + + State setDeleted(int index); + + State setVisible(int index, I instance, boolean visible); + + Status status(); + } + + public record Hidden(Supplier> supplier) implements State { + @Override + public State setChanged(int index) { + return this; + } + + @Override + public State setDeleted(int index) { + return this; + } + + @Override + public State setVisible(int index, I instance, boolean visible) { + if (!visible) { + return this; + } + var instancer = supplier.get(); + instancer.stealInstance(instance); + return instancer; + } + + @Override + public Status status() { + return Status.HIDDEN; + } + } + + public record Deleted() implements State { + private static final Deleted INSTANCE = new Deleted<>(); + + @SuppressWarnings("unchecked") + public static Deleted instance() { + return (Deleted) INSTANCE; + } + + @Override + public State setChanged(int index) { + return this; + } + + @Override + public State setDeleted(int index) { + return this; + } + + @Override + public State setVisible(int index, I instance, boolean visible) { + return this; + } + + @Override + public Status status() { + return Status.DELETED; + } + } }