diff --git a/src/main/java/li/cil/oc2/common/block/BusCableBlock.java b/src/main/java/li/cil/oc2/common/block/BusCableBlock.java index 74b64196..7cc705e7 100644 --- a/src/main/java/li/cil/oc2/common/block/BusCableBlock.java +++ b/src/main/java/li/cil/oc2/common/block/BusCableBlock.java @@ -77,6 +77,14 @@ public final class BusCableBlock extends Block { directions.put(Direction.DOWN, CONNECTION_DOWN); }); + public static ConnectionType getConnectionType(final BlockState state, @Nullable final Direction direction) { + if (direction != null) { + return state.get(BusCableBlock.FACING_TO_CONNECTION_MAP.get(direction)); + } else { + return ConnectionType.NONE; + } + } + /////////////////////////////////////////////////////////////////// private final VoxelShape[] shapes; diff --git a/src/main/java/li/cil/oc2/common/block/entity/AbstractTileEntity.java b/src/main/java/li/cil/oc2/common/block/entity/AbstractTileEntity.java index 8ff44bbf..3684eccc 100644 --- a/src/main/java/li/cil/oc2/common/block/entity/AbstractTileEntity.java +++ b/src/main/java/li/cil/oc2/common/block/entity/AbstractTileEntity.java @@ -1,5 +1,6 @@ package li.cil.oc2.common.block.entity; +import li.cil.oc2.common.util.ServerScheduler; import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntityType; import net.minecraft.util.Direction; @@ -9,10 +10,16 @@ import net.minecraftforge.common.util.LazyOptional; import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; -import java.util.HashMap; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Set; +import java.util.WeakHashMap; public abstract class AbstractTileEntity extends TileEntity { - protected final HashMap, LazyOptional> capabilities = new HashMap<>(); + protected final Set> capabilities = Collections.newSetFromMap(new WeakHashMap<>()); + protected boolean needsWorldUnloadEvent; + + private final Runnable onWorldUnloaded = this::onWorldUnloaded; /////////////////////////////////////////////////////////////////// @@ -25,80 +32,98 @@ public abstract class AbstractTileEntity extends TileEntity { @NotNull @Override public LazyOptional getCapability(@NotNull final Capability capability, @Nullable final Direction side) { - final LazyOptional optional = capabilities.get(capability); - if (optional != null) { - return optional.cast(); - } else { - return super.getCapability(capability, side); + final ArrayList list = new ArrayList<>(); + collectCapabilities(new CapabilityCollector() { + @SuppressWarnings("unchecked") + @Override + public void offer(final Capability offeredCapability, final TOffered instance) { + if (offeredCapability == capability) { + list.add((T) instance); + } + } + }, side); + + if (!list.isEmpty()) { + final LazyOptional optional = LazyOptional.of(() -> list.get(0)); + capabilities.add(optional); + return optional; } + + return super.getCapability(capability, side); } @Override public void onLoad() { super.onLoad(); - initialize(); - } - @Override - public void remove() { - super.remove(); - dispose(); + final World world = getWorld(); + if (world == null) { + return; + } + + if (world.isRemote()) { + loadClient(); + } else { + loadServer(); + + if (needsWorldUnloadEvent) { + ServerScheduler.scheduleOnUnload(world, onWorldUnloaded); + } + } } @Override public void onChunkUnloaded() { + super.onChunkUnloaded(); // -> invalidateCaps() + onUnload(); + } + + public void onWorldUnloaded() { invalidateCaps(); - dispose(); + onUnload(); + } + + @Override + public void remove() { + super.remove(); // -> invalidateCaps() + onUnload(); } /////////////////////////////////////////////////////////////////// - protected void setCapabilityIfAbsent(final Capability capability, final T value) { - capabilities.putIfAbsent(capability, LazyOptional.of(() -> value)); + @FunctionalInterface + protected interface CapabilityCollector { + void offer(Capability capability, T instance); + } + + protected void collectCapabilities(final CapabilityCollector collector, @Nullable final Direction direction) { } @Override protected void invalidateCaps() { super.invalidateCaps(); - for (final LazyOptional capability : capabilities.values()) { + for (final LazyOptional capability : capabilities) { capability.invalidate(); } } - protected void initialize() { + protected void onUnload() { final World world = getWorld(); if (world == null) { return; } - - if (world.isRemote()) { - initializeClient(); - } else { - initializeServer(); + if (!world.isRemote()) { + unloadServer(); + ServerScheduler.cancelOnUnload(world, onWorldUnloaded); } } - protected void dispose() { - final World world = getWorld(); - if (world == null) { - return; - } - if (world.isRemote()) { - disposeClient(); - } else { - disposeServer(); - } + protected void loadClient() { } - protected void initializeClient() { + protected void loadServer() { } - protected void initializeServer() { - } - - protected void disposeClient() { - } - - protected void disposeServer() { + protected void unloadServer() { } } diff --git a/src/main/java/li/cil/oc2/common/block/entity/BusCableTileEntity.java b/src/main/java/li/cil/oc2/common/block/entity/BusCableTileEntity.java index e6871d20..9be19680 100644 --- a/src/main/java/li/cil/oc2/common/block/entity/BusCableTileEntity.java +++ b/src/main/java/li/cil/oc2/common/block/entity/BusCableTileEntity.java @@ -7,11 +7,12 @@ import li.cil.oc2.common.init.TileEntities; import li.cil.oc2.common.serialization.NBTSerialization; import net.minecraft.block.BlockState; import net.minecraft.nbt.CompoundNBT; -import net.minecraft.state.EnumProperty; import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; -public class BusCableTileEntity extends AbstractTileEntity { +import javax.annotation.Nullable; + +public final class BusCableTileEntity extends AbstractTileEntity { private static final String BUS_ELEMENT_NBT_TAG_NAME = "busElement"; /////////////////////////////////////////////////////////////////// @@ -24,7 +25,6 @@ public class BusCableTileEntity extends AbstractTileEntity { super(TileEntities.BUS_CABLE_TILE_ENTITY.get()); busElement = new BusElement(); - setCapabilityIfAbsent(Capabilities.DEVICE_BUS_ELEMENT_CAPABILITY, busElement); } /////////////////////////////////////////////////////////////////// @@ -49,14 +49,21 @@ public class BusCableTileEntity extends AbstractTileEntity { /////////////////////////////////////////////////////////////////// @Override - protected void initializeServer() { - super.initializeServer(); + protected void collectCapabilities(final CapabilityCollector collector, @Nullable final Direction direction) { + if (busElement.canConnectToSide(direction)) { + collector.offer(Capabilities.DEVICE_BUS_ELEMENT_CAPABILITY, busElement); + } + } + + @Override + protected void loadServer() { + super.loadServer(); busElement.initialize(); } @Override - protected void disposeServer() { - super.disposeServer(); + protected void unloadServer() { + super.unloadServer(); busElement.dispose(); } @@ -68,18 +75,13 @@ public class BusCableTileEntity extends AbstractTileEntity { } @Override - protected boolean canConnectToSide(final Direction direction) { - return getConnectionType(direction) == BusCableBlock.ConnectionType.LINK; + public boolean canConnectToSide(@Nullable final Direction direction) { + return BusCableBlock.getConnectionType(getBlockState(), direction) == BusCableBlock.ConnectionType.LINK; } @Override - protected boolean hasInterfaceOnSide(final Direction direction) { - return getConnectionType(direction) == BusCableBlock.ConnectionType.PLUG; - } - - private BusCableBlock.ConnectionType getConnectionType(final Direction direction) { - final EnumProperty property = BusCableBlock.FACING_TO_CONNECTION_MAP.get(direction); - return getBlockState().get(property); + public boolean hasInterfaceOnSide(@Nullable final Direction direction) { + return BusCableBlock.getConnectionType(getBlockState(), direction) == BusCableBlock.ConnectionType.PLUG; } } } diff --git a/src/main/java/li/cil/oc2/common/block/entity/ComputerTileEntity.java b/src/main/java/li/cil/oc2/common/block/entity/ComputerTileEntity.java index dea78592..18c720f1 100644 --- a/src/main/java/li/cil/oc2/common/block/entity/ComputerTileEntity.java +++ b/src/main/java/li/cil/oc2/common/block/entity/ComputerTileEntity.java @@ -23,7 +23,6 @@ import li.cil.oc2.common.serialization.NBTSerialization; import li.cil.oc2.common.util.HorizontalBlockUtils; import li.cil.oc2.common.util.NBTTagIds; import li.cil.oc2.common.util.NBTUtils; -import li.cil.oc2.common.util.ServerScheduler; import li.cil.oc2.common.vm.Terminal; import li.cil.oc2.common.vm.VirtualMachine; import li.cil.oc2.common.vm.VirtualMachineRunner; @@ -55,8 +54,6 @@ import java.io.InputStream; import java.nio.ByteBuffer; import java.util.*; -import static java.util.Objects.requireNonNull; - public final class ComputerTileEntity extends AbstractTileEntity implements ITickableTileEntity { private static final Logger LOGGER = LogManager.getLogger(); @@ -86,7 +83,6 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic /////////////////////////////////////////////////////////////////// - private final Runnable onWorldUnloaded = this::onWorldUnloaded; private Chunk chunk; private final AbstractDeviceBusController busController; private AbstractDeviceBusController.BusState busState; @@ -108,6 +104,8 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic public ComputerTileEntity() { super(TileEntities.COMPUTER_TILE_ENTITY.get()); + needsWorldUnloadEvent = true; + busElement = new BusElement(); busController = new BusController(); busState = AbstractDeviceBusController.BusState.SCAN_PENDING; @@ -122,10 +120,6 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic context.getInterruptAllocator().claimInterrupt(VFS_INTERRUPT).ifPresent(interrupt -> vfs.getInterrupt().set(interrupt, context.getInterruptController())); context.getMemoryRangeAllocator().claimMemoryRange(vfs); - - setCapabilityIfAbsent(Capabilities.DEVICE_BUS_ELEMENT_CAPABILITY, busElement); - setCapabilityIfAbsent(Capabilities.DEVICE_BUS_CONTROLLER_CAPABILITY, busController); - setCapabilityIfAbsent(Capabilities.ITEM_HANDLER_CAPABILITY, itemHandler); } public Terminal getTerminal() { @@ -193,17 +187,12 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic @Override public @NotNull LazyOptional getCapability(final @NotNull Capability capability, @Nullable final Direction side) { - if (capability == Capabilities.DEVICE_BUS_ELEMENT_CAPABILITY || - capability == Capabilities.DEVICE_BUS_CONTROLLER_CAPABILITY) { - if (side == getBlockState().get(ComputerBlock.HORIZONTAL_FACING)) { - return LazyOptional.empty(); - } else { // Do not allow item devices to override our bus element and controller. - return super.getCapability(capability, side); - } + final LazyOptional optional = super.getCapability(capability, side); + if (optional.isPresent()) { + return optional; } final Direction localSide = HorizontalBlockUtils.toLocal(getBlockState(), side); - for (final Device device : busController.getDevices()) { if (device instanceof ICapabilityProvider) { final LazyOptional value = ((ICapabilityProvider) device).getCapability(capability, localSide); @@ -213,7 +202,17 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic } } - return super.getCapability(capability, side); + return LazyOptional.empty(); + } + + @Override + protected void collectCapabilities(final CapabilityCollector collector, @Nullable final Direction direction) { + if (direction != getBlockState().get(ComputerBlock.HORIZONTAL_FACING)) { + collector.offer(Capabilities.DEVICE_BUS_ELEMENT_CAPABILITY, busElement); + collector.offer(Capabilities.DEVICE_BUS_CONTROLLER_CAPABILITY, busController); + } + + collector.offer(Capabilities.ITEM_HANDLER_CAPABILITY, itemHandler); } @Override @@ -278,7 +277,7 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic public void remove() { super.remove(); - // Regular dispose only suspends, but we want to do a full unload when we get + // Unload only suspends, but we want to do a full clean-up when we get // destroyed, so stuff inside us can delete out-of-nbt persisted data. virtualMachine.vmAdapter.unload(); } @@ -367,28 +366,23 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic /////////////////////////////////////////////////////////////////// @Override - protected void initializeClient() { - super.initializeClient(); + protected void loadClient() { + super.loadClient(); terminal.setDisplayOnly(true); } @Override - protected void initializeServer() { - super.initializeServer(); - - final World world = requireNonNull(getWorld()); - ServerScheduler.scheduleOnUnload(world, onWorldUnloaded); + protected void loadServer() { + super.loadServer(); busElement.initialize(); - virtualMachine.rtc.setWorld(world); + virtualMachine.rtc.setWorld(getWorld()); } @Override - protected void disposeServer() { - super.disposeServer(); - - ServerScheduler.removeOnUnload(getWorld(), onWorldUnloaded); + protected void unloadServer() { + super.unloadServer(); joinVirtualMachine(); virtualMachine.vmAdapter.suspend(); @@ -469,10 +463,6 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic MemoryMaps.store(virtualMachine.board.getMemoryMap(), 0x80000000L + offset, stream); } - private void onWorldUnloaded() { - disposeServer(); - } - /////////////////////////////////////////////////////////////////// private final class BusController extends AbstractDeviceBusController { @@ -520,7 +510,7 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic } @Override - protected boolean canConnectToSide(final Direction direction) { + public boolean canConnectToSide(@Nullable final Direction direction) { return getBlockState().get(ComputerBlock.HORIZONTAL_FACING) != direction; } } diff --git a/src/main/java/li/cil/oc2/common/bus/TileEntityDeviceBusElement.java b/src/main/java/li/cil/oc2/common/bus/TileEntityDeviceBusElement.java index 950a42b1..3b8e86a1 100644 --- a/src/main/java/li/cil/oc2/common/bus/TileEntityDeviceBusElement.java +++ b/src/main/java/li/cil/oc2/common/bus/TileEntityDeviceBusElement.java @@ -14,6 +14,7 @@ import net.minecraft.util.math.ChunkPos; import net.minecraft.world.World; import net.minecraftforge.common.util.LazyOptional; +import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -72,6 +73,14 @@ public class TileEntityDeviceBusElement extends AbstractGroupingBlockDeviceBusEl return Optional.of(neighbors); } + public boolean canConnectToSide(@Nullable final Direction direction) { + return true; + } + + public boolean hasInterfaceOnSide(@Nullable final Direction direction) { + return canConnectToSide(direction); + } + public void handleNeighborChanged(final BlockPos pos) { final World world = tileEntity.getWorld(); if (world == null || world.isRemote()) { @@ -115,16 +124,6 @@ public class TileEntityDeviceBusElement extends AbstractGroupingBlockDeviceBusEl /////////////////////////////////////////////////////////////////// - protected boolean canConnectToSide(final Direction direction) { - return true; - } - - protected boolean hasInterfaceOnSide(final Direction direction) { - return canConnectToSide(direction); - } - - /////////////////////////////////////////////////////////////////// - private void scanNeighborsForDevices() { for (final Direction direction : Direction.values()) { handleNeighborChanged(tileEntity.getPos().offset(direction)); diff --git a/src/main/java/li/cil/oc2/common/util/ServerScheduler.java b/src/main/java/li/cil/oc2/common/util/ServerScheduler.java index 3193b7e2..62d59e1d 100644 --- a/src/main/java/li/cil/oc2/common/util/ServerScheduler.java +++ b/src/main/java/li/cil/oc2/common/util/ServerScheduler.java @@ -46,7 +46,7 @@ public final class ServerScheduler { worldUnloadSchedulers.computeIfAbsent(world, unused -> new UnloadScheduler()).add(listener); } - public static void removeOnUnload(@Nullable final IWorld world, final Runnable listener) { + public static void cancelOnUnload(@Nullable final IWorld world, final Runnable listener) { if (world == null) { return; } @@ -61,7 +61,7 @@ public final class ServerScheduler { chunkUnloadSchedulers.computeIfAbsent(chunk, unused -> new UnloadScheduler()).add(listener); } - public static void removeOnUnload(@Nullable final IChunk chunk, final Runnable listener) { + public static void cancelOnUnload(@Nullable final IChunk chunk, final Runnable listener) { if (chunk == null) { return; } diff --git a/src/main/java/li/cil/oc2/common/vm/MinecraftRealTimeCounter.java b/src/main/java/li/cil/oc2/common/vm/MinecraftRealTimeCounter.java index 1e761ab6..f04aae31 100644 --- a/src/main/java/li/cil/oc2/common/vm/MinecraftRealTimeCounter.java +++ b/src/main/java/li/cil/oc2/common/vm/MinecraftRealTimeCounter.java @@ -3,6 +3,8 @@ package li.cil.oc2.common.vm; import li.cil.sedna.api.device.rtc.RealTimeCounter; import net.minecraft.world.World; +import javax.annotation.Nullable; + public final class MinecraftRealTimeCounter implements RealTimeCounter { private static final int TICKS_PER_DAY = 24000; private static final int FREQUENCY = TICKS_PER_DAY; @@ -13,7 +15,7 @@ public final class MinecraftRealTimeCounter implements RealTimeCounter { /////////////////////////////////////////////////////////////////// - public void setWorld(final World world) { + public void setWorld(@Nullable final World world) { this.world = world; }