Pulled out a bunch more common functionality from computers for re-use in robots.
This commit is contained in:
@@ -9,7 +9,7 @@ import li.cil.oc2.client.gui.widget.ToggleImageButton;
|
||||
import li.cil.oc2.common.Constants;
|
||||
import li.cil.oc2.common.network.Network;
|
||||
import li.cil.oc2.common.network.message.ComputerPowerMessage;
|
||||
import li.cil.oc2.common.network.message.TerminalBlockInputMessage;
|
||||
import li.cil.oc2.common.network.message.ComputerTerminalInputMessage;
|
||||
import li.cil.oc2.common.tileentity.ComputerTileEntity;
|
||||
import li.cil.oc2.common.vm.Terminal;
|
||||
import net.minecraft.client.entity.player.ClientPlayerEntity;
|
||||
@@ -84,13 +84,13 @@ public final class TerminalScreen extends Screen {
|
||||
|
||||
super.render(matrixStack, mouseX, mouseY, partialTicks);
|
||||
|
||||
if (tileEntity.isRunning()) {
|
||||
if (tileEntity.getState().isRunning()) {
|
||||
final MatrixStack stack = new MatrixStack();
|
||||
stack.translate(windowLeft + TERMINAL_AREA_X, windowTop + TERMINAL_AREA_Y, this.itemRenderer.zLevel);
|
||||
stack.scale(TERMINAL_AREA_WIDTH / (float) terminal.getWidth(), TERMINAL_AREA_HEIGHT / (float) terminal.getHeight(), 1f);
|
||||
terminal.render(stack);
|
||||
} else {
|
||||
final ITextComponent bootError = tileEntity.getBootError();
|
||||
final ITextComponent bootError = tileEntity.getState().getBootError();
|
||||
if (bootError != null) {
|
||||
final int textWidth = font.getStringPropertyWidth(bootError);
|
||||
final int textOffsetX = (TERMINAL_AREA_WIDTH - textWidth) / 2;
|
||||
@@ -110,7 +110,7 @@ public final class TerminalScreen extends Screen {
|
||||
|
||||
final ByteBuffer input = terminal.getInput();
|
||||
if (input != null) {
|
||||
Network.INSTANCE.sendToServer(new TerminalBlockInputMessage(tileEntity, input));
|
||||
Network.INSTANCE.sendToServer(new ComputerTerminalInputMessage(tileEntity, input));
|
||||
}
|
||||
|
||||
assert minecraft != null;
|
||||
@@ -185,13 +185,13 @@ public final class TerminalScreen extends Screen {
|
||||
@Override
|
||||
public void onPress() {
|
||||
super.onPress();
|
||||
final ComputerPowerMessage message = new ComputerPowerMessage(tileEntity, !tileEntity.isRunning());
|
||||
final ComputerPowerMessage message = new ComputerPowerMessage(tileEntity, !tileEntity.getState().isRunning());
|
||||
Network.INSTANCE.sendToServer(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isToggled() {
|
||||
return tileEntity.isRunning();
|
||||
return tileEntity.getState().isRunning();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -220,7 +220,7 @@ public final class TerminalScreen extends Screen {
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private boolean shouldCaptureInput() {
|
||||
return isMouseOverTerminal && enableInputCapture && tileEntity.isRunning();
|
||||
return isMouseOverTerminal && enableInputCapture && tileEntity.getState().isRunning();
|
||||
}
|
||||
|
||||
private boolean isPointInRegion(final int x, final int y, final int width, final int height, double mouseX, double mouseY) {
|
||||
|
||||
@@ -70,7 +70,7 @@ public final class ComputerTileEntityRenderer extends TileEntityRenderer<Compute
|
||||
final float pixelScale = 1 / 16f;
|
||||
matrixStack.scale(pixelScale, pixelScale, pixelScale);
|
||||
|
||||
if (tileEntity.isRunning()) {
|
||||
if (tileEntity.getState().isRunning()) {
|
||||
renderTerminal(tileEntity, matrixStack, buffer, cameraPosition);
|
||||
} else {
|
||||
renderStatusText(tileEntity, matrixStack, cameraPosition);
|
||||
@@ -79,7 +79,7 @@ public final class ComputerTileEntityRenderer extends TileEntityRenderer<Compute
|
||||
matrixStack.translate(0, 0, -0.1f);
|
||||
final Matrix4f matrix = matrixStack.getLast().getMatrix();
|
||||
|
||||
switch (tileEntity.getBusState()) {
|
||||
switch (tileEntity.getState().getBusState()) {
|
||||
case SCAN_PENDING:
|
||||
case INCOMPLETE:
|
||||
renderStatus(matrix, buffer);
|
||||
@@ -91,7 +91,7 @@ public final class ComputerTileEntityRenderer extends TileEntityRenderer<Compute
|
||||
renderStatus(matrix, buffer, 250);
|
||||
break;
|
||||
case READY:
|
||||
switch (tileEntity.getRunState()) {
|
||||
switch (tileEntity.getState().getRunState()) {
|
||||
case STOPPED:
|
||||
break;
|
||||
case LOADING_DEVICES:
|
||||
@@ -154,7 +154,7 @@ public final class ComputerTileEntityRenderer extends TileEntityRenderer<Compute
|
||||
return;
|
||||
}
|
||||
|
||||
final ITextComponent bootError = tileEntity.getBootError();
|
||||
final ITextComponent bootError = tileEntity.getState().getBootError();
|
||||
if (bootError == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ public final class Constants {
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static final String BLOCK_ENTITY_TAG_NAME_IN_ITEM = "BlockEntityTag";
|
||||
public static final String BLOCK_ENTITY_INVENTORY_TAG_NAME = "items";
|
||||
public static final String INVENTORY_TAG_NAME = "items";
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ import li.cil.oc2.common.integration.Wrenches;
|
||||
import li.cil.oc2.common.item.Items;
|
||||
import li.cil.oc2.common.tileentity.ComputerTileEntity;
|
||||
import li.cil.oc2.common.tileentity.TileEntities;
|
||||
import li.cil.oc2.common.util.TooltipUtils;
|
||||
import li.cil.oc2.common.util.VoxelShapeUtils;
|
||||
import li.cil.oc2.common.vm.CommonVirtualMachineItemStackHandlers;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.HorizontalBlock;
|
||||
@@ -76,11 +76,7 @@ public final class ComputerBlock extends HorizontalBlock {
|
||||
@Override
|
||||
public void addInformation(final ItemStack stack, @Nullable final IBlockReader world, final List<ITextComponent> tooltip, final ITooltipFlag advanced) {
|
||||
super.addInformation(stack, world, tooltip, advanced);
|
||||
TooltipUtils.addInventoryInformation(stack, tooltip,
|
||||
ComputerTileEntity.MEMORY_TAG_NAME,
|
||||
ComputerTileEntity.HARD_DRIVE_TAG_NAME,
|
||||
ComputerTileEntity.FLASH_MEMORY_TAG_NAME,
|
||||
ComputerTileEntity.CARD_TAG_NAME);
|
||||
CommonVirtualMachineItemStackHandlers.addInformation(stack, tooltip);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -182,8 +178,8 @@ public final class ComputerBlock extends HorizontalBlock {
|
||||
final TileEntity tileEntity = world.getTileEntity(pos);
|
||||
if (!world.isRemote() && tileEntity instanceof ComputerTileEntity) {
|
||||
final ComputerTileEntity computer = (ComputerTileEntity) tileEntity;
|
||||
if (!computer.isEmpty()) {
|
||||
computer.exportDeviceDataToItemStacks();
|
||||
if (!computer.getItemHandlers().isEmpty()) {
|
||||
computer.getItemHandlers().exportDeviceDataToItemStacks();
|
||||
|
||||
if (player.isCreative()) {
|
||||
final ItemStack stack = new ItemStack(Items.COMPUTER_ITEM.get());
|
||||
|
||||
@@ -3,6 +3,7 @@ package li.cil.oc2.common.container;
|
||||
import li.cil.oc2.api.bus.device.DeviceTypes;
|
||||
import li.cil.oc2.common.block.Blocks;
|
||||
import li.cil.oc2.common.tileentity.ComputerTileEntity;
|
||||
import li.cil.oc2.common.vm.CommonVirtualMachineItemStackHandlers;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
@@ -36,25 +37,26 @@ public final class ComputerContainer extends AbstractContainer {
|
||||
this.world = inventory.player.getEntityWorld();
|
||||
this.pos = tileEntity.getPos();
|
||||
|
||||
tileEntity.getItemHandler(DeviceTypes.FLASH_MEMORY).ifPresent(itemHandler -> {
|
||||
final CommonVirtualMachineItemStackHandlers itemHandlers = tileEntity.getItemHandlers();
|
||||
itemHandlers.getItemHandler(DeviceTypes.FLASH_MEMORY).ifPresent(itemHandler -> {
|
||||
if (itemHandler.getSlots() > 0) {
|
||||
this.addSlot(new TypedSlotItemHandler(itemHandler, DeviceTypes.FLASH_MEMORY, 0, 64, 78));
|
||||
}
|
||||
});
|
||||
|
||||
tileEntity.getItemHandler(DeviceTypes.MEMORY).ifPresent(itemHandler -> {
|
||||
itemHandlers.getItemHandler(DeviceTypes.MEMORY).ifPresent(itemHandler -> {
|
||||
for (int slot = 0; slot < itemHandler.getSlots(); slot++) {
|
||||
this.addSlot(new TypedSlotItemHandler(itemHandler, DeviceTypes.MEMORY, slot, 64 + slot * SLOT_SIZE, 24));
|
||||
}
|
||||
});
|
||||
|
||||
tileEntity.getItemHandler(DeviceTypes.HARD_DRIVE).ifPresent(itemHandler -> {
|
||||
itemHandlers.getItemHandler(DeviceTypes.HARD_DRIVE).ifPresent(itemHandler -> {
|
||||
for (int slot = 0; slot < itemHandler.getSlots(); slot++) {
|
||||
this.addSlot(new TypedSlotItemHandler(itemHandler, DeviceTypes.HARD_DRIVE, slot, 100 + (slot % 2) * SLOT_SIZE, 60 + (slot / 2) * SLOT_SIZE));
|
||||
}
|
||||
});
|
||||
|
||||
tileEntity.getItemHandler(DeviceTypes.CARD).ifPresent(itemHandler -> {
|
||||
itemHandlers.getItemHandler(DeviceTypes.CARD).ifPresent(itemHandler -> {
|
||||
for (int slot = 0; slot < itemHandler.getSlots(); slot++) {
|
||||
this.addSlot(new TypedSlotItemHandler(itemHandler, DeviceTypes.CARD, slot, 38, 24 + slot * SLOT_SIZE));
|
||||
}
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
package li.cil.oc2.common.entity;
|
||||
|
||||
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.bus.AbstractDeviceBusController;
|
||||
import li.cil.oc2.common.bus.device.util.Devices;
|
||||
import li.cil.oc2.common.bus.device.util.ItemDeviceInfo;
|
||||
import li.cil.oc2.common.entity.robot.*;
|
||||
import li.cil.oc2.common.integration.Wrenches;
|
||||
import li.cil.oc2.common.network.Network;
|
||||
import li.cil.oc2.common.network.message.RobotBootErrorMessage;
|
||||
import li.cil.oc2.common.network.message.RobotTerminalOutputMessage;
|
||||
import li.cil.oc2.common.serialization.NBTSerialization;
|
||||
import li.cil.oc2.common.util.NBTTagIds;
|
||||
import li.cil.oc2.common.util.WorldUtils;
|
||||
import li.cil.oc2.common.vm.*;
|
||||
import net.minecraft.block.SoundType;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityType;
|
||||
@@ -18,27 +29,37 @@ import net.minecraft.network.datasync.EntityDataManager;
|
||||
import net.minecraft.util.ActionResultType;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.fml.network.NetworkHooks;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Queue;
|
||||
import java.util.Random;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
|
||||
public final class RobotEntity extends Entity {
|
||||
public static int MAX_QUEUED_ACTIONS = 16;
|
||||
|
||||
private static final String TERMINAL_TAG_NAME = "terminal";
|
||||
private static final String STATE_TAG_NAME = "state";
|
||||
private static final String COMMAND_PROCESSOR_TAG_NAME = "commands";
|
||||
|
||||
private static final DataParameter<Boolean> IS_RUNNING = EntityDataManager.createKey(RobotEntity.class, DataSerializers.BOOLEAN);
|
||||
private static final int MAX_QUEUED_ACTIONS = 16;
|
||||
|
||||
private static final int MEMORY_SLOTS = 4;
|
||||
private static final int HARD_DRIVE_SLOTS = 2;
|
||||
private static final int FLASH_MEMORY_SLOTS = 1;
|
||||
private static final int CARD_SLOTS = 2;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private final AnimationState animationState = new AnimationState();
|
||||
private final CommandProcessor commandProcessor = new CommandProcessor();
|
||||
private final Terminal terminal = new Terminal();
|
||||
|
||||
private final RobotVirtualMachineState state;
|
||||
private final RobotItemStackHandlers items = new RobotItemStackHandlers();
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -46,6 +67,9 @@ public final class RobotEntity extends Entity {
|
||||
super(type, world);
|
||||
this.preventEntitySpawning = true;
|
||||
setNoGravity(true);
|
||||
|
||||
final RobotBusController busController = new RobotBusController(items.busElement);
|
||||
state = new RobotVirtualMachineState(busController, new CommonVirtualMachine(busController));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
@@ -55,18 +79,38 @@ public final class RobotEntity extends Entity {
|
||||
return animationState;
|
||||
}
|
||||
|
||||
public Terminal getTerminal() {
|
||||
return terminal;
|
||||
}
|
||||
|
||||
public VirtualMachineState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public CommonVirtualMachineItemStackHandlers getItemHandlers() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return dataManager.get(IS_RUNNING);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
// todo start vm
|
||||
final World world = getEntityWorld();
|
||||
if (world == null || world.isRemote()) {
|
||||
return;
|
||||
}
|
||||
|
||||
state.start();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
// todo stop vm
|
||||
final World world = getEntityWorld();
|
||||
if (world == null || world.isRemote()) {
|
||||
return;
|
||||
}
|
||||
|
||||
commandProcessor.clear();
|
||||
state.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -90,10 +134,7 @@ public final class RobotEntity extends Entity {
|
||||
}
|
||||
} else {
|
||||
if (player.isSneaking()) {
|
||||
// TODO start machine
|
||||
if (!world.isRemote()) {
|
||||
dataManager.set(IS_RUNNING, !dataManager.get(IS_RUNNING));
|
||||
}
|
||||
start();
|
||||
} else {
|
||||
// if (rand.nextBoolean()) {
|
||||
// commandProcessor.move(MovementDirection.values()[rand.nextInt(MovementDirection.values().length)]);
|
||||
@@ -146,13 +187,22 @@ public final class RobotEntity extends Entity {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeAdditional(final CompoundNBT compound) {
|
||||
compound.put(COMMAND_PROCESSOR_TAG_NAME, commandProcessor.serialize());
|
||||
protected void writeAdditional(final CompoundNBT tag) {
|
||||
tag.put(STATE_TAG_NAME, state.serialize());
|
||||
tag.put(TERMINAL_TAG_NAME, NBTSerialization.serialize(terminal));
|
||||
tag.put(COMMAND_PROCESSOR_TAG_NAME, commandProcessor.serialize());
|
||||
tag.put(Constants.INVENTORY_TAG_NAME, items.serialize());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void readAdditional(final CompoundNBT compound) {
|
||||
commandProcessor.deserialize(compound.getCompound(COMMAND_PROCESSOR_TAG_NAME));
|
||||
protected void readAdditional(final CompoundNBT tag) {
|
||||
state.deserialize(tag.getCompound(STATE_TAG_NAME));
|
||||
NBTSerialization.deserialize(tag.getCompound(TERMINAL_TAG_NAME), terminal);
|
||||
commandProcessor.deserialize(tag.getCompound(COMMAND_PROCESSOR_TAG_NAME));
|
||||
|
||||
if (tag.contains(Constants.INVENTORY_TAG_NAME, NBTTagIds.TAG_COMPOUND)) {
|
||||
items.deserialize(tag.getCompound(Constants.INVENTORY_TAG_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -301,8 +351,6 @@ public final class RobotEntity extends Entity {
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private boolean addAction(final AbstractRobotAction action) {
|
||||
if (getEntityWorld().isRemote()) {
|
||||
return false;
|
||||
@@ -320,4 +368,84 @@ public final class RobotEntity extends Entity {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class RobotItemStackHandlers extends CommonVirtualMachineItemStackHandlers {
|
||||
public RobotItemStackHandlers() {
|
||||
super(MEMORY_SLOTS, HARD_DRIVE_SLOTS, FLASH_MEMORY_SLOTS, CARD_SLOTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ItemDeviceInfo> getDevices(final ItemStack stack) {
|
||||
return Devices.getDevices(RobotEntity.this, stack);
|
||||
}
|
||||
}
|
||||
|
||||
private final class RobotBusController extends AbstractDeviceBusController {
|
||||
public RobotBusController(final DeviceBusElement root) {
|
||||
super(root);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBeforeScan() {
|
||||
state.reload();
|
||||
state.virtualMachine.rpcAdapter.pause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAfterDeviceScan(final boolean didDevicesChange) {
|
||||
state.virtualMachine.rpcAdapter.resume(didDevicesChange);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDevicesAdded(final Collection<Device> devices) {
|
||||
state.virtualMachine.vmAdapter.addDevices(devices);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDevicesRemoved(final Collection<Device> devices) {
|
||||
state.virtualMachine.vmAdapter.removeDevices(devices);
|
||||
}
|
||||
}
|
||||
|
||||
private final class RobotVirtualMachineRunner extends AbstractTerminalVirtualMachineRunner {
|
||||
public RobotVirtualMachineRunner(final CommonVirtualMachine virtualMachine, final Terminal terminal) {
|
||||
super(virtualMachine, terminal);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendTerminalUpdateToClient(final ByteBuffer output) {
|
||||
final RobotTerminalOutputMessage message = new RobotTerminalOutputMessage(RobotEntity.this, output);
|
||||
Network.sendToClientsTrackingEntity(message, RobotEntity.this);
|
||||
}
|
||||
}
|
||||
|
||||
private final class RobotVirtualMachineState extends AbstractVirtualMachineState<RobotBusController, CommonVirtualMachine> {
|
||||
private RobotVirtualMachineState(final RobotBusController busController, final CommonVirtualMachine virtualMachine) {
|
||||
super(busController, virtualMachine);
|
||||
virtualMachine.vmAdapter.setDefaultAddressProvider(items::getDefaultDeviceAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractTerminalVirtualMachineRunner createRunner() {
|
||||
return new RobotVirtualMachineRunner(virtualMachine, terminal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopRunnerAndReset() {
|
||||
super.stopRunnerAndReset();
|
||||
|
||||
commandProcessor.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleRunStateChanged(final RunState value) {
|
||||
dataManager.set(IS_RUNNING, isRunning());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleBootErrorChanged(@Nullable final ITextComponent value) {
|
||||
final RobotBootErrorMessage message = new RobotBootErrorMessage(RobotEntity.this);
|
||||
Network.sendToClientsTrackingEntity(message, RobotEntity.this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ import li.cil.oc2.api.API;
|
||||
import li.cil.oc2.common.Constants;
|
||||
import li.cil.oc2.common.bus.device.data.BaseBlockDevices;
|
||||
import li.cil.oc2.common.bus.device.data.Firmwares;
|
||||
import li.cil.oc2.common.tileentity.ComputerTileEntity;
|
||||
import li.cil.oc2.common.util.ItemStackUtils;
|
||||
import li.cil.oc2.common.vm.CommonVirtualMachineItemStackHandlers;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.util.NonNullList;
|
||||
@@ -46,18 +46,18 @@ public final class ItemGroup {
|
||||
final ItemStack computer = new ItemStack(Items.COMPUTER_ITEM.get());
|
||||
|
||||
final CompoundNBT computerItems = ItemStackUtils.getOrCreateTileEntityInventoryTag(computer);
|
||||
computerItems.put(ComputerTileEntity.MEMORY_TAG_NAME, makeInventoryTag(
|
||||
computerItems.put(CommonVirtualMachineItemStackHandlers.MEMORY_TAG_NAME, makeInventoryTag(
|
||||
MemoryItem.withCapacity(8 * Constants.MEGABYTE),
|
||||
MemoryItem.withCapacity(8 * Constants.MEGABYTE),
|
||||
MemoryItem.withCapacity(8 * Constants.MEGABYTE)
|
||||
));
|
||||
computerItems.put(ComputerTileEntity.HARD_DRIVE_TAG_NAME, makeInventoryTag(
|
||||
computerItems.put(CommonVirtualMachineItemStackHandlers.HARD_DRIVE_TAG_NAME, makeInventoryTag(
|
||||
HardDriveItem.withBase(BaseBlockDevices.BUILDROOT.get())
|
||||
));
|
||||
computerItems.put(ComputerTileEntity.FLASH_MEMORY_TAG_NAME, makeInventoryTag(
|
||||
computerItems.put(CommonVirtualMachineItemStackHandlers.FLASH_MEMORY_TAG_NAME, makeInventoryTag(
|
||||
FlashMemoryItem.withFirmware(Firmwares.BUILDROOT.get())
|
||||
));
|
||||
computerItems.put(ComputerTileEntity.CARD_TAG_NAME, makeInventoryTag(
|
||||
computerItems.put(CommonVirtualMachineItemStackHandlers.CARD_TAG_NAME, makeInventoryTag(
|
||||
new ItemStack(Items.NETWORK_INTERFACE_CARD_ITEM.get())
|
||||
));
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package li.cil.oc2.common.network;
|
||||
import li.cil.oc2.common.util.WorldUtils;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.ServerPlayerEntity;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
@@ -27,6 +28,20 @@ public final class MessageUtils {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends Entity> void withServerEntity(final Supplier<NetworkEvent.Context> context, final int id, final Class<T> type, final Consumer<T> callback) {
|
||||
final ServerPlayerEntity player = context.get().getSender();
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ServerWorld world = player.getServerWorld();
|
||||
final Entity entity = world.getEntityByID(id);
|
||||
if (type.isInstance(entity)) {
|
||||
callback.accept((T) entity);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends TileEntity> void withClientTileEntityAt(final BlockPos pos, final Class<T> type, final Consumer<T> callback) {
|
||||
final ClientWorld world = Minecraft.getInstance().world;
|
||||
@@ -39,4 +54,17 @@ public final class MessageUtils {
|
||||
callback.accept((T) tileEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends Entity> void withClientEntity(final int id, final Class<T> type, final Consumer<T> callback) {
|
||||
final ClientWorld world = Minecraft.getInstance().world;
|
||||
if (world == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Entity entity = world.getEntityByID(id);
|
||||
if (type.isInstance(entity)) {
|
||||
callback.accept((T) entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package li.cil.oc2.common.network;
|
||||
|
||||
import li.cil.oc2.api.API;
|
||||
import li.cil.oc2.common.network.message.*;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraftforge.fml.network.NetworkDirection;
|
||||
@@ -26,16 +27,16 @@ public final class Network {
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void setup() {
|
||||
INSTANCE.messageBuilder(TerminalBlockOutputMessage.class, getNextPacketId(), NetworkDirection.PLAY_TO_CLIENT)
|
||||
.encoder(TerminalBlockOutputMessage::toBytes)
|
||||
.decoder(TerminalBlockOutputMessage::new)
|
||||
.consumer(TerminalBlockOutputMessage::handleMessage)
|
||||
INSTANCE.messageBuilder(ComputerTerminalOutputMessage.class, getNextPacketId(), NetworkDirection.PLAY_TO_CLIENT)
|
||||
.encoder(ComputerTerminalOutputMessage::toBytes)
|
||||
.decoder(ComputerTerminalOutputMessage::new)
|
||||
.consumer(ComputerTerminalOutputMessage::handleMessage)
|
||||
.add();
|
||||
|
||||
INSTANCE.messageBuilder(TerminalBlockInputMessage.class, getNextPacketId(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.encoder(TerminalBlockInputMessage::toBytes)
|
||||
.decoder(TerminalBlockInputMessage::new)
|
||||
.consumer(TerminalBlockInputMessage::handleMessage)
|
||||
INSTANCE.messageBuilder(ComputerTerminalInputMessage.class, getNextPacketId(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.encoder(ComputerTerminalInputMessage::toBytes)
|
||||
.decoder(ComputerTerminalInputMessage::new)
|
||||
.consumer(ComputerTerminalInputMessage::handleMessage)
|
||||
.add();
|
||||
|
||||
INSTANCE.messageBuilder(ComputerRunStateMessage.class, getNextPacketId(), NetworkDirection.PLAY_TO_CLIENT)
|
||||
@@ -67,12 +68,34 @@ public final class Network {
|
||||
.decoder(NetworkConnectorConnectionsMessage::new)
|
||||
.consumer(NetworkConnectorConnectionsMessage::handleMessage)
|
||||
.add();
|
||||
|
||||
INSTANCE.messageBuilder(RobotTerminalOutputMessage.class, getNextPacketId(), NetworkDirection.PLAY_TO_CLIENT)
|
||||
.encoder(RobotTerminalOutputMessage::toBytes)
|
||||
.decoder(RobotTerminalOutputMessage::new)
|
||||
.consumer(RobotTerminalOutputMessage::handleMessage)
|
||||
.add();
|
||||
|
||||
INSTANCE.messageBuilder(RobotTerminalInputMessage.class, getNextPacketId(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.encoder(RobotTerminalInputMessage::toBytes)
|
||||
.decoder(RobotTerminalInputMessage::new)
|
||||
.consumer(RobotTerminalInputMessage::handleMessage)
|
||||
.add();
|
||||
|
||||
INSTANCE.messageBuilder(RobotBootErrorMessage.class, getNextPacketId(), NetworkDirection.PLAY_TO_CLIENT)
|
||||
.encoder(RobotBootErrorMessage::toBytes)
|
||||
.decoder(RobotBootErrorMessage::new)
|
||||
.consumer(RobotBootErrorMessage::handleMessage)
|
||||
.add();
|
||||
}
|
||||
|
||||
public static <T> void sendToClientsTrackingChunk(final T message, final Chunk chunk) {
|
||||
Network.INSTANCE.send(PacketDistributor.TRACKING_CHUNK.with(() -> chunk), message);
|
||||
}
|
||||
|
||||
public static <T> void sendToClientsTrackingEntity(final T message, final Entity entity) {
|
||||
Network.INSTANCE.send(PacketDistributor.TRACKING_ENTITY.with(() -> entity), message);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private static int getNextPacketId() {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package li.cil.oc2.common.network.message;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public abstract class AbstractTerminalEntityMessage {
|
||||
protected int entityId;
|
||||
protected byte[] data;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
protected AbstractTerminalEntityMessage(final Entity entity, final ByteBuffer data) {
|
||||
this.entityId = entity.getEntityId();
|
||||
this.data = data.array();
|
||||
}
|
||||
|
||||
protected AbstractTerminalEntityMessage(final PacketBuffer buffer) {
|
||||
fromBytes(buffer);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public void fromBytes(final PacketBuffer buffer) {
|
||||
entityId = buffer.readVarInt();
|
||||
data = buffer.readByteArray();
|
||||
}
|
||||
|
||||
public static void toBytes(final AbstractTerminalEntityMessage message, final PacketBuffer buffer) {
|
||||
buffer.writeVarInt(message.entityId);
|
||||
buffer.writeByteArray(message.data);
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ public final class ComputerBootErrorMessage {
|
||||
|
||||
public ComputerBootErrorMessage(final ComputerTileEntity tileEntity) {
|
||||
this.pos = tileEntity.getPos();
|
||||
this.value = tileEntity.getBootError();
|
||||
this.value = tileEntity.getState().getBootError();
|
||||
}
|
||||
|
||||
public ComputerBootErrorMessage(final PacketBuffer buffer) {
|
||||
@@ -28,7 +28,7 @@ public final class ComputerBootErrorMessage {
|
||||
|
||||
public static boolean handleMessage(final ComputerBootErrorMessage message, final Supplier<NetworkEvent.Context> context) {
|
||||
context.get().enqueueWork(() -> MessageUtils.withClientTileEntityAt(message.pos, ComputerTileEntity.class,
|
||||
(tileEntity) -> tileEntity.setBootErrorClient(message.value)));
|
||||
(tileEntity) -> tileEntity.getState().setBootErrorClient(message.value)));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ public final class ComputerBusStateMessage {
|
||||
|
||||
public ComputerBusStateMessage(final ComputerTileEntity tileEntity) {
|
||||
this.pos = tileEntity.getPos();
|
||||
this.busState = tileEntity.getBusState();
|
||||
this.busState = tileEntity.getState().getBusState();
|
||||
}
|
||||
|
||||
public ComputerBusStateMessage(final PacketBuffer buffer) {
|
||||
@@ -28,7 +28,7 @@ public final class ComputerBusStateMessage {
|
||||
|
||||
public static boolean handleMessage(final ComputerBusStateMessage message, final Supplier<NetworkEvent.Context> context) {
|
||||
context.get().enqueueWork(() -> MessageUtils.withClientTileEntityAt(message.pos, ComputerTileEntity.class,
|
||||
(tileEntity) -> tileEntity.setBusStateClient(message.busState)));
|
||||
(tileEntity) -> tileEntity.getState().setBusStateClient(message.busState)));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package li.cil.oc2.common.network.message;
|
||||
|
||||
import li.cil.oc2.common.network.MessageUtils;
|
||||
import li.cil.oc2.common.tileentity.ComputerTileEntity;
|
||||
import li.cil.oc2.common.vm.VirtualMachineState;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraftforge.fml.network.NetworkEvent;
|
||||
@@ -10,13 +11,13 @@ import java.util.function.Supplier;
|
||||
|
||||
public final class ComputerRunStateMessage {
|
||||
private BlockPos pos;
|
||||
private ComputerTileEntity.RunState runState;
|
||||
private VirtualMachineState.RunState runState;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public ComputerRunStateMessage(final ComputerTileEntity tileEntity) {
|
||||
this.pos = tileEntity.getPos();
|
||||
this.runState = tileEntity.getRunState();
|
||||
this.runState = tileEntity.getState().getRunState();
|
||||
}
|
||||
|
||||
public ComputerRunStateMessage(final PacketBuffer buffer) {
|
||||
@@ -27,13 +28,13 @@ public final class ComputerRunStateMessage {
|
||||
|
||||
public static boolean handleMessage(final ComputerRunStateMessage message, final Supplier<NetworkEvent.Context> context) {
|
||||
context.get().enqueueWork(() -> MessageUtils.withClientTileEntityAt(message.pos, ComputerTileEntity.class,
|
||||
(tileEntity) -> tileEntity.setRunStateClient(message.runState)));
|
||||
(tileEntity) -> tileEntity.getState().setRunStateClient(message.runState)));
|
||||
return true;
|
||||
}
|
||||
|
||||
public void fromBytes(final PacketBuffer buffer) {
|
||||
pos = buffer.readBlockPos();
|
||||
runState = buffer.readEnumValue(ComputerTileEntity.RunState.class);
|
||||
runState = buffer.readEnumValue(VirtualMachineState.RunState.class);
|
||||
}
|
||||
|
||||
public static void toBytes(final ComputerRunStateMessage message, final PacketBuffer buffer) {
|
||||
|
||||
@@ -8,12 +8,12 @@ import net.minecraftforge.fml.network.NetworkEvent;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class TerminalBlockInputMessage extends AbstractTerminalBlockMessage {
|
||||
public TerminalBlockInputMessage(final ComputerTileEntity tileEntity, final ByteBuffer data) {
|
||||
public final class ComputerTerminalInputMessage extends AbstractTerminalBlockMessage {
|
||||
public ComputerTerminalInputMessage(final ComputerTileEntity tileEntity, final ByteBuffer data) {
|
||||
super(tileEntity, data);
|
||||
}
|
||||
|
||||
public TerminalBlockInputMessage(final PacketBuffer buffer) {
|
||||
public ComputerTerminalInputMessage(final PacketBuffer buffer) {
|
||||
super(buffer);
|
||||
}
|
||||
|
||||
@@ -8,12 +8,12 @@ import net.minecraftforge.fml.network.NetworkEvent;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class TerminalBlockOutputMessage extends AbstractTerminalBlockMessage {
|
||||
public TerminalBlockOutputMessage(final ComputerTileEntity tileEntity, final ByteBuffer data) {
|
||||
public final class ComputerTerminalOutputMessage extends AbstractTerminalBlockMessage {
|
||||
public ComputerTerminalOutputMessage(final ComputerTileEntity tileEntity, final ByteBuffer data) {
|
||||
super(tileEntity, data);
|
||||
}
|
||||
|
||||
public TerminalBlockOutputMessage(final PacketBuffer buffer) {
|
||||
public ComputerTerminalOutputMessage(final PacketBuffer buffer) {
|
||||
super(buffer);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package li.cil.oc2.common.network.message;
|
||||
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.network.MessageUtils;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraftforge.fml.network.NetworkEvent;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class RobotBootErrorMessage {
|
||||
private int entityId;
|
||||
private ITextComponent value;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public RobotBootErrorMessage(final RobotEntity robot) {
|
||||
this.entityId = robot.getEntityId();
|
||||
this.value = robot.getState().getBootError();
|
||||
}
|
||||
|
||||
public RobotBootErrorMessage(final PacketBuffer buffer) {
|
||||
fromBytes(buffer);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static boolean handleMessage(final RobotBootErrorMessage message, final Supplier<NetworkEvent.Context> context) {
|
||||
context.get().enqueueWork(() -> MessageUtils.withClientEntity(message.entityId, RobotEntity.class,
|
||||
(robot) -> robot.getState().setBootErrorClient(message.value)));
|
||||
return true;
|
||||
}
|
||||
|
||||
public void fromBytes(final PacketBuffer buffer) {
|
||||
entityId = buffer.readVarInt();
|
||||
value = buffer.readTextComponent();
|
||||
}
|
||||
|
||||
public static void toBytes(final RobotBootErrorMessage message, final PacketBuffer buffer) {
|
||||
buffer.writeVarInt(message.entityId);
|
||||
buffer.writeTextComponent(message.value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package li.cil.oc2.common.network.message;
|
||||
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.network.MessageUtils;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraftforge.fml.network.NetworkEvent;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class RobotTerminalInputMessage extends AbstractTerminalEntityMessage {
|
||||
public RobotTerminalInputMessage(final RobotEntity robot, final ByteBuffer data) {
|
||||
super(robot, data);
|
||||
}
|
||||
|
||||
public RobotTerminalInputMessage(final PacketBuffer buffer) {
|
||||
super(buffer);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static boolean handleMessage(final AbstractTerminalEntityMessage message, final Supplier<NetworkEvent.Context> context) {
|
||||
context.get().enqueueWork(() -> MessageUtils.withServerEntity(context, message.entityId, RobotEntity.class,
|
||||
(tileEntity) -> tileEntity.getTerminal().putInput(ByteBuffer.wrap(message.data))));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package li.cil.oc2.common.network.message;
|
||||
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.network.MessageUtils;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraftforge.fml.network.NetworkEvent;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class RobotTerminalOutputMessage extends AbstractTerminalEntityMessage {
|
||||
public RobotTerminalOutputMessage(final RobotEntity robot, final ByteBuffer data) {
|
||||
super(robot, data);
|
||||
}
|
||||
|
||||
public RobotTerminalOutputMessage(final PacketBuffer buffer) {
|
||||
super(buffer);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static boolean handleMessage(final AbstractTerminalEntityMessage message, final Supplier<NetworkEvent.Context> context) {
|
||||
context.get().enqueueWork(() -> MessageUtils.withClientEntity(message.entityId, RobotEntity.class,
|
||||
robot -> robot.getTerminal().putOutput(ByteBuffer.wrap(message.data))));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,6 @@ package li.cil.oc2.common.tileentity;
|
||||
|
||||
import li.cil.oc2.api.bus.DeviceBusElement;
|
||||
import li.cil.oc2.api.bus.device.Device;
|
||||
import li.cil.oc2.api.bus.device.DeviceType;
|
||||
import li.cil.oc2.api.bus.device.DeviceTypes;
|
||||
import li.cil.oc2.api.bus.device.vm.VMDevice;
|
||||
import li.cil.oc2.api.bus.device.vm.VMDeviceLifecycleEventType;
|
||||
import li.cil.oc2.api.bus.device.vm.VMDeviceLoadResult;
|
||||
import li.cil.oc2.common.Constants;
|
||||
import li.cil.oc2.common.block.ComputerBlock;
|
||||
import li.cil.oc2.common.bus.AbstractDeviceBusController;
|
||||
@@ -17,115 +12,56 @@ 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;
|
||||
import li.cil.oc2.common.container.DeviceItemStackHandler;
|
||||
import li.cil.oc2.common.container.TypedDeviceItemStackHandler;
|
||||
import li.cil.oc2.common.network.Network;
|
||||
import li.cil.oc2.common.network.message.ComputerBootErrorMessage;
|
||||
import li.cil.oc2.common.network.message.ComputerBusStateMessage;
|
||||
import li.cil.oc2.common.network.message.ComputerRunStateMessage;
|
||||
import li.cil.oc2.common.network.message.TerminalBlockOutputMessage;
|
||||
import li.cil.oc2.common.network.message.ComputerTerminalOutputMessage;
|
||||
import li.cil.oc2.common.serialization.NBTSerialization;
|
||||
import li.cil.oc2.common.util.HorizontalBlockUtils;
|
||||
import li.cil.oc2.common.util.ItemStackUtils;
|
||||
import li.cil.oc2.common.util.NBTTagIds;
|
||||
import li.cil.oc2.common.util.NBTUtils;
|
||||
import li.cil.oc2.common.vm.AbstractTerminalVirtualMachineRunner;
|
||||
import li.cil.oc2.common.vm.CommonVirtualMachine;
|
||||
import li.cil.oc2.common.vm.Terminal;
|
||||
import li.cil.sedna.api.memory.MemoryAccessException;
|
||||
import li.cil.oc2.common.vm.*;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.tileentity.ITickableTileEntity;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.TranslationTextComponent;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.capabilities.ICapabilityProvider;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
import net.minecraftforge.items.IItemHandler;
|
||||
import net.minecraftforge.items.wrapper.CombinedInvWrapper;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public final class ComputerTileEntity extends AbstractTileEntity implements ITickableTileEntity {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static final String MEMORY_TAG_NAME = "memory";
|
||||
public static final String HARD_DRIVE_TAG_NAME = "hard_drive";
|
||||
public static final String FLASH_MEMORY_TAG_NAME = "flash_memory";
|
||||
public static final String CARD_TAG_NAME = "card";
|
||||
|
||||
private static final String BUS_ELEMENT_TAG_NAME = "busElement";
|
||||
private static final String TERMINAL_TAG_NAME = "terminal";
|
||||
private static final String VIRTUAL_MACHINE_TAG_NAME = "virtualMachine";
|
||||
private static final String RUNNER_TAG_NAME = "runner";
|
||||
|
||||
private static final String BUS_STATE_TAG_NAME = "busState";
|
||||
private static final String RUN_STATE_TAG_NAME = "runState";
|
||||
private static final String BOOT_ERROR_TAG_NAME = "bootError";
|
||||
|
||||
private static final int DEVICE_LOAD_RETRY_INTERVAL = 10 * Constants.TICK_SECONDS;
|
||||
private static final String STATE_TAG_NAME = "state";
|
||||
|
||||
private static final int MEMORY_SLOTS = 4;
|
||||
private static final int HARD_DRIVE_SLOTS = 4;
|
||||
private static final int FLASH_MEMORY_SLOTS = 1;
|
||||
private static final int CARD_SLOTS = 4;
|
||||
|
||||
private static final long ITEM_DEVICE_BASE_ADDRESS = 0x40000000L;
|
||||
private static final int ITEM_DEVICE_STRIDE = 0x1000;
|
||||
|
||||
private static final ByteBuffer TERMINAL_RESET_SEQUENCE = ByteBuffer.wrap(new byte[]{
|
||||
// Make sure we're in normal mode.
|
||||
'J',
|
||||
// Reset color and style.
|
||||
'\033', '[', '0', 'm',
|
||||
// Clear screen.
|
||||
'\033', '[', '2', 'J'
|
||||
});
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public enum RunState {
|
||||
STOPPED,
|
||||
LOADING_DEVICES,
|
||||
RUNNING,
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private Chunk chunk;
|
||||
private final AbstractDeviceBusController busController;
|
||||
private AbstractDeviceBusController.BusState busState;
|
||||
private RunState runState;
|
||||
private ITextComponent bootError;
|
||||
private int loadDevicesDelay;
|
||||
private boolean hasAddedOwnDevices;
|
||||
private boolean isNeighborUpdateScheduled;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private final DeviceItemStackHandler memoryItemHandler = new ComputerItemHandler(MEMORY_SLOTS, this::getDevices, DeviceTypes.MEMORY);
|
||||
private final DeviceItemStackHandler hardDriveItemHandler = new ComputerItemHandler(HARD_DRIVE_SLOTS, this::getDevices, DeviceTypes.HARD_DRIVE);
|
||||
private final DeviceItemStackHandler flashMemoryItemHandler = new ComputerItemHandler(FLASH_MEMORY_SLOTS, this::getDevices, DeviceTypes.FLASH_MEMORY);
|
||||
private final DeviceItemStackHandler cardItemHandler = new ComputerItemHandler(CARD_SLOTS, this::getDevices, DeviceTypes.CARD);
|
||||
|
||||
private final IItemHandler itemHandlers = new CombinedInvWrapper(memoryItemHandler, hardDriveItemHandler, flashMemoryItemHandler, cardItemHandler);
|
||||
|
||||
private final Terminal terminal = new Terminal();
|
||||
private final TileEntityDeviceBusElement busElement = new ComputerBusElement();
|
||||
private final CommonVirtualMachine virtualMachine;
|
||||
private ComputerVirtualMachineRunner runner;
|
||||
|
||||
private final ComputerVirtualMachineState state;
|
||||
private final ComputerItemStackHandlers items = new ComputerItemStackHandlers();
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -135,124 +71,42 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic
|
||||
// We want to unload devices even on world unload to free global resources.
|
||||
setNeedsWorldUnloadEvent();
|
||||
|
||||
busController = new ComputerBusController(busElement);
|
||||
busState = AbstractDeviceBusController.BusState.SCAN_PENDING;
|
||||
runState = RunState.STOPPED;
|
||||
|
||||
virtualMachine = new CommonVirtualMachine(busController);
|
||||
virtualMachine.vmAdapter.setDefaultAddressProvider(this::getDefaultDeviceAddress);
|
||||
}
|
||||
|
||||
public Optional<IItemHandler> getItemHandler(final DeviceType deviceType) {
|
||||
if (deviceType == DeviceTypes.MEMORY) {
|
||||
return Optional.of(memoryItemHandler);
|
||||
} else if (deviceType == DeviceTypes.HARD_DRIVE) {
|
||||
return Optional.of(hardDriveItemHandler);
|
||||
} else if (deviceType == DeviceTypes.FLASH_MEMORY) {
|
||||
return Optional.of(flashMemoryItemHandler);
|
||||
} else if (deviceType == DeviceTypes.CARD) {
|
||||
return Optional.of(cardItemHandler);
|
||||
}
|
||||
return Optional.empty();
|
||||
final ComputerBusController busController = new ComputerBusController(busElement);
|
||||
state = new ComputerVirtualMachineState(busController, new CommonVirtualMachine(busController));
|
||||
}
|
||||
|
||||
public Terminal getTerminal() {
|
||||
return terminal;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (runState == RunState.RUNNING) {
|
||||
return;
|
||||
}
|
||||
public VirtualMachineState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public CommonVirtualMachineItemStackHandlers getItemHandlers() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
final World world = getWorld();
|
||||
if (world == null || world.isRemote()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setBootError(null);
|
||||
setRunState(RunState.LOADING_DEVICES);
|
||||
loadDevicesDelay = 0;
|
||||
state.start();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (runState == RunState.STOPPED) {
|
||||
return;
|
||||
}
|
||||
|
||||
final World world = getWorld();
|
||||
if (world == null || world.isRemote()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (runState == RunState.LOADING_DEVICES) {
|
||||
setRunState(RunState.STOPPED);
|
||||
return;
|
||||
}
|
||||
|
||||
stopRunnerAndReset();
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return getBusState() == AbstractDeviceBusController.BusState.READY &&
|
||||
getRunState() == RunState.RUNNING;
|
||||
}
|
||||
|
||||
public AbstractDeviceBusController.BusState getBusState() {
|
||||
return busState;
|
||||
}
|
||||
|
||||
public RunState getRunState() {
|
||||
return runState;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ITextComponent getBootError() {
|
||||
switch (getBusState()) {
|
||||
case SCAN_PENDING:
|
||||
case INCOMPLETE:
|
||||
return new TranslationTextComponent(Constants.COMPUTER_BUS_STATE_INCOMPLETE);
|
||||
case TOO_COMPLEX:
|
||||
return new TranslationTextComponent(Constants.COMPUTER_BUS_STATE_TOO_COMPLEX);
|
||||
case MULTIPLE_CONTROLLERS:
|
||||
return new TranslationTextComponent(Constants.COMPUTER_BUS_STATE_MULTIPLE_CONTROLLERS);
|
||||
case READY:
|
||||
switch (getRunState()) {
|
||||
case STOPPED:
|
||||
case LOADING_DEVICES:
|
||||
return bootError;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
state.stop();
|
||||
}
|
||||
|
||||
public void handleNeighborChanged() {
|
||||
busController.scheduleBusScan();
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public void setRunStateClient(final RunState value) {
|
||||
final World world = getWorld();
|
||||
if (world != null && world.isRemote()) {
|
||||
runState = value;
|
||||
}
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public void setBusStateClient(final AbstractDeviceBusController.BusState value) {
|
||||
final World world = getWorld();
|
||||
if (world != null && world.isRemote()) {
|
||||
busState = value;
|
||||
}
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public void setBootErrorClient(final ITextComponent value) {
|
||||
final World world = getWorld();
|
||||
if (world != null && world.isRemote()) {
|
||||
bootError = value;
|
||||
}
|
||||
state.busController.scheduleBusScan();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -267,7 +121,7 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic
|
||||
}
|
||||
|
||||
final Direction localSide = HorizontalBlockUtils.toLocal(getBlockState(), side);
|
||||
for (final Device device : busController.getDevices()) {
|
||||
for (final Device device : state.busController.getDevices()) {
|
||||
if (device instanceof ICapabilityProvider) {
|
||||
final LazyOptional<T> value = ((ICapabilityProvider) device).getCapability(capability, localSide);
|
||||
if (value.isPresent()) {
|
||||
@@ -286,10 +140,6 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic
|
||||
return;
|
||||
}
|
||||
|
||||
if (chunk == null) {
|
||||
chunk = world.getChunkAt(getPos());
|
||||
}
|
||||
|
||||
// Always add devices provided for the computer itself, even if there's no
|
||||
// adjacent cable. Because that would just be weird.
|
||||
if (!hasAddedOwnDevices) {
|
||||
@@ -304,79 +154,7 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic
|
||||
world.notifyNeighborsOfStateChange(getPos(), getBlockState().getBlock());
|
||||
}
|
||||
|
||||
final AbstractDeviceBusController.BusState oldBusState = busController.getState();
|
||||
busController.scan();
|
||||
setBusState(busController.getState());
|
||||
if (busState != AbstractDeviceBusController.BusState.READY) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (oldBusState != AbstractDeviceBusController.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());
|
||||
}
|
||||
|
||||
switch (runState) {
|
||||
case STOPPED:
|
||||
break;
|
||||
case LOADING_DEVICES:
|
||||
if (loadDevicesDelay > 0) {
|
||||
loadDevicesDelay--;
|
||||
break;
|
||||
}
|
||||
|
||||
final VMDeviceLoadResult loadResult = virtualMachine.vmAdapter.load();
|
||||
if (!loadResult.wasSuccessful()) {
|
||||
if (loadResult.getErrorMessage() != null) {
|
||||
setBootError(loadResult.getErrorMessage());
|
||||
} else {
|
||||
setBootError(new TranslationTextComponent(Constants.COMPUTER_BOOT_ERROR_UNKNOWN));
|
||||
}
|
||||
loadDevicesDelay = DEVICE_LOAD_RETRY_INTERVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
// May have a valid runner after load. In which case we just had to wait for
|
||||
// bus setup and devices to load. So we can keep using it.
|
||||
if (runner == null) {
|
||||
try {
|
||||
virtualMachine.board.reset();
|
||||
virtualMachine.board.initialize();
|
||||
virtualMachine.board.setRunning(true);
|
||||
} catch (final IllegalStateException e) {
|
||||
// FDT did not fit into memory. Technically it's possible to run with
|
||||
// a program that only uses registers. But not supporting that esoteric
|
||||
// use-case loses out against avoiding people getting confused for having
|
||||
// forgotten to add some RAM modules.
|
||||
setBootError(new TranslationTextComponent(Constants.COMPUTER_BOOT_ERROR_NO_MEMORY));
|
||||
setRunState(RunState.STOPPED);
|
||||
return;
|
||||
} catch (final MemoryAccessException e) {
|
||||
LOGGER.error(e);
|
||||
setBootError(new TranslationTextComponent(Constants.COMPUTER_BOOT_ERROR_UNKNOWN));
|
||||
setRunState(RunState.STOPPED);
|
||||
return;
|
||||
}
|
||||
|
||||
runner = new ComputerVirtualMachineRunner(virtualMachine, terminal);
|
||||
}
|
||||
|
||||
setRunState(RunState.RUNNING);
|
||||
|
||||
// Only start running next tick. This gives loaded devices one tick to do async
|
||||
// initialization. This is used by devices to restore data from disk, for example.
|
||||
break;
|
||||
case RUNNING:
|
||||
if (!virtualMachine.board.isRunning()) {
|
||||
stopRunnerAndReset();
|
||||
break;
|
||||
}
|
||||
|
||||
runner.tick();
|
||||
chunk.markDirty();
|
||||
break;
|
||||
}
|
||||
state.tick();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -385,127 +163,68 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic
|
||||
|
||||
// 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();
|
||||
state.virtualMachine.vmAdapter.unload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT getUpdateTag() {
|
||||
final CompoundNBT result = super.getUpdateTag();
|
||||
final CompoundNBT tag = super.getUpdateTag();
|
||||
|
||||
result.put(TERMINAL_TAG_NAME, NBTSerialization.serialize(terminal));
|
||||
result.putInt(BUS_STATE_TAG_NAME, busState.ordinal());
|
||||
result.putInt(RUN_STATE_TAG_NAME, runState.ordinal());
|
||||
result.putString(BOOT_ERROR_TAG_NAME, ITextComponent.Serializer.toJson(bootError));
|
||||
tag.put(TERMINAL_TAG_NAME, NBTSerialization.serialize(terminal));
|
||||
tag.putInt(AbstractVirtualMachineState.BUS_STATE_TAG_NAME, state.getBusState().ordinal());
|
||||
tag.putInt(AbstractVirtualMachineState.RUN_STATE_TAG_NAME, state.getRunState().ordinal());
|
||||
tag.putString(AbstractVirtualMachineState.BOOT_ERROR_TAG_NAME, ITextComponent.Serializer.toJson(state.getBootError()));
|
||||
|
||||
return result;
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleUpdateTag(final BlockState state, final CompoundNBT tag) {
|
||||
super.handleUpdateTag(state, tag);
|
||||
public void handleUpdateTag(final BlockState blockState, final CompoundNBT tag) {
|
||||
super.handleUpdateTag(blockState, tag);
|
||||
|
||||
NBTSerialization.deserialize(tag.getCompound(TERMINAL_TAG_NAME), terminal);
|
||||
busState = AbstractDeviceBusController.BusState.values()[tag.getInt(BUS_STATE_TAG_NAME)];
|
||||
runState = RunState.values()[tag.getInt(RUN_STATE_TAG_NAME)];
|
||||
bootError = ITextComponent.Serializer.getComponentFromJson(tag.getString(BOOT_ERROR_TAG_NAME));
|
||||
state.setBusStateClient(AbstractDeviceBusController.BusState.values()[tag.getInt(AbstractVirtualMachineState.BUS_STATE_TAG_NAME)]);
|
||||
state.setRunStateClient(VirtualMachineState.RunState.values()[tag.getInt(AbstractVirtualMachineState.RUN_STATE_TAG_NAME)]);
|
||||
state.setBootErrorClient(ITextComponent.Serializer.getComponentFromJson(tag.getString(AbstractVirtualMachineState.BOOT_ERROR_TAG_NAME)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT tag) {
|
||||
tag = super.write(tag);
|
||||
|
||||
joinVirtualMachine();
|
||||
|
||||
if (runner != null) {
|
||||
tag.put(RUNNER_TAG_NAME, NBTSerialization.serialize(runner));
|
||||
virtualMachine.vmAdapter.fireLifecycleEvent(VMDeviceLifecycleEventType.PAUSING);
|
||||
runner.scheduleResumeEvent(); // Allow synchronizing to async device saves.
|
||||
} else {
|
||||
NBTUtils.putEnum(tag, RUN_STATE_TAG_NAME, runState);
|
||||
}
|
||||
|
||||
tag.put(STATE_TAG_NAME, state.serialize());
|
||||
tag.put(TERMINAL_TAG_NAME, NBTSerialization.serialize(terminal));
|
||||
|
||||
tag.put(BUS_ELEMENT_TAG_NAME, NBTSerialization.serialize(busElement));
|
||||
tag.put(VIRTUAL_MACHINE_TAG_NAME, NBTSerialization.serialize(virtualMachine));
|
||||
|
||||
final CompoundNBT items = new CompoundNBT();
|
||||
items.put(MEMORY_TAG_NAME, memoryItemHandler.serializeNBT());
|
||||
items.put(HARD_DRIVE_TAG_NAME, hardDriveItemHandler.serializeNBT());
|
||||
items.put(FLASH_MEMORY_TAG_NAME, flashMemoryItemHandler.serializeNBT());
|
||||
items.put(CARD_TAG_NAME, cardItemHandler.serializeNBT());
|
||||
tag.put(Constants.BLOCK_ENTITY_INVENTORY_TAG_NAME, items);
|
||||
tag.put(Constants.INVENTORY_TAG_NAME, items.serialize());
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(final BlockState state, final CompoundNBT tag) {
|
||||
super.read(state, tag);
|
||||
|
||||
joinVirtualMachine();
|
||||
public void read(final BlockState blockState, final CompoundNBT tag) {
|
||||
super.read(blockState, tag);
|
||||
|
||||
state.deserialize(tag.getCompound(STATE_TAG_NAME));
|
||||
NBTSerialization.deserialize(tag.getCompound(TERMINAL_TAG_NAME), terminal);
|
||||
|
||||
if (tag.contains(BUS_ELEMENT_TAG_NAME, NBTTagIds.TAG_COMPOUND)) {
|
||||
NBTSerialization.deserialize(tag.getCompound(BUS_ELEMENT_TAG_NAME), busElement);
|
||||
}
|
||||
|
||||
if (tag.contains(VIRTUAL_MACHINE_TAG_NAME, NBTTagIds.TAG_COMPOUND)) {
|
||||
NBTSerialization.deserialize(tag.getCompound(VIRTUAL_MACHINE_TAG_NAME), virtualMachine);
|
||||
if (tag.contains(Constants.INVENTORY_TAG_NAME, NBTTagIds.TAG_COMPOUND)) {
|
||||
items.deserialize(tag.getCompound(Constants.INVENTORY_TAG_NAME));
|
||||
}
|
||||
|
||||
if (tag.contains(RUNNER_TAG_NAME, NBTTagIds.TAG_COMPOUND)) {
|
||||
runner = new ComputerVirtualMachineRunner(virtualMachine, terminal);
|
||||
NBTSerialization.deserialize(tag.getCompound(RUNNER_TAG_NAME), runner);
|
||||
runState = RunState.LOADING_DEVICES;
|
||||
} else {
|
||||
runState = NBTUtils.getEnum(tag, RUN_STATE_TAG_NAME, RunState.class);
|
||||
if (runState == null) {
|
||||
runState = RunState.STOPPED;
|
||||
} else if (runState == RunState.RUNNING) {
|
||||
runState = RunState.LOADING_DEVICES;
|
||||
}
|
||||
}
|
||||
|
||||
if (tag.contains(Constants.BLOCK_ENTITY_INVENTORY_TAG_NAME, NBTTagIds.TAG_COMPOUND)) {
|
||||
final CompoundNBT items = tag.getCompound(Constants.BLOCK_ENTITY_INVENTORY_TAG_NAME);
|
||||
memoryItemHandler.deserializeNBT(items.getCompound(MEMORY_TAG_NAME));
|
||||
hardDriveItemHandler.deserializeNBT(items.getCompound(HARD_DRIVE_TAG_NAME));
|
||||
flashMemoryItemHandler.deserializeNBT(items.getCompound(FLASH_MEMORY_TAG_NAME));
|
||||
cardItemHandler.deserializeNBT(items.getCompound(CARD_TAG_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
for (int slot = 0; slot < itemHandlers.getSlots(); slot++) {
|
||||
if (!itemHandlers.getStackInSlot(slot).isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void exportDeviceDataToItemStacks() {
|
||||
memoryItemHandler.exportDeviceDataToItemStacks();
|
||||
hardDriveItemHandler.exportDeviceDataToItemStacks();
|
||||
flashMemoryItemHandler.exportDeviceDataToItemStacks();
|
||||
cardItemHandler.exportDeviceDataToItemStacks();
|
||||
}
|
||||
|
||||
public void exportToItemStack(final ItemStack stack) {
|
||||
final CompoundNBT items = ItemStackUtils.getOrCreateTileEntityInventoryTag(stack);
|
||||
items.put(MEMORY_TAG_NAME, memoryItemHandler.serializeNBT());
|
||||
items.put(HARD_DRIVE_TAG_NAME, hardDriveItemHandler.serializeNBT());
|
||||
items.put(FLASH_MEMORY_TAG_NAME, flashMemoryItemHandler.serializeNBT());
|
||||
items.put(CARD_TAG_NAME, cardItemHandler.serializeNBT());
|
||||
items.exportToItemStack(stack);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected void collectCapabilities(final CapabilityCollector collector, @Nullable final Direction direction) {
|
||||
collector.offer(Capabilities.ITEM_HANDLER, itemHandlers);
|
||||
collector.offer(Capabilities.ITEM_HANDLER, items.itemHandlers);
|
||||
collector.offer(Capabilities.DEVICE_BUS_ELEMENT, busElement);
|
||||
}
|
||||
|
||||
@@ -521,17 +240,17 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic
|
||||
super.loadServer();
|
||||
|
||||
busElement.initialize();
|
||||
virtualMachine.rtcMinecraft.setWorld(getWorld());
|
||||
state.virtualMachine.rtcMinecraft.setWorld(getWorld());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unloadServer() {
|
||||
super.unloadServer();
|
||||
|
||||
joinVirtualMachine();
|
||||
virtualMachine.vmAdapter.suspend();
|
||||
state.joinVirtualMachine();
|
||||
state.virtualMachine.vmAdapter.suspend();
|
||||
|
||||
busController.dispose();
|
||||
state.busController.dispose();
|
||||
|
||||
// This is necessary in case some other controller found us before our controller
|
||||
// did its scan, which can happen because the scan can happen with a delay.
|
||||
@@ -540,106 +259,19 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private List<ItemDeviceInfo> getDevices(final ItemStack stack) {
|
||||
return Devices.getDevices(this, stack);
|
||||
}
|
||||
|
||||
private void setBusState(final AbstractDeviceBusController.BusState value) {
|
||||
if (value == busState) {
|
||||
return;
|
||||
}
|
||||
|
||||
busState = value;
|
||||
|
||||
final ComputerBusStateMessage message = new ComputerBusStateMessage(this);
|
||||
Network.sendToClientsTrackingChunk(message, chunk);
|
||||
}
|
||||
|
||||
private void setRunState(final RunState value) {
|
||||
if (value == runState) {
|
||||
return;
|
||||
}
|
||||
|
||||
runState = value;
|
||||
|
||||
// This method can be called from disposal logic, so if we are disposed quickly enough
|
||||
// chunk may not be initialized yet. Avoid resulting NRE in network logic.
|
||||
if (chunk != null) {
|
||||
final ComputerRunStateMessage message = new ComputerRunStateMessage(this);
|
||||
Network.sendToClientsTrackingChunk(message, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
private void setBootError(@Nullable final ITextComponent value) {
|
||||
if (Objects.equals(value, bootError)) {
|
||||
return;
|
||||
}
|
||||
|
||||
bootError = value;
|
||||
final ComputerBootErrorMessage message = new ComputerBootErrorMessage(this);
|
||||
Network.sendToClientsTrackingChunk(message, chunk);
|
||||
}
|
||||
|
||||
private void stopRunnerAndReset() {
|
||||
joinVirtualMachine();
|
||||
setRunState(RunState.STOPPED);
|
||||
|
||||
virtualMachine.reset();
|
||||
|
||||
TERMINAL_RESET_SEQUENCE.clear();
|
||||
runner.putTerminalOutput(TERMINAL_RESET_SEQUENCE);
|
||||
runner = null;
|
||||
}
|
||||
|
||||
private void joinVirtualMachine() {
|
||||
if (runner != null) {
|
||||
try {
|
||||
runner.join();
|
||||
} catch (final Throwable e) {
|
||||
LOGGER.error(e);
|
||||
runner = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private OptionalLong getDefaultDeviceAddress(final VMDevice wrapper) {
|
||||
long address = ITEM_DEVICE_BASE_ADDRESS;
|
||||
|
||||
for (int slot = 0; slot < hardDriveItemHandler.getSlots(); slot++) {
|
||||
final Collection<ItemDeviceInfo> devices = hardDriveItemHandler.getBusElement().getDeviceGroup(slot);
|
||||
for (final ItemDeviceInfo info : devices) {
|
||||
if (Objects.equals(info.device, wrapper)) {
|
||||
return OptionalLong.of(address);
|
||||
}
|
||||
}
|
||||
|
||||
address += ITEM_DEVICE_STRIDE;
|
||||
}
|
||||
|
||||
for (int slot = 0; slot < cardItemHandler.getSlots(); slot++) {
|
||||
final Collection<ItemDeviceInfo> devices = cardItemHandler.getBusElement().getDeviceGroup(slot);
|
||||
for (final ItemDeviceInfo info : devices) {
|
||||
if (Objects.equals(info.device, wrapper)) {
|
||||
return OptionalLong.of(address);
|
||||
}
|
||||
}
|
||||
|
||||
address += ITEM_DEVICE_STRIDE;
|
||||
}
|
||||
|
||||
return OptionalLong.empty();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private final class ComputerItemHandler extends TypedDeviceItemStackHandler {
|
||||
public ComputerItemHandler(final int size, final Function<ItemStack, List<ItemDeviceInfo>> deviceLookup, final DeviceType deviceType) {
|
||||
super(size, deviceLookup, deviceType);
|
||||
private final class ComputerItemStackHandlers extends CommonVirtualMachineItemStackHandlers {
|
||||
public ComputerItemStackHandlers() {
|
||||
super(MEMORY_SLOTS, HARD_DRIVE_SLOTS, FLASH_MEMORY_SLOTS, CARD_SLOTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onContentsChanged(final int slot) {
|
||||
super.onContentsChanged(slot);
|
||||
protected List<ItemDeviceInfo> getDevices(final ItemStack stack) {
|
||||
return Devices.getDevices(ComputerTileEntity.this, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onContentsChanged(final DeviceItemStackHandler itemStackHandler, final int slot) {
|
||||
super.onContentsChanged(itemStackHandler, slot);
|
||||
markDirty();
|
||||
isNeighborUpdateScheduled = true;
|
||||
}
|
||||
@@ -652,26 +284,23 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic
|
||||
|
||||
@Override
|
||||
protected void onBeforeScan() {
|
||||
if (runState == RunState.RUNNING) {
|
||||
runState = RunState.LOADING_DEVICES;
|
||||
}
|
||||
|
||||
virtualMachine.rpcAdapter.pause();
|
||||
state.reload();
|
||||
state.virtualMachine.rpcAdapter.pause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAfterDeviceScan(final boolean didDevicesChange) {
|
||||
virtualMachine.rpcAdapter.resume(didDevicesChange);
|
||||
state.virtualMachine.rpcAdapter.resume(didDevicesChange);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDevicesAdded(final Collection<Device> devices) {
|
||||
virtualMachine.vmAdapter.addDevices(devices);
|
||||
state.virtualMachine.vmAdapter.addDevices(devices);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDevicesRemoved(final Collection<Device> devices) {
|
||||
virtualMachine.vmAdapter.removeDevices(devices);
|
||||
state.virtualMachine.vmAdapter.removeDevices(devices);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -684,10 +313,7 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic
|
||||
public Optional<Collection<LazyOptional<DeviceBusElement>>> getNeighbors() {
|
||||
return super.getNeighbors().map(neighbors -> {
|
||||
final ArrayList<LazyOptional<DeviceBusElement>> list = new ArrayList<>(neighbors);
|
||||
list.add(LazyOptional.of(flashMemoryItemHandler::getBusElement));
|
||||
list.add(LazyOptional.of(memoryItemHandler::getBusElement));
|
||||
list.add(LazyOptional.of(hardDriveItemHandler::getBusElement));
|
||||
list.add(LazyOptional.of(cardItemHandler::getBusElement));
|
||||
list.add(LazyOptional.of(() -> items.busElement));
|
||||
return list;
|
||||
});
|
||||
}
|
||||
@@ -705,7 +331,62 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic
|
||||
|
||||
@Override
|
||||
protected void sendTerminalUpdateToClient(final ByteBuffer output) {
|
||||
final TerminalBlockOutputMessage message = new TerminalBlockOutputMessage(ComputerTileEntity.this, output);
|
||||
final ComputerTerminalOutputMessage message = new ComputerTerminalOutputMessage(ComputerTileEntity.this, output);
|
||||
Network.sendToClientsTrackingChunk(message, state.chunk);
|
||||
}
|
||||
}
|
||||
|
||||
private final class ComputerVirtualMachineState extends AbstractVirtualMachineState<ComputerBusController, CommonVirtualMachine> {
|
||||
private Chunk chunk;
|
||||
|
||||
private ComputerVirtualMachineState(final ComputerBusController busController, final CommonVirtualMachine virtualMachine) {
|
||||
super(busController, virtualMachine);
|
||||
virtualMachine.vmAdapter.setDefaultAddressProvider(items::getDefaultDeviceAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
if (chunk == null) {
|
||||
chunk = world.getChunkAt(getPos());
|
||||
}
|
||||
|
||||
if (isRunning()) {
|
||||
chunk.markDirty();
|
||||
}
|
||||
|
||||
super.tick();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractTerminalVirtualMachineRunner createRunner() {
|
||||
return new ComputerVirtualMachineRunner(virtualMachine, terminal);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleBusStateChanged(final AbstractDeviceBusController.BusState value) {
|
||||
final ComputerBusStateMessage message = new ComputerBusStateMessage(ComputerTileEntity.this);
|
||||
Network.sendToClientsTrackingChunk(message, chunk);
|
||||
|
||||
if (value == AbstractDeviceBusController.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());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleRunStateChanged(final RunState value) {
|
||||
// This method can be called from disposal logic, so if we are disposed quickly enough
|
||||
// chunk may not be initialized yet. Avoid resulting NRE in network logic.
|
||||
if (chunk != null) {
|
||||
final ComputerRunStateMessage message = new ComputerRunStateMessage(ComputerTileEntity.this);
|
||||
Network.sendToClientsTrackingChunk(message, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleBootErrorChanged(@Nullable final ITextComponent value) {
|
||||
final ComputerBootErrorMessage message = new ComputerBootErrorMessage(ComputerTileEntity.this);
|
||||
Network.sendToClientsTrackingChunk(message, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import javax.annotation.Nullable;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
|
||||
import static li.cil.oc2.common.Constants.BLOCK_ENTITY_INVENTORY_TAG_NAME;
|
||||
import static li.cil.oc2.common.Constants.INVENTORY_TAG_NAME;
|
||||
import static li.cil.oc2.common.Constants.BLOCK_ENTITY_TAG_NAME_IN_ITEM;
|
||||
|
||||
public final class ItemStackUtils {
|
||||
@@ -48,19 +48,19 @@ public final class ItemStackUtils {
|
||||
@Nullable
|
||||
public static CompoundNBT getTileEntityInventoryTag(final ItemStack stack) {
|
||||
final CompoundNBT tag = getTileEntityTag(stack);
|
||||
return tag != null && tag.contains(BLOCK_ENTITY_INVENTORY_TAG_NAME, NBTTagIds.TAG_COMPOUND)
|
||||
? tag.getCompound(BLOCK_ENTITY_INVENTORY_TAG_NAME) : null;
|
||||
return tag != null && tag.contains(INVENTORY_TAG_NAME, NBTTagIds.TAG_COMPOUND)
|
||||
? tag.getCompound(INVENTORY_TAG_NAME) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static CompoundNBT getOrCreateTileEntityInventoryTag(final ItemStack stack) {
|
||||
final CompoundNBT tag = getOrCreateTileEntityTag(stack);
|
||||
if (tag.contains(BLOCK_ENTITY_INVENTORY_TAG_NAME, NBTTagIds.TAG_COMPOUND)) {
|
||||
return tag.getCompound(BLOCK_ENTITY_INVENTORY_TAG_NAME);
|
||||
if (tag.contains(INVENTORY_TAG_NAME, NBTTagIds.TAG_COMPOUND)) {
|
||||
return tag.getCompound(INVENTORY_TAG_NAME);
|
||||
}
|
||||
|
||||
final CompoundNBT inventoryNbt = new CompoundNBT();
|
||||
tag.put(BLOCK_ENTITY_INVENTORY_TAG_NAME, inventoryNbt);
|
||||
tag.put(INVENTORY_TAG_NAME, inventoryNbt);
|
||||
|
||||
return inventoryNbt;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,15 @@ import li.cil.oc2.api.bus.device.vm.VMDeviceLifecycleEventType;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public abstract class AbstractTerminalVirtualMachineRunner extends VirtualMachineRunner {
|
||||
private static final ByteBuffer TERMINAL_RESET_SEQUENCE = ByteBuffer.wrap(new byte[]{
|
||||
// Make sure we're in normal mode.
|
||||
'J',
|
||||
// Reset color and style.
|
||||
'\033', '[', '0', 'm',
|
||||
// Clear screen.
|
||||
'\033', '[', '2', 'J'
|
||||
});
|
||||
|
||||
private final CommonVirtualMachine virtualMachine;
|
||||
private final Terminal terminal;
|
||||
|
||||
@@ -33,6 +42,11 @@ public abstract class AbstractTerminalVirtualMachineRunner extends VirtualMachin
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public void resetTerminal() {
|
||||
TERMINAL_RESET_SEQUENCE.clear();
|
||||
putTerminalOutput(TERMINAL_RESET_SEQUENCE);
|
||||
}
|
||||
|
||||
public void putTerminalOutput(final ByteBuffer output) {
|
||||
if (output.hasRemaining()) {
|
||||
terminal.putOutput(output);
|
||||
|
||||
@@ -0,0 +1,317 @@
|
||||
package li.cil.oc2.common.vm;
|
||||
|
||||
import li.cil.oc2.api.bus.device.vm.VMDeviceLifecycleEventType;
|
||||
import li.cil.oc2.api.bus.device.vm.VMDeviceLoadResult;
|
||||
import li.cil.oc2.common.Constants;
|
||||
import li.cil.oc2.common.bus.AbstractDeviceBusController;
|
||||
import li.cil.oc2.common.serialization.NBTSerialization;
|
||||
import li.cil.oc2.common.util.NBTTagIds;
|
||||
import li.cil.oc2.common.util.NBTUtils;
|
||||
import li.cil.sedna.api.memory.MemoryAccessException;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.TranslationTextComponent;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class AbstractVirtualMachineState<TBusController extends AbstractDeviceBusController, TVirtualMachine extends VirtualMachine> implements VirtualMachineState {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private static final String VIRTUAL_MACHINE_TAG_NAME = "virtualMachine";
|
||||
private static final String RUNNER_TAG_NAME = "runner";
|
||||
|
||||
public static final String BUS_STATE_TAG_NAME = "busState";
|
||||
public static final String RUN_STATE_TAG_NAME = "runState";
|
||||
public static final String BOOT_ERROR_TAG_NAME = "bootError";
|
||||
|
||||
private static final int DEVICE_LOAD_RETRY_INTERVAL = 10 * Constants.TICK_SECONDS;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public final TBusController busController;
|
||||
private AbstractDeviceBusController.BusState busState = AbstractDeviceBusController.BusState.SCAN_PENDING;
|
||||
|
||||
public final TVirtualMachine virtualMachine;
|
||||
private AbstractTerminalVirtualMachineRunner runner;
|
||||
private RunState runState = RunState.STOPPED;
|
||||
private ITextComponent bootError;
|
||||
private int loadDevicesDelay;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public AbstractVirtualMachineState(final TBusController busController, final TVirtualMachine virtualMachine) {
|
||||
this.busController = busController;
|
||||
this.virtualMachine = virtualMachine;
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return getBusState() == AbstractDeviceBusController.BusState.READY &&
|
||||
getRunState() == RunState.RUNNING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractDeviceBusController.BusState getBusState() {
|
||||
return busState;
|
||||
}
|
||||
|
||||
@Override
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public void setBusStateClient(final AbstractDeviceBusController.BusState value) {
|
||||
busState = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RunState getRunState() {
|
||||
return runState;
|
||||
}
|
||||
|
||||
@Override
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public void setRunStateClient(final RunState value) {
|
||||
runState = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public ITextComponent getBootError() {
|
||||
switch (busState) {
|
||||
case SCAN_PENDING:
|
||||
case INCOMPLETE:
|
||||
return new TranslationTextComponent(Constants.COMPUTER_BUS_STATE_INCOMPLETE);
|
||||
case TOO_COMPLEX:
|
||||
return new TranslationTextComponent(Constants.COMPUTER_BUS_STATE_TOO_COMPLEX);
|
||||
case MULTIPLE_CONTROLLERS:
|
||||
return new TranslationTextComponent(Constants.COMPUTER_BUS_STATE_MULTIPLE_CONTROLLERS);
|
||||
case READY:
|
||||
switch (runState) {
|
||||
case STOPPED:
|
||||
case LOADING_DEVICES:
|
||||
return bootError;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public void setBootErrorClient(final ITextComponent value) {
|
||||
bootError = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (runState == RunState.RUNNING) {
|
||||
return;
|
||||
}
|
||||
|
||||
setBootError(null);
|
||||
setRunState(RunState.LOADING_DEVICES);
|
||||
loadDevicesDelay = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
switch (runState) {
|
||||
case LOADING_DEVICES:
|
||||
setRunState(RunState.STOPPED);
|
||||
break;
|
||||
case RUNNING:
|
||||
stopRunnerAndReset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
if (runState == RunState.RUNNING) {
|
||||
runState = RunState.LOADING_DEVICES;
|
||||
}
|
||||
}
|
||||
|
||||
public void joinVirtualMachine() {
|
||||
if (runner != null) {
|
||||
try {
|
||||
runner.join();
|
||||
} catch (final Throwable e) {
|
||||
LOGGER.error(e);
|
||||
runner = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stopRunnerAndReset() {
|
||||
joinVirtualMachine();
|
||||
setRunState(RunState.STOPPED);
|
||||
|
||||
virtualMachine.reset();
|
||||
|
||||
if (runner != null) {
|
||||
runner.resetTerminal();
|
||||
runner = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
busController.scan();
|
||||
setBusState(busController.getState());
|
||||
if (busState != AbstractDeviceBusController.BusState.READY) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (runState) {
|
||||
case STOPPED:
|
||||
break;
|
||||
case LOADING_DEVICES:
|
||||
if (loadDevicesDelay > 0) {
|
||||
loadDevicesDelay--;
|
||||
break;
|
||||
}
|
||||
|
||||
final VMDeviceLoadResult loadResult = virtualMachine.vmAdapter.load();
|
||||
if (!loadResult.wasSuccessful()) {
|
||||
if (loadResult.getErrorMessage() != null) {
|
||||
setBootError(loadResult.getErrorMessage());
|
||||
} else {
|
||||
setBootError(new TranslationTextComponent(Constants.COMPUTER_BOOT_ERROR_UNKNOWN));
|
||||
}
|
||||
loadDevicesDelay = DEVICE_LOAD_RETRY_INTERVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
// May have a valid runner after load. In which case we just had to wait for
|
||||
// bus setup and devices to load. So we can keep using it.
|
||||
if (runner == null) {
|
||||
try {
|
||||
virtualMachine.board.reset();
|
||||
virtualMachine.board.initialize();
|
||||
virtualMachine.board.setRunning(true);
|
||||
} catch (final IllegalStateException e) {
|
||||
// FDT did not fit into memory. Technically it's possible to run with
|
||||
// a program that only uses registers. But not supporting that esoteric
|
||||
// use-case loses out against avoiding people getting confused for having
|
||||
// forgotten to add some RAM modules.
|
||||
setBootError(new TranslationTextComponent(Constants.COMPUTER_BOOT_ERROR_NO_MEMORY));
|
||||
setRunState(RunState.STOPPED);
|
||||
return;
|
||||
} catch (final MemoryAccessException e) {
|
||||
LOGGER.error(e);
|
||||
setBootError(new TranslationTextComponent(Constants.COMPUTER_BOOT_ERROR_UNKNOWN));
|
||||
setRunState(RunState.STOPPED);
|
||||
return;
|
||||
}
|
||||
|
||||
runner = createRunner();
|
||||
}
|
||||
|
||||
setRunState(RunState.RUNNING);
|
||||
|
||||
// Only start running next tick. This gives loaded devices one tick to do async
|
||||
// initialization. This is used by devices to restore data from disk, for example.
|
||||
break;
|
||||
case RUNNING:
|
||||
if (!virtualMachine.board.isRunning()) {
|
||||
stopRunnerAndReset();
|
||||
break;
|
||||
}
|
||||
|
||||
runner.tick();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public CompoundNBT serialize() {
|
||||
final CompoundNBT tag = new CompoundNBT();
|
||||
|
||||
joinVirtualMachine();
|
||||
|
||||
if (runner != null) {
|
||||
tag.put(RUNNER_TAG_NAME, NBTSerialization.serialize(runner));
|
||||
virtualMachine.vmAdapter.fireLifecycleEvent(VMDeviceLifecycleEventType.PAUSING);
|
||||
runner.scheduleResumeEvent(); // Allow synchronizing to async device saves.
|
||||
} else {
|
||||
NBTUtils.putEnum(tag, RUN_STATE_TAG_NAME, runState);
|
||||
}
|
||||
tag.put(VIRTUAL_MACHINE_TAG_NAME, NBTSerialization.serialize(virtualMachine));
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
public void deserialize(final CompoundNBT tag) {
|
||||
joinVirtualMachine();
|
||||
|
||||
if (tag.contains(RUNNER_TAG_NAME, NBTTagIds.TAG_COMPOUND)) {
|
||||
runner = createRunner();
|
||||
NBTSerialization.deserialize(tag.getCompound(RUNNER_TAG_NAME), runner);
|
||||
runState = RunState.LOADING_DEVICES;
|
||||
} else {
|
||||
runState = NBTUtils.getEnum(tag, RUN_STATE_TAG_NAME, RunState.class);
|
||||
if (runState == null) {
|
||||
runState = RunState.STOPPED;
|
||||
} else if (runState == RunState.RUNNING) {
|
||||
runState = RunState.LOADING_DEVICES;
|
||||
}
|
||||
}
|
||||
|
||||
if (tag.contains(VIRTUAL_MACHINE_TAG_NAME, NBTTagIds.TAG_COMPOUND)) {
|
||||
NBTSerialization.deserialize(tag.getCompound(VIRTUAL_MACHINE_TAG_NAME), virtualMachine);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
protected abstract AbstractTerminalVirtualMachineRunner createRunner();
|
||||
|
||||
protected void handleBusStateChanged(final AbstractDeviceBusController.BusState value) {
|
||||
}
|
||||
|
||||
protected void handleRunStateChanged(final RunState value) {
|
||||
}
|
||||
|
||||
protected void handleBootErrorChanged(@Nullable final ITextComponent value) {
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private void setBusState(final AbstractDeviceBusController.BusState value) {
|
||||
if (value == busState) {
|
||||
return;
|
||||
}
|
||||
|
||||
busState = value;
|
||||
|
||||
handleBusStateChanged(busState);
|
||||
}
|
||||
|
||||
private void setRunState(final RunState value) {
|
||||
if (value == runState) {
|
||||
return;
|
||||
}
|
||||
|
||||
runState = value;
|
||||
|
||||
handleRunStateChanged(value);
|
||||
}
|
||||
|
||||
private void setBootError(@Nullable final ITextComponent value) {
|
||||
if (Objects.equals(value, bootError)) {
|
||||
return;
|
||||
}
|
||||
|
||||
bootError = value;
|
||||
|
||||
handleBootErrorChanged(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
package li.cil.oc2.common.vm;
|
||||
|
||||
import li.cil.oc2.api.bus.DeviceBusElement;
|
||||
import li.cil.oc2.api.bus.device.DeviceType;
|
||||
import li.cil.oc2.api.bus.device.DeviceTypes;
|
||||
import li.cil.oc2.api.bus.device.vm.VMDevice;
|
||||
import li.cil.oc2.common.bus.AbstractDeviceBusElement;
|
||||
import li.cil.oc2.common.bus.device.util.ItemDeviceInfo;
|
||||
import li.cil.oc2.common.container.DeviceItemStackHandler;
|
||||
import li.cil.oc2.common.container.TypedDeviceItemStackHandler;
|
||||
import li.cil.oc2.common.util.ItemStackUtils;
|
||||
import li.cil.oc2.common.util.TooltipUtils;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
import net.minecraftforge.items.IItemHandler;
|
||||
import net.minecraftforge.items.wrapper.CombinedInvWrapper;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
public abstract class CommonVirtualMachineItemStackHandlers {
|
||||
private static final long ITEM_DEVICE_BASE_ADDRESS = 0x40000000L;
|
||||
private static final int ITEM_DEVICE_STRIDE = 0x1000;
|
||||
|
||||
public static final String MEMORY_TAG_NAME = "memory";
|
||||
public static final String HARD_DRIVE_TAG_NAME = "hard_drive";
|
||||
public static final String FLASH_MEMORY_TAG_NAME = "flash_memory";
|
||||
public static final String CARD_TAG_NAME = "card";
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void addInformation(final ItemStack stack, final List<ITextComponent> tooltip) {
|
||||
TooltipUtils.addInventoryInformation(stack, tooltip,
|
||||
MEMORY_TAG_NAME,
|
||||
HARD_DRIVE_TAG_NAME,
|
||||
FLASH_MEMORY_TAG_NAME,
|
||||
CARD_TAG_NAME);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public final DeviceBusElement busElement = new BusElement();
|
||||
|
||||
public final DeviceItemStackHandler memoryItemHandler;
|
||||
public final DeviceItemStackHandler hardDriveItemHandler;
|
||||
public final DeviceItemStackHandler flashMemoryItemHandler;
|
||||
public final DeviceItemStackHandler cardItemHandler;
|
||||
|
||||
public final IItemHandler itemHandlers;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public CommonVirtualMachineItemStackHandlers(final int memorySlots,
|
||||
final int hardDriveSlots,
|
||||
final int flashMemorySlots,
|
||||
final int cardSlots) {
|
||||
memoryItemHandler = new ItemHandler(memorySlots, this::getDevices, DeviceTypes.MEMORY);
|
||||
hardDriveItemHandler = new ItemHandler(hardDriveSlots, this::getDevices, DeviceTypes.HARD_DRIVE);
|
||||
flashMemoryItemHandler = new ItemHandler(flashMemorySlots, this::getDevices, DeviceTypes.FLASH_MEMORY);
|
||||
cardItemHandler = new ItemHandler(cardSlots, this::getDevices, DeviceTypes.CARD);
|
||||
|
||||
itemHandlers = new CombinedInvWrapper(memoryItemHandler, hardDriveItemHandler, flashMemoryItemHandler, cardItemHandler);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public Optional<IItemHandler> getItemHandler(final DeviceType deviceType) {
|
||||
if (deviceType == DeviceTypes.MEMORY) {
|
||||
return Optional.of(memoryItemHandler);
|
||||
} else if (deviceType == DeviceTypes.HARD_DRIVE) {
|
||||
return Optional.of(hardDriveItemHandler);
|
||||
} else if (deviceType == DeviceTypes.FLASH_MEMORY) {
|
||||
return Optional.of(flashMemoryItemHandler);
|
||||
} else if (deviceType == DeviceTypes.CARD) {
|
||||
return Optional.of(cardItemHandler);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
for (int slot = 0; slot < itemHandlers.getSlots(); slot++) {
|
||||
if (!itemHandlers.getStackInSlot(slot).isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public OptionalLong getDefaultDeviceAddress(final VMDevice wrapper) {
|
||||
long address = ITEM_DEVICE_BASE_ADDRESS;
|
||||
|
||||
for (int slot = 0; slot < hardDriveItemHandler.getSlots(); slot++) {
|
||||
final Collection<ItemDeviceInfo> devices = hardDriveItemHandler.getBusElement().getDeviceGroup(slot);
|
||||
for (final ItemDeviceInfo info : devices) {
|
||||
if (Objects.equals(info.device, wrapper)) {
|
||||
return OptionalLong.of(address);
|
||||
}
|
||||
}
|
||||
|
||||
address += ITEM_DEVICE_STRIDE;
|
||||
}
|
||||
|
||||
for (int slot = 0; slot < cardItemHandler.getSlots(); slot++) {
|
||||
final Collection<ItemDeviceInfo> devices = cardItemHandler.getBusElement().getDeviceGroup(slot);
|
||||
for (final ItemDeviceInfo info : devices) {
|
||||
if (Objects.equals(info.device, wrapper)) {
|
||||
return OptionalLong.of(address);
|
||||
}
|
||||
}
|
||||
|
||||
address += ITEM_DEVICE_STRIDE;
|
||||
}
|
||||
|
||||
return OptionalLong.empty();
|
||||
}
|
||||
|
||||
public void exportDeviceDataToItemStacks() {
|
||||
memoryItemHandler.exportDeviceDataToItemStacks();
|
||||
hardDriveItemHandler.exportDeviceDataToItemStacks();
|
||||
flashMemoryItemHandler.exportDeviceDataToItemStacks();
|
||||
cardItemHandler.exportDeviceDataToItemStacks();
|
||||
}
|
||||
|
||||
public void exportToItemStack(final ItemStack stack) {
|
||||
final CompoundNBT items = ItemStackUtils.getOrCreateTileEntityInventoryTag(stack);
|
||||
items.put(MEMORY_TAG_NAME, memoryItemHandler.serializeNBT());
|
||||
items.put(HARD_DRIVE_TAG_NAME, hardDriveItemHandler.serializeNBT());
|
||||
items.put(FLASH_MEMORY_TAG_NAME, flashMemoryItemHandler.serializeNBT());
|
||||
items.put(CARD_TAG_NAME, cardItemHandler.serializeNBT());
|
||||
}
|
||||
|
||||
public CompoundNBT serialize() {
|
||||
final CompoundNBT tag = new CompoundNBT();
|
||||
|
||||
tag.put(MEMORY_TAG_NAME, memoryItemHandler.serializeNBT());
|
||||
tag.put(HARD_DRIVE_TAG_NAME, hardDriveItemHandler.serializeNBT());
|
||||
tag.put(FLASH_MEMORY_TAG_NAME, flashMemoryItemHandler.serializeNBT());
|
||||
tag.put(CARD_TAG_NAME, cardItemHandler.serializeNBT());
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
public void deserialize(final CompoundNBT tag) {
|
||||
memoryItemHandler.deserializeNBT(tag.getCompound(MEMORY_TAG_NAME));
|
||||
hardDriveItemHandler.deserializeNBT(tag.getCompound(HARD_DRIVE_TAG_NAME));
|
||||
flashMemoryItemHandler.deserializeNBT(tag.getCompound(FLASH_MEMORY_TAG_NAME));
|
||||
cardItemHandler.deserializeNBT(tag.getCompound(CARD_TAG_NAME));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
protected abstract List<ItemDeviceInfo> getDevices(final ItemStack stack);
|
||||
|
||||
protected void onContentsChanged(final DeviceItemStackHandler itemHandler, final int slot) {
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private final class ItemHandler extends TypedDeviceItemStackHandler {
|
||||
public ItemHandler(final int size, final Function<ItemStack, List<ItemDeviceInfo>> deviceLookup, final DeviceType deviceType) {
|
||||
super(size, deviceLookup, deviceType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onContentsChanged(final int slot) {
|
||||
super.onContentsChanged(slot);
|
||||
CommonVirtualMachineItemStackHandlers.this.onContentsChanged(this, slot);
|
||||
}
|
||||
}
|
||||
|
||||
private final class BusElement extends AbstractDeviceBusElement {
|
||||
@Override
|
||||
public Optional<Collection<LazyOptional<DeviceBusElement>>> getNeighbors() {
|
||||
return Optional.of(Arrays.asList(
|
||||
LazyOptional.of(memoryItemHandler::getBusElement),
|
||||
LazyOptional.of(hardDriveItemHandler::getBusElement),
|
||||
LazyOptional.of(flashMemoryItemHandler::getBusElement),
|
||||
LazyOptional.of(cardItemHandler::getBusElement)
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
38
src/main/java/li/cil/oc2/common/vm/VirtualMachineState.java
Normal file
38
src/main/java/li/cil/oc2/common/vm/VirtualMachineState.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package li.cil.oc2.common.vm;
|
||||
|
||||
import li.cil.oc2.common.bus.AbstractDeviceBusController;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface VirtualMachineState {
|
||||
boolean isRunning();
|
||||
|
||||
AbstractDeviceBusController.BusState getBusState();
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
void setBusStateClient(AbstractDeviceBusController.BusState value);
|
||||
|
||||
RunState getRunState();
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
void setRunStateClient(RunState value);
|
||||
|
||||
@Nullable
|
||||
ITextComponent getBootError();
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
void setBootErrorClient(ITextComponent value);
|
||||
|
||||
void start();
|
||||
|
||||
void stop();
|
||||
|
||||
public enum RunState {
|
||||
STOPPED,
|
||||
LOADING_DEVICES,
|
||||
RUNNING,
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static li.cil.oc2.common.Constants.BLOCK_ENTITY_INVENTORY_TAG_NAME;
|
||||
import static li.cil.oc2.common.Constants.INVENTORY_TAG_NAME;
|
||||
import static li.cil.oc2.common.Constants.BLOCK_ENTITY_TAG_NAME_IN_ITEM;
|
||||
|
||||
public final class LootTables extends LootTableProvider {
|
||||
@@ -63,8 +63,8 @@ public final class LootTables extends LootTableProvider {
|
||||
.rolls(ConstantRange.of(1))
|
||||
.addEntry(ItemLootEntry.builder(block)
|
||||
.acceptFunction(CopyNbt.builder(CopyNbt.Source.BLOCK_ENTITY)
|
||||
.addOperation(BLOCK_ENTITY_INVENTORY_TAG_NAME,
|
||||
concat(BLOCK_ENTITY_TAG_NAME_IN_ITEM, BLOCK_ENTITY_INVENTORY_TAG_NAME),
|
||||
.addOperation(INVENTORY_TAG_NAME,
|
||||
concat(BLOCK_ENTITY_TAG_NAME_IN_ITEM, INVENTORY_TAG_NAME),
|
||||
CopyNbt.Action.REPLACE)
|
||||
)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user