Moved all of TE lifecycle management into base class.

Added simplified way of subclasses to offer capabilities.
This commit is contained in:
Florian Nücke
2020-12-25 16:19:52 +01:00
parent be68df4f8c
commit db319fa933
7 changed files with 131 additions and 105 deletions

View File

@@ -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;

View File

@@ -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() {
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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));

View File

@@ -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;
}

View File

@@ -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;
}