More events, fewer subclassing.

This commit is contained in:
Florian Nücke
2021-02-03 01:09:52 +01:00
parent 64a7776d38
commit 81ee809560
12 changed files with 120 additions and 99 deletions

View File

@@ -4,13 +4,15 @@ import li.cil.oc2.api.bus.DeviceBusController;
import li.cil.oc2.api.bus.DeviceBusElement;
import li.cil.oc2.api.bus.device.Device;
import li.cil.oc2.common.Constants;
import li.cil.oc2.common.util.Event;
import li.cil.oc2.common.util.ParameterizedEvent;
import net.minecraftforge.common.util.LazyOptional;
import java.util.*;
import static java.util.Collections.emptySet;
public abstract class AbstractDeviceBusController implements DeviceBusController {
public class CommonDeviceBusController implements DeviceBusController {
public enum BusState {
SCAN_PENDING,
INCOMPLETE,
@@ -27,6 +29,12 @@ public abstract class AbstractDeviceBusController implements DeviceBusController
///////////////////////////////////////////////////////////////////
public final Event onAfterBusScan = new Event();
public final Event onBeforeScan = new Event();
public final ParameterizedEvent<AfterDeviceScanEvent> onAfterDeviceScan = new ParameterizedEvent<>();
public final ParameterizedEvent<DevicesChangedEvent> onDevicesAdded = new ParameterizedEvent<>();
public final ParameterizedEvent<DevicesChangedEvent> onDevicesRemoved = new ParameterizedEvent<>();
private final DeviceBusElement root;
private final Set<DeviceBusElement> elements = new HashSet<>();
@@ -38,7 +46,7 @@ public abstract class AbstractDeviceBusController implements DeviceBusController
///////////////////////////////////////////////////////////////////
protected AbstractDeviceBusController(final DeviceBusElement root) {
public CommonDeviceBusController(final DeviceBusElement root) {
this.root = root;
}
@@ -208,18 +216,23 @@ public abstract class AbstractDeviceBusController implements DeviceBusController
}
protected void onAfterBusScan() {
onAfterBusScan.run();
}
protected void onBeforeScan() {
onBeforeScan.run();
}
protected void onAfterDeviceScan(final boolean didDevicesChange) {
onAfterDeviceScan.accept(new AfterDeviceScanEvent(didDevicesChange));
}
protected void onDevicesAdded(final Collection<Device> devices) {
onDevicesAdded.accept(new DevicesChangedEvent(devices));
}
protected void onDevicesRemoved(final Collection<Device> devices) {
onDevicesRemoved.accept(new DevicesChangedEvent(devices));
}
///////////////////////////////////////////////////////////////////
@@ -231,4 +244,22 @@ public abstract class AbstractDeviceBusController implements DeviceBusController
elements.clear();
}
///////////////////////////////////////////////////////////////////
public static final class AfterDeviceScanEvent {
public final boolean didDevicesChange;
public AfterDeviceScanEvent(final boolean didDevicesChange) {
this.didDevicesChange = didDevicesChange;
}
}
public static final class DevicesChangedEvent {
public final Collection<Device> devices;
public DevicesChangedEvent(final Collection<Device> devices) {
this.devices = devices;
}
}
}

View File

@@ -12,7 +12,7 @@ import net.minecraft.world.World;
import java.util.Collection;
import java.util.HashSet;
public class TileEntityDeviceBusController extends AbstractDeviceBusController {
public class TileEntityDeviceBusController extends CommonDeviceBusController {
private final Runnable onBusChunkLoadedStateChanged = this::scheduleBusScan;
private final HashSet<ChunkPos> trackedChunks = new HashSet<>();
private final TileEntity tileEntity;
@@ -38,6 +38,8 @@ public class TileEntityDeviceBusController extends AbstractDeviceBusController {
@Override
protected void onAfterBusScan() {
super.onAfterBusScan();
final World world = tileEntity.getWorld();
if (world == null) {
return;

View File

@@ -8,8 +8,8 @@ import li.cil.oc2.api.bus.device.object.ObjectDevice;
import li.cil.oc2.api.bus.device.object.Parameter;
import li.cil.oc2.api.capabilities.Robot;
import li.cil.oc2.common.Constants;
import li.cil.oc2.common.bus.AbstractDeviceBusController;
import li.cil.oc2.common.bus.AbstractDeviceBusElement;
import li.cil.oc2.common.bus.CommonDeviceBusController;
import li.cil.oc2.common.bus.device.util.Devices;
import li.cil.oc2.common.bus.device.util.ItemDeviceInfo;
import li.cil.oc2.common.capabilities.Capabilities;
@@ -116,7 +116,7 @@ public final class RobotEntity extends Entity implements Robot {
this.preventEntitySpawning = true;
setNoGravity(true);
final RobotBusController busController = new RobotBusController(busElement);
final CommonDeviceBusController busController = new CommonDeviceBusController(busElement);
virtualMachine = new RobotVirtualMachine(busController);
virtualMachine.state.builtinDevices.rtcMinecraft.setWorld(world);
@@ -761,32 +761,6 @@ public final class RobotEntity extends Entity implements Robot {
}
}
private final class RobotBusController extends AbstractDeviceBusController {
public RobotBusController(final DeviceBusElement root) {
super(root);
}
@Override
protected void onBeforeScan() {
virtualMachine.pauseAndReload();
}
@Override
protected void onAfterDeviceScan(final boolean didDevicesChange) {
virtualMachine.resume(didDevicesChange);
}
@Override
protected void onDevicesAdded(final Collection<Device> devices) {
virtualMachine.state.vmAdapter.addDevices(devices);
}
@Override
protected void onDevicesRemoved(final Collection<Device> devices) {
virtualMachine.state.vmAdapter.removeDevices(devices);
}
}
private final class RobotVMRunner extends AbstractTerminalVMRunner {
public RobotVMRunner(final AbstractVirtualMachine virtualMachine, final Terminal terminal) {
super(virtualMachine, terminal);
@@ -799,7 +773,7 @@ public final class RobotEntity extends Entity implements Robot {
}
private final class RobotVirtualMachine extends AbstractVirtualMachine {
private RobotVirtualMachine(final RobotBusController busController) {
private RobotVirtualMachine(final CommonDeviceBusController busController) {
super(busController);
state.vmAdapter.setBaseAddressProvider(deviceItems::getDeviceAddressBase);
}
@@ -820,7 +794,7 @@ public final class RobotEntity extends Entity implements Robot {
}
@Override
protected void handleBusStateChanged(final AbstractDeviceBusController.BusState value) {
protected void handleBusStateChanged(final CommonDeviceBusController.BusState value) {
Network.sendToClientsTrackingEntity(new RobotBusStateMessage(RobotEntity.this), RobotEntity.this);
}

View File

@@ -1,6 +1,6 @@
package li.cil.oc2.common.network.message;
import li.cil.oc2.common.bus.AbstractDeviceBusController;
import li.cil.oc2.common.bus.CommonDeviceBusController;
import li.cil.oc2.common.network.MessageUtils;
import li.cil.oc2.common.tileentity.ComputerTileEntity;
import net.minecraft.network.PacketBuffer;
@@ -11,7 +11,7 @@ import java.util.function.Supplier;
public final class ComputerBusStateMessage {
private BlockPos pos;
private AbstractDeviceBusController.BusState value;
private CommonDeviceBusController.BusState value;
///////////////////////////////////////////////////////////////////
@@ -34,7 +34,7 @@ public final class ComputerBusStateMessage {
public void fromBytes(final PacketBuffer buffer) {
pos = buffer.readBlockPos();
value = buffer.readEnumValue(AbstractDeviceBusController.BusState.class);
value = buffer.readEnumValue(CommonDeviceBusController.BusState.class);
}
public static void toBytes(final ComputerBusStateMessage message, final PacketBuffer buffer) {

View File

@@ -1,6 +1,6 @@
package li.cil.oc2.common.network.message;
import li.cil.oc2.common.bus.AbstractDeviceBusController;
import li.cil.oc2.common.bus.CommonDeviceBusController;
import li.cil.oc2.common.entity.RobotEntity;
import li.cil.oc2.common.network.MessageUtils;
import net.minecraft.network.PacketBuffer;
@@ -10,7 +10,7 @@ import java.util.function.Supplier;
public final class RobotBusStateMessage {
private int entityId;
private AbstractDeviceBusController.BusState value;
private CommonDeviceBusController.BusState value;
///////////////////////////////////////////////////////////////////
@@ -33,7 +33,7 @@ public final class RobotBusStateMessage {
public void fromBytes(final PacketBuffer buffer) {
entityId = buffer.readVarInt();
value = buffer.readEnumValue(AbstractDeviceBusController.BusState.class);
value = buffer.readEnumValue(CommonDeviceBusController.BusState.class);
}
public static void toBytes(final RobotBusStateMessage message, final PacketBuffer buffer) {

View File

@@ -1,6 +1,6 @@
package li.cil.oc2.common.network.message;
import li.cil.oc2.common.bus.AbstractDeviceBusController;
import li.cil.oc2.common.bus.CommonDeviceBusController;
import li.cil.oc2.common.entity.RobotEntity;
import li.cil.oc2.common.network.MessageUtils;
import li.cil.oc2.common.serialization.NBTSerialization;
@@ -14,7 +14,7 @@ import java.util.function.Supplier;
public final class RobotInitializationMessage {
private int entityId;
private AbstractDeviceBusController.BusState busState;
private CommonDeviceBusController.BusState busState;
private VMRunState runState;
private ITextComponent bootError;
private CompoundNBT terminal;
@@ -48,7 +48,7 @@ public final class RobotInitializationMessage {
public void fromBytes(final PacketBuffer buffer) {
entityId = buffer.readVarInt();
busState = buffer.readEnumValue(AbstractDeviceBusController.BusState.class);
busState = buffer.readEnumValue(CommonDeviceBusController.BusState.class);
runState = buffer.readEnumValue(VMRunState.class);
bootError = buffer.readTextComponent();
terminal = buffer.readCompoundTag();

View File

@@ -5,7 +5,7 @@ import li.cil.oc2.api.bus.device.Device;
import li.cil.oc2.api.bus.device.DeviceTypes;
import li.cil.oc2.common.Constants;
import li.cil.oc2.common.block.ComputerBlock;
import li.cil.oc2.common.bus.AbstractDeviceBusController;
import li.cil.oc2.common.bus.CommonDeviceBusController;
import li.cil.oc2.common.bus.TileEntityDeviceBusController;
import li.cil.oc2.common.bus.TileEntityDeviceBusElement;
import li.cil.oc2.common.bus.device.util.BlockDeviceInfo;
@@ -63,7 +63,7 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic
private final Terminal terminal = new Terminal();
private final TileEntityDeviceBusElement busElement = new ComputerBusElement();
private final ComputerItemStackHandlers items = new ComputerItemStackHandlers();
private final ComputerVirtualMachine virtualMachine = new ComputerVirtualMachine(new ComputerBusController(busElement), items::getDeviceAddressBase);
private final ComputerVirtualMachine virtualMachine = new ComputerVirtualMachine(new TileEntityDeviceBusController(busElement, this), items::getDeviceAddressBase);
///////////////////////////////////////////////////////////////////
@@ -183,7 +183,7 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic
super.handleUpdateTag(blockState, tag);
NBTSerialization.deserialize(tag.getCompound(TERMINAL_TAG_NAME), terminal);
virtualMachine.setBusStateClient(AbstractDeviceBusController.BusState.values()[tag.getInt(AbstractVirtualMachine.BUS_STATE_TAG_NAME)]);
virtualMachine.setBusStateClient(CommonDeviceBusController.BusState.values()[tag.getInt(AbstractVirtualMachine.BUS_STATE_TAG_NAME)]);
virtualMachine.setRunStateClient(VMRunState.values()[tag.getInt(AbstractVirtualMachine.RUN_STATE_TAG_NAME)]);
virtualMachine.setBootErrorClient(ITextComponent.Serializer.getComponentFromJson(tag.getString(AbstractVirtualMachine.BOOT_ERROR_TAG_NAME)));
}
@@ -282,32 +282,6 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic
}
}
private final class ComputerBusController extends TileEntityDeviceBusController {
private ComputerBusController(final DeviceBusElement root) {
super(root, ComputerTileEntity.this);
}
@Override
protected void onBeforeScan() {
virtualMachine.pauseAndReload();
}
@Override
protected void onAfterDeviceScan(final boolean didDevicesChange) {
virtualMachine.resume(didDevicesChange);
}
@Override
protected void onDevicesAdded(final Collection<Device> devices) {
virtualMachine.state.vmAdapter.addDevices(devices);
}
@Override
protected void onDevicesRemoved(final Collection<Device> devices) {
virtualMachine.state.vmAdapter.removeDevices(devices);
}
}
private final class ComputerBusElement extends TileEntityDeviceBusElement {
public ComputerBusElement() {
super(ComputerTileEntity.this);
@@ -342,7 +316,7 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic
private final class ComputerVirtualMachine extends AbstractVirtualMachine {
private Chunk chunk;
private ComputerVirtualMachine(final AbstractDeviceBusController busController, final BaseAddressProvider baseAddressProvider) {
private ComputerVirtualMachine(final CommonDeviceBusController busController, final BaseAddressProvider baseAddressProvider) {
super(busController);
state.vmAdapter.setBaseAddressProvider(baseAddressProvider);
state.board.setStandardOutputDevice(state.builtinDevices.uart);
@@ -375,10 +349,10 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic
}
@Override
protected void handleBusStateChanged(final AbstractDeviceBusController.BusState value) {
protected void handleBusStateChanged(final CommonDeviceBusController.BusState value) {
Network.sendToClientsTrackingChunk(new ComputerBusStateMessage(ComputerTileEntity.this), chunk);
if (value == AbstractDeviceBusController.BusState.READY) {
if (value == CommonDeviceBusController.BusState.READY) {
// Bus just became ready, meaning new devices may be available, meaning new
// capabilities may be available, so we need to tell our neighbors.
world.notifyNeighborsOfStateChange(getPos(), getBlockState().getBlock());

View File

@@ -0,0 +1,12 @@
package li.cil.oc2.common.util;
import java.util.HashSet;
public final class Event extends HashSet<Runnable> implements Runnable {
@Override
public void run() {
for (final Runnable runnable : this) {
runnable.run();
}
}
}

View File

@@ -0,0 +1,13 @@
package li.cil.oc2.common.util;
import java.util.HashSet;
import java.util.function.Consumer;
public final class ParameterizedEvent<TEventParameter> extends HashSet<Consumer<TEventParameter>> implements Consumer<TEventParameter> {
@Override
public void accept(final TEventParameter event) {
for (final Consumer<TEventParameter> listener : this) {
listener.accept(event);
}
}
}

View File

@@ -5,7 +5,7 @@ import li.cil.oc2.api.bus.device.vm.FirmwareLoader;
import li.cil.oc2.api.bus.device.vm.VMDeviceLoadResult;
import li.cil.oc2.api.bus.device.vm.event.VMPausingEvent;
import li.cil.oc2.common.Constants;
import li.cil.oc2.common.bus.AbstractDeviceBusController;
import li.cil.oc2.common.bus.CommonDeviceBusController;
import li.cil.oc2.common.bus.RPCDeviceBusAdapter;
import li.cil.oc2.common.serialization.NBTSerialization;
import li.cil.oc2.common.util.NBTTagIds;
@@ -40,8 +40,8 @@ public abstract class AbstractVirtualMachine implements VirtualMachine {
///////////////////////////////////////////////////////////////////
public final AbstractDeviceBusController busController;
private AbstractDeviceBusController.BusState busState = AbstractDeviceBusController.BusState.SCAN_PENDING;
public final CommonDeviceBusController busController;
private CommonDeviceBusController.BusState busState = CommonDeviceBusController.BusState.SCAN_PENDING;
private int loadDevicesDelay;
@Serialized
@@ -60,9 +60,14 @@ public abstract class AbstractVirtualMachine implements VirtualMachine {
///////////////////////////////////////////////////////////////////
public AbstractVirtualMachine(final AbstractDeviceBusController busController) {
public AbstractVirtualMachine(final CommonDeviceBusController busController) {
this.busController = busController;
busController.onBeforeScan.add(this::handleBeforeScan);
busController.onAfterDeviceScan.add(this::handleAfterDeviceScan);
busController.onDevicesAdded.add(this::handleDevicesAdded);
busController.onDevicesRemoved.add(this::handleDevicesRemoved);
state.board = new R5Board();
state.context = new GlobalVMContext(state.board, this::joinWorkerThread);
@@ -86,18 +91,18 @@ public abstract class AbstractVirtualMachine implements VirtualMachine {
@Override
public boolean isRunning() {
return getBusState() == AbstractDeviceBusController.BusState.READY &&
return getBusState() == CommonDeviceBusController.BusState.READY &&
getRunState() == VMRunState.RUNNING;
}
@Override
public AbstractDeviceBusController.BusState getBusState() {
public CommonDeviceBusController.BusState getBusState() {
return busState;
}
@Override
@OnlyIn(Dist.CLIENT)
public void setBusStateClient(final AbstractDeviceBusController.BusState value) {
public void setBusStateClient(final CommonDeviceBusController.BusState value) {
busState = value;
}
@@ -206,7 +211,7 @@ public abstract class AbstractVirtualMachine implements VirtualMachine {
public void tick() {
busController.scan();
setBusState(busController.getState());
if (busState != AbstractDeviceBusController.BusState.READY) {
if (busState != CommonDeviceBusController.BusState.READY) {
return;
}
@@ -325,7 +330,7 @@ public abstract class AbstractVirtualMachine implements VirtualMachine {
protected abstract AbstractTerminalVMRunner createRunner();
protected void handleBusStateChanged(final AbstractDeviceBusController.BusState value) {
protected void handleBusStateChanged(final CommonDeviceBusController.BusState value) {
}
protected void handleRunStateChanged(final VMRunState value) {
@@ -336,7 +341,7 @@ public abstract class AbstractVirtualMachine implements VirtualMachine {
///////////////////////////////////////////////////////////////////
private void setBusState(final AbstractDeviceBusController.BusState value) {
private void setBusState(final CommonDeviceBusController.BusState value) {
if (value == busState) {
return;
}
@@ -365,4 +370,20 @@ public abstract class AbstractVirtualMachine implements VirtualMachine {
handleBootErrorChanged(value);
}
private void handleBeforeScan() {
pauseAndReload();
}
private void handleAfterDeviceScan(final CommonDeviceBusController.AfterDeviceScanEvent event) {
resume(event.didDevicesChange);
}
private void handleDevicesAdded(final CommonDeviceBusController.DevicesChangedEvent event) {
state.vmAdapter.addDevices(event.devices);
}
private void handleDevicesRemoved(final CommonDeviceBusController.DevicesChangedEvent event) {
state.vmAdapter.removeDevices(event.devices);
}
}

View File

@@ -1,6 +1,6 @@
package li.cil.oc2.common.vm;
import li.cil.oc2.common.bus.AbstractDeviceBusController;
import li.cil.oc2.common.bus.CommonDeviceBusController;
import net.minecraft.util.text.ITextComponent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@@ -8,10 +8,10 @@ import net.minecraftforge.api.distmarker.OnlyIn;
import javax.annotation.Nullable;
public interface VirtualMachine {
AbstractDeviceBusController.BusState getBusState();
CommonDeviceBusController.BusState getBusState();
@OnlyIn(Dist.CLIENT)
void setBusStateClient(AbstractDeviceBusController.BusState value);
void setBusStateClient(CommonDeviceBusController.BusState value);
VMRunState getRunState();

View File

@@ -24,7 +24,7 @@ import static org.mockito.Mockito.*;
public class DeviceBusTests {
@Mock
private Capability<DeviceBusElement> busElementCapability;
private AbstractDeviceBusController busController;
private CommonDeviceBusController busController;
private DeviceBusElement busControllerBusElement;
@BeforeAll
@@ -43,13 +43,13 @@ public class DeviceBusTests {
when(busControllerBusElement.getLocalDevices()).thenReturn(emptyList());
when(busControllerBusElement.getNeighbors()).thenReturn(Optional.empty());
busController = new TestBusController();
busController = new CommonDeviceBusController(busControllerBusElement);
}
@Test
public void scanPendingWhenTileEntityNotLoaded() {
busController.scan();
assertEquals(AbstractDeviceBusController.BusState.INCOMPLETE, busController.getState());
assertEquals(CommonDeviceBusController.BusState.INCOMPLETE, busController.getState());
}
@Test
@@ -57,7 +57,7 @@ public class DeviceBusTests {
when(busControllerBusElement.getNeighbors()).thenReturn(Optional.of(Collections.emptyList()));
busController.scan();
assertEquals(AbstractDeviceBusController.BusState.READY, busController.getState());
assertEquals(CommonDeviceBusController.BusState.READY, busController.getState());
}
@Test
@@ -68,7 +68,7 @@ public class DeviceBusTests {
when(busControllerBusElement.getLocalDevices()).thenReturn(singletonList(device));
busController.scan();
assertEquals(AbstractDeviceBusController.BusState.READY, busController.getState());
assertEquals(CommonDeviceBusController.BusState.READY, busController.getState());
verify(busControllerBusElement).addController(busController);
assertTrue(busController.getDevices().contains(device));
@@ -88,15 +88,9 @@ public class DeviceBusTests {
when(busElement2.getNeighbors()).thenReturn(Optional.of(Collections.singleton(LazyOptional.of(() -> busElement1))));
busController.scan();
assertEquals(AbstractDeviceBusController.BusState.READY, busController.getState());
assertEquals(CommonDeviceBusController.BusState.READY, busController.getState());
verify(busElement1).addController(busController);
verify(busElement2).addController(busController);
}
private final class TestBusController extends AbstractDeviceBusController {
public TestBusController() {
super(busControllerBusElement);
}
}
}