Interfaces can now be given a custom label.

This will act as an additional device name, which allows referencing them by this label.
This commit is contained in:
Florian Nücke
2021-03-07 15:15:03 +01:00
parent e49f6cd5a1
commit 40a2c5fa25
12 changed files with 515 additions and 40 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -128,6 +128,14 @@ public final class RPCDeviceBusAdapter implements Steppable {
final HashMap<RPCDeviceList, ArrayList<UUID>> 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);

View File

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

View File

@@ -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 <T> void sendToClientsTrackingChunk(final T message, final Chunk chunk) {

View File

@@ -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<NetworkEvent.Context> 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<NetworkEvent.Context> 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);
}
}
}

View File

@@ -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<BlockDeviceInfo> 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()

View File

@@ -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",

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB