From 94e57af093e022decfda2fbc012b5cde0cb78a93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Fri, 7 Jan 2022 07:48:40 +0100 Subject: [PATCH 01/27] Some documentation improvements. --- src/main/java/li/cil/oc2/api/bus/DeviceBusElement.java | 6 +++--- .../java/li/cil/oc2/api/bus/device/data/Firmware.java | 2 +- .../cil/oc2/api/bus/device/provider/BlockDeviceQuery.java | 2 +- .../li/cil/oc2/api/capabilities/TerminalUserProvider.java | 8 ++++++++ 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main/java/li/cil/oc2/api/bus/DeviceBusElement.java b/src/main/java/li/cil/oc2/api/bus/DeviceBusElement.java index daa89be4..819b9245 100644 --- a/src/main/java/li/cil/oc2/api/bus/DeviceBusElement.java +++ b/src/main/java/li/cil/oc2/api/bus/DeviceBusElement.java @@ -18,7 +18,7 @@ import java.util.UUID; * connected to this element. *

* This interface is relevant when implementing means to extend the bus, e.g. - * to provide a custom cable implementation or some kind of a device container. + * to provide a custom cable implementation or some kind of device container. *

* Implementations must call {@link #scheduleScan()} when they become * invalid, e.g. due to being in a chunk that is being unloaded or the block @@ -31,7 +31,7 @@ public interface DeviceBusElement extends DeviceBus { *

* This will be called by {@link DeviceBusController}s when scanning. *

- * Bus elements can be have multiple controllers at the same time. This is used + * Bus elements can have multiple controllers at the same time. This is used * by controllers to detect each other on the bus. *

* When {@link #scheduleScan()} is called, {@link DeviceBusController#scheduleBusScan()} @@ -89,7 +89,7 @@ public interface DeviceBusElement extends DeviceBus { * track of the device. Note that some device types (e.g. {@link RPCDevice}s) * require for an ID to be provided for them to work at all. *

- * It is possible for multiple devices to have the same identifier. Typically + * It is possible for multiple devices to have the same identifier. Typically, * this means they represent a view on the same underlying object. How this is * handled depends on the device type and may or may not be supported. *

diff --git a/src/main/java/li/cil/oc2/api/bus/device/data/Firmware.java b/src/main/java/li/cil/oc2/api/bus/device/data/Firmware.java index 89487623..e71396be 100644 --- a/src/main/java/li/cil/oc2/api/bus/device/data/Firmware.java +++ b/src/main/java/li/cil/oc2/api/bus/device/data/Firmware.java @@ -31,7 +31,7 @@ public interface Firmware extends IForgeRegistryEntry { *

* This will usually load machine code into memory at the specified start address. *

- * Typically only returns {@code false} when there was not enough memory to fit the firmware. + * Typically, only returns {@code false} when there was not enough memory to fit the firmware. * * @param memory access to the memory map of the machine. * @param startAddress the memory address where execution will commence. diff --git a/src/main/java/li/cil/oc2/api/bus/device/provider/BlockDeviceQuery.java b/src/main/java/li/cil/oc2/api/bus/device/provider/BlockDeviceQuery.java index ae73e62e..27660680 100644 --- a/src/main/java/li/cil/oc2/api/bus/device/provider/BlockDeviceQuery.java +++ b/src/main/java/li/cil/oc2/api/bus/device/provider/BlockDeviceQuery.java @@ -28,7 +28,7 @@ public interface BlockDeviceQuery { BlockPos getQueryPosition(); /** - * The side of the block this query is performed on, if any. + * The world-space side of the block this query is performed on, if any. *

* May be {@code null} just as when requesting a capability from a {@link BlockEntity}. * diff --git a/src/main/java/li/cil/oc2/api/capabilities/TerminalUserProvider.java b/src/main/java/li/cil/oc2/api/capabilities/TerminalUserProvider.java index 3b238fa9..642fa0f8 100644 --- a/src/main/java/li/cil/oc2/api/capabilities/TerminalUserProvider.java +++ b/src/main/java/li/cil/oc2/api/capabilities/TerminalUserProvider.java @@ -1,6 +1,10 @@ package li.cil.oc2.api.capabilities; +import li.cil.oc2.api.bus.device.provider.BlockDeviceQuery; +import li.cil.oc2.api.bus.device.provider.ItemDeviceQuery; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.block.entity.BlockEntity; /** * This interface provides access to a list of {@link Player}s that are currently @@ -8,6 +12,10 @@ import net.minecraft.world.entity.player.Player; *

* For example, for computers and robots this is the list of players that currently have * the terminal UI opened. + *

+ * Must be implemented by the {@link BlockEntity} or {@link Entity} that serves as the + * context for device creation via {@link BlockDeviceQuery}s or {@link ItemDeviceQuery}s, + * respectively. */ public interface TerminalUserProvider { /** From 998dab052b2d76b3df95e7f9f7532cdf68c7d2fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Fri, 7 Jan 2022 07:50:33 +0100 Subject: [PATCH 02/27] Reduce Side util class API surface a bit. --- .../cil/oc2/api/util/RobotOperationSide.java | 17 ++++++++++------- src/main/java/li/cil/oc2/api/util/Side.java | 10 +--------- .../RedstoneInterfaceBlockEntity.java | 8 +++++--- .../item/BlockOperationsModuleDevice.java | 4 ++-- .../item/InventoryOperationsModuleDevice.java | 8 ++++---- .../item/RedstoneInterfaceCardItemDevice.java | 11 +++++++---- .../oc2/common/util/HorizontalBlockUtils.java | 19 +++++++++---------- 7 files changed, 38 insertions(+), 39 deletions(-) diff --git a/src/main/java/li/cil/oc2/api/util/RobotOperationSide.java b/src/main/java/li/cil/oc2/api/util/RobotOperationSide.java index c4e07dfa..d7f18c45 100644 --- a/src/main/java/li/cil/oc2/api/util/RobotOperationSide.java +++ b/src/main/java/li/cil/oc2/api/util/RobotOperationSide.java @@ -32,14 +32,17 @@ public enum RobotOperationSide { this(parent.direction); } - public Direction getDirection() { - return direction; - } - - public static Direction getAdjustedDirection(@Nullable final RobotOperationSide side, final Entity entity) { + /** + * Gets the world-space direction for the specified side relative to the specified entity. + * + * @param entity the entity to which the side is relative. + * @param side the side to convert to a world-space direction. + * @return a world-space direction. + */ + public static Direction toGlobal(final Entity entity, @Nullable final RobotOperationSide side) { Direction direction = side == null - ? RobotOperationSide.FRONT.getDirection() - : side.getDirection(); + ? RobotOperationSide.FRONT.direction + : side.direction; if (direction.getAxis().isHorizontal()) { final int horizontalIndex = entity.getDirection().get2DDataValue(); for (int i = 0; i < horizontalIndex; i++) { diff --git a/src/main/java/li/cil/oc2/api/util/Side.java b/src/main/java/li/cil/oc2/api/util/Side.java index 14f45281..b542e184 100644 --- a/src/main/java/li/cil/oc2/api/util/Side.java +++ b/src/main/java/li/cil/oc2/api/util/Side.java @@ -8,7 +8,7 @@ import javax.annotation.Nullable; * This enum indicates a side of a block device. *

* It is intended to be used by {@link li.cil.oc2.api.bus.device.rpc.RPCDevice} APIs, - * providing both convenience for the called by providing a range of aliases, and also + * providing both convenience for the caller by providing a range of aliases, and also * stability, in case Mojang decide to rename the enum fields of the {@link Direction} * enum at some time in the future. */ @@ -67,14 +67,6 @@ public enum Side { return direction; } - public int get2DDataValue() { - return direction.get2DDataValue(); - } - - public int get3DDataValue() { - return direction.get3DDataValue(); - } - @Override public String toString() { return base != null ? base.toString() : super.toString(); diff --git a/src/main/java/li/cil/oc2/common/blockentity/RedstoneInterfaceBlockEntity.java b/src/main/java/li/cil/oc2/common/blockentity/RedstoneInterfaceBlockEntity.java index 08b1728d..c6a9df35 100644 --- a/src/main/java/li/cil/oc2/common/blockentity/RedstoneInterfaceBlockEntity.java +++ b/src/main/java/li/cil/oc2/common/blockentity/RedstoneInterfaceBlockEntity.java @@ -86,20 +86,22 @@ public final class RedstoneInterfaceBlockEntity extends BlockEntity implements N @Callback(name = GET_REDSTONE_OUTPUT, synchronize = false) public int getRedstoneOutput(@Parameter(SIDE) @Nullable final Side side) { if (side == null) throw new IllegalArgumentException(); + final int index = side.getDirection().get3DDataValue(); - return output[side.get3DDataValue()]; + return output[index]; } @Callback(name = SET_REDSTONE_OUTPUT) public void setRedstoneOutput(@Parameter(SIDE) @Nullable final Side side, @Parameter(VALUE) final int value) { if (side == null) throw new IllegalArgumentException(); + final int index = side.getDirection().get3DDataValue(); final byte clampedValue = (byte) Mth.clamp(value, 0, 15); - if (clampedValue == output[side.get3DDataValue()]) { + if (clampedValue == output[index]) { return; } - output[side.get3DDataValue()] = clampedValue; + output[index] = clampedValue; final Direction direction = HorizontalBlockUtils.toGlobal(getBlockState(), side); if (direction != null) { diff --git a/src/main/java/li/cil/oc2/common/bus/device/item/BlockOperationsModuleDevice.java b/src/main/java/li/cil/oc2/common/bus/device/item/BlockOperationsModuleDevice.java index 77f40fdd..65cc3d57 100644 --- a/src/main/java/li/cil/oc2/common/bus/device/item/BlockOperationsModuleDevice.java +++ b/src/main/java/li/cil/oc2/common/bus/device/item/BlockOperationsModuleDevice.java @@ -91,7 +91,7 @@ public final class BlockOperationsModuleDevice extends AbstractItemRPCDevice { final List oldItems = getItemsInRange(); - final Direction direction = RobotOperationSide.getAdjustedDirection(side, entity); + final Direction direction = RobotOperationSide.toGlobal(entity, side); if (!tryHarvestBlock(serverLevel, entity.blockPosition().relative(direction))) { return false; } @@ -129,7 +129,7 @@ public final class BlockOperationsModuleDevice extends AbstractItemRPCDevice { return false; } - final Direction direction = RobotOperationSide.getAdjustedDirection(side, entity); + final Direction direction = RobotOperationSide.toGlobal(entity, side); final BlockPos blockPos = entity.blockPosition().relative(direction); final Direction oppositeDirection = direction.getOpposite(); final BlockHitResult hit = new BlockHitResult( diff --git a/src/main/java/li/cil/oc2/common/bus/device/item/InventoryOperationsModuleDevice.java b/src/main/java/li/cil/oc2/common/bus/device/item/InventoryOperationsModuleDevice.java index c8070717..5874b540 100644 --- a/src/main/java/li/cil/oc2/common/bus/device/item/InventoryOperationsModuleDevice.java +++ b/src/main/java/li/cil/oc2/common/bus/device/item/InventoryOperationsModuleDevice.java @@ -80,7 +80,7 @@ public final class InventoryOperationsModuleDevice extends AbstractItemRPCDevice } final int originalStackSize = stack.getCount(); - final Direction direction = RobotOperationSide.getAdjustedDirection(side, entity); + final Direction direction = RobotOperationSide.toGlobal(entity, side); final List itemHandlers = getItemStackHandlersInDirection(direction).toList(); for (final IItemHandler handler : itemHandlers) { stack = ItemHandlerHelper.insertItemStacked(handler, stack, false); @@ -122,7 +122,7 @@ public final class InventoryOperationsModuleDevice extends AbstractItemRPCDevice } final int originalStackSize = stack.getCount(); - final Direction direction = RobotOperationSide.getAdjustedDirection(side, entity); + final Direction direction = RobotOperationSide.toGlobal(entity, side); final Optional optional = getItemStackHandlersInDirection(direction).findFirst(); if (optional.isPresent()) { stack = optional.get().insertItem(intoSlot, stack, false); @@ -150,7 +150,7 @@ public final class InventoryOperationsModuleDevice extends AbstractItemRPCDevice return 0; } - final Direction direction = RobotOperationSide.getAdjustedDirection(side, entity); + final Direction direction = RobotOperationSide.toGlobal(entity, side); final List handlers = getItemStackHandlersInDirection(direction).collect(Collectors.toList()); if (handlers.isEmpty()) { return takeFromWorld(count); @@ -167,7 +167,7 @@ public final class InventoryOperationsModuleDevice extends AbstractItemRPCDevice return 0; } - final Direction direction = RobotOperationSide.getAdjustedDirection(side, entity); + final Direction direction = RobotOperationSide.toGlobal(entity, side); return getItemStackHandlersInDirection(direction).findFirst().map(handler -> takeFromInventory(count, handler, fromSlot)).orElse(0); } diff --git a/src/main/java/li/cil/oc2/common/bus/device/item/RedstoneInterfaceCardItemDevice.java b/src/main/java/li/cil/oc2/common/bus/device/item/RedstoneInterfaceCardItemDevice.java index 98990f37..254352cf 100644 --- a/src/main/java/li/cil/oc2/common/bus/device/item/RedstoneInterfaceCardItemDevice.java +++ b/src/main/java/li/cil/oc2/common/bus/device/item/RedstoneInterfaceCardItemDevice.java @@ -57,7 +57,8 @@ public final class RedstoneInterfaceCardItemDevice extends AbstractItemRPCDevice @Override public LazyOptional getCapability(@Nonnull final Capability capability, @Nullable final Direction side) { if (capability == Capabilities.REDSTONE_EMITTER && side != null) { - return LazyOptional.of(() -> capabilities[side.get3DDataValue()]).cast(); + final int index = side.get3DDataValue(); + return LazyOptional.of(() -> capabilities[index]).cast(); } return LazyOptional.empty(); @@ -101,20 +102,22 @@ public final class RedstoneInterfaceCardItemDevice extends AbstractItemRPCDevice @Callback(name = GET_REDSTONE_OUTPUT, synchronize = false) public int getRedstoneOutput(@Parameter(SIDE) @Nullable final Side side) { if (side == null) throw new IllegalArgumentException(); + final int index = side.getDirection().get3DDataValue(); - return output[side.get3DDataValue()]; + return output[index]; } @Callback(name = SET_REDSTONE_OUTPUT) public void setRedstoneOutput(@Parameter(SIDE) @Nullable final Side side, @Parameter(VALUE) final int value) { if (side == null) throw new IllegalArgumentException(); + final int index = side.getDirection().get3DDataValue(); final byte clampedValue = (byte) Mth.clamp(value, 0, 15); - if (clampedValue == output[side.get3DDataValue()]) { + if (clampedValue == output[index]) { return; } - output[side.get3DDataValue()] = clampedValue; + output[index] = clampedValue; final Direction direction = HorizontalBlockUtils.toGlobal(blockEntity.getBlockState(), side); if (direction != null) { diff --git a/src/main/java/li/cil/oc2/common/util/HorizontalBlockUtils.java b/src/main/java/li/cil/oc2/common/util/HorizontalBlockUtils.java index ae717fff..7bf71db4 100644 --- a/src/main/java/li/cil/oc2/common/util/HorizontalBlockUtils.java +++ b/src/main/java/li/cil/oc2/common/util/HorizontalBlockUtils.java @@ -18,18 +18,17 @@ public final class HorizontalBlockUtils { return null; } - final int index = direction.get2DDataValue(); - if (index < 0) { + if (direction.getAxis().isVertical()) { return direction; } - if (!blockState.hasProperty(HorizontalDirectionalBlock.FACING)) { return direction; } final Direction facing = blockState.getValue(HorizontalDirectionalBlock.FACING); - final int toLocal = HORIZONTAL_DIRECTION_COUNT - facing.get2DDataValue(); - final int rotatedIndex = (index + toLocal) % HORIZONTAL_DIRECTION_COUNT; + final int index = direction.get2DDataValue(); + final int toLocal = -facing.get2DDataValue(); + final int rotatedIndex = (index + toLocal + HORIZONTAL_DIRECTION_COUNT) % HORIZONTAL_DIRECTION_COUNT; return Direction.from2DDataValue(rotatedIndex); } @@ -39,16 +38,16 @@ public final class HorizontalBlockUtils { return null; } - final int index = side.get2DDataValue(); - if (index < 0) { - return side.getDirection(); + final Direction direction = side.getDirection(); + if (direction.getAxis().isVertical()) { + return direction; } - if (!blockState.hasProperty(HorizontalDirectionalBlock.FACING)) { - return side.getDirection(); + return direction; } final Direction facing = blockState.getValue(HorizontalDirectionalBlock.FACING); + final int index = direction.get2DDataValue(); final int toGlobal = facing.get2DDataValue(); final int rotatedIndex = (index + toGlobal) % HORIZONTAL_DIRECTION_COUNT; return Direction.from2DDataValue(rotatedIndex); From b466ff932b880fd5d71635d9256672a70a34d387 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Fri, 7 Jan 2022 07:51:31 +0100 Subject: [PATCH 03/27] Support terminal user tracking on robots. Unused for now, but we claim it exists in the documentation, and it doesn't hurt to pre-emptively do so. --- .../container/AbstractRobotContainer.java | 11 ++++++++++- .../container/ComputerTerminalContainer.java | 6 +++--- .../container/RobotInventoryContainer.java | 2 +- .../container/RobotTerminalContainer.java | 8 ++++---- .../li/cil/oc2/common/entity/RobotEntity.java | 17 ++++++++++++++++- 5 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/main/java/li/cil/oc2/common/container/AbstractRobotContainer.java b/src/main/java/li/cil/oc2/common/container/AbstractRobotContainer.java index e893f312..875130cc 100644 --- a/src/main/java/li/cil/oc2/common/container/AbstractRobotContainer.java +++ b/src/main/java/li/cil/oc2/common/container/AbstractRobotContainer.java @@ -20,9 +20,11 @@ public abstract class AbstractRobotContainer extends AbstractMachineTerminalCont /////////////////////////////////////////////////////////////////// - public AbstractRobotContainer(final MenuType type, final int id, final RobotEntity robot, final IntPrecisionContainerData energyInfo) { + public AbstractRobotContainer(final MenuType type, final int id, final Player player, final RobotEntity robot, final IntPrecisionContainerData energyInfo) { super(type, id, energyInfo); this.robot = robot; + + this.robot.addTerminalUser(player); } /////////////////////////////////////////////////////////////////// @@ -66,6 +68,13 @@ public abstract class AbstractRobotContainer extends AbstractMachineTerminalCont return robot.isAlive() && robot.closerThan(player, 8); } + @Override + public void removed(final Player player) { + super.removed(player); + + this.robot.removeTerminalUser(player); + } + /////////////////////////////////////////////////////////////////// protected static IntPrecisionContainerData createEnergyInfo(final FixedEnergyStorage energy, final CommonDeviceBusController busController) { diff --git a/src/main/java/li/cil/oc2/common/container/ComputerTerminalContainer.java b/src/main/java/li/cil/oc2/common/container/ComputerTerminalContainer.java index 07666172..38cdff84 100644 --- a/src/main/java/li/cil/oc2/common/container/ComputerTerminalContainer.java +++ b/src/main/java/li/cil/oc2/common/container/ComputerTerminalContainer.java @@ -30,11 +30,11 @@ public final class ComputerTerminalContainer extends AbstractComputerContainer { }, computer.getBlockPos()); } - public static ComputerTerminalContainer createClient(final int id, final Inventory playerInventory, final FriendlyByteBuf data) { + public static ComputerTerminalContainer createClient(final int id, final Inventory inventory, final FriendlyByteBuf data) { final BlockPos pos = data.readBlockPos(); - final BlockEntity blockEntity = playerInventory.player.level.getBlockEntity(pos); + final BlockEntity blockEntity = inventory.player.level.getBlockEntity(pos); if (blockEntity instanceof final ComputerBlockEntity computer) { - return new ComputerTerminalContainer(id, playerInventory.player, computer, createEnergyInfo()); + return new ComputerTerminalContainer(id, inventory.player, computer, createEnergyInfo()); } throw new IllegalArgumentException(); diff --git a/src/main/java/li/cil/oc2/common/container/RobotInventoryContainer.java b/src/main/java/li/cil/oc2/common/container/RobotInventoryContainer.java index 0f2e9bea..d9369ea8 100644 --- a/src/main/java/li/cil/oc2/common/container/RobotInventoryContainer.java +++ b/src/main/java/li/cil/oc2/common/container/RobotInventoryContainer.java @@ -45,7 +45,7 @@ public final class RobotInventoryContainer extends AbstractRobotContainer { /////////////////////////////////////////////////////////////////// private RobotInventoryContainer(final int id, final RobotEntity robot, final Player player, final IntPrecisionContainerData energyInfo) { - super(Containers.ROBOT.get(), id, robot, energyInfo); + super(Containers.ROBOT.get(), id, player, robot, energyInfo); final VMItemStackHandlers handlers = robot.getItemStackHandlers(); diff --git a/src/main/java/li/cil/oc2/common/container/RobotTerminalContainer.java b/src/main/java/li/cil/oc2/common/container/RobotTerminalContainer.java index d1461cb9..70d00b06 100644 --- a/src/main/java/li/cil/oc2/common/container/RobotTerminalContainer.java +++ b/src/main/java/li/cil/oc2/common/container/RobotTerminalContainer.java @@ -26,7 +26,7 @@ public final class RobotTerminalContainer extends AbstractRobotContainer { @Override public AbstractContainerMenu createMenu(final int id, final Inventory inventory, final Player player) { - return new RobotTerminalContainer(id, robot, createEnergyInfo(energy, busController)); + return new RobotTerminalContainer(id, player, robot, createEnergyInfo(energy, busController)); } }, b -> b.writeVarInt(robot.getId())); } @@ -35,7 +35,7 @@ public final class RobotTerminalContainer extends AbstractRobotContainer { final int entityId = data.readVarInt(); final Entity entity = inventory.player.level.getEntity(entityId); if (entity instanceof final RobotEntity robot) { - return new RobotTerminalContainer(id, robot, createEnergyInfo()); + return new RobotTerminalContainer(id, inventory.player, robot, createEnergyInfo()); } throw new IllegalArgumentException(); @@ -43,8 +43,8 @@ public final class RobotTerminalContainer extends AbstractRobotContainer { /////////////////////////////////////////////////////////////////// - private RobotTerminalContainer(final int id, final RobotEntity robot, final IntPrecisionContainerData energyInfo) { - super(Containers.ROBOT_TERMINAL.get(), id, robot, energyInfo); + private RobotTerminalContainer(final int id, final Player player, final RobotEntity robot, final IntPrecisionContainerData energyInfo) { + super(Containers.ROBOT_TERMINAL.get(), id, player, robot, energyInfo); // It's kinda dumb we need to access technically-client-side stuff here, but that's the nature of containers // needing to specify display positions for some reason. diff --git a/src/main/java/li/cil/oc2/common/entity/RobotEntity.java b/src/main/java/li/cil/oc2/common/entity/RobotEntity.java index 7a1fc62e..73daa1cb 100644 --- a/src/main/java/li/cil/oc2/common/entity/RobotEntity.java +++ b/src/main/java/li/cil/oc2/common/entity/RobotEntity.java @@ -8,6 +8,7 @@ import li.cil.oc2.api.bus.device.object.ObjectDevice; import li.cil.oc2.api.bus.device.object.Parameter; import li.cil.oc2.api.bus.device.provider.ItemDeviceQuery; import li.cil.oc2.api.capabilities.Robot; +import li.cil.oc2.api.capabilities.TerminalUserProvider; import li.cil.oc2.common.Config; import li.cil.oc2.common.bus.AbstractDeviceBusElement; import li.cil.oc2.common.bus.CommonDeviceBusController; @@ -82,7 +83,7 @@ import java.util.function.Consumer; import static java.util.Collections.singleton; import static li.cil.oc2.common.Constants.*; -public final class RobotEntity extends Entity implements Robot { +public final class RobotEntity extends Entity implements Robot, TerminalUserProvider { public static final EntityDataAccessor TARGET_POSITION = SynchedEntityData.defineId(RobotEntity.class, EntityDataSerializers.BLOCK_POS); public static final EntityDataAccessor TARGET_DIRECTION = SynchedEntityData.defineId(RobotEntity.class, EntityDataSerializers.DIRECTION); public static final EntityDataAccessor SELECTED_SLOT = SynchedEntityData.defineId(RobotEntity.class, EntityDataSerializers.BYTE); @@ -118,6 +119,7 @@ public final class RobotEntity extends Entity implements Robot { private final RobotItemStackHandlers deviceItems = new RobotItemStackHandlers(); private final FixedEnergyStorage energy = new FixedEnergyStorage(Config.robotEnergyStorage); private final ItemStackHandler inventory = new FixedSizeItemStackHandler(INVENTORY_SIZE); + private final Set terminalUsers = Collections.newSetFromMap(new WeakHashMap<>()); private long lastPistonMovement; /////////////////////////////////////////////////////////////////// @@ -224,6 +226,19 @@ public final class RobotEntity extends Entity implements Robot { RobotInventoryContainer.createServer(this, energy, virtualMachine.busController, player); } + public void addTerminalUser(final Player player) { + terminalUsers.add(player); + } + + public void removeTerminalUser(final Player player) { + terminalUsers.remove(player); + } + + @Override + public Iterable getTerminalUsers() { + return terminalUsers; + } + public void dropSelf() { if (!isAlive()) { return; From 708745def2a785240207eccaeb091bda4b913aaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Fri, 7 Jan 2022 07:52:08 +0100 Subject: [PATCH 04/27] Fix duplicate character sending for alt+{key} combinations. --- .../java/li/cil/oc2/client/gui/MachineTerminalWidget.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/li/cil/oc2/client/gui/MachineTerminalWidget.java b/src/main/java/li/cil/oc2/client/gui/MachineTerminalWidget.java index bfdd2dd9..16c4b47f 100644 --- a/src/main/java/li/cil/oc2/client/gui/MachineTerminalWidget.java +++ b/src/main/java/li/cil/oc2/client/gui/MachineTerminalWidget.java @@ -90,7 +90,9 @@ public final class MachineTerminalWidget extends GuiComponent { } public boolean charTyped(final char ch, final int modifier) { - terminal.putInput((byte) ch); + if (modifier == 0 || modifier == GLFW.GLFW_MOD_SHIFT) { + terminal.putInput((byte) ch); + } return true; } From ed81efb4d685efdd78dc7e8971ead88dff03a840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Fri, 7 Jan 2022 07:53:10 +0100 Subject: [PATCH 05/27] Correctly send \r instead of \n for returns and fix some other combinations. Fixes #76. --- .../client/gui/terminal/TerminalInput.java | 48 +++++++++++++++---- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/src/main/java/li/cil/oc2/client/gui/terminal/TerminalInput.java b/src/main/java/li/cil/oc2/client/gui/terminal/TerminalInput.java index 6de39001..5ece20d5 100644 --- a/src/main/java/li/cil/oc2/client/gui/terminal/TerminalInput.java +++ b/src/main/java/li/cil/oc2/client/gui/terminal/TerminalInput.java @@ -9,7 +9,7 @@ public final class TerminalInput { private static final Int2ObjectArrayMap> KEYCODE_SEQUENCES = new Int2ObjectArrayMap<>(); static { - addSequence(GLFW.GLFW_KEY_ENTER, '\n'); + addSequence(GLFW.GLFW_KEY_ENTER, '\r'); addSequence(GLFW.GLFW_KEY_TAB, '\t'); addSequence(GLFW.GLFW_KEY_BACKSPACE, '\b'); @@ -21,26 +21,54 @@ public final class TerminalInput { addSequence(GLFW.GLFW_KEY_PAGE_UP, "\033[5~"); addSequence(GLFW.GLFW_KEY_PAGE_DOWN, "\033[6~"); + addSequence(GLFW.GLFW_KEY_F1, "\033[11~"); + addSequence(GLFW.GLFW_KEY_F2, "\033[12~"); + addSequence(GLFW.GLFW_KEY_F3, "\033[13~"); + addSequence(GLFW.GLFW_KEY_F4, "\033[14~"); + addSequence(GLFW.GLFW_KEY_F5, "\033[15~"); + addSequence(GLFW.GLFW_KEY_F6, "\033[17~"); + addSequence(GLFW.GLFW_KEY_F7, "\033[18~"); + addSequence(GLFW.GLFW_KEY_F8, "\033[19~"); + addSequence(GLFW.GLFW_KEY_F9, "\033[20~"); + addSequence(GLFW.GLFW_KEY_F10, "\033[21~"); + addSequence(GLFW.GLFW_KEY_F11, "\033[23~"); + addSequence(GLFW.GLFW_KEY_F12, "\033[24~"); + addSequence(GLFW.GLFW_KEY_UP, "\033[A"); addSequence(GLFW.GLFW_KEY_DOWN, "\033[B"); addSequence(GLFW.GLFW_KEY_RIGHT, "\033[C"); addSequence(GLFW.GLFW_KEY_LEFT, "\033[D"); for (int i = 'A'; i <= 'Z'; i++) { - addSequence(GLFW.GLFW_MOD_CONTROL, + addSequence( + GLFW.GLFW_MOD_CONTROL, GLFW.GLFW_KEY_A + (i - 'A'), - (byte) (1 + i - 'A')); - addSequence(GLFW.GLFW_MOD_CONTROL | GLFW.GLFW_MOD_ALT, + (byte) (1 + i - 'A') + ); + addSequence( + GLFW.GLFW_MOD_CONTROL | GLFW.GLFW_MOD_SHIFT, GLFW.GLFW_KEY_A + (i - 'A'), - (byte) 27, (byte) ('a' + i - 'A')); - addSequence(GLFW.GLFW_MOD_CONTROL | GLFW.GLFW_MOD_ALT | GLFW.GLFW_MOD_SHIFT, + (byte) (1 + i - 'A') + ); + + addSequence( + GLFW.GLFW_MOD_ALT, GLFW.GLFW_KEY_A + (i - 'A'), - (byte) 27, (byte) i); + (byte) '\033', (byte) ('a' + i - 'A') + ); + addSequence( + GLFW.GLFW_MOD_ALT | GLFW.GLFW_MOD_SHIFT, + GLFW.GLFW_KEY_A + (i - 'A'), + (byte) '\033', (byte) (i + 128) + ); } - addSequence(GLFW.GLFW_MOD_CONTROL, GLFW.GLFW_KEY_LEFT_BRACKET, (byte) 27); - addSequence(GLFW.GLFW_MOD_CONTROL, GLFW.GLFW_KEY_BACKSLASH, (byte) 28); - addSequence(GLFW.GLFW_MOD_CONTROL, GLFW.GLFW_KEY_RIGHT_BRACKET, (byte) 29); + addSequence(GLFW.GLFW_MOD_CONTROL, GLFW.GLFW_KEY_LEFT_BRACKET, (byte) '\033'); + addSequence(GLFW.GLFW_MOD_CONTROL | GLFW.GLFW_MOD_SHIFT, GLFW.GLFW_KEY_LEFT_BRACKET, (byte) '\033'); + addSequence(GLFW.GLFW_MOD_CONTROL, GLFW.GLFW_KEY_BACKSLASH, (byte) '\034'); + addSequence(GLFW.GLFW_MOD_CONTROL | GLFW.GLFW_MOD_SHIFT, GLFW.GLFW_KEY_BACKSLASH, (byte) '\034'); + addSequence(GLFW.GLFW_MOD_CONTROL, GLFW.GLFW_KEY_RIGHT_BRACKET, (byte) '\035'); + addSequence(GLFW.GLFW_MOD_CONTROL | GLFW.GLFW_MOD_SHIFT, GLFW.GLFW_KEY_RIGHT_BRACKET, (byte) '\035'); } /////////////////////////////////////////////////////////////////// From 54ab9418e3b4f17e650cfada012a925aa2eda0ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Fri, 7 Jan 2022 07:55:53 +0100 Subject: [PATCH 06/27] Add some missing functionality to terminal emulator and fix some things. Also make it a little more readable by splitting actual commands out into separate methods. --- .../java/li/cil/oc2/common/vm/Terminal.java | 515 +++++++++++++----- 1 file changed, 382 insertions(+), 133 deletions(-) diff --git a/src/main/java/li/cil/oc2/common/vm/Terminal.java b/src/main/java/li/cil/oc2/common/vm/Terminal.java index f14d93af..188d9d4a 100644 --- a/src/main/java/li/cil/oc2/common/vm/Terminal.java +++ b/src/main/java/li/cil/oc2/common/vm/Terminal.java @@ -23,7 +23,7 @@ import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.atomic.AtomicInteger; -// Implements a couple of control sequences from here: https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences +// VT100 emulation: https://vt100.net/docs/vt100-ug/chapter3.html @Serialized public final class Terminal { public static final int WIDTH = 80, HEIGHT = 24; @@ -32,14 +32,31 @@ public final class Terminal { private static final int TAB_WIDTH = 4; - private static final int COLOR_BLACK = 0; - private static final int COLOR_RED = 1; - private static final int COLOR_GREEN = 2; - private static final int COLOR_YELLOW = 3; - private static final int COLOR_BLUE = 4; - private static final int COLOR_MAGENTA = 5; - private static final int COLOR_CYAN = 6; - private static final int COLOR_WHITE = 7; + @SuppressWarnings("unused") + private static final class Color { + static final int BLACK = 0; + static final int RED = 1; + static final int GREEN = 2; + static final int YELLOW = 3; + static final int BLUE = 4; + static final int MAGENTA = 5; + static final int CYAN = 6; + static final int WHITE = 7; + } + + @SuppressWarnings("unused") + private static final class Mode { + static final int LNM = 20; // Line Feed/New Line Mode + static final int DECCKM = 1; // Cursor key + static final int DECANM = 2; // ANSI/VT52 + static final int DECCOLM = 3; // Column + static final int DECSCLM = 4; // Scrolling + static final int DECSCNM = 5; // Screen + static final int DECOM = 6; // Origin + static final int DECAWM = 7; // Auto wrap + static final int DECARM = 8; // Auto repeating + static final int DECINLM = 9; // Interlace + } private static final int COLOR_MASK = 0b111; private static final int COLOR_FOREGROUND_SHIFT = 3; @@ -52,7 +69,7 @@ public final class Terminal { private static final int STYLE_HIDDEN_MASK = 1 << 5; // Default style: no modifiers, white foreground, black background. - private static final byte DEFAULT_COLORS = COLOR_WHITE << COLOR_FOREGROUND_SHIFT; + private static final byte DEFAULT_COLORS = Color.WHITE << COLOR_FOREGROUND_SHIFT; private static final byte DEFAULT_STYLE = 0; /////////////////////////////////////////////////////////////////// @@ -60,7 +77,10 @@ public final class Terminal { public enum State { // Must be public for serialization. NORMAL, // Currently reading characters normally. ESCAPE, // Last character was ESC, figure out what kind next. - SEQUENCE, // Know what sequence we have, now parsing it. + SHIFT_IN_CHARACTER_SET, // Shift in character set. + SHIFT_OUT_CHARACTER_SET, // Shift out character set. + HASH, // Escape sequence with # intermediate. + CONTROL_SEQUENCE, // Know what sequence we have, now parsing it. } public interface RendererView { @@ -73,18 +93,21 @@ public final class Terminal { private final byte[] buffer = new byte[WIDTH * HEIGHT]; private final byte[] colors = new byte[WIDTH * HEIGHT]; private final byte[] styles = new byte[WIDTH * HEIGHT]; + private final boolean[] tabs = new boolean[WIDTH]; private State state = State.NORMAL; private final int[] args = new int[4]; private int argCount = 0; + private int modes; + private int scrollFirst = 0, scrollLast = HEIGHT - 1; private int x, y; private int savedX, savedY; // Color info packed into one byte for compact storage // 0-2: background color (index) // 3-5: foreground color (index) - private byte color = DEFAULT_COLORS; + private byte color; // Style info packed into one byte for compact storage - private byte style = DEFAULT_STYLE; + private byte style; // Rendering data for client private final transient Set renderers = Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<>())); @@ -94,7 +117,7 @@ public final class Terminal { /////////////////////////////////////////////////////////////////// public Terminal() { - clear(); + RIS(); } /////////////////////////////////////////////////////////////////// @@ -179,36 +202,56 @@ public final class Terminal { switch (state) { case NORMAL -> { switch (value) { - case (byte) '\r' -> setCursorPos(0, y); - case (byte) '\n' -> putNewLine(); - case (byte) '\t' -> { - if (x + TAB_WIDTH > WIDTH) { - setCursorPos(0, y); - putNewLine(); + case '\007' -> hasPendingBell = true; + case '\033' -> state = State.ESCAPE; + case '\016' -> { } // SO + case '\017' -> { } // SI + + case (byte) '\r' /* 015 */ -> setCursorPos(0, y); + case (byte) '\n' /* 012 */, '\013', '\014' -> { + if (getMode(Mode.LNM)) { + NEL(); } else { - setCursorPos(x + TAB_WIDTH - (x % TAB_WIDTH), y); + IND(); } } - case (byte) '\b' -> setCursorPos(x - 1, y); - case 7 -> hasPendingBell = true; - case 27 -> state = State.ESCAPE; - default -> { - if (!Character.isISOControl(ch)) { - putChar(ch); + case (byte) '\t' /* 011 */ -> { + while (x < WIDTH && !tabs[x]) { + x++; } } + case (byte) '\b' /* 010 */ -> setCursorPos(Math.min(x, WIDTH - 1) - 1, y); + + default -> putChar(ch); } } case ESCAPE -> { - if (ch == '[') { + if (ch == '[') { // Control Sequence Indicator Arrays.fill(args, (byte) 0); argCount = 0; - state = State.SEQUENCE; + state = State.CONTROL_SEQUENCE; + } else if (ch == '(') { // SCS – Select Character Set + state = State.SHIFT_IN_CHARACTER_SET; + } else if (ch == ')') { // SCS – Select Character Set + state = State.SHIFT_OUT_CHARACTER_SET; + } else if (ch == '#') { // # Intermediate + state = State.HASH; } else { state = State.NORMAL; + switch (ch) { + case 'D' -> IND(); // IND – Index + case 'E' -> NEL(); // NEL – Next Line + case 'M' -> RI(); // RI – Reverse Index + case '7' -> DECSC(); // DECSC – Save Cursor (DEC Private) + case '8' -> DECRC(); // DECRC – Restore Cursor (DEC Private) + case 'H' -> HTS(); // HTS – Horizontal Tabulation Set + case 'c' -> RIS(); // RIS – Reset To Initial State + case '=' -> { } // DECKPAM – Keypad Application Mode (DEC Private) + case '>' -> { } // DECKPNM – Keypad Numeric Mode (DEC Private) + } } } - case SEQUENCE -> { + case CONTROL_SEQUENCE -> { if (ch >= '0' && ch <= '9') { if (argCount < args.length) { final int digit = ch - '0'; @@ -219,101 +262,272 @@ public final class Terminal { } } } else { + if (ch == '?') { + break; // Ignore ? intermediate character. + } + if (argCount < args.length) { argCount++; } - if (ch == ';' || ch == '?') { - break; + if (ch == ';') { + break; // Keep going, we have another argument. } state = State.NORMAL; - switch (ch) { - case 'A' -> // Cursor Up - setCursorPos(x, y - Math.max(1, args[0])); - case 'B' -> // Cursor Down - setCursorPos(x, y + Math.max(1, args[0])); - case 'C' -> // Cursor Forward - setCursorPos(x + Math.max(1, args[0]), y); - case 'D' -> // Cursor Back - setCursorPos(x - Math.max(1, args[0]), y); - case 'E' -> // Cursor Next Line - setCursorPos(0, y + Math.min(1, args[0])); - case 'F' -> // Cursor Previous Line - setCursorPos(0, y - Math.min(1, args[0])); - case 'G' -> // Cursor Horizontal Absolute - setCursorPos(args[0] - 1, y); - // Don't care about terminal mode fanciness so just alias. - case 'f', 'H' -> // Cursor Position - setCursorPos(args[1] - 1, args[0] - 1); - case 'J' -> { // Erase in Display - if (args[0] == 0) { // Cursor and down - clearLine(y, x, WIDTH); - for (int iy = y + 1; iy < HEIGHT; iy++) { - clearLine(iy); - } - } else if (args[0] == 1) { // Cursor and up - clearLine(y, 0, x + 1); - for (int iy = 0; iy < y; iy++) { - clearLine(iy); - } - } else if (args[0] == 2) { // Everything - clear(); - } - } - case 'K' -> { // Erase in Line - if (args[0] == 0) { // Cursor and right - clearLine(y, x, WIDTH); - } else if (args[0] == 1) { // Cursor and left - clearLine(y, 0, x + 1); - } else if (args[0] == 2) { // ...entirely - clearLine(y); - } - } - - // S, T: Scroll Up/Down. We don't have scrollback. - case 'm' -> { // Select Graphic Rendition - for (int i = 0; i < argCount; i++) { - final int arg = args[i]; - selectStyle(arg); - } - } - case 'n' -> { // Device Status Report - switch (args[0]) { - case 5 -> { // Report console status - if (!displayOnly) { - putInput((byte) 27); - for (final char i : "[0n".toCharArray()) { - putInput((byte) i); - } - } - } - case 6 -> { // Report cursor position - if (!displayOnly) { - putInput((byte) 27); - for (final char i : String.format("[%d;%dR", (y % HEIGHT) + 1, x + 1).toCharArray()) { - putInput((byte) i); - } - } - } - } - } - case 's' -> { // Save Current Cursor Position - savedX = x; - savedY = y; - } - case 'u' -> { // Restore Saved Cursor Position - x = savedX; - y = savedY; - } + case 'A' -> CUU(); // CUU - Cursor Up + case 'B' -> CUD(); // CUD – Cursor Down + case 'C' -> CUF(); // CUF – Cursor Forward + case 'D' -> CUB(); // CUB – Cursor Backward + case 'H' -> CUP(); // CUP - Cursor Position + case 'f' -> HVP(); // HVP – Horizontal and Vertical Position + case 'm' -> SGR(); // SGR – Select Graphic Rendition + case 'K' -> EL(); // EL – Erase In Line + case 'J' -> ED(); // ED – Erase In Display + case 'r' -> DECSTBM(); // DECSTBM – Set Top and Bottom Margins (DEC Private) + case 'g' -> TBC(); // TBC – Tabulation Clear + case 'h' -> SM(); // SM – Set Mode + case 'l' -> RM(); // RM – Reset Mode + case 'n' -> DSR(); // DSR – Device Status Report + case 'c' -> DA(); // DA – Device Attributes + } + } + } + case SHIFT_IN_CHARACTER_SET, SHIFT_OUT_CHARACTER_SET -> { + state = State.NORMAL; + switch (ch) { + case 'A' -> { } // United Kingdom Set + case 'B' -> { } // ASCII Set + case '0' -> { } // Special Graphics + case '1' -> { } // Alternate Character ROM Standard Character Set + case '2' -> { } // Alternate Character ROM Special Graphics + } + } + case HASH -> { + state = State.NORMAL; + switch (ch) { + case '3' -> { } // Change this line to double-height top half (DECDHL) + case '4' -> { } // Change this line to double-height bottom half (DECDHL) + case '5' -> { } // Change this line to single-width single-height (DECSWL) + case '6' -> { } // Change this line to double-width single-height (DECDWL) + case '8' -> { // Fill Screen with Es (DECALN) + Arrays.fill(buffer, (byte) 'E'); + renderers.forEach(model -> model.getDirtyMask().set(-1)); } } } } } - /////////////////////////////////////////////////////////////////// + private void IND() { + if (y >= scrollLast) { + shiftUpOne(); + } else { + setCursorPos(x, y + 1); + } + } + + private void NEL() { + if (y >= scrollLast) { + shiftUpOne(); + setCursorPos(0, y); + } else { + setCursorPos(0, y + 1); + } + } + + private void RI() { + if (y <= scrollFirst) { + shiftDownOne(); + } else { + setCursorPos(0, y - 1); + } + } + + private void DECSC() { + savedX = x; + savedY = y; + } + + private void DECRC() { + x = savedX; + y = savedY; + } + + private void HTS() { + if (x >= 0 && x < WIDTH) { + tabs[x] = true; + } + } + + private void RIS() { + color = DEFAULT_COLORS; + style = DEFAULT_STYLE; + clear(); + Arrays.fill(tabs, false); + for (int i = 0; i < WIDTH; i++) { + if (i % TAB_WIDTH == 0) { + tabs[i] = true; + } + } + } + + private void CUU() { + setClampedCursorPos(x, y - Math.max(1, args[0])); + } + + private void CUD() { + setClampedCursorPos(x, y + Math.max(1, args[0])); + } + + private void CUF() { + setClampedCursorPos(x + Math.max(1, args[0]), y); + } + + private void CUB() { + setClampedCursorPos(x - Math.max(1, args[0]), y); + } + + private void CUP() { + setRelativeCursorPos(args[1] - 1, args[0] - 1); + } + + private void HVP() { + CUP(); + } + + private void SGR() { + for (int i = 0; i < argCount; i++) { + selectStyle(args[i]); + } + } + + private void EL() { + switch (args[0]) { + case 0 -> // From cursor to end of line + clearLine(y, x, WIDTH); + case 1 -> // From beginning of line to cursor + clearLine(y, 0, x + 1); + case 2 -> // Entire line containing cursor + clearLine(y); + } + } + + private void ED() { + switch (args[0]) { + case 0 -> { // From cursor to end of screen + clearLine(y, x, WIDTH); + for (int iy = y + 1; iy < HEIGHT; iy++) { + clearLine(iy); + } + } + case 1 -> { // From beginning of screen to cursor + for (int iy = 0; iy < y; iy++) { + clearLine(iy); + } + clearLine(y, 0, x + 1); + } + case 2 -> // Entire screen + clear(); + } + } + + private void DECSTBM() { + final int first, last; + if (argCount == 2) { + first = args[0] - 1; + last = args[1] - 1; + } else { + first = 0; + last = HEIGHT - 1; + } + if (first < 0 || last > HEIGHT - 1 || last - first <= 0) { + return; + } + scrollFirst = first; // to index + scrollLast = last; // to index + setRelativeCursorPos(0, 0); // send cursor home + } + + private void TBC() { + switch (args[0]) { + case 0 -> { // Clear tab at current column + if (x >= 0 && x < WIDTH) { + tabs[x] = false; + } + } + case 3 -> // Clear all tabs + Arrays.fill(tabs, false); + } + } + + private void SM() { + for (int i = 0; i < argCount; i++) { + final int mode = args[i]; + if (mode != 0) { + setMode(mode); + } + if (mode == Mode.DECOM) { + setRelativeCursorPos(0, 0); + } + } + } + + private void RM() { + for (int i = 0; i < argCount; i++) { + final int mode = args[i]; + if (mode != 0) { + resetMode(mode); + } + if (mode == Mode.DECOM) { + setRelativeCursorPos(0, 0); + clear(); + } + } + } + + private void DSR() { + switch (args[0]) { + case 5 -> // Report console status + putResponse("\033[0n"); // Ready, No malfunctions detected + case 6 -> { // Report cursor position + if (getMode(Mode.DECOM)) { + putResponse(String.format("\033[%d;%dR", (y - scrollFirst) + 1, x + 1)); + } else { + putResponse(String.format("\033[%d;%dR", (y % HEIGHT) + 1, x + 1)); + } + } + } + } + + private void DA() { + putResponse("\033[?1;0c"); // No options. + } + + private void setMode(final int mode) { + modes |= 1 << mode; + } + + private void resetMode(final int mode) { + modes &= ~(1 << mode); + } + + private boolean getMode(final int mode) { + return (modes & (1 << mode)) != 0; + } + + private void putResponse(final String value) { + for (int i = 0; i < value.length(); i++) { + putResponse((byte) value.charAt(i)); + } + } + + private void putResponse(final byte value) { + if (!displayOnly) { + putInput(value); + } + } private void selectStyle(final int sgr) { switch (sgr) { @@ -325,11 +539,11 @@ public final class Terminal { style |= STYLE_BOLD_MASK; case 2 -> // Faint or decreased intensity style |= STYLE_DIM_MASK; - case 4 -> // Underline + case 4 -> // Underscore style |= STYLE_UNDERLINE_MASK; - case 5 -> // Slow Blink + case 5 -> // Blink style |= STYLE_BLINK_MASK; - case 7 -> // Reverse video + case 7 -> // Negative (reverse) image style |= STYLE_INVERT_MASK; case 8 -> // Conceal aka Hide style |= STYLE_HIDDEN_MASK; @@ -354,15 +568,33 @@ public final class Terminal { } } + private void setRelativeCursorPos(final int x, final int y) { + if (getMode(Mode.DECOM)) { + setCursorPos(x, Math.min(scrollFirst + y, scrollLast)); + } else { + setCursorPos(x, y); + } + } + + private void setClampedCursorPos(final int x, final int y) { + setCursorPos(x, Math.max(scrollFirst, Math.min(scrollLast, y))); + } + private void setCursorPos(final int x, final int y) { this.x = Math.max(0, Math.min(WIDTH - 1, x)); this.y = Math.max(0, Math.min(HEIGHT - 1, y)); } private void putChar(final char ch) { + if (Character.isISOControl(ch)) + return; + if (x >= WIDTH) { - setCursorPos(0, y); - putNewLine(); + if (getMode(Mode.DECAWM)) { + NEL(); + } else { + setCursorPos(WIDTH - 1, y); + } } setChar(x, y, ch); @@ -387,7 +619,7 @@ public final class Terminal { Arrays.fill(buffer, (byte) ' '); Arrays.fill(colors, DEFAULT_COLORS); Arrays.fill(styles, DEFAULT_STYLE); - renderers.forEach(model -> model.getDirtyMask().set((1 << HEIGHT) - 1)); + renderers.forEach(model -> model.getDirtyMask().set(-1)); } private void clearLine(final int y) { @@ -401,24 +633,41 @@ public final class Terminal { renderers.forEach(model -> model.getDirtyMask().accumulateAndGet(1 << y, (prev, next) -> prev | next)); } - private void putNewLine() { - y++; - if (y >= HEIGHT) { - y = HEIGHT - 1; - shiftUpOne(); - } + private void shiftUpOne() { + shiftLines(scrollFirst + 1, scrollLast, -1); } - private void shiftUpOne() { - System.arraycopy(buffer, WIDTH, buffer, 0, buffer.length - WIDTH); - System.arraycopy(colors, WIDTH, colors, 0, colors.length - WIDTH); - System.arraycopy(styles, WIDTH, styles, 0, styles.length - WIDTH); - Arrays.fill(buffer, WIDTH * HEIGHT - WIDTH, WIDTH * HEIGHT, (byte) ' '); - Arrays.fill(colors, WIDTH * HEIGHT - WIDTH, WIDTH * HEIGHT, DEFAULT_COLORS); - Arrays.fill(styles, WIDTH * HEIGHT - WIDTH, WIDTH * HEIGHT, DEFAULT_STYLE); + private void shiftDownOne() { + shiftLines(scrollFirst, scrollLast - 1, 1); + } - // Offset is baked into buffers so we must rebuild them all. - renderers.forEach(model -> model.getDirtyMask().set(-1)); + private void shiftLines(final int firstLine, final int lastLine, final int count) { + if (count == 0) + return; + + final int srcIndex = firstLine * WIDTH; + final int charCount = (lastLine + 1) * WIDTH - srcIndex; + final int dstIndex = srcIndex + count * WIDTH; + + System.arraycopy(buffer, srcIndex, buffer, dstIndex, charCount); + System.arraycopy(colors, srcIndex, colors, dstIndex, charCount); + System.arraycopy(styles, srcIndex, styles, dstIndex, charCount); + + final int clearIndex = count > 0 ? srcIndex : (dstIndex + charCount); + final int clearCount = Math.abs(count * WIDTH); + Arrays.fill(buffer, clearIndex, clearIndex + clearCount, (byte) ' '); + // TODO Copy color and style from last line. + Arrays.fill(colors, clearIndex, clearIndex + clearCount, DEFAULT_COLORS); + Arrays.fill(styles, clearIndex, clearIndex + clearCount, DEFAULT_STYLE); + + int dirtyLinesMask = 0; + final int dirtyStart = Math.min(firstLine, firstLine + count); + final int dirtyEnd = Math.max(lastLine, lastLine + count); + for (int i = dirtyStart; i <= dirtyEnd; i++) { + dirtyLinesMask |= 1 << i; + } + final int finalDirtyLinesMask = dirtyLinesMask; + renderers.forEach(model -> model.getDirtyMask().accumulateAndGet(finalDirtyLinesMask, (left, right) -> left | right)); } /////////////////////////////////////////////////////////////////// @@ -671,7 +920,7 @@ public final class Terminal { final BufferBuilder buffer = Tesselator.getInstance().getBuilder(); buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); - final int foreground = COLORS[COLOR_WHITE]; + final int foreground = COLORS[Color.WHITE]; final float r = ((foreground >> 16) & 0xFF) / 255f; final float g = ((foreground >> 8) & 0xFF) / 255f; final float b = ((foreground) & 0xFF) / 255f; From 1b9d4061af000e8348bee04ec2acac10a3346ceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Fri, 7 Jan 2022 08:12:21 +0100 Subject: [PATCH 07/27] Horrible hack to avoid Forge stealing keyboard input in terminal screens. --- .../client/gui/AbstractMachineTerminalScreen.java | 11 +++++++++++ .../cil/oc2/client/gui/ComputerTerminalScreen.java | 12 ++++++++++++ .../li/cil/oc2/client/gui/RobotTerminalScreen.java | 12 ++++++++++++ 3 files changed, 35 insertions(+) diff --git a/src/main/java/li/cil/oc2/client/gui/AbstractMachineTerminalScreen.java b/src/main/java/li/cil/oc2/client/gui/AbstractMachineTerminalScreen.java index e35a707c..e2d1448e 100644 --- a/src/main/java/li/cil/oc2/client/gui/AbstractMachineTerminalScreen.java +++ b/src/main/java/li/cil/oc2/client/gui/AbstractMachineTerminalScreen.java @@ -9,9 +9,11 @@ import li.cil.oc2.common.Constants; import li.cil.oc2.common.container.AbstractMachineTerminalContainer; import li.cil.oc2.common.util.TooltipUtils; import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.FormattedText; +import net.minecraft.network.chat.TextComponent; import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.world.entity.player.Inventory; import net.minecraftforge.api.distmarker.Dist; @@ -103,6 +105,10 @@ public abstract class AbstractMachineTerminalScreen { + @SuppressWarnings("all") private EditBox focusIndicatorEditBox; + + /////////////////////////////////////////////////////////////////// + public ComputerTerminalScreen(final ComputerTerminalContainer container, final Inventory playerInventory, final Component title) { super(container, playerInventory, title); } + + /////////////////////////////////////////////////////////////////// + + @Override + protected void setFocusIndicatorEditBox(final EditBox editBox) { + focusIndicatorEditBox = editBox; + } } diff --git a/src/main/java/li/cil/oc2/client/gui/RobotTerminalScreen.java b/src/main/java/li/cil/oc2/client/gui/RobotTerminalScreen.java index 45c32636..551a6fc6 100644 --- a/src/main/java/li/cil/oc2/client/gui/RobotTerminalScreen.java +++ b/src/main/java/li/cil/oc2/client/gui/RobotTerminalScreen.java @@ -3,6 +3,7 @@ package li.cil.oc2.client.gui; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; import li.cil.oc2.common.container.RobotTerminalContainer; +import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.player.Inventory; @@ -16,6 +17,10 @@ public final class RobotTerminalScreen extends AbstractMachineTerminalScreen Date: Fri, 7 Jan 2022 08:18:25 +0100 Subject: [PATCH 08/27] Set run state if runner errored to avoid NRE later on (since RUNNING implies runner != null). --- src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachine.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachine.java b/src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachine.java index 80ddd278..481c325d 100644 --- a/src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachine.java +++ b/src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachine.java @@ -173,6 +173,7 @@ public abstract class AbstractVirtualMachine implements VirtualMachine { } catch (final Throwable e) { LOGGER.error(e); runner = null; + setRunState(VMRunState.STOPPED); } } } From 28734a68d951c321b89adb7d65dcbf8d241eba33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Fri, 7 Jan 2022 08:18:33 +0100 Subject: [PATCH 09/27] Fix javadoc. --- .../java/li/cil/oc2/api/bus/device/vm/context/VMContext.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/li/cil/oc2/api/bus/device/vm/context/VMContext.java b/src/main/java/li/cil/oc2/api/bus/device/vm/context/VMContext.java index d562dbf5..ace2fec2 100644 --- a/src/main/java/li/cil/oc2/api/bus/device/vm/context/VMContext.java +++ b/src/main/java/li/cil/oc2/api/bus/device/vm/context/VMContext.java @@ -97,10 +97,6 @@ public interface VMContext { /** * Waits for the executor thread of the virtual machine to finish running. *

- * Events subscribers can only be registered inside {@link VMDevice#mount(VMContext)}. - * Trying to register subscribers after that method has returned will result in an - * exception. - *

* Note that this may trigger a {@link li.cil.oc2.api.bus.device.vm.event.VMPausingEvent} * if the virtual machine has not been paused before. Calling this on a paused virtual * machine is a no-op. From d139ea4679ef5b53d9f7584b2fb30861dc1a1771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Fri, 7 Jan 2022 12:48:50 +0100 Subject: [PATCH 10/27] Fix potential NRE when selecting file to import. --- .../cil/oc2/client/gui/FileChooserScreen.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/java/li/cil/oc2/client/gui/FileChooserScreen.java b/src/main/java/li/cil/oc2/client/gui/FileChooserScreen.java index 4512c2cd..de7077f5 100644 --- a/src/main/java/li/cil/oc2/client/gui/FileChooserScreen.java +++ b/src/main/java/li/cil/oc2/client/gui/FileChooserScreen.java @@ -1,6 +1,7 @@ package li.cil.oc2.client.gui; import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; @@ -17,6 +18,7 @@ import javax.annotation.Nullable; import java.io.IOException; import java.nio.file.*; import java.util.List; +import java.util.Objects; import java.util.Optional; import static li.cil.oc2.common.util.TranslationUtils.text; @@ -181,7 +183,7 @@ public final class FileChooserScreen extends Screen { final FileList.FileEntry selected = fileList.getSelected(); if (selected != null) { - return selected.file.equals(directory.getParent()); + return Objects.equals(selected.file, directory.getParent()); } final String selectedFileEntry = fileNameTextField.getValue(); @@ -191,7 +193,7 @@ public final class FileChooserScreen extends Screen { private Optional getPath() { final FileList.FileEntry selected = fileList.getSelected(); if (selected != null) { - return Optional.of(selected.file); + return Optional.ofNullable(selected.file); } if (directory == null) { @@ -277,7 +279,7 @@ public final class FileChooserScreen extends Screen { super(FileChooserScreen.this.getMinecraft(), FileChooserScreen.this.width, FileChooserScreen.this.height, y, y + height, slotHeight); } - public void refreshFiles(@Nullable Path directory) { + public void refreshFiles(@Nullable final Path directory) { FileChooserScreen.directory = directory; setScrollAmount(0); @@ -328,7 +330,7 @@ public final class FileChooserScreen extends Screen { refreshFiles(path); } else { refreshFiles(path.getParent()); - children().stream().filter(entry -> entry.file.equals(path)) + children().stream().filter(entry -> path.equals(entry.file)) .findFirst().ifPresent(entry -> { entry.select(); centerScrollOn(entry); @@ -356,12 +358,12 @@ public final class FileChooserScreen extends Screen { } private final class FileEntry extends ObjectSelectionList.Entry { - private final Path file; + @Nullable private final Path file; private final Component displayName; private long lastEntryClickTime = 0; - public FileEntry(final Path file, final Component displayName) { + public FileEntry(@Nullable final Path file, final Component displayName) { this.file = file; this.displayName = displayName; } @@ -390,11 +392,13 @@ public final class FileChooserScreen extends Screen { } public void select() { - if (directory != null && file.equals(directory.getParent())) { + if (directory != null && Objects.equals(directory.getParent(), file)) { fileNameTextField.setValue(".."); - } else { + } else if (file != null) { final Path fileName = file.getFileName(); fileNameTextField.setValue(fileName != null ? fileName.toString() : file.toString()); + } else { + return; } fileNameTextField.moveCursorToStart(); fileNameTextField.setHighlightPos(0); From c392ad0f902349b2cb64347484b51366576fc004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Fri, 7 Jan 2022 12:49:46 +0100 Subject: [PATCH 11/27] Don't allow opening files/dirs that are not accessible via double-click (when button is already disabled for that reason). --- src/main/java/li/cil/oc2/client/gui/FileChooserScreen.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/li/cil/oc2/client/gui/FileChooserScreen.java b/src/main/java/li/cil/oc2/client/gui/FileChooserScreen.java index de7077f5..a7670e9a 100644 --- a/src/main/java/li/cil/oc2/client/gui/FileChooserScreen.java +++ b/src/main/java/li/cil/oc2/client/gui/FileChooserScreen.java @@ -381,7 +381,7 @@ public final class FileChooserScreen extends Screen { select(); final boolean isDoubleClick = System.currentTimeMillis() - lastEntryClickTime < 250; - if (isDoubleClick) { + if (isDoubleClick && okButton.active) { confirm(); } From 99fa59cfc68d5ea15f7b10a37b9e5d59dc57e25a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Fri, 7 Jan 2022 12:50:07 +0100 Subject: [PATCH 12/27] Color directories that cannot be opened gray. --- src/main/java/li/cil/oc2/client/gui/FileChooserScreen.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/li/cil/oc2/client/gui/FileChooserScreen.java b/src/main/java/li/cil/oc2/client/gui/FileChooserScreen.java index a7670e9a..75a13a73 100644 --- a/src/main/java/li/cil/oc2/client/gui/FileChooserScreen.java +++ b/src/main/java/li/cil/oc2/client/gui/FileChooserScreen.java @@ -352,9 +352,12 @@ public final class FileChooserScreen extends Screen { return createDirectoryEntry(path, path.getFileName().toString() + path.getFileSystem().getSeparator()); } - private FileList.FileEntry createDirectoryEntry(final Path path, final String displayName) { + private FileList.FileEntry createDirectoryEntry(@Nullable final Path path, final String displayName) { + final TextColor color = path != null && Files.exists(path) + ? TextColor.fromRgb(0xA0A0FF) + : TextColor.fromLegacyFormat(ChatFormatting.GRAY); return new FileList.FileEntry(path, new TextComponent(displayName) - .withStyle(s -> s.withColor(TextColor.fromRgb(0xA0A0FF)))); + .withStyle(s -> s.withColor(color))); } private final class FileEntry extends ObjectSelectionList.Entry { From 63602bfef7be272d9197eb50b9ba6b4a2370af7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sat, 8 Jan 2022 06:30:16 +0100 Subject: [PATCH 13/27] Use RIS for terminal reset. --- src/main/java/li/cil/oc2/common/util/TerminalUtils.java | 6 ++---- src/main/java/li/cil/oc2/common/vm/Terminal.java | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/li/cil/oc2/common/util/TerminalUtils.java b/src/main/java/li/cil/oc2/common/util/TerminalUtils.java index 3d3f2edd..41eccc48 100644 --- a/src/main/java/li/cil/oc2/common/util/TerminalUtils.java +++ b/src/main/java/li/cil/oc2/common/util/TerminalUtils.java @@ -9,10 +9,8 @@ public final class TerminalUtils { 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' + // Reset. + '\033', 'c', }); /////////////////////////////////////////////////////////////////// diff --git a/src/main/java/li/cil/oc2/common/vm/Terminal.java b/src/main/java/li/cil/oc2/common/vm/Terminal.java index 188d9d4a..b2ad501e 100644 --- a/src/main/java/li/cil/oc2/common/vm/Terminal.java +++ b/src/main/java/li/cil/oc2/common/vm/Terminal.java @@ -366,7 +366,7 @@ public final class Terminal { style = DEFAULT_STYLE; clear(); Arrays.fill(tabs, false); - for (int i = 0; i < WIDTH; i++) { + for (int i = 1; i < WIDTH; i++) { if (i % TAB_WIDTH == 0) { tabs[i] = true; } From 922c7bab517f74e30a304cfd7a22b3aa1ec62179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sat, 8 Jan 2022 06:30:25 +0100 Subject: [PATCH 14/27] Update Sedna. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 6b9da427..74f62134 100644 --- a/build.gradle +++ b/build.gradle @@ -58,7 +58,7 @@ repositories { dependencies { minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}" - implementation "li.cil.sedna:sedna-${minecraft_version}-forge:1.0.3" + implementation "li.cil.sedna:sedna-${minecraft_version}-forge:1.0.4" implementation fg.deobf("curse.maven:markdownmanual-502485:3565800") From 8798f958f92c76af8241127510efff65f7d833eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sat, 8 Jan 2022 06:30:59 +0100 Subject: [PATCH 15/27] Add startup script that sets terminfo and terminal size environment variables. --- src/main/scripts/init.d/S10export_terminfo | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/main/scripts/init.d/S10export_terminfo diff --git a/src/main/scripts/init.d/S10export_terminfo b/src/main/scripts/init.d/S10export_terminfo new file mode 100644 index 00000000..b6a1b6b8 --- /dev/null +++ b/src/main/scripts/init.d/S10export_terminfo @@ -0,0 +1,27 @@ +#!/bin/sh + +TERMINFO_PROFILE="/etc/profile.d/02_init_terminfo.sh" + +# Registers terminfo environment variables in global profile config if they don't exist yet +start() { + if [ ! -f $TERMINFO_PROFILE ] + then + echo "TERMINFO=/usr/lib/terminfo/v/vt100" > $TERMINFO_PROFILE + echo "COLUMNS=80" >> $TERMINFO_PROFILE + echo "ROWS=24" >> $TERMINFO_PROFILE + fi +} + +case "$1" in + start) + start + ;; + stop) + exit 0 + ;; + *) + echo "Usage: $0 {start}" + exit 1 +esac + +exit $? From e8b2c836e8f67902c21806879758ae1d8b54d887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sat, 8 Jan 2022 07:46:08 +0100 Subject: [PATCH 16/27] Clean up worker thread join logic a bit and fire resuming event immediately to avoid performance issues/race conditions for some devices (e.g. network cards). --- .../HardDriveVMDeviceWithInitialData.java | 4 +-- .../oc2/common/vm/AbstractVirtualMachine.java | 2 -- .../java/li/cil/oc2/common/vm/VMRunner.java | 36 +++++++++---------- 3 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/main/java/li/cil/oc2/common/bus/device/item/HardDriveVMDeviceWithInitialData.java b/src/main/java/li/cil/oc2/common/bus/device/item/HardDriveVMDeviceWithInitialData.java index 77dc6014..829acc83 100644 --- a/src/main/java/li/cil/oc2/common/bus/device/item/HardDriveVMDeviceWithInitialData.java +++ b/src/main/java/li/cil/oc2/common/bus/device/item/HardDriveVMDeviceWithInitialData.java @@ -2,7 +2,7 @@ package li.cil.oc2.common.bus.device.item; import com.google.common.eventbus.Subscribe; import com.google.common.io.ByteStreams; -import li.cil.oc2.api.bus.device.vm.event.VMResumingRunningEvent; +import li.cil.oc2.api.bus.device.vm.event.VMResumedRunningEvent; import li.cil.oc2.common.util.Location; import li.cil.sedna.api.device.BlockDevice; import li.cil.sedna.device.block.ByteBufferBlockDevice; @@ -39,7 +39,7 @@ public final class HardDriveVMDeviceWithInitialData extends HardDriveVMDevice { } @Subscribe - public void handleResumingRunningEvent(final VMResumingRunningEvent event) { + public void handleResumedRunningEvent(final VMResumedRunningEvent event) { if (copyJob != null) { try { copyJob.get(); diff --git a/src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachine.java b/src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachine.java index 481c325d..ce4710cb 100644 --- a/src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachine.java +++ b/src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachine.java @@ -167,9 +167,7 @@ public abstract class AbstractVirtualMachine implements VirtualMachine { private void joinWorkerThread() { if (runner != null) { try { - state.context.postEvent(new VMPausingEvent()); runner.join(); - runner.scheduleResumeEvent(); } catch (final Throwable e) { LOGGER.error(e); runner = null; diff --git a/src/main/java/li/cil/oc2/common/vm/VMRunner.java b/src/main/java/li/cil/oc2/common/vm/VMRunner.java index 1f5b3285..fe2dbce7 100644 --- a/src/main/java/li/cil/oc2/common/vm/VMRunner.java +++ b/src/main/java/li/cil/oc2/common/vm/VMRunner.java @@ -1,10 +1,7 @@ package li.cil.oc2.common.vm; import li.cil.ceres.api.Serialized; -import li.cil.oc2.api.bus.device.vm.event.VMInitializationException; -import li.cil.oc2.api.bus.device.vm.event.VMInitializingEvent; -import li.cil.oc2.api.bus.device.vm.event.VMResumedRunningEvent; -import li.cil.oc2.api.bus.device.vm.event.VMResumingRunningEvent; +import li.cil.oc2.api.bus.device.vm.event.*; import li.cil.oc2.common.Constants; import li.cil.oc2.common.bus.RPCDeviceBusAdapter; import li.cil.oc2.common.vm.context.global.GlobalVMContext; @@ -40,7 +37,7 @@ public class VMRunner implements Runnable { /////////////////////////////////////////////////////////////////// - private boolean firedResumeEvent; + private boolean firedResumedRunningEvent; @Serialized private boolean firedInitializationEvent; @Serialized private Component runtimeError; @@ -57,10 +54,6 @@ public class VMRunner implements Runnable { /////////////////////////////////////////////////////////////////// - public void scheduleResumeEvent() { - firedResumeEvent = false; - } - @Nullable public Component getRuntimeError() { return runtimeError; @@ -79,14 +72,20 @@ public class VMRunner implements Runnable { } public void join() throws Throwable { - if (lastSchedule != null) { - try { - lastSchedule.get(); - } catch (final InterruptedException e) { - // We do not mind this. - } catch (final ExecutionException e) { - throw e.getCause(); + context.postEvent(new VMPausingEvent()); + try { + if (lastSchedule != null) { + try { + lastSchedule.get(); + } catch (final InterruptedException e) { + // We do not mind this. + } catch (final ExecutionException e) { + throw e.getCause(); + } } + } finally { + context.postEvent(new VMResumingRunningEvent()); + firedResumedRunningEvent = false; } } @@ -136,9 +135,8 @@ public class VMRunner implements Runnable { } } - if (!firedResumeEvent) { - firedResumeEvent = true; - context.postEvent(new VMResumingRunningEvent()); + if (!firedResumedRunningEvent) { + firedResumedRunningEvent = true; context.postEvent(new VMResumedRunningEvent()); } } From a0885e7350d7d970b55ff99281e72744361064fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sat, 8 Jan 2022 08:09:53 +0100 Subject: [PATCH 17/27] Fix ROWS -> LINES. --- src/main/scripts/init.d/S10export_terminfo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scripts/init.d/S10export_terminfo b/src/main/scripts/init.d/S10export_terminfo index b6a1b6b8..3a5d55f8 100644 --- a/src/main/scripts/init.d/S10export_terminfo +++ b/src/main/scripts/init.d/S10export_terminfo @@ -8,7 +8,7 @@ start() { then echo "TERMINFO=/usr/lib/terminfo/v/vt100" > $TERMINFO_PROFILE echo "COLUMNS=80" >> $TERMINFO_PROFILE - echo "ROWS=24" >> $TERMINFO_PROFILE + echo "LINES=24" >> $TERMINFO_PROFILE fi } From 7797c068fae10999738f26c8cc2779dfb1d2181d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sun, 9 Jan 2022 07:02:31 +0100 Subject: [PATCH 18/27] Update to Sedna 1.0.5. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 74f62134..2ceac374 100644 --- a/build.gradle +++ b/build.gradle @@ -58,7 +58,7 @@ repositories { dependencies { minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}" - implementation "li.cil.sedna:sedna-${minecraft_version}-forge:1.0.4" + implementation "li.cil.sedna:sedna-${minecraft_version}-forge:1.0.5" implementation fg.deobf("curse.maven:markdownmanual-502485:3565800") From 1e59043b01b78960eb7114cdb85597647931ff40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sun, 9 Jan 2022 09:13:05 +0100 Subject: [PATCH 19/27] Fixed potential crash when computer stops immediately while still initializing disk from image. --- .../item/AbstractBlockDeviceVMDevice.java | 24 ++++++++----- .../HardDriveVMDeviceWithInitialData.java | 34 ++++++++++++++----- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/main/java/li/cil/oc2/common/bus/device/item/AbstractBlockDeviceVMDevice.java b/src/main/java/li/cil/oc2/common/bus/device/item/AbstractBlockDeviceVMDevice.java index 3a6d5f38..77a0268b 100644 --- a/src/main/java/li/cil/oc2/common/bus/device/item/AbstractBlockDeviceVMDevice.java +++ b/src/main/java/li/cil/oc2/common/bus/device/item/AbstractBlockDeviceVMDevice.java @@ -89,19 +89,11 @@ public abstract class AbstractBlockDeviceVMDevice Date: Sun, 9 Jan 2022 09:53:37 +0100 Subject: [PATCH 20/27] Fixed devices not being updated when loading item list. Fix not applying loaded device data to existing devices. Fixes initial robot state on placement. --- .../common/bus/AbstractGroupingDeviceBusElement.java | 10 ++++++++++ .../oc2/common/container/DeviceItemStackHandler.java | 6 +++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/main/java/li/cil/oc2/common/bus/AbstractGroupingDeviceBusElement.java b/src/main/java/li/cil/oc2/common/bus/AbstractGroupingDeviceBusElement.java index d85a2bbd..36b21d19 100644 --- a/src/main/java/li/cil/oc2/common/bus/AbstractGroupingDeviceBusElement.java +++ b/src/main/java/li/cil/oc2/common/bus/AbstractGroupingDeviceBusElement.java @@ -78,6 +78,16 @@ public abstract class AbstractGroupingDeviceBusElement { + if (devicesTag.contains(key, NBTTagIds.TAG_COMPOUND)) { + entry.getDevice().deserializeNBT(devicesTag.getCompound(key)); + } + }); + } } } diff --git a/src/main/java/li/cil/oc2/common/container/DeviceItemStackHandler.java b/src/main/java/li/cil/oc2/common/container/DeviceItemStackHandler.java index 5d5f2dad..543384e1 100644 --- a/src/main/java/li/cil/oc2/common/container/DeviceItemStackHandler.java +++ b/src/main/java/li/cil/oc2/common/container/DeviceItemStackHandler.java @@ -55,13 +55,13 @@ public class DeviceItemStackHandler extends FixedSizeItemStackHandler { public void loadItems(final CompoundTag tag) { super.deserializeNBT(tag); + for (int slot = 0; slot < getSlots(); slot++) { + busElement.updateDevices(slot, getStackInSlot(slot)); + } } public void loadDevices(final CompoundTag tag) { busElement.load(tag); - for (int slot = 0; slot < getSlots(); slot++) { - busElement.updateDevices(slot, getStackInSlot(slot)); - } } @Override From eecee804e6cf4721c291175f58bfe27fb03a57b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sun, 9 Jan 2022 09:53:52 +0100 Subject: [PATCH 21/27] Update Sedna version in mods.toml. --- src/main/resources/META-INF/mods.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 522749eb..1d6a593c 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -11,7 +11,7 @@ authors = "Sangar" description = ''' Modern computers for the modern player. -This mod uses the Terminus Font under the Open Font License. +This mod uses the Terminus Font under the Open Font License. The full license can be found in the JAR of this mod. ''' [[dependencies.oc2]] @@ -29,7 +29,7 @@ side = "BOTH" [[dependencies.oc2]] modId = "sedna" mandatory = true -versionRange = "[1.0.0,)" +versionRange = "[1.0.5,)" ordering = "NONE" side = "BOTH" [[dependencies.oc2]] From 75559957994a4f1c865c1bc1ddd61af91fb1e0fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sun, 9 Jan 2022 09:54:00 +0100 Subject: [PATCH 22/27] Update Forge. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 5d664d7d..dc744546 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx3G org.gradle.daemon=false -forge_version=39.0.4 +forge_version=39.0.13 semver=0.0.0 From 8e714c3761834c6157dff564a6033abb174f3ace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sun, 9 Jan 2022 11:56:07 +0100 Subject: [PATCH 23/27] Update publish workflow. --- .github/workflows/publish.yml | 58 +++++++++++++++++++++++++++++------ build.gradle | 2 +- gradle.properties | 1 - 3 files changed, 49 insertions(+), 12 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 41b0b8b2..0de6a717 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -2,11 +2,10 @@ name: publish on: release: - types: [ published ] + types: [published] jobs: - build: - name: publish + publish-github: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -54,10 +53,49 @@ jobs: GITHUB_MAVEN_URL: 'https://maven.pkg.github.com/${{ github.repository }}' GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -# - name: Publish to Curseforge -# run: ./gradlew -Psemver='${{ steps.split_tag.outputs._1 }}' curseforge -# env: -# GPR_USER: ${{ secrets.GPR_USER }} -# GPR_KEY: ${{ secrets.GPR_KEY }} -# CURSEFORGE_API_KEY: ${{ secrets.CURSEFORGE_API_KEY }} -# CHANGELOG: ${{ github.event.release.body }} + publish-curse: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Extract Version from Tag + uses: rishabhgupta/split-by@v1 + id: split_tag + with: + string: ${{ github.event.release.tag_name }} + split-by: '/' + + - name: Set up JDK + uses: actions/setup-java@v1 + with: + java-version: 17 + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Validate Gradle wrapper + uses: gradle/wrapper-validation-action@v1 + - uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ubuntu-latest-gradle-${{ hashFiles('**/*.gradle*') }} + restore-keys: ubuntu-latest-gradle- + + # Set Curseforge release type based on pre-release flag. + - name: Set release type to 'release' + run: | + echo "CURSEFORGE_RELEASE_TYPE=release" >> $GITHUB_ENV + if: github.event.release.prerelease == false + - name: Set release type to 'alpha' + run: | + echo "CURSEFORGE_RELEASE_TYPE=alpha" >> $GITHUB_ENV + if: github.event.release.prerelease == true + + - name: Publish to Curseforge + run: ./gradlew -Psemver='${{ steps.split_tag.outputs._1 }}' curseforge + env: + GPR_USER: ${{ secrets.GPR_USER }} + GPR_KEY: ${{ secrets.GPR_KEY }} + CURSEFORGE_API_KEY: ${{ secrets.CURSEFORGE_API_KEY }} + CURSEFORGE_RELEASE_TYPE: ${{ env.CURSEFORGE_RELEASE_TYPE }} + CHANGELOG: ${{ github.event.release.body }} diff --git a/build.gradle b/build.gradle index 2ceac374..02c22a7b 100644 --- a/build.gradle +++ b/build.gradle @@ -184,7 +184,7 @@ curseforge { apiKey = System.getenv('CURSEFORGE_API_KEY') ?: "" project { id = curse_project_id - releaseType = curse_project_releaseType + releaseType = System.getenv('CURSEFORGE_RELEASE_TYPE') ?: "alpha" changelogType = 'markdown' changelog = System.getenv("CHANGELOG") ?: "Changelog not available." addGameVersion 'Forge' diff --git a/gradle.properties b/gradle.properties index dc744546..cc38aecf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,4 +8,3 @@ forge_version=39.0.13 semver=0.0.0 curse_project_id=437654 -curse_project_releaseType=alpha From 01dba403892de15fefbd15224d27f5dd6555fa8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sun, 9 Jan 2022 11:56:34 +0100 Subject: [PATCH 24/27] Mention interface renaming in scripting docs and vice versa. --- .../resources/assets/oc2/doc/en_us/block/bus_interface.md | 4 ++-- src/main/resources/assets/oc2/doc/en_us/scripting.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/resources/assets/oc2/doc/en_us/block/bus_interface.md b/src/main/resources/assets/oc2/doc/en_us/block/bus_interface.md index 88a17991..11cafbc8 100644 --- a/src/main/resources/assets/oc2/doc/en_us/block/bus_interface.md +++ b/src/main/resources/assets/oc2/doc/en_us/block/bus_interface.md @@ -3,6 +3,6 @@ Bus interfaces connect external devices to [computers](computer.md). This includes explicit device blocks, such as the [redstone interface](redstone_interface.md). Some generic functionality blocks of blocks is also available, such as information on energy storage. -It is possible to configure an explicit name for a bus interface using a [wrench](../item/wrench.md). This is useful when attaching multiple devices of the same type to a computer: when searching devices by name, these custom names will also work. +It is possible to configure an explicit name for a bus interface using a [wrench](../item/wrench.md). This is useful when attaching multiple devices of the same type to a computer: when searching devices by type name (`devices:find(typeName)`), these custom names will also work. -Note that [computers](computer.md) must also be explicitly connected to a [bus](bus_cable.md) with a bus connector. \ No newline at end of file +Note that [computers](computer.md) must also be explicitly connected to a [bus](bus_cable.md) with a bus connector. diff --git a/src/main/resources/assets/oc2/doc/en_us/scripting.md b/src/main/resources/assets/oc2/doc/en_us/scripting.md index dd01f9d9..45523054 100644 --- a/src/main/resources/assets/oc2/doc/en_us/scripting.md +++ b/src/main/resources/assets/oc2/doc/en_us/scripting.md @@ -14,7 +14,7 @@ To use the `devices` library, import it using `require("devices")`. - `id` is the unique identifier of the device. - Returns a wrapper for the specified device. -`find(typeName):Device` returns a wrapper for a device of the specified type. If there are many devices of this type, it is undefined which one will be returned. +`find(typeName):Device` returns a wrapper for a device of the specified type. If there are many devices of this type, it is undefined which one will be returned. Aliases set in [Bus Interfaces](block/bus_interface.md) may also be used. - `typeName` is the device type for which to find a device. - Returns a wrapper for a device of the specified device. @@ -72,4 +72,4 @@ The lamp should now light up! ![Lit redstone lamp](../img/scripting_lamp.png) -With this, you have the tools to learn the names of connected devices, which methods they offer and how to obtain their documentation. Go ahead and experiment with the other methods of the redstone interface to read incoming redstone signals, or try out other devices! \ No newline at end of file +With this, you have the tools to learn the names of connected devices, which methods they offer and how to obtain their documentation. Go ahead and experiment with the other methods of the redstone interface to read incoming redstone signals, or try out other devices! From bfb4798332fd8bd5e05267e70369f7f2ab832b1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sun, 9 Jan 2022 11:57:43 +0100 Subject: [PATCH 25/27] Renamed RobotEntity -> Robot to follow mapping style. --- .../client/renderer/entity/RobotRenderer.java | 10 +++--- .../renderer/entity/model/RobotModel.java | 8 ++--- .../container/AbstractRobotContainer.java | 8 ++--- .../container/RobotInventoryContainer.java | 8 ++--- .../container/RobotTerminalContainer.java | 8 ++--- .../li/cil/oc2/common/entity/Entities.java | 2 +- .../entity/{RobotEntity.java => Robot.java} | 35 +++++++++---------- .../entity/robot/AbstractRobotAction.java | 6 ++-- .../entity/robot/AbstractRobotActionType.java | 8 ++--- .../oc2/common/entity/robot/RobotActions.java | 8 ++--- .../entity/robot/RobotMovementAction.java | 18 +++++----- .../entity/robot/RobotMovementActionType.java | 14 ++++---- .../entity/robot/RobotRotationAction.java | 10 +++--- .../entity/robot/RobotRotationActionType.java | 14 ++++---- .../li/cil/oc2/common/item/RobotItem.java | 4 +-- .../message/OpenRobotInventoryMessage.java | 6 ++-- .../message/OpenRobotTerminalMessage.java | 6 ++-- .../message/RobotBootErrorMessage.java | 6 ++-- .../network/message/RobotBusStateMessage.java | 6 ++-- .../message/RobotInitializationMessage.java | 6 ++-- .../RobotInitializationRequestMessage.java | 6 ++-- .../network/message/RobotPowerMessage.java | 6 ++-- .../network/message/RobotRunStateMessage.java | 6 ++-- .../message/RobotTerminalInputMessage.java | 6 ++-- .../message/RobotTerminalOutputMessage.java | 6 ++-- 25 files changed, 110 insertions(+), 111 deletions(-) rename src/main/java/li/cil/oc2/common/entity/{RobotEntity.java => Robot.java} (96%) diff --git a/src/main/java/li/cil/oc2/client/renderer/entity/RobotRenderer.java b/src/main/java/li/cil/oc2/client/renderer/entity/RobotRenderer.java index d237f981..f81096cc 100644 --- a/src/main/java/li/cil/oc2/client/renderer/entity/RobotRenderer.java +++ b/src/main/java/li/cil/oc2/client/renderer/entity/RobotRenderer.java @@ -4,7 +4,7 @@ import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.math.Vector3f; import li.cil.oc2.client.renderer.entity.model.RobotModel; -import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.entity.Robot; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.entity.EntityRenderer; import net.minecraft.client.renderer.entity.EntityRendererProvider; @@ -12,7 +12,7 @@ import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; -public final class RobotRenderer extends EntityRenderer { +public final class RobotRenderer extends EntityRenderer { private final RobotModel model; /////////////////////////////////////////////////////////////////// @@ -25,13 +25,13 @@ public final class RobotRenderer extends EntityRenderer { /////////////////////////////////////////////////////////////////// @Override - public ResourceLocation getTextureLocation(final RobotEntity entity) { + public ResourceLocation getTextureLocation(final Robot entity) { return RobotModel.ROBOT_ENTITY_TEXTURE; } @Override - public void render(final RobotEntity entity, final float entityYaw, final float partialTicks, final PoseStack stack, final MultiBufferSource bufferSource, final int packedLight) { - final RobotEntity.AnimationState state = entity.getAnimationState(); + public void render(final Robot entity, final float entityYaw, final float partialTicks, final PoseStack stack, final MultiBufferSource bufferSource, final int packedLight) { + final Robot.AnimationState state = entity.getAnimationState(); state.update(partialTicks, entity.level.random); stack.pushPose(); diff --git a/src/main/java/li/cil/oc2/client/renderer/entity/model/RobotModel.java b/src/main/java/li/cil/oc2/client/renderer/entity/model/RobotModel.java index 0e690333..a3d736d8 100644 --- a/src/main/java/li/cil/oc2/client/renderer/entity/model/RobotModel.java +++ b/src/main/java/li/cil/oc2/client/renderer/entity/model/RobotModel.java @@ -3,7 +3,7 @@ package li.cil.oc2.client.renderer.entity.model; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import li.cil.oc2.api.API; -import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.entity.Robot; import net.minecraft.client.model.EntityModel; import net.minecraft.client.model.geom.ModelLayerLocation; import net.minecraft.client.model.geom.ModelPart; @@ -16,7 +16,7 @@ import net.minecraft.client.renderer.LightTexture; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.common.model.TransformationHelper; -public final class RobotModel extends EntityModel { +public final class RobotModel extends EntityModel { public static final ModelLayerLocation ROBOT_MODEL_LAYER = new ModelLayerLocation(new ResourceLocation(API.MOD_ID, "robot"), "main"); public static final ResourceLocation ROBOT_ENTITY_TEXTURE = new ResourceLocation(API.MOD_ID, "textures/entity/robot/robot.png"); @@ -57,8 +57,8 @@ public final class RobotModel extends EntityModel { /////////////////////////////////////////////////////////////////// @Override - public void setupAnim(final RobotEntity entity, final float limbSwing, final float limbSwingAmount, final float ageInTicks, final float netHeadYaw, final float headPitch) { - final RobotEntity.AnimationState state = entity.getAnimationState(); + public void setupAnim(final Robot entity, final float limbSwing, final float limbSwingAmount, final float ageInTicks, final float netHeadYaw, final float headPitch) { + final Robot.AnimationState state = entity.getAnimationState(); baseY = state.baseRenderOffsetY; topY = state.topRenderOffsetY; topRotation[1] = state.topRenderRotationY; diff --git a/src/main/java/li/cil/oc2/common/container/AbstractRobotContainer.java b/src/main/java/li/cil/oc2/common/container/AbstractRobotContainer.java index 875130cc..70890118 100644 --- a/src/main/java/li/cil/oc2/common/container/AbstractRobotContainer.java +++ b/src/main/java/li/cil/oc2/common/container/AbstractRobotContainer.java @@ -2,7 +2,7 @@ package li.cil.oc2.common.container; import li.cil.oc2.common.bus.CommonDeviceBusController; import li.cil.oc2.common.energy.FixedEnergyStorage; -import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.entity.Robot; import li.cil.oc2.common.network.Network; import li.cil.oc2.common.network.message.OpenRobotInventoryMessage; import li.cil.oc2.common.network.message.OpenRobotTerminalMessage; @@ -16,11 +16,11 @@ import net.minecraft.world.inventory.MenuType; import java.nio.ByteBuffer; public abstract class AbstractRobotContainer extends AbstractMachineTerminalContainer { - private final RobotEntity robot; + private final Robot robot; /////////////////////////////////////////////////////////////////// - public AbstractRobotContainer(final MenuType type, final int id, final Player player, final RobotEntity robot, final IntPrecisionContainerData energyInfo) { + public AbstractRobotContainer(final MenuType type, final int id, final Player player, final Robot robot, final IntPrecisionContainerData energyInfo) { super(type, id, energyInfo); this.robot = robot; @@ -39,7 +39,7 @@ public abstract class AbstractRobotContainer extends AbstractMachineTerminalCont Network.INSTANCE.sendToServer(new OpenRobotTerminalMessage(robot)); } - public RobotEntity getRobot() { + public Robot getRobot() { return robot; } diff --git a/src/main/java/li/cil/oc2/common/container/RobotInventoryContainer.java b/src/main/java/li/cil/oc2/common/container/RobotInventoryContainer.java index d9369ea8..2f7f8ec2 100644 --- a/src/main/java/li/cil/oc2/common/container/RobotInventoryContainer.java +++ b/src/main/java/li/cil/oc2/common/container/RobotInventoryContainer.java @@ -3,7 +3,7 @@ package li.cil.oc2.common.container; import li.cil.oc2.api.bus.device.DeviceTypes; import li.cil.oc2.common.bus.CommonDeviceBusController; import li.cil.oc2.common.energy.FixedEnergyStorage; -import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.entity.Robot; import li.cil.oc2.common.vm.VMItemStackHandlers; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.chat.Component; @@ -18,7 +18,7 @@ import net.minecraftforge.items.SlotItemHandler; import net.minecraftforge.network.NetworkHooks; public final class RobotInventoryContainer extends AbstractRobotContainer { - public static void createServer(final RobotEntity robot, final FixedEnergyStorage energy, final CommonDeviceBusController busController, final ServerPlayer player) { + public static void createServer(final Robot robot, final FixedEnergyStorage energy, final CommonDeviceBusController busController, final ServerPlayer player) { NetworkHooks.openGui(player, new MenuProvider() { @Override public Component getDisplayName() { @@ -35,7 +35,7 @@ public final class RobotInventoryContainer extends AbstractRobotContainer { public static RobotInventoryContainer createClient(final int id, final Inventory inventory, final FriendlyByteBuf data) { final int entityId = data.readVarInt(); final Entity entity = inventory.player.level.getEntity(entityId); - if (entity instanceof final RobotEntity robot) { + if (entity instanceof final Robot robot) { return new RobotInventoryContainer(id, robot, inventory.player, createEnergyInfo()); } @@ -44,7 +44,7 @@ public final class RobotInventoryContainer extends AbstractRobotContainer { /////////////////////////////////////////////////////////////////// - private RobotInventoryContainer(final int id, final RobotEntity robot, final Player player, final IntPrecisionContainerData energyInfo) { + private RobotInventoryContainer(final int id, final Robot robot, final Player player, final IntPrecisionContainerData energyInfo) { super(Containers.ROBOT.get(), id, player, robot, energyInfo); final VMItemStackHandlers handlers = robot.getItemStackHandlers(); diff --git a/src/main/java/li/cil/oc2/common/container/RobotTerminalContainer.java b/src/main/java/li/cil/oc2/common/container/RobotTerminalContainer.java index 70d00b06..d8512683 100644 --- a/src/main/java/li/cil/oc2/common/container/RobotTerminalContainer.java +++ b/src/main/java/li/cil/oc2/common/container/RobotTerminalContainer.java @@ -3,7 +3,7 @@ package li.cil.oc2.common.container; import li.cil.oc2.client.gui.Sprites; import li.cil.oc2.common.bus.CommonDeviceBusController; import li.cil.oc2.common.energy.FixedEnergyStorage; -import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.entity.Robot; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; @@ -17,7 +17,7 @@ import net.minecraftforge.items.SlotItemHandler; import net.minecraftforge.network.NetworkHooks; public final class RobotTerminalContainer extends AbstractRobotContainer { - public static void createServer(final RobotEntity robot, final FixedEnergyStorage energy, final CommonDeviceBusController busController, final ServerPlayer player) { + public static void createServer(final Robot robot, final FixedEnergyStorage energy, final CommonDeviceBusController busController, final ServerPlayer player) { NetworkHooks.openGui(player, new MenuProvider() { @Override public Component getDisplayName() { @@ -34,7 +34,7 @@ public final class RobotTerminalContainer extends AbstractRobotContainer { public static RobotTerminalContainer createClient(final int id, final Inventory inventory, final FriendlyByteBuf data) { final int entityId = data.readVarInt(); final Entity entity = inventory.player.level.getEntity(entityId); - if (entity instanceof final RobotEntity robot) { + if (entity instanceof final Robot robot) { return new RobotTerminalContainer(id, inventory.player, robot, createEnergyInfo()); } @@ -43,7 +43,7 @@ public final class RobotTerminalContainer extends AbstractRobotContainer { /////////////////////////////////////////////////////////////////// - private RobotTerminalContainer(final int id, final Player player, final RobotEntity robot, final IntPrecisionContainerData energyInfo) { + private RobotTerminalContainer(final int id, final Player player, final Robot robot, final IntPrecisionContainerData energyInfo) { super(Containers.ROBOT_TERMINAL.get(), id, player, robot, energyInfo); // It's kinda dumb we need to access technically-client-side stuff here, but that's the nature of containers diff --git a/src/main/java/li/cil/oc2/common/entity/Entities.java b/src/main/java/li/cil/oc2/common/entity/Entities.java index 48e2c8bf..0b2b7310 100644 --- a/src/main/java/li/cil/oc2/common/entity/Entities.java +++ b/src/main/java/li/cil/oc2/common/entity/Entities.java @@ -15,7 +15,7 @@ public final class Entities { /////////////////////////////////////////////////////////////////// - public static final RegistryObject> ROBOT = register("robot", RobotEntity::new, MobCategory.MISC, b -> b.sized(14f / 16f, 14f / 16f).fireImmune().noSummon()); + public static final RegistryObject> ROBOT = register("robot", Robot::new, MobCategory.MISC, b -> b.sized(14f / 16f, 14f / 16f).fireImmune().noSummon()); /////////////////////////////////////////////////////////////////// diff --git a/src/main/java/li/cil/oc2/common/entity/RobotEntity.java b/src/main/java/li/cil/oc2/common/entity/Robot.java similarity index 96% rename from src/main/java/li/cil/oc2/common/entity/RobotEntity.java rename to src/main/java/li/cil/oc2/common/entity/Robot.java index 73daa1cb..860b5846 100644 --- a/src/main/java/li/cil/oc2/common/entity/RobotEntity.java +++ b/src/main/java/li/cil/oc2/common/entity/Robot.java @@ -7,7 +7,6 @@ import li.cil.oc2.api.bus.device.object.Callback; import li.cil.oc2.api.bus.device.object.ObjectDevice; import li.cil.oc2.api.bus.device.object.Parameter; import li.cil.oc2.api.bus.device.provider.ItemDeviceQuery; -import li.cil.oc2.api.capabilities.Robot; import li.cil.oc2.api.capabilities.TerminalUserProvider; import li.cil.oc2.common.Config; import li.cil.oc2.common.bus.AbstractDeviceBusElement; @@ -83,10 +82,10 @@ import java.util.function.Consumer; import static java.util.Collections.singleton; import static li.cil.oc2.common.Constants.*; -public final class RobotEntity extends Entity implements Robot, TerminalUserProvider { - public static final EntityDataAccessor TARGET_POSITION = SynchedEntityData.defineId(RobotEntity.class, EntityDataSerializers.BLOCK_POS); - public static final EntityDataAccessor TARGET_DIRECTION = SynchedEntityData.defineId(RobotEntity.class, EntityDataSerializers.DIRECTION); - public static final EntityDataAccessor SELECTED_SLOT = SynchedEntityData.defineId(RobotEntity.class, EntityDataSerializers.BYTE); +public final class Robot extends Entity implements li.cil.oc2.api.capabilities.Robot, TerminalUserProvider { + public static final EntityDataAccessor TARGET_POSITION = SynchedEntityData.defineId(Robot.class, EntityDataSerializers.BLOCK_POS); + public static final EntityDataAccessor TARGET_DIRECTION = SynchedEntityData.defineId(Robot.class, EntityDataSerializers.DIRECTION); + public static final EntityDataAccessor SELECTED_SLOT = SynchedEntityData.defineId(Robot.class, EntityDataSerializers.BYTE); private static final String TERMINAL_TAG_NAME = "terminal"; private static final String STATE_TAG_NAME = "state"; @@ -124,7 +123,7 @@ public final class RobotEntity extends Entity implements Robot, TerminalUserProv /////////////////////////////////////////////////////////////////// - public RobotEntity(final EntityType type, final Level world) { + public Robot(final EntityType type, final Level world) { super(type, world); this.blocksBuilding = true; setNoGravity(true); @@ -614,10 +613,10 @@ public final class RobotEntity extends Entity implements Robot, TerminalUserProv public void tick() { if (level.isClientSide()) { - RobotActions.performClient(RobotEntity.this); + RobotActions.performClient(Robot.this); } else { if (action != null) { - final RobotActionResult result = action.perform(RobotEntity.this); + final RobotActionResult result = action.perform(Robot.this); if (result != RobotActionResult.INCOMPLETE) { synchronized (results) { if (results.size() == MAX_QUEUED_RESULTS) { @@ -633,10 +632,10 @@ public final class RobotEntity extends Entity implements Robot, TerminalUserProv if (action == null) { action = queue.poll(); if (action != null) { - action.initialize(RobotEntity.this); + action.initialize(Robot.this); } } - RobotActions.performServer(RobotEntity.this, action); + RobotActions.performServer(Robot.this, action); } } @@ -729,7 +728,7 @@ public final class RobotEntity extends Entity implements Robot, TerminalUserProv @Override protected ItemDeviceQuery getDeviceQuery(final ItemStack stack) { - return Devices.makeQuery(RobotEntity.this, stack); + return Devices.makeQuery(Robot.this, stack); } @Override @@ -782,7 +781,7 @@ public final class RobotEntity extends Entity implements Robot, TerminalUserProv @Override protected void sendTerminalUpdateToClient(final ByteBuffer output) { - Network.sendToClientsTrackingEntity(new RobotTerminalOutputMessage(RobotEntity.this, output), RobotEntity.this); + Network.sendToClientsTrackingEntity(new RobotTerminalOutputMessage(Robot.this, output), Robot.this); } } @@ -811,7 +810,7 @@ public final class RobotEntity extends Entity implements Robot, TerminalUserProv super.stopRunnerAndReset(); TerminalUtils.resetTerminal(terminal, output -> Network.sendToClientsTrackingEntity( - new RobotTerminalOutputMessage(RobotEntity.this, output), RobotEntity.this)); + new RobotTerminalOutputMessage(Robot.this, output), Robot.this)); actionProcessor.clear(); } @@ -823,17 +822,17 @@ public final class RobotEntity extends Entity implements Robot, TerminalUserProv @Override protected void handleBusStateChanged(final CommonDeviceBusController.BusState value) { - Network.sendToClientsTrackingEntity(new RobotBusStateMessage(RobotEntity.this), RobotEntity.this); + Network.sendToClientsTrackingEntity(new RobotBusStateMessage(Robot.this), Robot.this); } @Override protected void handleRunStateChanged(final VMRunState value) { - Network.sendToClientsTrackingEntity(new RobotRunStateMessage(RobotEntity.this), RobotEntity.this); + Network.sendToClientsTrackingEntity(new RobotRunStateMessage(Robot.this), Robot.this); } @Override protected void handleBootErrorChanged(@Nullable final Component value) { - Network.sendToClientsTrackingEntity(new RobotBootErrorMessage(RobotEntity.this), RobotEntity.this); + Network.sendToClientsTrackingEntity(new RobotBootErrorMessage(Robot.this), Robot.this); } } @@ -850,12 +849,12 @@ public final class RobotEntity extends Entity implements Robot, TerminalUserProv @Callback(synchronize = false) public int getSelectedSlot() { - return RobotEntity.this.getSelectedSlot(); + return Robot.this.getSelectedSlot(); } @Callback(synchronize = false) public void setSelectedSlot(@Parameter("slot") final int slot) { - RobotEntity.this.setSelectedSlot(slot); + Robot.this.setSelectedSlot(slot); } @Callback diff --git a/src/main/java/li/cil/oc2/common/entity/robot/AbstractRobotAction.java b/src/main/java/li/cil/oc2/common/entity/robot/AbstractRobotAction.java index 10a50fd6..48863468 100644 --- a/src/main/java/li/cil/oc2/common/entity/robot/AbstractRobotAction.java +++ b/src/main/java/li/cil/oc2/common/entity/robot/AbstractRobotAction.java @@ -1,6 +1,6 @@ package li.cil.oc2.common.entity.robot; -import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.entity.Robot; import net.minecraft.nbt.CompoundTag; public abstract class AbstractRobotAction { @@ -36,10 +36,10 @@ public abstract class AbstractRobotAction { id = value; } - public void initialize(final RobotEntity robot) { + public void initialize(final Robot robot) { } - public abstract RobotActionResult perform(RobotEntity robot); + public abstract RobotActionResult perform(Robot robot); public CompoundTag serialize() { final CompoundTag tag = new CompoundTag(); diff --git a/src/main/java/li/cil/oc2/common/entity/robot/AbstractRobotActionType.java b/src/main/java/li/cil/oc2/common/entity/robot/AbstractRobotActionType.java index c3b04511..492d9b84 100644 --- a/src/main/java/li/cil/oc2/common/entity/robot/AbstractRobotActionType.java +++ b/src/main/java/li/cil/oc2/common/entity/robot/AbstractRobotActionType.java @@ -1,6 +1,6 @@ package li.cil.oc2.common.entity.robot; -import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.entity.Robot; import net.minecraft.nbt.CompoundTag; public abstract class AbstractRobotActionType { @@ -18,13 +18,13 @@ public abstract class AbstractRobotActionType { return id; } - public void initializeData(final RobotEntity robot) { + public void initializeData(final Robot robot) { } - public void performServer(final RobotEntity robot, final AbstractRobotAction currentAction) { + public void performServer(final Robot robot, final AbstractRobotAction currentAction) { } - public void performClient(final RobotEntity robot) { + public void performClient(final Robot robot) { } public abstract AbstractRobotAction deserialize(final CompoundTag tag); diff --git a/src/main/java/li/cil/oc2/common/entity/robot/RobotActions.java b/src/main/java/li/cil/oc2/common/entity/robot/RobotActions.java index 982b5f12..8dbd0e1a 100644 --- a/src/main/java/li/cil/oc2/common/entity/robot/RobotActions.java +++ b/src/main/java/li/cil/oc2/common/entity/robot/RobotActions.java @@ -1,6 +1,6 @@ package li.cil.oc2.common.entity.robot; -import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.entity.Robot; import net.minecraft.nbt.CompoundTag; import javax.annotation.Nullable; @@ -19,19 +19,19 @@ public final class RobotActions { /////////////////////////////////////////////////////////////////// - public static void initializeData(final RobotEntity robot) { + public static void initializeData(final Robot robot) { for (final AbstractRobotActionType type : ACTIONS) { type.initializeData(robot); } } - public static void performServer(final RobotEntity robot, final AbstractRobotAction currentAction) { + public static void performServer(final Robot robot, final AbstractRobotAction currentAction) { for (final AbstractRobotActionType type : ACTIONS) { type.performServer(robot, currentAction); } } - public static void performClient(final RobotEntity robot) { + public static void performClient(final Robot robot) { for (final AbstractRobotActionType type : ACTIONS) { type.performClient(robot); } diff --git a/src/main/java/li/cil/oc2/common/entity/robot/RobotMovementAction.java b/src/main/java/li/cil/oc2/common/entity/robot/RobotMovementAction.java index c52ba967..e505e64f 100644 --- a/src/main/java/li/cil/oc2/common/entity/robot/RobotMovementAction.java +++ b/src/main/java/li/cil/oc2/common/entity/robot/RobotMovementAction.java @@ -2,7 +2,7 @@ package li.cil.oc2.common.entity.robot; import li.cil.oc2.common.Constants; import li.cil.oc2.common.entity.Entities; -import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.entity.Robot; import li.cil.oc2.common.util.NBTTagIds; import li.cil.oc2.common.util.NBTUtils; import net.minecraft.core.BlockPos; @@ -53,7 +53,7 @@ public final class RobotMovementAction extends AbstractRobotAction { return Vec3.atBottomCenterOf(position).add(0, 0.5f * (1 - Entities.ROBOT.get().getHeight()), 0); } - public static void moveTowards(final RobotEntity robot, final Vec3 targetPosition) { + public static void moveTowards(final Robot robot, final Vec3 targetPosition) { Vec3 delta = targetPosition.subtract(robot.position()); if (delta.lengthSqr() > MOVEMENT_SPEED * MOVEMENT_SPEED) { delta = delta.normalize().scale(MOVEMENT_SPEED); @@ -65,7 +65,7 @@ public final class RobotMovementAction extends AbstractRobotAction { /////////////////////////////////////////////////////////////////// @Override - public void initialize(final RobotEntity robot) { + public void initialize(final Robot robot) { if (origin == null || start == null || target == null) { origin = robot.blockPosition(); start = origin; @@ -81,11 +81,11 @@ public final class RobotMovementAction extends AbstractRobotAction { } targetPos = getTargetPositionInBlock(target); - robot.getEntityData().set(RobotEntity.TARGET_POSITION, target); + robot.getEntityData().set(Robot.TARGET_POSITION, target); } @Override - public RobotActionResult perform(final RobotEntity robot) { + public RobotActionResult perform(final Robot robot) { if (targetPos == null) { throw new IllegalStateException(); } @@ -142,7 +142,7 @@ public final class RobotMovementAction extends AbstractRobotAction { } } - private void moveAndResolveCollisions(final RobotEntity robot) { + private void moveAndResolveCollisions(final Robot robot) { if (start == null || target == null || targetPos == null) { return; } @@ -157,11 +157,11 @@ public final class RobotMovementAction extends AbstractRobotAction { target = start; start = newStart; targetPos = getTargetPositionInBlock(target); - robot.getEntityData().set(RobotEntity.TARGET_POSITION, target); + robot.getEntityData().set(Robot.TARGET_POSITION, target); } } - private void validateTarget(final RobotEntity robot) { + private void validateTarget(final Robot robot) { final BlockPos currentPosition = robot.blockPosition(); if (start == null || Objects.equals(currentPosition, start) || target == null || Objects.equals(currentPosition, target)) { @@ -184,6 +184,6 @@ public final class RobotMovementAction extends AbstractRobotAction { } targetPos = getTargetPositionInBlock(target); - robot.getEntityData().set(RobotEntity.TARGET_POSITION, target); + robot.getEntityData().set(Robot.TARGET_POSITION, target); } } diff --git a/src/main/java/li/cil/oc2/common/entity/robot/RobotMovementActionType.java b/src/main/java/li/cil/oc2/common/entity/robot/RobotMovementActionType.java index a10af21b..4bb9180f 100644 --- a/src/main/java/li/cil/oc2/common/entity/robot/RobotMovementActionType.java +++ b/src/main/java/li/cil/oc2/common/entity/robot/RobotMovementActionType.java @@ -1,6 +1,6 @@ package li.cil.oc2.common.entity.robot; -import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.entity.Robot; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.phys.Vec3; @@ -12,20 +12,20 @@ public final class RobotMovementActionType extends AbstractRobotActionType { /////////////////////////////////////////////////////////////////// @Override - public void initializeData(final RobotEntity robot) { - robot.getEntityData().set(RobotEntity.TARGET_POSITION, robot.blockPosition()); + public void initializeData(final Robot robot) { + robot.getEntityData().set(Robot.TARGET_POSITION, robot.blockPosition()); } @Override - public void performServer(final RobotEntity robot, final AbstractRobotAction currentAction) { + public void performServer(final Robot robot, final AbstractRobotAction currentAction) { if (!(currentAction instanceof RobotMovementAction)) { - robot.getEntityData().set(RobotEntity.TARGET_POSITION, robot.blockPosition()); + robot.getEntityData().set(Robot.TARGET_POSITION, robot.blockPosition()); } } @Override - public void performClient(final RobotEntity robot) { - final Vec3 target = RobotMovementAction.getTargetPositionInBlock(robot.getEntityData().get(RobotEntity.TARGET_POSITION)); + public void performClient(final Robot robot) { + final Vec3 target = RobotMovementAction.getTargetPositionInBlock(robot.getEntityData().get(Robot.TARGET_POSITION)); if (robot.position().distanceToSqr(target) > RobotMovementAction.TARGET_EPSILON) { RobotMovementAction.moveTowards(robot, target); } diff --git a/src/main/java/li/cil/oc2/common/entity/robot/RobotRotationAction.java b/src/main/java/li/cil/oc2/common/entity/robot/RobotRotationAction.java index fc755670..0cdd8bc5 100644 --- a/src/main/java/li/cil/oc2/common/entity/robot/RobotRotationAction.java +++ b/src/main/java/li/cil/oc2/common/entity/robot/RobotRotationAction.java @@ -1,7 +1,7 @@ package li.cil.oc2.common.entity.robot; import li.cil.oc2.common.Constants; -import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.entity.Robot; import li.cil.oc2.common.util.NBTTagIds; import li.cil.oc2.common.util.NBTUtils; import net.minecraft.core.Direction; @@ -39,14 +39,14 @@ public final class RobotRotationAction extends AbstractRobotAction { /////////////////////////////////////////////////////////////////// - public static void rotateTowards(final RobotEntity robot, final Direction targetRotation) { + public static void rotateTowards(final Robot robot, final Direction targetRotation) { robot.setYRot(Mth.approachDegrees(robot.getYRot(), targetRotation.toYRot(), ROTATION_SPEED)); } /////////////////////////////////////////////////////////////////// @Override - public void initialize(final RobotEntity robot) { + public void initialize(final Robot robot) { if (target == null) { target = robot.getDirection(); if (direction != null) { @@ -57,11 +57,11 @@ public final class RobotRotationAction extends AbstractRobotAction { } } - robot.getEntityData().set(RobotEntity.TARGET_DIRECTION, target); + robot.getEntityData().set(Robot.TARGET_DIRECTION, target); } @Override - public RobotActionResult perform(final RobotEntity robot) { + public RobotActionResult perform(final Robot robot) { if (target == null) { throw new IllegalStateException(); } diff --git a/src/main/java/li/cil/oc2/common/entity/robot/RobotRotationActionType.java b/src/main/java/li/cil/oc2/common/entity/robot/RobotRotationActionType.java index b4c1bab4..dadff814 100644 --- a/src/main/java/li/cil/oc2/common/entity/robot/RobotRotationActionType.java +++ b/src/main/java/li/cil/oc2/common/entity/robot/RobotRotationActionType.java @@ -1,6 +1,6 @@ package li.cil.oc2.common.entity.robot; -import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.entity.Robot; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.util.Mth; @@ -13,20 +13,20 @@ public final class RobotRotationActionType extends AbstractRobotActionType { /////////////////////////////////////////////////////////////////// @Override - public void initializeData(final RobotEntity robot) { - robot.getEntityData().set(RobotEntity.TARGET_DIRECTION, robot.getDirection()); + public void initializeData(final Robot robot) { + robot.getEntityData().set(Robot.TARGET_DIRECTION, robot.getDirection()); } @Override - public void performServer(final RobotEntity robot, final AbstractRobotAction currentAction) { + public void performServer(final Robot robot, final AbstractRobotAction currentAction) { if (!(currentAction instanceof RobotRotationAction)) { - robot.getEntityData().set(RobotEntity.TARGET_DIRECTION, robot.getDirection()); + robot.getEntityData().set(Robot.TARGET_DIRECTION, robot.getDirection()); } } @Override - public void performClient(final RobotEntity robot) { - final Direction target = robot.getEntityData().get(RobotEntity.TARGET_DIRECTION); + public void performClient(final Robot robot) { + final Direction target = robot.getEntityData().get(Robot.TARGET_DIRECTION); if (Mth.degreesDifferenceAbs(robot.getYRot(), target.toYRot()) > RobotRotationAction.TARGET_EPSILON) { RobotRotationAction.rotateTowards(robot, target); } diff --git a/src/main/java/li/cil/oc2/common/item/RobotItem.java b/src/main/java/li/cil/oc2/common/item/RobotItem.java index 69d0aa63..874057cc 100644 --- a/src/main/java/li/cil/oc2/common/item/RobotItem.java +++ b/src/main/java/li/cil/oc2/common/item/RobotItem.java @@ -6,7 +6,7 @@ import li.cil.oc2.client.renderer.entity.RobotWithoutLevelRenderer; import li.cil.oc2.common.Config; import li.cil.oc2.common.energy.EnergyStorageItemStack; import li.cil.oc2.common.entity.Entities; -import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.entity.Robot; import li.cil.oc2.common.entity.robot.RobotActions; import li.cil.oc2.common.util.LevelUtils; import li.cil.oc2.common.util.NBTUtils; @@ -77,7 +77,7 @@ public final class RobotItem extends ModItem { position = Vec3.atCenterOf(pos.relative(context.getClickedFace())); } - final RobotEntity robot = Entities.ROBOT.get().create(context.getLevel()); + final Robot robot = Entities.ROBOT.get().create(context.getLevel()); if (robot == null) { return InteractionResult.FAIL; } diff --git a/src/main/java/li/cil/oc2/common/network/message/OpenRobotInventoryMessage.java b/src/main/java/li/cil/oc2/common/network/message/OpenRobotInventoryMessage.java index b2427900..9fd32b78 100644 --- a/src/main/java/li/cil/oc2/common/network/message/OpenRobotInventoryMessage.java +++ b/src/main/java/li/cil/oc2/common/network/message/OpenRobotInventoryMessage.java @@ -1,6 +1,6 @@ package li.cil.oc2.common.network.message; -import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.entity.Robot; import li.cil.oc2.common.network.MessageUtils; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerPlayer; @@ -11,7 +11,7 @@ public final class OpenRobotInventoryMessage extends AbstractMessage { /////////////////////////////////////////////////////////////////// - public OpenRobotInventoryMessage(final RobotEntity robot) { + public OpenRobotInventoryMessage(final Robot robot) { this.entityId = robot.getId(); } @@ -37,7 +37,7 @@ public final class OpenRobotInventoryMessage extends AbstractMessage { protected void handleMessage(final NetworkEvent.Context context) { final ServerPlayer player = context.getSender(); if (player != null) { - MessageUtils.withNearbyServerEntity(context, entityId, RobotEntity.class, + MessageUtils.withNearbyServerEntity(context, entityId, Robot.class, robot -> robot.openInventoryScreen(player)); } } diff --git a/src/main/java/li/cil/oc2/common/network/message/OpenRobotTerminalMessage.java b/src/main/java/li/cil/oc2/common/network/message/OpenRobotTerminalMessage.java index 4b02efb9..030daa2f 100644 --- a/src/main/java/li/cil/oc2/common/network/message/OpenRobotTerminalMessage.java +++ b/src/main/java/li/cil/oc2/common/network/message/OpenRobotTerminalMessage.java @@ -1,6 +1,6 @@ package li.cil.oc2.common.network.message; -import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.entity.Robot; import li.cil.oc2.common.network.MessageUtils; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerPlayer; @@ -11,7 +11,7 @@ public final class OpenRobotTerminalMessage extends AbstractMessage { /////////////////////////////////////////////////////////////////// - public OpenRobotTerminalMessage(final RobotEntity robot) { + public OpenRobotTerminalMessage(final Robot robot) { this.entityId = robot.getId(); } @@ -38,7 +38,7 @@ public final class OpenRobotTerminalMessage extends AbstractMessage { protected void handleMessage(final NetworkEvent.Context context) { final ServerPlayer player = context.getSender(); if (player != null) { - MessageUtils.withNearbyServerEntity(context, entityId, RobotEntity.class, + MessageUtils.withNearbyServerEntity(context, entityId, Robot.class, robot -> robot.openTerminalScreen(player)); } } diff --git a/src/main/java/li/cil/oc2/common/network/message/RobotBootErrorMessage.java b/src/main/java/li/cil/oc2/common/network/message/RobotBootErrorMessage.java index ec4c9a28..e74eaec6 100644 --- a/src/main/java/li/cil/oc2/common/network/message/RobotBootErrorMessage.java +++ b/src/main/java/li/cil/oc2/common/network/message/RobotBootErrorMessage.java @@ -1,6 +1,6 @@ package li.cil.oc2.common.network.message; -import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.entity.Robot; import li.cil.oc2.common.network.MessageUtils; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.chat.Component; @@ -12,7 +12,7 @@ public final class RobotBootErrorMessage extends AbstractMessage { /////////////////////////////////////////////////////////////////// - public RobotBootErrorMessage(final RobotEntity robot) { + public RobotBootErrorMessage(final Robot robot) { this.entityId = robot.getId(); this.value = robot.getVirtualMachine().getBootError(); } @@ -39,7 +39,7 @@ public final class RobotBootErrorMessage extends AbstractMessage { @Override protected void handleMessage(final NetworkEvent.Context context) { - MessageUtils.withClientEntity(entityId, RobotEntity.class, + MessageUtils.withClientEntity(entityId, Robot.class, robot -> robot.getVirtualMachine().setBootErrorClient(value)); } } diff --git a/src/main/java/li/cil/oc2/common/network/message/RobotBusStateMessage.java b/src/main/java/li/cil/oc2/common/network/message/RobotBusStateMessage.java index 80416b86..764a3533 100644 --- a/src/main/java/li/cil/oc2/common/network/message/RobotBusStateMessage.java +++ b/src/main/java/li/cil/oc2/common/network/message/RobotBusStateMessage.java @@ -1,7 +1,7 @@ package li.cil.oc2.common.network.message; import li.cil.oc2.common.bus.CommonDeviceBusController; -import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.entity.Robot; import li.cil.oc2.common.network.MessageUtils; import net.minecraft.network.FriendlyByteBuf; import net.minecraftforge.network.NetworkEvent; @@ -12,7 +12,7 @@ public final class RobotBusStateMessage extends AbstractMessage { /////////////////////////////////////////////////////////////////// - public RobotBusStateMessage(final RobotEntity robot) { + public RobotBusStateMessage(final Robot robot) { this.entityId = robot.getId(); this.value = robot.getVirtualMachine().getBusState(); } @@ -39,7 +39,7 @@ public final class RobotBusStateMessage extends AbstractMessage { @Override protected void handleMessage(final NetworkEvent.Context context) { - MessageUtils.withClientEntity(entityId, RobotEntity.class, + MessageUtils.withClientEntity(entityId, Robot.class, robot -> robot.getVirtualMachine().setBusStateClient(value)); } } diff --git a/src/main/java/li/cil/oc2/common/network/message/RobotInitializationMessage.java b/src/main/java/li/cil/oc2/common/network/message/RobotInitializationMessage.java index 8c64951f..fe8d7a28 100644 --- a/src/main/java/li/cil/oc2/common/network/message/RobotInitializationMessage.java +++ b/src/main/java/li/cil/oc2/common/network/message/RobotInitializationMessage.java @@ -1,7 +1,7 @@ package li.cil.oc2.common.network.message; import li.cil.oc2.common.bus.CommonDeviceBusController; -import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.entity.Robot; import li.cil.oc2.common.network.MessageUtils; import li.cil.oc2.common.serialization.NBTSerialization; import li.cil.oc2.common.vm.VMRunState; @@ -19,7 +19,7 @@ public final class RobotInitializationMessage extends AbstractMessage { /////////////////////////////////////////////////////////////////// - public RobotInitializationMessage(final RobotEntity robot) { + public RobotInitializationMessage(final Robot robot) { this.entityId = robot.getId(); this.busState = robot.getVirtualMachine().getBusState(); this.runState = robot.getVirtualMachine().getRunState(); @@ -55,7 +55,7 @@ public final class RobotInitializationMessage extends AbstractMessage { @Override protected void handleMessage(final NetworkEvent.Context context) { - MessageUtils.withClientEntity(entityId, RobotEntity.class, + MessageUtils.withClientEntity(entityId, Robot.class, robot -> { robot.getVirtualMachine().setBusStateClient(busState); robot.getVirtualMachine().setRunStateClient(runState); diff --git a/src/main/java/li/cil/oc2/common/network/message/RobotInitializationRequestMessage.java b/src/main/java/li/cil/oc2/common/network/message/RobotInitializationRequestMessage.java index 6550f4eb..98a35c5d 100644 --- a/src/main/java/li/cil/oc2/common/network/message/RobotInitializationRequestMessage.java +++ b/src/main/java/li/cil/oc2/common/network/message/RobotInitializationRequestMessage.java @@ -1,6 +1,6 @@ package li.cil.oc2.common.network.message; -import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.entity.Robot; import li.cil.oc2.common.network.MessageUtils; import li.cil.oc2.common.network.Network; import net.minecraft.network.FriendlyByteBuf; @@ -11,7 +11,7 @@ public final class RobotInitializationRequestMessage extends AbstractMessage { /////////////////////////////////////////////////////////////////// - public RobotInitializationRequestMessage(final RobotEntity robot) { + public RobotInitializationRequestMessage(final Robot robot) { this.entityId = robot.getId(); } @@ -35,7 +35,7 @@ public final class RobotInitializationRequestMessage extends AbstractMessage { @Override protected void handleMessage(final NetworkEvent.Context context) { - MessageUtils.withServerEntity(context, entityId, RobotEntity.class, + MessageUtils.withServerEntity(context, entityId, Robot.class, robot -> Network.INSTANCE.reply(new RobotInitializationMessage(robot), context)); } } diff --git a/src/main/java/li/cil/oc2/common/network/message/RobotPowerMessage.java b/src/main/java/li/cil/oc2/common/network/message/RobotPowerMessage.java index 2f484883..0331e70e 100644 --- a/src/main/java/li/cil/oc2/common/network/message/RobotPowerMessage.java +++ b/src/main/java/li/cil/oc2/common/network/message/RobotPowerMessage.java @@ -1,6 +1,6 @@ package li.cil.oc2.common.network.message; -import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.entity.Robot; import li.cil.oc2.common.network.MessageUtils; import net.minecraft.network.FriendlyByteBuf; import net.minecraftforge.network.NetworkEvent; @@ -11,7 +11,7 @@ public final class RobotPowerMessage extends AbstractMessage { /////////////////////////////////////////////////////////////////// - public RobotPowerMessage(final RobotEntity robot, final boolean power) { + public RobotPowerMessage(final Robot robot, final boolean power) { this.entityId = robot.getId(); this.power = power; } @@ -38,7 +38,7 @@ public final class RobotPowerMessage extends AbstractMessage { @Override protected void handleMessage(final NetworkEvent.Context context) { - MessageUtils.withNearbyServerEntity(context, entityId, RobotEntity.class, + MessageUtils.withNearbyServerEntity(context, entityId, Robot.class, robot -> { if (power) { robot.start(); diff --git a/src/main/java/li/cil/oc2/common/network/message/RobotRunStateMessage.java b/src/main/java/li/cil/oc2/common/network/message/RobotRunStateMessage.java index a5bad877..05334799 100644 --- a/src/main/java/li/cil/oc2/common/network/message/RobotRunStateMessage.java +++ b/src/main/java/li/cil/oc2/common/network/message/RobotRunStateMessage.java @@ -1,6 +1,6 @@ package li.cil.oc2.common.network.message; -import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.entity.Robot; import li.cil.oc2.common.network.MessageUtils; import li.cil.oc2.common.vm.VMRunState; import net.minecraft.network.FriendlyByteBuf; @@ -12,7 +12,7 @@ public final class RobotRunStateMessage extends AbstractMessage { /////////////////////////////////////////////////////////////////// - public RobotRunStateMessage(final RobotEntity robot) { + public RobotRunStateMessage(final Robot robot) { this.entityId = robot.getId(); this.value = robot.getVirtualMachine().getRunState(); } @@ -39,7 +39,7 @@ public final class RobotRunStateMessage extends AbstractMessage { @Override protected void handleMessage(final NetworkEvent.Context context) { - MessageUtils.withClientEntity(entityId, RobotEntity.class, + MessageUtils.withClientEntity(entityId, Robot.class, robot -> robot.getVirtualMachine().setRunStateClient(value)); } } diff --git a/src/main/java/li/cil/oc2/common/network/message/RobotTerminalInputMessage.java b/src/main/java/li/cil/oc2/common/network/message/RobotTerminalInputMessage.java index 89737c08..30818eeb 100644 --- a/src/main/java/li/cil/oc2/common/network/message/RobotTerminalInputMessage.java +++ b/src/main/java/li/cil/oc2/common/network/message/RobotTerminalInputMessage.java @@ -1,6 +1,6 @@ package li.cil.oc2.common.network.message; -import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.entity.Robot; import li.cil.oc2.common.network.MessageUtils; import net.minecraft.network.FriendlyByteBuf; import net.minecraftforge.network.NetworkEvent; @@ -8,7 +8,7 @@ import net.minecraftforge.network.NetworkEvent; import java.nio.ByteBuffer; public final class RobotTerminalInputMessage extends AbstractTerminalEntityMessage { - public RobotTerminalInputMessage(final RobotEntity robot, final ByteBuffer data) { + public RobotTerminalInputMessage(final Robot robot, final ByteBuffer data) { super(robot, data); } @@ -20,7 +20,7 @@ public final class RobotTerminalInputMessage extends AbstractTerminalEntityMessa @Override protected void handleMessage(final NetworkEvent.Context context) { - MessageUtils.withNearbyServerEntity(context, entityId, RobotEntity.class, + MessageUtils.withNearbyServerEntity(context, entityId, Robot.class, robot -> robot.getTerminal().putInput(ByteBuffer.wrap(data))); } } diff --git a/src/main/java/li/cil/oc2/common/network/message/RobotTerminalOutputMessage.java b/src/main/java/li/cil/oc2/common/network/message/RobotTerminalOutputMessage.java index 1f093a6f..3b9c793b 100644 --- a/src/main/java/li/cil/oc2/common/network/message/RobotTerminalOutputMessage.java +++ b/src/main/java/li/cil/oc2/common/network/message/RobotTerminalOutputMessage.java @@ -1,6 +1,6 @@ package li.cil.oc2.common.network.message; -import li.cil.oc2.common.entity.RobotEntity; +import li.cil.oc2.common.entity.Robot; import li.cil.oc2.common.network.MessageUtils; import net.minecraft.network.FriendlyByteBuf; import net.minecraftforge.network.NetworkEvent; @@ -8,7 +8,7 @@ import net.minecraftforge.network.NetworkEvent; import java.nio.ByteBuffer; public final class RobotTerminalOutputMessage extends AbstractTerminalEntityMessage { - public RobotTerminalOutputMessage(final RobotEntity robot, final ByteBuffer data) { + public RobotTerminalOutputMessage(final Robot robot, final ByteBuffer data) { super(robot, data); } @@ -20,7 +20,7 @@ public final class RobotTerminalOutputMessage extends AbstractTerminalEntityMess @Override protected void handleMessage(final NetworkEvent.Context context) { - MessageUtils.withClientEntity(entityId, RobotEntity.class, + MessageUtils.withClientEntity(entityId, Robot.class, robot -> robot.getTerminal().putOutput(ByteBuffer.wrap(data))); } } From 44b2bfdc1d70973d8c9e2300dd29e782468c6ef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sun, 9 Jan 2022 14:38:13 +0100 Subject: [PATCH 26/27] Fixed terminal tab evaluation when cursor sits on a tabulation. --- src/main/java/li/cil/oc2/common/vm/Terminal.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/li/cil/oc2/common/vm/Terminal.java b/src/main/java/li/cil/oc2/common/vm/Terminal.java index b2ad501e..40da591d 100644 --- a/src/main/java/li/cil/oc2/common/vm/Terminal.java +++ b/src/main/java/li/cil/oc2/common/vm/Terminal.java @@ -216,8 +216,10 @@ public final class Terminal { } } case (byte) '\t' /* 011 */ -> { - while (x < WIDTH && !tabs[x]) { - x++; + if (x < WIDTH) { + do { + x++; + } while (x < WIDTH && !tabs[x]); } } case (byte) '\b' /* 010 */ -> setCursorPos(Math.min(x, WIDTH - 1) - 1, y); From 60871d30aad6f5231deb321e326ae617ca9caf25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Mon, 10 Jan 2022 18:19:57 +0100 Subject: [PATCH 27/27] Made network cards configurable as to what faces they connect to. Closes #53. --- .../gui/NetworkInterfaceCardScreen.java | 294 ++++++++++++++++++ .../java/li/cil/oc2/client/gui/Sprites.java | 1 + .../java/li/cil/oc2/client/gui/Textures.java | 4 + .../oc2/client/renderer/ModRenderType.java | 18 ++ .../item/NetworkInterfaceCardItemDevice.java | 3 +- .../java/li/cil/oc2/common/item/Items.java | 2 +- .../common/item/NetworkInterfaceCardItem.java | 111 +++++++ .../li/cil/oc2/common/network/Network.java | 2 + ...workInterfaceCardConfigurationMessage.java | 61 ++++ .../doc/en_us/item/network_interface_card.md | 4 +- src/main/resources/assets/oc2/lang/en_us.json | 8 +- .../gui/overlay/block_face_disabled.png | Bin 0 -> 1758 bytes .../gui/overlay/block_face_enabled.png | Bin 0 -> 1738 bytes .../gui/overlay/block_face_focused.png | Bin 0 -> 1730 bytes .../widget/network_interface_card_screen.png | Bin 0 -> 2917 bytes 15 files changed, 504 insertions(+), 4 deletions(-) create mode 100644 src/main/java/li/cil/oc2/client/gui/NetworkInterfaceCardScreen.java create mode 100644 src/main/java/li/cil/oc2/common/item/NetworkInterfaceCardItem.java create mode 100644 src/main/java/li/cil/oc2/common/network/message/NetworkInterfaceCardConfigurationMessage.java create mode 100644 src/main/resources/assets/oc2/textures/gui/overlay/block_face_disabled.png create mode 100644 src/main/resources/assets/oc2/textures/gui/overlay/block_face_enabled.png create mode 100644 src/main/resources/assets/oc2/textures/gui/overlay/block_face_focused.png create mode 100644 src/main/resources/assets/oc2/textures/gui/widget/network_interface_card_screen.png diff --git a/src/main/java/li/cil/oc2/client/gui/NetworkInterfaceCardScreen.java b/src/main/java/li/cil/oc2/client/gui/NetworkInterfaceCardScreen.java new file mode 100644 index 00000000..094bd662 --- /dev/null +++ b/src/main/java/li/cil/oc2/client/gui/NetworkInterfaceCardScreen.java @@ -0,0 +1,294 @@ +package li.cil.oc2.client.gui; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.math.Quaternion; +import com.mojang.math.Vector3f; +import li.cil.oc2.client.gui.widget.Texture; +import li.cil.oc2.client.renderer.ModRenderType; +import li.cil.oc2.common.item.Items; +import li.cil.oc2.common.item.NetworkInterfaceCardItem; +import li.cil.oc2.common.network.Network; +import li.cil.oc2.common.network.message.NetworkInterfaceCardConfigurationMessage; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.block.model.ItemTransforms; +import net.minecraft.client.renderer.entity.ItemRenderer; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.sounds.SimpleSoundInstance; +import net.minecraft.core.Direction; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.util.Mth; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.InventoryMenu; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; + +import javax.annotation.Nullable; + +import static li.cil.oc2.common.util.TranslationUtils.key; +import static li.cil.oc2.common.util.TranslationUtils.text; + +public final class NetworkInterfaceCardScreen extends Screen { + private static final String SIDE_STATE_TEXT = key("gui.{mod}.network_interface_card.side_state"); + private static final Component CONNECTIVITY_ENABLED_TEXT = text("gui.{mod}.network_interface_card.connectivity.enabled"); + private static final Component CONNECTIVITY_DISABLED_TEXT = text("gui.{mod}.network_interface_card.connectivity.disabled"); + private static final Component INFO_TEXT = text("gui.{mod}.network_interface_card.info"); + + public static final int UI_WIDTH = Sprites.NETWORK_INTERFACE_CARD_SCREEN.width; + public static final int UI_HEIGHT = Sprites.NETWORK_INTERFACE_CARD_SCREEN.height; + public static final int BLOCK_LEFT = UI_WIDTH / 2; + public static final int BLOCK_TOP = 53; + public static final int INFO_TEXT_LEFT = 8; + public static final int INFO_TEXT_TOP = 104; + public static final int INFO_TEXT_WIDTH = UI_WIDTH - 16; + public static final int MAX_BLOCK_PITCH = 30; + + /////////////////////////////////////////////////////////////////// + + private final Player player; + private final InteractionHand hand; + + private final ComputerBlockItemRenderer computerBlockItemRenderer = new ComputerBlockItemRenderer(); + + private Vector3f blockRotation = new Vector3f(-30, 45, 0); + private int left, top; + @Nullable private Direction focusedSide; + private boolean isDraggingBlock, hasDraggedBlock; + private double dragStartX, dragStartY; + + /////////////////////////////////////////////////////////////////// + + public NetworkInterfaceCardScreen(final Player player, final InteractionHand hand) { + super(Items.NETWORK_INTERFACE_CARD.get().getDescription()); + this.player = player; + this.hand = hand; + } + + /////////////////////////////////////////////////////////////////// + + @Override + protected void init() { + super.init(); + + left = (width - UI_WIDTH) / 2; + top = (height - UI_HEIGHT) / 2; + } + + @Override + public void tick() { + super.tick(); + + final ItemStack heldItem = player.getItemInHand(hand); + if (!heldItem.is(Items.NETWORK_INTERFACE_CARD.get())) { + onClose(); + } + } + + @Override + public boolean mouseClicked(final double mouseX, final double mouseY, final int button) { + final boolean result = super.mouseClicked(mouseX, mouseY, button); + + if (!result && isMouseInBlockArea(mouseX, mouseY) && button == 0) { + isDraggingBlock = true; + hasDraggedBlock = false; + dragStartX = mouseX; + dragStartY = mouseY; + } + + return result; + } + + @Override + public boolean mouseReleased(final double mouseX, final double mouseY, final int button) { + if (isDraggingBlock && button == 0) { + isDraggingBlock = false; + if (!hasDraggedBlock && focusedSide != null) { + final NetworkInterfaceCardConfigurationMessage message = new NetworkInterfaceCardConfigurationMessage(hand, focusedSide, !getConfiguration(focusedSide)); + Network.INSTANCE.sendToServer(message); + Minecraft.getInstance().getSoundManager() + .play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1)); + } + } + + return super.mouseReleased(mouseX, mouseY, button); + } + + @Override + public boolean mouseDragged(final double mouseX, final double mouseY, final int activeButton, final double deltaX, final double deltaY) { + if (isDraggingBlock && activeButton == 0) { + if (!hasDraggedBlock) { + final double dx = mouseX - dragStartX; + final double dy = mouseY - dragStartY; + final double delta = Math.sqrt(dx * dx + dy * dy); + hasDraggedBlock = delta > 3; + } + if (hasDraggedBlock) { + blockRotation = new Vector3f( + Mth.clamp(blockRotation.x() - (float) deltaY, -MAX_BLOCK_PITCH, MAX_BLOCK_PITCH), + Mth.wrapDegrees(blockRotation.y() + (float) deltaX), + blockRotation.z() + ); + } + } + + return true; + } + + @Override + public void render(final PoseStack stack, final int mouseX, final int mouseY, final float partialTicks) { + renderBackground(stack); + Sprites.NETWORK_INTERFACE_CARD_SCREEN.draw(stack, left, top); + + super.render(stack, mouseX, mouseY, partialTicks); + + final int blockX = left + BLOCK_LEFT; + final int blockY = top + BLOCK_TOP; + focusedSide = computerBlockItemRenderer.getFocusedSide(blockX - mouseX, blockY - mouseY, blockRotation); + computerBlockItemRenderer.render(blockX, blockY, blockRotation); + + if (focusedSide != null) { + final Component enabledComponent = getConfiguration(focusedSide) ? CONNECTIVITY_ENABLED_TEXT : CONNECTIVITY_DISABLED_TEXT; + final TranslatableComponent tooltip = new TranslatableComponent(SIDE_STATE_TEXT, enabledComponent); + renderTooltip(stack, tooltip, mouseX, mouseY); + } + + font.drawWordWrap(INFO_TEXT, left + INFO_TEXT_LEFT, top + INFO_TEXT_TOP, INFO_TEXT_WIDTH, 0xAAAAAA); + } + + @Override + public boolean isPauseScreen() { + return false; + } + + /////////////////////////////////////////////////////////////////// + + private boolean isMouseInBlockArea(final double mouseX, final double mouseY) { + return mouseX >= left + 37 && mouseX <= left + (37 + 102) && + mouseY >= top + 10 && mouseY <= top + (10 + 102); + } + + private boolean getConfiguration(@Nullable final Direction side) { + return side != null && NetworkInterfaceCardItem.getSideConfiguration(player.getItemInHand(hand), side); + } + + /////////////////////////////////////////////////////////////////// + + private final class ComputerBlockItemRenderer { + public static final int BLOCK_RENDER_SIZE = 48; + + private final ItemStack computerItemStack = new ItemStack(Items.COMPUTER.get()); + private final ItemRenderer itemRenderer = Minecraft.getInstance().getItemRenderer(); + private final BakedModel model = itemRenderer.getModel(computerItemStack, null, null, 0); + + @Nullable + private Direction getFocusedSide(final float mouseX, final float mouseY, final Vector3f rotation) { + // Rotate ray inversely around block to represent visual block rotation. + final Quaternion quaternion = Quaternion.fromXYZDegrees(rotation); + quaternion.conj(); + + // Move ray in screen space to mouse position. + final float relMouseX = -mouseX / (float) BLOCK_RENDER_SIZE; + final float relMouseY = -mouseY / (float) BLOCK_RENDER_SIZE; + + final Vector3f source = new Vector3f(); + source.add(relMouseX, relMouseY, 1); + source.transform(quaternion); + + final Vector3f target = new Vector3f(); + target.add(relMouseX, relMouseY, -1); + target.transform(quaternion); + + // Intersect rotated ray with bounding box representing block. + final AABB aabb = new AABB(-0.5, -0.5, -0.5, 0.5, 0.5, 0.5); + return aabb.clip(new Vec3(source), new Vec3(target)) + .map(hit -> Direction.getNearest(hit.x, -hit.y(), hit.z())) + .filter(side -> side != Direction.SOUTH) + .orElse(null); + } + + public void render(final int x, final int y, final Vector3f rotation) { + RenderSystem.setShaderTexture(0, InventoryMenu.BLOCK_ATLAS); + RenderSystem.enableBlend(); + RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); + RenderSystem.setShaderColor(1, 1, 1, 1); + + final Vector3f renderRotation = rotation.copy(); + renderRotation.add(0, 180, 0); + + final PoseStack stack = RenderSystem.getModelViewStack(); + stack.pushPose(); + stack.translate(x, y, 0); + stack.mulPose(Quaternion.fromXYZDegrees(renderRotation)); + stack.scale(BLOCK_RENDER_SIZE, -BLOCK_RENDER_SIZE, BLOCK_RENDER_SIZE); + RenderSystem.applyModelViewMatrix(); + + final MultiBufferSource.BufferSource bufferSource = Minecraft.getInstance().renderBuffers().bufferSource(); + renderBlock(bufferSource); + renderOverlays(stack, bufferSource); + bufferSource.endBatch(); + + stack.popPose(); + RenderSystem.applyModelViewMatrix(); + } + + private void renderBlock(final MultiBufferSource.BufferSource bufferSource) { + itemRenderer.render(computerItemStack, ItemTransforms.TransformType.NONE, false, new PoseStack(), bufferSource, 0xF000F0, OverlayTexture.NO_OVERLAY, model); + } + + private void renderOverlays(final PoseStack poseStack, final MultiBufferSource.BufferSource bufferSource) { + for (final Direction side : Direction.values()) { + // South face of computers is the front face (screen) and there's no connectivity allowed there. + if (side == Direction.SOUTH) { + continue; + } + + poseStack.pushPose(); + poseStack.setIdentity(); + + poseStack.translate(-side.getStepX() * 0.51, side.getStepY() * 0.51, -side.getStepZ() * 0.51); + + final Vector3f sideRotation = switch (side) { + case DOWN -> new Vector3f(-90, 0, 0); + case UP -> new Vector3f(90, 0, 0); + case NORTH -> new Vector3f(0, 180, 0); + case WEST -> new Vector3f(0, -90, 0); + case EAST -> new Vector3f(0, 90, 0); + default -> throw new IllegalStateException("Unexpected value: " + side); + }; + poseStack.mulPose(Quaternion.fromXYZDegrees(sideRotation)); + + poseStack.translate(-0.5, -0.5, 0); + + if (getConfiguration(side)) { + renderOverlay(poseStack, bufferSource, Textures.BLOCK_FACE_ENABLED_TEXTURE); + } else { + renderOverlay(poseStack, bufferSource, Textures.BLOCK_FACE_DISABLED_TEXTURE); + } + + if (side == focusedSide) { + renderOverlay(poseStack, bufferSource, Textures.BLOCK_FACE_FOCUSED_TEXTURE); + } + + poseStack.popPose(); + } + } + + private void renderOverlay(final PoseStack poseStack, final MultiBufferSource.BufferSource bufferSource, final Texture texture) { + final VertexConsumer buffer = bufferSource.getBuffer(ModRenderType.getOverlay(texture.location)); + + buffer.vertex(poseStack.last().pose(), 0, 0, 0).uv(0, 0).endVertex(); + buffer.vertex(poseStack.last().pose(), 0, 1, 0).uv(0, 1).endVertex(); + buffer.vertex(poseStack.last().pose(), 1, 1, 0).uv(1, 1).endVertex(); + buffer.vertex(poseStack.last().pose(), 1, 0, 0).uv(1, 0).endVertex(); + } + } +} diff --git a/src/main/java/li/cil/oc2/client/gui/Sprites.java b/src/main/java/li/cil/oc2/client/gui/Sprites.java index 58c92a3c..771c9938 100644 --- a/src/main/java/li/cil/oc2/client/gui/Sprites.java +++ b/src/main/java/li/cil/oc2/client/gui/Sprites.java @@ -9,6 +9,7 @@ public final class Sprites { public static final Sprite ROBOT_CONTAINER = new Sprite(ROBOT_CONTAINER_TEXTURE); public static final Sprite TERMINAL_SCREEN = new Sprite(TERMINAL_SCREEN_TEXTURE); public static final Sprite BUS_INTERFACE_SCREEN = new Sprite(BUS_INTERFACE_SCREEN_TEXTURE); + public static final Sprite NETWORK_INTERFACE_CARD_SCREEN = new Sprite(NETWORK_INTERFACE_CARD_SCREEN_TEXTURE); public static final Sprite TERMINAL_FOCUSED = new Sprite(TERMINAL_FOCUSED_TEXTURE); public static final Sprite SLOT_SELECTION = new Sprite(SLOT_SELECTION_TEXTURE, 18, 18, 0, 0); diff --git a/src/main/java/li/cil/oc2/client/gui/Textures.java b/src/main/java/li/cil/oc2/client/gui/Textures.java index 042d5268..9786d594 100644 --- a/src/main/java/li/cil/oc2/client/gui/Textures.java +++ b/src/main/java/li/cil/oc2/client/gui/Textures.java @@ -7,11 +7,15 @@ public final class Textures { public static final Texture ROBOT_CONTAINER_TEXTURE = new Texture("textures/gui/widget/robot_container.png", 176, 197); public static final Texture TERMINAL_SCREEN_TEXTURE = new Texture("textures/gui/widget/terminal_screen.png", 336, 208); public static final Texture BUS_INTERFACE_SCREEN_TEXTURE = new Texture("textures/gui/widget/bus_interface_screen.png", 240, 30); + public static final Texture NETWORK_INTERFACE_CARD_SCREEN_TEXTURE = new Texture("textures/gui/widget/network_interface_card_screen.png", 176, 130); public static final Texture TERMINAL_FOCUSED_TEXTURE = new Texture("textures/gui/overlay/terminal_focused.png", 336, 208); public static final Texture SLOT_SELECTION_TEXTURE = new Texture("textures/gui/overlay/slot_selection.png", 18, 270); public static final Texture INFO_ICON_TEXTURE = new Texture("textures/gui/overlay/slot_info.png", 28, 28); public static final Texture WARN_ICON_TEXTURE = new Texture("textures/gui/overlay/slot_warn.png", 28, 28); + public static final Texture BLOCK_FACE_FOCUSED_TEXTURE = new Texture("textures/gui/overlay/block_face_focused.png", 16, 16); + public static final Texture BLOCK_FACE_ENABLED_TEXTURE = new Texture("textures/gui/overlay/block_face_enabled.png", 16, 16); + public static final Texture BLOCK_FACE_DISABLED_TEXTURE = new Texture("textures/gui/overlay/block_face_disabled.png", 16, 16); public static final Texture HOTBAR_TEXTURE = new Texture("textures/gui/widget/hotbar.png", 224, 26); public static final Texture SIDEBAR_2_TEXTURE = new Texture("textures/gui/widget/sidebar_2.png", 19, 34); diff --git a/src/main/java/li/cil/oc2/client/renderer/ModRenderType.java b/src/main/java/li/cil/oc2/client/renderer/ModRenderType.java index b222ff23..8eb819b8 100644 --- a/src/main/java/li/cil/oc2/client/renderer/ModRenderType.java +++ b/src/main/java/li/cil/oc2/client/renderer/ModRenderType.java @@ -47,6 +47,24 @@ public abstract class ModRenderType extends RenderType { state); } + public static RenderType getOverlay(final ResourceLocation location) { + final TextureStateShard texture = new TextureStateShard(location, false, true); + final RenderType.CompositeState state = RenderType.CompositeState.builder() + .setShaderState(RenderStateShard.POSITION_TEX_SHADER) + .setTextureState(texture) + .setOutputState(TRANSLUCENT_TARGET) + .setTransparencyState(ADDITIVE_TRANSPARENCY) + .createCompositeState(false); + return create( + API.MOD_ID + "/overlay", + DefaultVertexFormat.POSITION_TEX, + VertexFormat.Mode.QUADS, + 256, + false, + true, + state); + } + /////////////////////////////////////////////////////////////////// private ModRenderType(final String name, final VertexFormat format, final VertexFormat.Mode drawMode, final int bufferSize, final boolean useDelegate, final boolean needsSorting, final Runnable setupTask, final Runnable clearTask) { diff --git a/src/main/java/li/cil/oc2/common/bus/device/item/NetworkInterfaceCardItemDevice.java b/src/main/java/li/cil/oc2/common/bus/device/item/NetworkInterfaceCardItemDevice.java index d4798f58..3d3e4885 100644 --- a/src/main/java/li/cil/oc2/common/bus/device/item/NetworkInterfaceCardItemDevice.java +++ b/src/main/java/li/cil/oc2/common/bus/device/item/NetworkInterfaceCardItemDevice.java @@ -12,6 +12,7 @@ import li.cil.oc2.common.bus.device.util.IdentityProxy; import li.cil.oc2.common.bus.device.util.OptionalAddress; import li.cil.oc2.common.bus.device.util.OptionalInterrupt; import li.cil.oc2.common.capabilities.Capabilities; +import li.cil.oc2.common.item.NetworkInterfaceCardItem; import li.cil.oc2.common.serialization.NBTSerialization; import li.cil.oc2.common.util.NBTTagIds; import li.cil.sedna.device.virtio.VirtIONetworkDevice; @@ -51,7 +52,7 @@ public final class NetworkInterfaceCardItemDevice extends IdentityProxy LazyOptional getCapability(final Capability cap, @Nullable final Direction side) { - if (cap == Capabilities.NETWORK_INTERFACE && side != null) { + if (cap == Capabilities.NETWORK_INTERFACE && NetworkInterfaceCardItem.getSideConfiguration(identity, side)) { return LazyOptional.of(() -> networkInterface).cast(); } diff --git a/src/main/java/li/cil/oc2/common/item/Items.java b/src/main/java/li/cil/oc2/common/item/Items.java index 3051e4db..5506705a 100644 --- a/src/main/java/li/cil/oc2/common/item/Items.java +++ b/src/main/java/li/cil/oc2/common/item/Items.java @@ -63,7 +63,7 @@ public final class Items { new FloppyItem(512 * Constants.KILOBYTE)); public static final RegistryObject REDSTONE_INTERFACE_CARD = register("redstone_interface_card"); - public static final RegistryObject NETWORK_INTERFACE_CARD = register("network_interface_card"); + public static final RegistryObject NETWORK_INTERFACE_CARD = register("network_interface_card", NetworkInterfaceCardItem::new); public static final RegistryObject FILE_IMPORT_EXPORT_CARD = register("file_import_export_card"); public static final RegistryObject SOUND_CARD = register("sound_card"); diff --git a/src/main/java/li/cil/oc2/common/item/NetworkInterfaceCardItem.java b/src/main/java/li/cil/oc2/common/item/NetworkInterfaceCardItem.java new file mode 100644 index 00000000..edbcbd56 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/item/NetworkInterfaceCardItem.java @@ -0,0 +1,111 @@ +package li.cil.oc2.common.item; + +import li.cil.oc2.client.gui.NetworkInterfaceCardScreen; +import li.cil.oc2.common.Constants; +import li.cil.oc2.common.util.ItemStackUtils; +import li.cil.oc2.common.util.NBTTagIds; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import javax.annotation.Nullable; +import java.util.Arrays; +import java.util.List; + +import static li.cil.oc2.common.util.TooltipUtils.withColor; +import static li.cil.oc2.common.util.TranslationUtils.text; + +public final class NetworkInterfaceCardItem extends ModItem { + private static final String SIDE_CONFIGURATION_TAG_NAME = "sides"; + private static final Component IS_CONFIGURED_TEXT = withColor(text("item.{mod}.network_interface_card.is_configured"), ChatFormatting.GREEN); + + /////////////////////////////////////////////////////////////////// + + public static void setSideConfiguration(final ItemStack stack, final Direction side, final boolean enabled) { + final int index = side.get3DDataValue(); + + final CompoundTag tag = ItemStackUtils.getOrCreateModDataTag(stack); + final byte[] values; + if (tag.contains(SIDE_CONFIGURATION_TAG_NAME, NBTTagIds.TAG_BYTE_ARRAY) && + tag.getByteArray(SIDE_CONFIGURATION_TAG_NAME).length == Constants.BLOCK_FACE_COUNT) { + values = tag.getByteArray(SIDE_CONFIGURATION_TAG_NAME); + } else { + values = new byte[Constants.BLOCK_FACE_COUNT]; + Arrays.fill(values, (byte) 1); + } + + values[index] = (byte) (enabled ? 1 : 0); + + tag.putByteArray(SIDE_CONFIGURATION_TAG_NAME, values); + } + + public static boolean getSideConfiguration(final ItemStack stack, @Nullable final Direction side) { + if (side == null) { + return false; + } + + final int index = side.get3DDataValue(); + + final CompoundTag tag = ItemStackUtils.getModDataTag(stack); + if (tag.contains(SIDE_CONFIGURATION_TAG_NAME, NBTTagIds.TAG_BYTE_ARRAY)) { + final byte[] values = tag.getByteArray(SIDE_CONFIGURATION_TAG_NAME); + if (index < values.length) { + return values[index] != 0; + } + } + + return true; + } + + public static boolean hasConfiguration(final ItemStack stack) { + final byte[] values = ItemStackUtils.getModDataTag(stack).getByteArray(SIDE_CONFIGURATION_TAG_NAME); + for (final byte value : values) { + if (value == 0) { + return true; + } + } + + return false; + } + + /////////////////////////////////////////////////////////////////// + + + @Override + public void appendHoverText(final ItemStack stack, @Nullable final Level level, final List tooltip, final TooltipFlag flag) { + super.appendHoverText(stack, level, tooltip, flag); + if (NetworkInterfaceCardItem.hasConfiguration(stack)) { + tooltip.add(IS_CONFIGURED_TEXT); + } + } + + @Override + public InteractionResultHolder use(final Level level, final Player player, final InteractionHand hand) { + final ItemStack itemStack = player.getItemInHand(hand); + + if (player.getLevel().isClientSide()) { + if (itemStack.is(Items.NETWORK_INTERFACE_CARD.get())) { + openConfigurationScreen(player, hand); + } + } + + return InteractionResultHolder.sidedSuccess(itemStack, player.getLevel().isClientSide()); + } + + /////////////////////////////////////////////////////////////////// + + @OnlyIn(Dist.CLIENT) + private void openConfigurationScreen(final Player player, final InteractionHand hand) { + Minecraft.getInstance().setScreen(new NetworkInterfaceCardScreen(player, hand)); + } +} diff --git a/src/main/java/li/cil/oc2/common/network/Network.java b/src/main/java/li/cil/oc2/common/network/Network.java index a7dc6fac..f797953a 100644 --- a/src/main/java/li/cil/oc2/common/network/Network.java +++ b/src/main/java/li/cil/oc2/common/network/Network.java @@ -66,6 +66,8 @@ public final class Network { registerMessage(ClientCanceledImportFileMessage.class, ClientCanceledImportFileMessage::new, NetworkDirection.PLAY_TO_SERVER); registerMessage(BusCableFacadeMessage.class, BusCableFacadeMessage::new, NetworkDirection.PLAY_TO_CLIENT); + + registerMessage(NetworkInterfaceCardConfigurationMessage.class, NetworkInterfaceCardConfigurationMessage::new, NetworkDirection.PLAY_TO_SERVER); } public static void sendToClientsTrackingChunk(final T message, final LevelChunk chunk) { diff --git a/src/main/java/li/cil/oc2/common/network/message/NetworkInterfaceCardConfigurationMessage.java b/src/main/java/li/cil/oc2/common/network/message/NetworkInterfaceCardConfigurationMessage.java new file mode 100644 index 00000000..682925e7 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/network/message/NetworkInterfaceCardConfigurationMessage.java @@ -0,0 +1,61 @@ +package li.cil.oc2.common.network.message; + +import li.cil.oc2.common.item.Items; +import li.cil.oc2.common.item.NetworkInterfaceCardItem; +import net.minecraft.core.Direction; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.network.NetworkEvent; + +public final class NetworkInterfaceCardConfigurationMessage extends AbstractMessage { + private InteractionHand hand; + private Direction side; + private boolean value; + + /////////////////////////////////////////////////////////////////// + + public NetworkInterfaceCardConfigurationMessage(final InteractionHand hand, final Direction side, final boolean value) { + this.hand = hand; + this.side = side; + this.value = value; + } + + public NetworkInterfaceCardConfigurationMessage(final FriendlyByteBuf buffer) { + super(buffer); + } + + /////////////////////////////////////////////////////////////////// + + @Override + public void fromBytes(final FriendlyByteBuf buffer) { + hand = buffer.readEnum(InteractionHand.class); + side = buffer.readEnum(Direction.class); + value = buffer.readBoolean(); + } + + @Override + public void toBytes(final FriendlyByteBuf buffer) { + buffer.writeEnum(hand); + buffer.writeEnum(side); + buffer.writeBoolean(value); + } + + /////////////////////////////////////////////////////////////////// + + @Override + protected void handleMessage(final NetworkEvent.Context context) { + final ServerPlayer player = context.getSender(); + if (player == null) { + return; + } + + final ItemStack itemStack = player.getItemInHand(hand); + if (!itemStack.is(Items.NETWORK_INTERFACE_CARD.get())) { + return; + } + + NetworkInterfaceCardItem.setSideConfiguration(itemStack, side, value); + } +} diff --git a/src/main/resources/assets/oc2/doc/en_us/item/network_interface_card.md b/src/main/resources/assets/oc2/doc/en_us/item/network_interface_card.md index 6be9fe3f..256c218a 100644 --- a/src/main/resources/assets/oc2/doc/en_us/item/network_interface_card.md +++ b/src/main/resources/assets/oc2/doc/en_us/item/network_interface_card.md @@ -5,6 +5,8 @@ The network interface card (NIC) allows [computers](../block/computer.md) to sen Computers *have to be shut down* before installing or removing this component. Installing it while the computer is running will have no effect, removing it may lead to system errors. +These cards can be configured to only connect to selected sides (use while holding). This allows using multiple cards to build a custom router, for example. + When using the default Linux distribution, this device will provide a regular ethernet device. Network setup can either be performed manually, or using the convenience script `setup-network.lua`. This script provides the option to either use a fixed-address setup, or a DHCP setup. For a DHCP setup, exactly one computer in the network must act as a DHCP server. -After initial setup, use the command `ifconfig` to see the currently used IP address. \ No newline at end of file +After initial setup, use the command `ifconfig` to see the currently used IP address. diff --git a/src/main/resources/assets/oc2/lang/en_us.json b/src/main/resources/assets/oc2/lang/en_us.json index d1a0ab6d..9a8f1dcd 100644 --- a/src/main/resources/assets/oc2/lang/en_us.json +++ b/src/main/resources/assets/oc2/lang/en_us.json @@ -27,6 +27,7 @@ "item.oc2.flash_memory": "Flash Memory", "item.oc2.redstone_interface_card": "Redstone Interface Card", "item.oc2.network_interface_card": "Network Interface Card", + "item.oc2.network_interface_card.is_configured": "Has connectivity configuration.", "item.oc2.file_import_export_card": "File Import/Export Card", "item.oc2.file_import_export_card.desc": "Provides an API to import and export files into and out of a virtual computer from and into your real file system.", "item.oc2.robot": "Robot", @@ -72,6 +73,11 @@ "gui.oc2.file_chooser.confirm_button.overwrite": "Overwrite", "gui.oc2.file_chooser.cancel_button": "Cancel", + "gui.oc2.network_interface_card.side_state": "Connectivity: %s", + "gui.oc2.network_interface_card.connectivity.enabled": "Enabled", + "gui.oc2.network_interface_card.connectivity.disabled": "Disabled", + "gui.oc2.network_interface_card.info": "Drag to rotate. Click faces to toggle connectivity.", + "manual.oc2.home": "Home", "manual.oc2.blocks": "Blocks", "manual.oc2.items": "Items", @@ -96,4 +102,4 @@ "subtitles.oc2.floppy_eject": "Floppy ejected", "subtitles.oc2.floppy_insert": "Floppy inserted", "subtitles.oc2.hdd": "Hard drive access" -} \ No newline at end of file +} diff --git a/src/main/resources/assets/oc2/textures/gui/overlay/block_face_disabled.png b/src/main/resources/assets/oc2/textures/gui/overlay/block_face_disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..bbdb928165675e84f364710281b6e9cb309c7289 GIT binary patch literal 1758 zcmah~eM}Q)7{3Ob&_ZyyA|oc8hhqZa?%D#gXN#+CfdWT5==gBVd9>H|Lfb2M2QAEf zNK{b6fG}`#4w-Coaf_CX#5orUhK!N0NV3gjUlJwR7$I@!{+K%F-n$|n0$Y>o^?iT8 z=l4AC^E~g3T3Uc>XcyztBR4M&Au!|6BNf>sPWp|sn<;4G=(45~o4u*lu5}wvKWn4j&(8~ zfbpgIyI_min4{9>9x*tb3`l1qY!y2fR-q~!&01~|aMM6-?XzL?kzwMEdl;Gy zTM@iYA}qP0z%IN8u0EI{*uoG2i-j>n`@rnsz>o>Kzo>#BSu+F5vZ5Riw5_wg^Foo) zu%TsW^9^NN-@$|C4tIC!`qrF;=skT2mdO5`F-=Ry$mq+_@t$=r7z~4R>3?Dq<)MT~ z^W!6>pD4Xqy5FAKAF29mcJ}X-4~DnnUO?P+Ivl)dr2RaefW-joNnjeULOj|-vv zE`@yZou z&Dbr(q38Q6ryN~O+vxTCF}p8Mys99JKIrYfqJ8DS#G!rP$Id1{dv0XMvDaj` z4!Z{27ytPj{9Q!*3#xbsQfw3-8Fc(aBG}Bcm<+bz;_n^E4-aNZxBFtKkfV zQLjL|I+auEj{V>hJruF!_`navZ^{m2O=Z8H_)Sbq@G|6#+7gi`Q$o$dPsJv`AceGF23vI959kk$_ zk+`v!5FiW87i0*UnaymVL|kTM7$TC8j6~fBog-t3PC_!-{21lSdIudtLpECeATgc2u-)pU_{Y(kfib#$ML6FI@f`IZw{v&AyJ z&JmVxW0wHq+lWll|4sLJ)}$eb#s*l3rJ}Bh>Cpy)wb2KNQXp{kybTx|fLBbBDMlFBS4X(iQ^T1kw5|t|am7wH|&KfNGoPMl3}}NGI-5Xj)=j@LHL$ z>WD(WpbZ{O3BPJU1cYU~hssv_E%3-O@fb{#rz=JM2Jy{?L5JAH-DoKtiZ-`&j8G z@t$n$ug||3s{CwjZg}MVXBwQNNrma`&o{UIR$Lxg{GdAWma*btWR+c;l{}IFJr%pAIRR?mVUk~z9+V)>e~6#y9$5W#J4AZzdT?%9jTap zuW4wEOMRj6Z`-E{ia$qxOr0yV)(xHP{IDyb>&)$vb9~`K-Y-Qv1}6_&u6y5krzvUH|G|o zznY!@V6?QpW!vn*sN4PFv;N)zPvZ3Wv3)y3eo|M1jD=bgc`yAzgHcFs7UJ-Wnft|U z_;BxibE~VP&wiS19l!N3YX8?$Mk9s<e%5QqUVxdx-_=y%o`#1PB;f$ zSN{0|el8+>1yurqM8-)^2=Z=sA~dSZhJ5SJR@6YV~OG7olaQJ z(T#mb23cT!Aj6^AoMr z;{!MZsmen>!q!Z3a1-gIylUje`4I%B>}sUG)P$LQTGB-sS{bsowZ>{|ZMG@xNO_q^ z6~cjlhvW!201hvRlObwR-ODbd4+??u2r89e=u$dIIo6=_$O^j8MRPRkqJ8lMmTeJ- zS2%+5t?m*ce5;X7rvIjUJj>ExIeh>WVxg$ZV!FQpYYqEI)IzefpRtkp0O{rOgi7#r z!Oc{NbT{ZI59wt=b=8Pe@c`Jk2B@XoG*b(PiUdxA4_`Ul=pmeBJ!R)yajivrHR*Le z2=I}){d{Ym^->@g*`ij%BBn;3l%Y$uX88GtqVsoLmrGgp69PnV}t&8(^%Z zVmoZqYZX$ZLQyP}#C8Bp!+|g52p7$ztSY;YFr z1V{WIpPy)01w2e9+(5A$&4dM0Y|LxPme?w`bW_57!?4h0oUrjlQX@Www)<_QUDzDR z#ZZ=Za6y70HBQiAHKK7iC{WHYZ008ymP(|Uq-42A&_x2ZwR1xkB18ETcRw^0x*~X$ zKv;4_fnUr9jy||U(D)?+7V&F{^n$yG0hi2%kCOcml+Z?jwybCe1a0Z^cb%)!YBJhK zHeJo`=-<0n-|6b!ziz)moU*H5Y)l+bOvu|i$Hrey$#g&ToJKPoE&KzWsv8t1>K_@i ze3I=cQT?|0wM6M>k;vJR_Z|&6M{_C*s9&z6|6W~}T0OTjbw*!*uSlHJ(sZKq`1M)0 z57C+aYVC9lZ9CKWap3aEr7ttT&mJsEK3s6BC%Z4Rukqrk{2QX+mWgi<-aI#0aU@kV z{ch{<7@hw_<=?hX#iBc-KjlX%tu4a`yFcg=_Z+=ebAqY7TlQ!)?>YJ#15*oPL9+er6MNW22zc6{sW@56Vh2fGqW9Q?CDt_p-9-heApw7?JUy005 ze>EF@f7Ie{PoLej{@TF0*3{4C!|7T#^Hz-|@H1@vrg8=ECLU9^Ks zmz?dtZR~J$9k(B$I>u-2t>5wW)T^ncst^0RFDhSoV`|^-AJZbaPn{Xtc63j|tpm;> z*ZF_G0H2E#pP`Y3pwuk>Nr2wz-3W|@MxDmGwZo>0&dl#gs)RNt_1?KTU!JA^T#jLH l=3p;fIGK=f!9WF)px3#F|GYf+-DCWx$EdH-o!td4PDntiZ4{%W zR2c=@NfoqxL}ZB07JZm9Cspa_q*Jj@Q$U763knoO5oO;=!ma4~*PiDju+8=;=rzkrqZrMI?g14d7Gm@CNuEB9~D}Bs&yNPQ%m0aa<{)q)B8*CPdRI z)c_}vcwsuVM4AKP1yU66#3ip|UJ) zV#M#P#$d&#$Z=fFrPH-qElnFhL(ptGGbAL0&S245EGl47u{;$n(NR^H58=Y%BLc#t zD6Ga|L`5ciN-_}*&ZkfSPPRISbnuLK6=p3T2tg+hI+Mnr&!(&82##QKL_M9+9Ghu) zo*@ory(SKU%%cPOUL8RvQNM~T7XNR$QaPs#3>W1BA*`a#DPbxAmYk=C=!p=9XizC6 z%7s+8&uk@R(rJ6O2343_B9qb~1*8O23?#|4gd}w23K0c@CV^K8;!~JrH|FgGSO+Q6 zB4A*GfhGYgi1RZveKyKwn9*sOEq=v_47d&W85AuPCZI?btN<967#U8E7701b5DtgR zqFGWPl;=h(CD~9Mq{_zSAiOoe46*^1nE`8FW`Gbyp&{O?6cr3)WSJwIp|lx{PYGht zX9Y@sB?KDH6@!V8NpMMo1P8gY7~qX&FsV#NGLy*-WN=x*{tPCUVJY3rM`UnT-Y>C< zDIoIzUo7TE!5EI9dFCz4Xe2?~EGx@W0h?!nm08ki< zD2)DpOocr+_gSehSyUD~nGwJZ4CJzyR*hesig{jMtBM8vB~_RVTHsVJ7K`azUej-D zE<8GMc1<;mN1xwPo=nQcVO#;tYOp|o&sCK;@VE+zDw|KyQDsn;M5Dkdq2Q!bYj6nN zB1ORY39BH~5>5eIeVKz#Ipfi1N3t+y9kRg0vIh=ta0}3x>K1Ky;qUIbE%O-rNZ&(+ni3&?o_|1oYVFDN6S;SrJN~-(x?4k_!+yWo za<@Z^52atL^?mH9UEO;1gTZqRk)=+Kod@&Jv>?8#VkV@g-5sA^{I_p^O!BrfAC!Gw z?p|Ke6Mqbic@}yPO5x%w-7lOs{=I#NlQ{N^ zLuJ=O-re24r{Lzo{uS#^wydq#X*0Yp`;@%thayL1`ZdqmghVwI$YWMi@M~1CBp1~f& zN1hl?giJgi9UUFVyw*PBdl~zlkBct78LVZvd{$*{hFRdrm(cP0j=(d*Zue) z*IrIKu^JIP&9K{^>Y%?qc>MJGmB7Yz=DGOH)fPl$YyC1*rEdD8M%Ai0as0EBSTZ6N z>r+=i+4Z)+ZRDMS)LC8x>KkAE88E88xo>KFFm`*xEc;Pgjh+9{%d|MQ($}H3f6p}#eVn|= z->$K><1*L^U(q7@y6du8wyBc4YLs_+%EPZWzc<;l^e0~2(?QOUFDpzZ;ch9{{|n`f B6v6-i literal 0 HcmV?d00001