From 40a2c5fa256bcabfa00612b311cb62493be46677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sun, 7 Mar 2021 15:15:03 +0100 Subject: [PATCH] Interfaces can now be given a custom label. This will act as an additional device name, which allows referencing them by this label. --- .../java/li/cil/oc2/client/ClientSetup.java | 2 + .../oc2/client/gui/BusInterfaceScreen.java | 156 ++++++++++++++++++ .../renderer/BusInterfaceNameRenderer.java | 103 ++++++++++++ .../java/li/cil/oc2/common/Constants.java | 2 + .../cil/oc2/common/block/BusCableBlock.java | 59 +++++-- .../oc2/common/bus/RPCDeviceBusAdapter.java | 8 + .../bus/TileEntityDeviceBusElement.java | 19 +-- .../li/cil/oc2/common/network/Network.java | 12 ++ .../message/BusInterfaceNameMessage.java | 82 +++++++++ .../common/tileentity/BusCableTileEntity.java | 108 ++++++++++-- src/main/resources/assets/oc2/lang/en_us.json | 4 +- .../oc2/textures/gui/screen/bus_interface.png | Bin 0 -> 3489 bytes 12 files changed, 515 insertions(+), 40 deletions(-) create mode 100644 src/main/java/li/cil/oc2/client/gui/BusInterfaceScreen.java create mode 100644 src/main/java/li/cil/oc2/client/renderer/BusInterfaceNameRenderer.java create mode 100644 src/main/java/li/cil/oc2/common/network/message/BusInterfaceNameMessage.java create mode 100644 src/main/resources/assets/oc2/textures/gui/screen/bus_interface.png diff --git a/src/main/java/li/cil/oc2/client/ClientSetup.java b/src/main/java/li/cil/oc2/client/ClientSetup.java index 1b03eb6d..4ba8dccb 100644 --- a/src/main/java/li/cil/oc2/client/ClientSetup.java +++ b/src/main/java/li/cil/oc2/client/ClientSetup.java @@ -9,6 +9,7 @@ import li.cil.oc2.client.gui.RobotTerminalScreen; import li.cil.oc2.client.item.CustomItemColors; import li.cil.oc2.client.item.CustomItemModelProperties; import li.cil.oc2.client.model.BusCableModelLoader; +import li.cil.oc2.client.renderer.BusInterfaceNameRenderer; import li.cil.oc2.client.renderer.NetworkCableRenderer; import li.cil.oc2.client.renderer.entity.RobotEntityRenderer; import li.cil.oc2.client.renderer.tileentity.ChargerTileEntityRenderer; @@ -35,6 +36,7 @@ public final class ClientSetup { @SubscribeEvent public static void handleSetupEvent(final FMLClientSetupEvent event) { NetworkCableRenderer.initialize(); + BusInterfaceNameRenderer.initialize(); CustomItemModelProperties.initialize(); CustomItemColors.initialize(); diff --git a/src/main/java/li/cil/oc2/client/gui/BusInterfaceScreen.java b/src/main/java/li/cil/oc2/client/gui/BusInterfaceScreen.java new file mode 100644 index 00000000..b25bc8f0 --- /dev/null +++ b/src/main/java/li/cil/oc2/client/gui/BusInterfaceScreen.java @@ -0,0 +1,156 @@ +package li.cil.oc2.client.gui; + +import com.mojang.blaze3d.matrix.MatrixStack; +import com.mojang.blaze3d.systems.RenderSystem; +import li.cil.oc2.api.API; +import li.cil.oc2.client.gui.widget.ImageButton; +import li.cil.oc2.client.gui.widget.Sprite; +import li.cil.oc2.common.Constants; +import li.cil.oc2.common.item.Items; +import li.cil.oc2.common.network.Network; +import li.cil.oc2.common.network.message.BusInterfaceNameMessage; +import li.cil.oc2.common.tileentity.BusCableTileEntity; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.TextFieldWidget; +import net.minecraft.util.Direction; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.vector.Vector3d; +import net.minecraft.util.text.TranslationTextComponent; +import org.lwjgl.glfw.GLFW; + +public final class BusInterfaceScreen extends Screen { + private static final ResourceLocation BACKGROUND_LOCATION = new ResourceLocation(API.MOD_ID, "textures/gui/screen/bus_interface.png"); + + private static final Sprite BACKGROUND = new Sprite(BACKGROUND_LOCATION, 256, 240, 30, 0, 0); + private static final Sprite CONFIRM_BASE = new Sprite(BACKGROUND_LOCATION, 256, 12, 12, 5, 35); + private static final Sprite CONFIRM_PRESSED = new Sprite(BACKGROUND_LOCATION, 256, 12, 12, 20, 35); + private static final Sprite CANCEL_BASE = new Sprite(BACKGROUND_LOCATION, 256, 12, 12, 5, 50); + private static final Sprite CANCEL_PRESSED = new Sprite(BACKGROUND_LOCATION, 256, 12, 12, 20, 50); + + private static final int TEXT_LEFT = 9; + private static final int TEXT_TOP = 11; + private static final int CONFIRM_LEFT = 206; + private static final int CONFIRM_TOP = 9; + private static final int CANCEL_LEFT = 219; + private static final int CANCEL_TOP = 9; + + private final BusCableTileEntity tileEntity; + private final Direction side; + + private TextFieldWidget nameField; + + private int left, top; + + /////////////////////////////////////////////////////////////////// + + public BusInterfaceScreen(final BusCableTileEntity tileEntity, final Direction side) { + super(Items.BUS_INTERFACE.get().getName()); + this.tileEntity = tileEntity; + this.side = side; + } + + /////////////////////////////////////////////////////////////////// + + @Override + protected void init() { + super.init(); + + getMinecraft().keyboardListener.enableRepeatEvents(true); + + left = (width - BACKGROUND.width) / 2; + top = (height - BACKGROUND.height) / 2; + + nameField = new TextFieldWidget(font, left + TEXT_LEFT, top + TEXT_TOP, 192, 12, new TranslationTextComponent("oc2.gui.bus_interface_name")); + nameField.setCanLoseFocus(false); + nameField.setTextColor(0xFFFFFFFF); + nameField.setEnableBackgroundDrawing(false); + nameField.setMaxStringLength(32); + nameField.setText(tileEntity.getInterfaceName(side)); + addListener(nameField); + setFocusedDefault(nameField); + + addButton(new ImageButton( + this, + left + CONFIRM_LEFT, top + CONFIRM_TOP, + CONFIRM_BASE.width, CONFIRM_BASE.height, + new TranslationTextComponent(Constants.TOOLTIP_CONFIRM), + null, + CONFIRM_BASE, + CONFIRM_PRESSED + ) { + @Override + public void onPress() { + super.onPress(); + setInterfaceName(nameField.getText()); + closeScreen(); + } + }); + addButton(new ImageButton( + this, + left + CANCEL_LEFT, top + CANCEL_TOP, + CANCEL_BASE.width, CANCEL_BASE.height, + new TranslationTextComponent(Constants.TOOLTIP_CANCEL), + null, + CANCEL_BASE, + CANCEL_PRESSED + ) { + @Override + public void onPress() { + super.onPress(); + closeScreen(); + } + }); + } + + @Override + public void onClose() { + super.onClose(); + + getMinecraft().keyboardListener.enableRepeatEvents(false); + } + + @Override + public void tick() { + super.tick(); + nameField.tick(); + + final Vector3d busCableCenter = Vector3d.copyCentered(tileEntity.getPos()); + if (getMinecraft().player.getDistanceSq(busCableCenter) > 8 * 8) { + closeScreen(); + } + } + + @Override + public boolean keyPressed(final int keyCode, final int scanCode, final int modifiers) { + if (keyCode == GLFW.GLFW_KEY_ENTER || + keyCode == GLFW.GLFW_KEY_KP_ENTER) { + setInterfaceName(nameField.getText()); + closeScreen(); + return true; + } + + return nameField.keyPressed(keyCode, scanCode, modifiers) || super.keyPressed(keyCode, scanCode, modifiers); + } + + @Override + public void render(final MatrixStack matrixStack, final int mouseX, final int mouseY, final float partialTicks) { + renderBackground(matrixStack); + BACKGROUND.draw(matrixStack, left, top); + + super.render(matrixStack, mouseX, mouseY, partialTicks); + + RenderSystem.disableBlend(); + nameField.render(matrixStack, mouseX, mouseY, partialTicks); + } + + @Override + public boolean isPauseScreen() { + return false; + } + + /////////////////////////////////////////////////////////////////// + + private void setInterfaceName(final String name) { + Network.INSTANCE.sendToServer(new BusInterfaceNameMessage.ToServer(tileEntity, side, name)); + } +} diff --git a/src/main/java/li/cil/oc2/client/renderer/BusInterfaceNameRenderer.java b/src/main/java/li/cil/oc2/client/renderer/BusInterfaceNameRenderer.java new file mode 100644 index 00000000..c78d0ce3 --- /dev/null +++ b/src/main/java/li/cil/oc2/client/renderer/BusInterfaceNameRenderer.java @@ -0,0 +1,103 @@ +package li.cil.oc2.client.renderer; + +import com.mojang.blaze3d.matrix.MatrixStack; +import li.cil.oc2.common.block.BusCableBlock; +import li.cil.oc2.common.integration.Wrenches; +import li.cil.oc2.common.tileentity.BusCableTileEntity; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.renderer.ActiveRenderInfo; +import net.minecraft.client.renderer.IRenderTypeBuffer; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.entity.EntityRendererManager; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.BlockRayTraceResult; +import net.minecraft.util.math.vector.Matrix4f; +import net.minecraft.world.World; +import net.minecraftforge.client.event.RenderWorldLastEvent; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.eventbus.api.SubscribeEvent; + +public enum BusInterfaceNameRenderer { + INSTANCE; + + /////////////////////////////////////////////////////////////////// + + public static void initialize() { + MinecraftForge.EVENT_BUS.register(INSTANCE); + } + + @SubscribeEvent + public void handleRenderLastEvent(final RenderWorldLastEvent event) { + final Minecraft mc = Minecraft.getInstance(); + final PlayerEntity player = mc.player; + final World world = player.getEntityWorld(); + + if (!Wrenches.isHoldingWrench(player)) { + return; + } + + if (!(mc.objectMouseOver instanceof BlockRayTraceResult)) { + return; + } + + final BlockRayTraceResult hit = (BlockRayTraceResult) mc.objectMouseOver; + final BlockPos blockPos = hit.getPos(); + final TileEntity tileEntity = world.getTileEntity(blockPos); + if (!(tileEntity instanceof BusCableTileEntity)) { + return; + } + + final BusCableTileEntity busCable = (BusCableTileEntity) tileEntity; + final Direction side = BusCableBlock.getHitSide(blockPos, hit); + if (BusCableBlock.getConnectionType(world.getBlockState(blockPos), side) != BusCableBlock.ConnectionType.INTERFACE) { + return; + } + + final String name = busCable.getInterfaceName(side); + if (name.isEmpty()) { + return; + } + + + final MatrixStack stack = event.getMatrixStack(); + stack.push(); + + stack.translate(0.5, 1, 0.5); + stack.translate(side.getXOffset() * 0.5f, 0, side.getZOffset() * 0.5f); + + final ActiveRenderInfo info = mc.gameRenderer.getActiveRenderInfo(); + stack.translate( + blockPos.getX() - info.getProjectedView().getX(), + blockPos.getY() - info.getProjectedView().getY(), + blockPos.getZ() - info.getProjectedView().getZ()); + + final EntityRendererManager renderManager = mc.getRenderManager(); + stack.rotate(renderManager.getCameraOrientation()); + + stack.scale(-0.025f, -0.025f, 0.025f); + + final Matrix4f matrix = stack.getLast().getMatrix(); + + final FontRenderer fontrenderer = renderManager.getFontRenderer(); + final IRenderTypeBuffer.Impl buffer = IRenderTypeBuffer.getImpl(Tessellator.getInstance().getBuffer()); + + final float horizontalTextOffset = -fontrenderer.getStringWidth(name) * 0.5f; + final float backgroundOpacity = Minecraft.getInstance().gameSettings.getTextBackgroundOpacity(0.25F); + final int backgroundColor = (int) (backgroundOpacity * 255.0F) << 24; + final int packedLight = LightTexture.packLight(15, 15); + + fontrenderer.renderString(name, horizontalTextOffset, 0, 0xffffffff, + false, matrix, buffer, true, backgroundColor, packedLight); + fontrenderer.renderString(name, horizontalTextOffset, 0, 0xffffffff, + false, matrix, buffer, false, 0, packedLight); + + buffer.finish(); + + stack.pop(); + } +} diff --git a/src/main/java/li/cil/oc2/common/Constants.java b/src/main/java/li/cil/oc2/common/Constants.java index 8ccf6896..0265e16f 100644 --- a/src/main/java/li/cil/oc2/common/Constants.java +++ b/src/main/java/li/cil/oc2/common/Constants.java @@ -64,6 +64,8 @@ public final class Constants { public static final String TOOLTIP_HARD_DRIVE_MISSING = "tooltip.oc2.hard_drive_missing"; public static final String TOOLTIP_ENERGY = "tooltip.oc2.energy"; public static final String TOOLTIP_ENERGY_CONSUMPTION = "tooltip.oc2.energyConsumption"; + public static final String TOOLTIP_CONFIRM = "tooltip.oc2.confirm"; + public static final String TOOLTIP_CANCEL = "tooltip.oc2.cancel"; /////////////////////////////////////////////////////////////////// diff --git a/src/main/java/li/cil/oc2/common/block/BusCableBlock.java b/src/main/java/li/cil/oc2/common/block/BusCableBlock.java index ae1f3412..05a978e7 100644 --- a/src/main/java/li/cil/oc2/common/block/BusCableBlock.java +++ b/src/main/java/li/cil/oc2/common/block/BusCableBlock.java @@ -1,6 +1,7 @@ package li.cil.oc2.common.block; import com.google.common.collect.Maps; +import li.cil.oc2.client.gui.BusInterfaceScreen; import li.cil.oc2.common.Constants; import li.cil.oc2.common.integration.Wrenches; import li.cil.oc2.common.item.Items; @@ -12,6 +13,7 @@ import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.SoundType; import net.minecraft.block.material.Material; +import net.minecraft.client.Minecraft; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.BlockItemUseContext; import net.minecraft.item.ItemStack; @@ -33,6 +35,8 @@ import net.minecraft.world.GameRules; import net.minecraft.world.IBlockReader; import net.minecraft.world.IWorld; import net.minecraft.world.World; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.common.util.Constants.BlockFlags; import javax.annotation.Nullable; @@ -99,6 +103,11 @@ public final class BusCableBlock extends Block { return partCount; } + public static Direction getHitSide(final BlockPos pos, final BlockRayTraceResult hit) { + final Vector3d localHitPos = hit.getHitVec().subtract(Vector3d.copyCentered(pos)); + return Direction.getFacingFromVector(localHitPos.x, localHitPos.y, localHitPos.z); + } + /////////////////////////////////////////////////////////////////// private final VoxelShape[] shapes; @@ -171,14 +180,27 @@ public final class BusCableBlock extends Block { @SuppressWarnings("deprecation") @Override public ActionResultType onBlockActivated(final BlockState state, final World world, final BlockPos pos, final PlayerEntity player, final Hand hand, final BlockRayTraceResult hit) { - if (player.isSneaking() && Wrenches.isWrench(player.getHeldItem(hand))) { - // NB: leave wrenching logic up to wrench when the to-be-removed interface is the last - // part of this bus. This ensures we properly remove the block itself without having - // to duplicate the logic needed for that. - if (getPartCount(state) > 1) - if (tryRemovePlug(state, world, pos, player, hit) || tryRemoveCable(state, world, pos, player)) { - return ActionResultType.func_233537_a_(world.isRemote()); + if (Wrenches.isWrench(player.getHeldItem(hand))) { + if (player.isSneaking()) { + // NB: leave wrenching logic up to wrench when the to-be-removed interface is the last + // part of this bus. This ensures we properly remove the block itself without having + // to duplicate the logic needed for that. + if (getPartCount(state) > 1) + if (tryRemovePlug(state, world, pos, player, hit) || tryRemoveCable(state, world, pos, player)) { + return ActionResultType.func_233537_a_(world.isRemote()); + } + } else { + final TileEntity tileEntity = world.getTileEntity(pos); + if (tileEntity instanceof BusCableTileEntity) { + final BusCableTileEntity busCableTileEntity = (BusCableTileEntity) tileEntity; + + final Direction side = getHitSide(pos, hit); + if (getConnectionType(state, side) == ConnectionType.INTERFACE) { + openBusInterfaceScreen(busCableTileEntity, side); + return ActionResultType.func_233537_a_(world.isRemote()); + } } + } } return super.onBlockActivated(state, world, pos, player, hand, hit); @@ -261,8 +283,10 @@ public final class BusCableBlock extends Block { /////////////////////////////////////////////////////////////////// - private boolean canHaveCableTo(final BlockState state, final Direction side) { - return state.getBlock() == this && state.get(HAS_CABLE) && state.get(FACING_TO_CONNECTION_MAP.get(side)) != ConnectionType.INTERFACE; + private static boolean canHaveCableTo(final BlockState state, final Direction side) { + return state.getBlock() == Blocks.BUS_CABLE.get() && + state.get(HAS_CABLE) && + state.get(FACING_TO_CONNECTION_MAP.get(side)) != ConnectionType.INTERFACE; } private static int getPartCount(final BlockState state) { @@ -273,9 +297,8 @@ public final class BusCableBlock extends Block { return partCount; } - private boolean tryRemovePlug(final BlockState state, final World world, final BlockPos pos, final PlayerEntity player, final BlockRayTraceResult hit) { - final Vector3d localHitPos = hit.getHitVec().subtract(Vector3d.copyCentered(pos)); - final Direction side = Direction.getFacingFromVector(localHitPos.x, localHitPos.y, localHitPos.z); + private static boolean tryRemovePlug(final BlockState state, final World world, final BlockPos pos, final PlayerEntity player, final BlockRayTraceResult hit) { + final Direction side = getHitSide(pos, hit); final EnumProperty property = FACING_TO_CONNECTION_MAP.get(side); if (state.get(property) != ConnectionType.INTERFACE) { @@ -294,7 +317,7 @@ public final class BusCableBlock extends Block { return true; } - private boolean tryRemoveCable(final BlockState state, final World world, final BlockPos pos, final PlayerEntity player) { + private static boolean tryRemoveCable(final BlockState state, final World world, final BlockPos pos, final PlayerEntity player) { if (!state.get(HAS_CABLE)) { return false; } @@ -306,7 +329,7 @@ public final class BusCableBlock extends Block { return true; } - private void handlePartRemoved(final BlockState state, final World world, final BlockPos pos, @Nullable final Direction side, final PlayerEntity player, final ItemStack drop) { + private static void handlePartRemoved(final BlockState state, final World world, final BlockPos pos, @Nullable final Direction side, final PlayerEntity player, final ItemStack drop) { onConnectionTypeChanged(world, pos, side); if (!player.isCreative() && world.getGameRules().getBoolean(GameRules.DO_TILE_DROPS)) { @@ -319,7 +342,7 @@ public final class BusCableBlock extends Block { WorldUtils.playSound(world, pos, state.getSoundType(), SoundType::getBreakSound); } - private void onConnectionTypeChanged(final IWorld world, final BlockPos pos, @Nullable final Direction face) { + private static void onConnectionTypeChanged(final IWorld world, final BlockPos pos, @Nullable final Direction face) { final TileEntity tileEntity = world.getTileEntity(pos); if (tileEntity instanceof BusCableTileEntity) { final BusCableTileEntity busCable = (BusCableTileEntity) tileEntity; @@ -327,6 +350,12 @@ public final class BusCableBlock extends Block { } } + @OnlyIn(Dist.CLIENT) + private static void openBusInterfaceScreen(final BusCableTileEntity tileEntity, final Direction side) { + final BusInterfaceScreen screen = new BusInterfaceScreen(tileEntity, side); + Minecraft.getInstance().displayGuiScreen(screen); + } + private static VoxelShape[] makeShapes() { final VoxelShape ownCableBounds = Block.makeCuboidShape(5, 5, 5, 11, 11, 11); final VoxelShape[] cableShapes = new VoxelShape[Constants.BLOCK_FACE_COUNT]; diff --git a/src/main/java/li/cil/oc2/common/bus/RPCDeviceBusAdapter.java b/src/main/java/li/cil/oc2/common/bus/RPCDeviceBusAdapter.java index ebc81a6e..a00272ae 100644 --- a/src/main/java/li/cil/oc2/common/bus/RPCDeviceBusAdapter.java +++ b/src/main/java/li/cil/oc2/common/bus/RPCDeviceBusAdapter.java @@ -128,6 +128,14 @@ public final class RPCDeviceBusAdapter implements Steppable { final HashMap> identifiersByDevice = new HashMap<>(); devicesByIdentifier.forEach((identifier, devices) -> { final RPCDeviceList device = new RPCDeviceList(devices); + + // If there are no methods we have either no devices at all, or all synthetic + // devices, i.e. devices that only contribute type names, but have no methods + // to call. We do not expose these to avoid cluttering the device list. + if (device.getMethods().isEmpty()) { + return; + } + identifiersByDevice .computeIfAbsent(device, unused -> new ArrayList<>()) .add(identifier); diff --git a/src/main/java/li/cil/oc2/common/bus/TileEntityDeviceBusElement.java b/src/main/java/li/cil/oc2/common/bus/TileEntityDeviceBusElement.java index adefd8fd..7aad145e 100644 --- a/src/main/java/li/cil/oc2/common/bus/TileEntityDeviceBusElement.java +++ b/src/main/java/li/cil/oc2/common/bus/TileEntityDeviceBusElement.java @@ -4,7 +4,6 @@ import li.cil.oc2.api.bus.BlockDeviceBusElement; import li.cil.oc2.api.bus.DeviceBus; import li.cil.oc2.api.bus.DeviceBusElement; import li.cil.oc2.api.bus.device.provider.BlockDeviceQuery; -import li.cil.oc2.api.bus.device.rpc.RPCDevice; import li.cil.oc2.common.Constants; import li.cil.oc2.common.bus.device.rpc.TypeNameRPCDevice; import li.cil.oc2.common.bus.device.util.BlockDeviceInfo; @@ -101,7 +100,7 @@ public class TileEntityDeviceBusElement extends AbstractGroupingBlockDeviceBusEl } } - insertBlockNameDevice(world, pos, newDevices); + collectSyntheticDevices(world, pos, direction, newDevices); setDevicesForGroup(index, newDevices); } @@ -128,6 +127,13 @@ public class TileEntityDeviceBusElement extends AbstractGroupingBlockDeviceBusEl return canScanContinueTowards(direction); } + protected void collectSyntheticDevices(final World world, final BlockPos pos, final Direction direction, final HashSet devices) { + final String blockName = WorldUtils.getBlockName(world, pos); + if (blockName != null) { + devices.add(new BlockDeviceInfo(null, new TypeNameRPCDevice(blockName))); + } + } + /////////////////////////////////////////////////////////////////// private void scanNeighborsForDevices() { @@ -151,13 +157,4 @@ public class TileEntityDeviceBusElement extends AbstractGroupingBlockDeviceBusEl capability.ifPresent(DeviceBus::scheduleScan); } } - - private void insertBlockNameDevice(final World world, final BlockPos pos, final HashSet devices) { - if (devices.stream().anyMatch(info -> info.device instanceof RPCDevice)) { - final String blockName = WorldUtils.getBlockName(world, pos); - if (blockName != null) { - devices.add(new BlockDeviceInfo(null, new TypeNameRPCDevice(blockName))); - } - } - } } 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 7f9fbbdb..54b075ee 100644 --- a/src/main/java/li/cil/oc2/common/network/Network.java +++ b/src/main/java/li/cil/oc2/common/network/Network.java @@ -122,6 +122,18 @@ public final class Network { .decoder(DiskDriveFloppyMessage::new) .consumer(DiskDriveFloppyMessage::handleMessage) .add(); + + INSTANCE.messageBuilder(BusInterfaceNameMessage.ToClient.class, getNextPacketId(), NetworkDirection.PLAY_TO_CLIENT) + .encoder(BusInterfaceNameMessage::toBytes) + .decoder(BusInterfaceNameMessage.ToClient::new) + .consumer(BusInterfaceNameMessage::handleMessageClient) + .add(); + + INSTANCE.messageBuilder(BusInterfaceNameMessage.ToServer.class, getNextPacketId(), NetworkDirection.PLAY_TO_SERVER) + .encoder(BusInterfaceNameMessage::toBytes) + .decoder(BusInterfaceNameMessage.ToServer::new) + .consumer(BusInterfaceNameMessage::handleMessageServer) + .add(); } public static void sendToClientsTrackingChunk(final T message, final Chunk chunk) { diff --git a/src/main/java/li/cil/oc2/common/network/message/BusInterfaceNameMessage.java b/src/main/java/li/cil/oc2/common/network/message/BusInterfaceNameMessage.java new file mode 100644 index 00000000..2d654740 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/network/message/BusInterfaceNameMessage.java @@ -0,0 +1,82 @@ +package li.cil.oc2.common.network.message; + +import li.cil.oc2.common.network.MessageUtils; +import li.cil.oc2.common.tileentity.BusCableTileEntity; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.vector.Vector3d; +import net.minecraftforge.fml.network.NetworkEvent; + +import java.util.function.Supplier; + +public abstract class BusInterfaceNameMessage { + private BlockPos pos; + private Direction side; + private String value; + + /////////////////////////////////////////////////////////////////// + + protected BusInterfaceNameMessage(final BusCableTileEntity tileEntity, final Direction side, final String value) { + this.pos = tileEntity.getPos(); + this.side = side; + this.value = value; + } + + protected BusInterfaceNameMessage(final PacketBuffer buffer) { + fromBytes(buffer); + } + + /////////////////////////////////////////////////////////////////// + + public static boolean handleMessageClient(final BusInterfaceNameMessage message, final Supplier context) { + context.get().enqueueWork(() -> MessageUtils.withClientTileEntityAt(message.pos, BusCableTileEntity.class, + (tileEntity) -> tileEntity.setInterfaceName(message.side, message.value))); + return true; + } + + public static boolean handleMessageServer(final BusInterfaceNameMessage message, final Supplier context) { + context.get().enqueueWork(() -> MessageUtils.withServerTileEntityAt(context, message.pos, BusCableTileEntity.class, + (tileEntity) -> { + final Vector3d busCableCenter = Vector3d.copyCentered(tileEntity.getPos()); + if (context.get().getSender().getDistanceSq(busCableCenter) <= 8 * 8) { + tileEntity.setInterfaceName(message.side, message.value); + } + })); + return true; + } + + public void fromBytes(final PacketBuffer buffer) { + pos = buffer.readBlockPos(); + side = buffer.readEnumValue(Direction.class); + value = buffer.readString(32); + } + + public static void toBytes(final BusInterfaceNameMessage message, final PacketBuffer buffer) { + buffer.writeBlockPos(message.pos); + buffer.writeEnumValue(message.side); + buffer.writeString(message.value, 32); + } + + /////////////////////////////////////////////////////////////////// + + public static final class ToClient extends BusInterfaceNameMessage { + public ToClient(final BusCableTileEntity tileEntity, final Direction side, final String value) { + super(tileEntity, side, value); + } + + public ToClient(final PacketBuffer buffer) { + super(buffer); + } + } + + public static final class ToServer extends BusInterfaceNameMessage { + public ToServer(final BusCableTileEntity tileEntity, final Direction side, final String value) { + super(tileEntity, side, value); + } + + public ToServer(final PacketBuffer buffer) { + super(buffer); + } + } +} diff --git a/src/main/java/li/cil/oc2/common/tileentity/BusCableTileEntity.java b/src/main/java/li/cil/oc2/common/tileentity/BusCableTileEntity.java index 62057e92..231cedd6 100644 --- a/src/main/java/li/cil/oc2/common/tileentity/BusCableTileEntity.java +++ b/src/main/java/li/cil/oc2/common/tileentity/BusCableTileEntity.java @@ -2,25 +2,37 @@ package li.cil.oc2.common.tileentity; import li.cil.oc2.client.model.BusCableBakedModel; import li.cil.oc2.common.Config; +import li.cil.oc2.common.Constants; import li.cil.oc2.common.block.BusCableBlock; import li.cil.oc2.common.bus.TileEntityDeviceBusElement; +import li.cil.oc2.common.bus.device.rpc.TypeNameRPCDevice; +import li.cil.oc2.common.bus.device.util.BlockDeviceInfo; import li.cil.oc2.common.capabilities.Capabilities; +import li.cil.oc2.common.network.Network; +import li.cil.oc2.common.network.message.BusInterfaceNameMessage; import li.cil.oc2.common.util.NBTTagIds; import net.minecraft.block.BlockState; import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.ListNBT; +import net.minecraft.nbt.StringNBT; import net.minecraft.util.Direction; +import net.minecraft.util.StringUtils; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.client.model.data.IModelData; import javax.annotation.Nullable; +import java.util.HashSet; +import java.util.Objects; public final class BusCableTileEntity extends AbstractTileEntity { private static final String BUS_ELEMENT_TAG_NAME = "busElement"; + private static final String INTERFACE_NAMES_TAG_NAME = "interfaceNames"; /////////////////////////////////////////////////////////////////// private final TileEntityDeviceBusElement busElement = new BusCableBusElement(); + private final String[] interfaceNames = new String[Constants.BLOCK_FACE_COUNT]; /////////////////////////////////////////////////////////////////// @@ -30,6 +42,25 @@ public final class BusCableTileEntity extends AbstractTileEntity { /////////////////////////////////////////////////////////////////// + public String getInterfaceName(final Direction side) { + final String interfaceName = interfaceNames[side.getIndex()]; + return interfaceName == null ? "" : interfaceName; + } + + public void setInterfaceName(final Direction side, final String name) { + final String validatedName = validateName(name); + if (Objects.equals(validatedName, interfaceNames[side.getIndex()])) { + return; + } + + interfaceNames[side.getIndex()] = validatedName; + if (!getWorld().isRemote()) { + final BusInterfaceNameMessage message = new BusInterfaceNameMessage.ToClient(this, side, interfaceNames[side.getIndex()]); + Network.sendToClientsTrackingChunk(message, getWorld().getChunkAt(getPos())); + handleNeighborChanged(getPos().offset(side)); + } + } + public void handleNeighborChanged(final BlockPos pos) { busElement.handleNeighborChanged(pos); @@ -44,24 +75,54 @@ public final class BusCableTileEntity extends AbstractTileEntity { // TODO Remove if https://github.com/MinecraftForge/MinecraftForge/pull/7595 gets merged. requestModelDataUpdate(); } else { + // Whenever they type changes we can clear it. Technically only needed + // for the interface->none transition, but all others are no-ops, so + // we can just do this. + setInterfaceName(side, ""); + invalidateCapability(Capabilities.DEVICE_BUS_ELEMENT, side); handleNeighborChanged(getPos().offset(side)); } } + @Override + public void remove() { + super.remove(); + + // Bus element will usually be discovered via bus scan, not via capability request, so + // automatic invalidation via capability will *not* necessarily schedule a scan on the + // controller of our current bus. So we need to trigger that manually. + busElement.scheduleScan(); + } + + @Override + public CompoundNBT getUpdateTag() { + final CompoundNBT tag = super.getUpdateTag(); + + tag.put(INTERFACE_NAMES_TAG_NAME, serializeInterfaceNames()); + + return tag; + } + + @Override + public void handleUpdateTag(final BlockState state, final CompoundNBT tag) { + deserializeInterfaceNames(tag.getList(INTERFACE_NAMES_TAG_NAME, NBTTagIds.TAG_STRING)); + } + @Override public CompoundNBT write(CompoundNBT tag) { tag = super.write(tag); tag.put(BUS_ELEMENT_TAG_NAME, busElement.serializeNBT()); + tag.put(INTERFACE_NAMES_TAG_NAME, serializeInterfaceNames()); + return tag; } @Override public void read(final BlockState state, final CompoundNBT tag) { super.read(state, tag); - if (tag.contains(BUS_ELEMENT_TAG_NAME, NBTTagIds.TAG_LIST)) { - busElement.deserializeNBT(tag.getList(BUS_ELEMENT_TAG_NAME, NBTTagIds.TAG_COMPOUND)); - } + busElement.deserializeNBT(tag.getList(BUS_ELEMENT_TAG_NAME, NBTTagIds.TAG_COMPOUND)); + deserializeInterfaceNames(tag.getList(INTERFACE_NAMES_TAG_NAME, NBTTagIds.TAG_STRING)); } /////////////////////////////////////////////////////////////////// @@ -80,16 +141,6 @@ public final class BusCableTileEntity extends AbstractTileEntity { busElement.initialize(); } - @Override - public void remove() { - super.remove(); - - // Bus element will usually be discovered via bus scan, not via capability request, so - // automatic invalidation via capability will *not* necessarily schedule a scan on the - // controller of our current bus. So we need to trigger that manually. - busElement.scheduleScan(); - } - // TODO Remove if https://github.com/MinecraftForge/MinecraftForge/pull/7595 gets merged. @Override public IModelData getModelData() { @@ -102,6 +153,28 @@ public final class BusCableTileEntity extends AbstractTileEntity { /////////////////////////////////////////////////////////////////// + private ListNBT serializeInterfaceNames() { + final ListNBT tag = new ListNBT(); + for (int i = 0; i < Constants.BLOCK_FACE_COUNT; i++) { + tag.add(StringNBT.valueOf(getInterfaceName(Direction.byIndex(i)))); + } + return tag; + } + + private void deserializeInterfaceNames(final ListNBT tag) { + for (int i = 0; i < Constants.BLOCK_FACE_COUNT; i++) { + final String name = tag.getString(i).trim(); + interfaceNames[i] = name.substring(0, Math.min(32, name.length())); + } + } + + private static String validateName(final String name) { + final String trimmed = name.trim(); + return trimmed.length() > 32 ? trimmed.substring(0, 32) : trimmed; + } + + /////////////////////////////////////////////////////////////////// + private final class BusCableBusElement extends TileEntityDeviceBusElement { public BusCableBusElement() { super(BusCableTileEntity.this); @@ -120,6 +193,15 @@ public final class BusCableTileEntity extends AbstractTileEntity { return connectionType == BusCableBlock.ConnectionType.INTERFACE; } + @Override + protected void collectSyntheticDevices(final World world, final BlockPos pos, final Direction direction, final HashSet devices) { + super.collectSyntheticDevices(world, pos, direction, devices); + final String interfaceName = interfaceNames[direction.getIndex()]; + if (!StringUtils.isNullOrEmpty(interfaceName)) { + devices.add(new BlockDeviceInfo(null, new TypeNameRPCDevice(interfaceName))); + } + } + @Override public double getEnergyConsumption() { return super.getEnergyConsumption() diff --git a/src/main/resources/assets/oc2/lang/en_us.json b/src/main/resources/assets/oc2/lang/en_us.json index f5ccccf5..fa9b4cd6 100644 --- a/src/main/resources/assets/oc2/lang/en_us.json +++ b/src/main/resources/assets/oc2/lang/en_us.json @@ -62,7 +62,9 @@ "tooltip.oc2.memory_missing": "Some memory is required to load the flash memory for execution to boot.", "tooltip.oc2.hard_drive_missing": "Most systems will require a root file system to boot.", "tooltip.oc2.energy": "Energy: %s", - "tooltip.oc2.energyConsumption": "Energy Consumption: %s", + "tooltip.oc2.energyConsumption": "Energy Consumption: %s/t", + "tooltip.oc2.confirm": "Confirm", + "tooltip.oc2.cancel": "Cancel", "subtitles.oc2.computer": "Computer fans running", "subtitles.oc2.floppy": "Floppy access", diff --git a/src/main/resources/assets/oc2/textures/gui/screen/bus_interface.png b/src/main/resources/assets/oc2/textures/gui/screen/bus_interface.png new file mode 100644 index 0000000000000000000000000000000000000000..b1c1e7b34e8d191bc96e9ba5ed6afc8a957c15a7 GIT binary patch literal 3489 zcmdT`YgAKL7QV=c6{unG&_W)yfcJEepCIv7HUO;bXfjqWwv9JYD1pa=0GRV(>t;moGD#$8=z!ot< z?Ot+5R#Y?xhDBr?E;2F_8@U||i9>M&2L}fno`@q7Z4nDwNt6&~$ZUm@%_<5FhZ`ti zin$_~3kgvwPDThMg{f#Xf}@tmSbOeh5Gu#+3k}DBUB-rAKw#1#jL_C>bPbS%8@B}hmL#4Kd zSX_40Us(jjJS8EqAu}2O+|zLRVofkg@vS|aac~}Pi}N66^+ENM55r{ zl2K#uhjE4+L9>U9No3yQmIp4fSRUD+5Si0rWFU8ztV>6RemoaxiMLt-0JbJelgsyM zyBK_a+s%E_<^96z*WdfvFOl<)uaey3ao(q_;`e7feP;A*=a`XIn{G*bJf}DBm)pAz z)dU)txEnau1U}f^m$`4n>JavYEa+JI_K~J|QmP*@y4QepBgz)vC&8Xf%lru!FjRBg zBF61OOIbH7;nr-_nYz=Z!XiJV#Hg5aow8fxZ>(=kW-OY!{%9P;&RZyRHB}nK=6ye75hu`Y zD)>GS-hj2sez8A^lYI79l#p)KJ!(R(?Y?>ZVW0Tt4_{YP8M~S~{EljQ zk4#)!ya0HU~@1_G4s3{ z^eowA?jUA$*OE6rOLfdVe^A zi>8i!a>HxX;$irpukez7dd1pxenz5h55POb2uN*XJ*=OZnc3f>Js8YpvpHbsmbiq3 z;^N}$QS-Zgc`dW_xpfYMgC@XU&lPb-DZp2EtbnbA)$d$7$_on#V`PBJQEsa4@9(b$ zM~`Z6?eRYtccN|^kQ&h15?353Fqr}BQQNf|8yiiY-M>=Zm2zO2Q2YCY7etDk&X>}u zBpZ6np$>f(k1X%9+pv%^8nb3<{1>QlmD0w%etPOUltAO16wB0L`mzLfw zvHZ2S4XV@+iz#1ll=Doa=|8j%uL{XI786rG97B%1$co)d_ln3`ZE@9Ww0$^oe&R{h z+&+hq%6}JJJyfXrg9lg>lV^KsYd1Ej5#))Y0_P9>Z{IXaDoVQQmzTS;L}&ha;>z6^slj!r?!-{q z<@(wTy~!W9JKyQllY}cfh9B%qE6^TD6j+-DQ;7bxJDTiz6#XsfF|pXAFBVHVnO$8A zbJzqf^=D(f)Bfxg2x<60XV$#(;do&bx9GdInKpL3>xQOP`6uF9!boFBZe1>)Dm^mY z%=-u*SjN`U8LB>5SPukO__^hl)S37`iQtprUO1T zy|3Ck)#KbZcb8KOv6m;ms34K?3xpIR$NIgGoSB(s=hc@B{%)}XNeEbgWhR?EOGam`8RvZiLgvFu%JZ<>C&cxCGO(C+cAOlykn@hk zN~I(_JG+Scw6i%EtZ4adzy0=GKwzNQ8g-hM-&Z{oO_e%KC=`mS*1-G6Q>