Moved all of TE lifecycle management into base class.
Added simplified way of subclasses to offer capabilities.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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<Capability<?>, LazyOptional<?>> capabilities = new HashMap<>();
|
||||
protected final Set<LazyOptional<?>> 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 <T> LazyOptional<T> getCapability(@NotNull final Capability<T> 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<T> list = new ArrayList<>();
|
||||
collectCapabilities(new CapabilityCollector() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <TOffered> void offer(final Capability<TOffered> offeredCapability, final TOffered instance) {
|
||||
if (offeredCapability == capability) {
|
||||
list.add((T) instance);
|
||||
}
|
||||
}
|
||||
}, side);
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
final LazyOptional<T> 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 <T> void setCapabilityIfAbsent(final Capability<T> capability, final T value) {
|
||||
capabilities.putIfAbsent(capability, LazyOptional.of(() -> value));
|
||||
@FunctionalInterface
|
||||
protected interface CapabilityCollector {
|
||||
<T> void offer(Capability<T> 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() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<BusCableBlock.ConnectionType> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <T> LazyOptional<T> getCapability(final @NotNull Capability<T> 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<T> 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<T> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user