Added network tunnel devices.

This commit is contained in:
Florian Nücke
2022-01-13 13:19:31 +01:00
parent c8a3001760
commit 11acafdae4
41 changed files with 761 additions and 11 deletions

View File

@@ -14,4 +14,5 @@ public final class DeviceTypes {
@ObjectHolder("card") public static DeviceType CARD = null;
@ObjectHolder("robot_module") public static DeviceType ROBOT_MODULE = null;
@ObjectHolder("floppy") public static DeviceType FLOPPY = null;
@ObjectHolder("network_tunnel") public static DeviceType NETWORK_TUNNEL = null;
}

View File

@@ -1,10 +1,7 @@
package li.cil.oc2.client;
import li.cil.oc2.api.bus.device.DeviceType;
import li.cil.oc2.client.gui.ComputerContainerScreen;
import li.cil.oc2.client.gui.ComputerTerminalScreen;
import li.cil.oc2.client.gui.RobotContainerScreen;
import li.cil.oc2.client.gui.RobotTerminalScreen;
import li.cil.oc2.client.gui.*;
import li.cil.oc2.client.item.CustomItemColors;
import li.cil.oc2.client.item.CustomItemModelProperties;
import li.cil.oc2.client.model.BusCableModelLoader;
@@ -55,6 +52,7 @@ public final class ClientSetup {
MenuScreens.register(Containers.COMPUTER_TERMINAL.get(), ComputerTerminalScreen::new);
MenuScreens.register(Containers.ROBOT.get(), RobotContainerScreen::new);
MenuScreens.register(Containers.ROBOT_TERMINAL.get(), RobotTerminalScreen::new);
MenuScreens.register(Containers.NETWORK_TUNNEL.get(), NetworkTunnelScreen::new);
ItemBlockRenderTypes.setRenderLayer(Blocks.BUS_CABLE.get(), renderType -> true);
Minecraft.getInstance().getBlockColors().register(new BusCableBlockColor(), Blocks.BUS_CABLE.get());

View File

@@ -0,0 +1,78 @@
package li.cil.oc2.client.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import li.cil.oc2.client.gui.widget.ImageButton;
import li.cil.oc2.common.container.NetworkTunnelContainer;
import li.cil.oc2.common.network.Network;
import li.cil.oc2.common.network.message.NetworkTunnelLinkMessage;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Inventory;
import static li.cil.oc2.common.util.TranslationUtils.text;
public final class NetworkTunnelScreen extends AbstractModContainerScreen<NetworkTunnelContainer> {
private static final int LINK_BUTTON_LEFT = 48;
private static final int LINK_BUTTON_TOP = 78;
private static final Component LINK_BUTTON_CAPTION = text("gui.{mod}.network_tunnel.link");
private ImageButton linkButton;
///////////////////////////////////////////////////////////////////
public NetworkTunnelScreen(final NetworkTunnelContainer container, final Inventory inventory, final Component title) {
super(container, inventory, title);
imageWidth = Sprites.NETWORK_TUNNEL_SCREEN.width;
imageHeight = Sprites.NETWORK_TUNNEL_SCREEN.height;
inventoryLabelY = imageHeight - 94;
}
///////////////////////////////////////////////////////////////////
@Override
public void render(final PoseStack stack, final int mouseX, final int mouseY, final float partialTicks) {
renderBackground(stack);
linkButton.active = getMenu().hasLinkSlotItem();
super.render(stack, mouseX, mouseY, partialTicks);
renderTooltip(stack, mouseX, mouseY);
}
///////////////////////////////////////////////////////////////////
@Override
protected void init() {
super.init();
linkButton = addRenderableWidget(new ImageButton(
leftPos + LINK_BUTTON_LEFT, topPos + LINK_BUTTON_TOP,
Sprites.NETWORK_TUNNEL_LINK_BUTTON_INACTIVE.width, Sprites.NETWORK_TUNNEL_LINK_BUTTON_INACTIVE.height,
Sprites.NETWORK_TUNNEL_LINK_BUTTON_INACTIVE,
Sprites.NETWORK_TUNNEL_LINK_BUTTON_ACTIVE) {
@Override
public void onPress() {
super.onPress();
createTunnel();
}
}).withMessage(LINK_BUTTON_CAPTION);
}
@Override
protected void renderBg(final PoseStack stack, final float partialTicks, final int mouseX, final int mouseY) {
RenderSystem.setShader(GameRenderer::getPositionTexShader);
RenderSystem.setShaderColor(1, 1, 1, 1);
Sprites.NETWORK_TUNNEL_SCREEN.draw(stack, leftPos, topPos);
}
///////////////////////////////////////////////////////////////////
private void createTunnel() {
final NetworkTunnelLinkMessage message = new NetworkTunnelLinkMessage(getMenu().containerId);
Network.INSTANCE.sendToServer(message);
}
}

View File

@@ -10,6 +10,7 @@ public final class Sprites {
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 NETWORK_TUNNEL_SCREEN = new Sprite(NETWORK_TUNNEL_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);
@@ -34,6 +35,9 @@ public final class Sprites {
public static final Sprite INVENTORY_BUTTON_INACTIVE = new Sprite(INVENTORY_BUTTON_TEXTURE, 12, 12, 1, 1);
public static final Sprite INVENTORY_BUTTON_ACTIVE = new Sprite(INVENTORY_BUTTON_TEXTURE, 12, 12, 15, 1);
public static final Sprite NETWORK_TUNNEL_LINK_BUTTON_INACTIVE = new Sprite(NETWORK_TUNNEL_LINK_BUTTON_TEXTURE, 80, 20, 0, 0);
public static final Sprite NETWORK_TUNNEL_LINK_BUTTON_ACTIVE = new Sprite(NETWORK_TUNNEL_LINK_BUTTON_TEXTURE, 80, 20, 0, 20);
public static final Sprite CONFIRM_PRESSED = new Sprite(CONFIRM_BUTTON_TEXTURE, 12, 12, 14, 1);
public static final Sprite CONFIRM_BASE = new Sprite(CONFIRM_BUTTON_TEXTURE, 12, 12, 1, 1);
public static final Sprite CANCEL_PRESSED = new Sprite(CANCEL_BUTTON_TEXTURE, 12, 12, 14, 1);

View File

@@ -8,6 +8,7 @@ public final class Textures {
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 NETWORK_TUNNEL_SCREEN_TEXTURE = new Texture("textures/gui/widget/network_tunnel_screen.png", 176, 197);
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);
@@ -27,4 +28,5 @@ public final class Textures {
public static final Texture POWER_BUTTON_TEXTURE = new Texture("textures/gui/widget/power_button.png", 42, 14);
public static final Texture INPUT_BUTTON_TEXTURE = new Texture("textures/gui/widget/input_button.png", 42, 14);
public static final Texture INVENTORY_BUTTON_TEXTURE = new Texture("textures/gui/widget/inventory_button.png", 28, 14);
public static final Texture NETWORK_TUNNEL_LINK_BUTTON_TEXTURE = new Texture("textures/gui/widget/network_tunnel_link_button.png", 80, 40);
}

View File

@@ -32,6 +32,7 @@ public final class Config {
@Path("energy.items") public static int soundCardEnergyPerTick = 1;
@Path("energy.items") public static int blockOperationsModuleEnergyPerTick = 2;
@Path("energy.items") public static int inventoryOperationsModuleEnergyPerTick = 1;
@Path("energy.items") public static int networkTunnelEnergyPerTick = 2;
@Path("gameplay") public static ResourceLocation blockOperationsModuleToolTier = TierSortingRegistry.getName(Tiers.DIAMOND);

View File

@@ -32,6 +32,7 @@ public final class DeviceTypes {
register(ItemTags.DEVICES_CARD);
register(ItemTags.DEVICES_ROBOT_MODULE);
register(ItemTags.DEVICES_FLOPPY);
register(ItemTags.DEVICES_NETWORK_TUNNEL);
}
///////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,112 @@
package li.cil.oc2.common.bus.device.item;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import li.cil.oc2.api.bus.device.vm.VMDeviceLoadResult;
import li.cil.oc2.api.bus.device.vm.context.VMContext;
import li.cil.oc2.api.capabilities.NetworkInterface;
import li.cil.oc2.common.item.NetworkTunnelItem;
import li.cil.oc2.common.util.TickUtils;
import net.minecraft.core.Direction;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.server.ServerStoppedEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.time.Duration;
import java.util.*;
public final class NetworkTunnelItemDevice extends AbstractNetworkInterfaceItemDevice {
public NetworkTunnelItemDevice(final ItemStack identity) {
super(identity);
}
///////////////////////////////////////////////////////////////
@NotNull
@Override
public <T> LazyOptional<T> getCapability(final Capability<T> cap, @Nullable final Direction side) {
return LazyOptional.empty();
}
@Override
public VMDeviceLoadResult mount(final VMContext context) {
final VMDeviceLoadResult result = super.mount(context);
if (result.wasSuccessful()) {
NetworkTunnelItem.getTunnelId(identity).ifPresent(id ->
TunnelManager.registerEndpoint(id, getNetworkInterface()));
}
return result;
}
@Override
public void unmount() {
super.unmount();
TunnelManager.unregisterEndpoint(getNetworkInterface());
}
///////////////////////////////////////////////////////////////
@Mod.EventBusSubscriber
private static final class TunnelManager {
private static final int BYTES_PER_TICK = 32 * 1024 / TickUtils.toTicks(Duration.ofSeconds(1)); // bytes / sec -> bytes / tick
private static final int MIN_ETHERNET_FRAME_SIZE = 42;
private static final BiMap<UUID, Set<NetworkInterface>> TUNNELS = HashBiMap.create();
public static void registerEndpoint(final UUID id, final NetworkInterface networkInterface) {
TUNNELS.computeIfAbsent(id, unused -> new HashSet<>())
.add(networkInterface);
}
public static void unregisterEndpoint(final NetworkInterface networkInterface) {
for (final Set<NetworkInterface> tunnel : TUNNELS.values()) {
tunnel.remove(networkInterface);
}
}
@SubscribeEvent
public static void handleServerTick(final TickEvent.ServerTickEvent event) {
if (event.phase == TickEvent.Phase.START) {
pumpMessages();
}
}
@SubscribeEvent
public static void handleServerStopped(final ServerStoppedEvent event) {
TUNNELS.clear();
}
private static void pumpMessages() {
final Iterator<Set<NetworkInterface>> iterator = TUNNELS.values().iterator();
while (iterator.hasNext()) {
final Set<NetworkInterface> tunnel = iterator.next();
if (tunnel.isEmpty()) {
iterator.remove();
} else {
pumpMessages(tunnel);
}
}
}
private static void pumpMessages(final Collection<NetworkInterface> tunnel) {
for (final NetworkInterface source : tunnel) {
int byteBudget = BYTES_PER_TICK;
byte[] frame;
while ((frame = source.readEthernetFrame()) != null && byteBudget > 0) {
byteBudget -= Math.max(frame.length, MIN_ETHERNET_FRAME_SIZE); // Avoid bogus packets messing with us.
for (final NetworkInterface destination : tunnel) {
if (destination != source) {
destination.writeEthernetFrame(source, frame, 1);
}
}
}
}
}
}
}

View File

@@ -39,10 +39,12 @@ public final class Providers {
ITEM_DEVICE_PROVIDERS.register("flash_memory_custom", FlashMemoryWithExternalDataItemDeviceProvider::new);
ITEM_DEVICE_PROVIDERS.register("redstone_interface_card", RedstoneInterfaceCardItemDeviceProvider::new);
ITEM_DEVICE_PROVIDERS.register("network_interface_card", NetworkInterfaceCardItemDeviceProvider::new);
ITEM_DEVICE_PROVIDERS.register("network_tunnel_card", NetworkTunnelCardItemDeviceProvider::new);
ITEM_DEVICE_PROVIDERS.register("file_import_export_card", FileImportExportCardItemDeviceProvider::new);
ITEM_DEVICE_PROVIDERS.register("sound_card", SoundCardItemDeviceProvider::new);
ITEM_DEVICE_PROVIDERS.register("inventory_operations_module", InventoryOperationsModuleDeviceProvider::new);
ITEM_DEVICE_PROVIDERS.register("block_operations_module", BlockOperationsModuleDeviceProvider::new);
ITEM_DEVICE_PROVIDERS.register("network_tunnel_module", NetworkTunnelModuleItemDeviceProvider::new);
}
}

View File

@@ -0,0 +1,28 @@
package li.cil.oc2.common.bus.device.provider.item;
import li.cil.oc2.api.bus.device.ItemDevice;
import li.cil.oc2.api.bus.device.provider.ItemDeviceQuery;
import li.cil.oc2.common.Config;
import li.cil.oc2.common.bus.device.item.NetworkTunnelItemDevice;
import li.cil.oc2.common.bus.device.provider.util.AbstractItemDeviceProvider;
import li.cil.oc2.common.item.Items;
import java.util.Optional;
public final class NetworkTunnelCardItemDeviceProvider extends AbstractItemDeviceProvider {
public NetworkTunnelCardItemDeviceProvider() {
super(Items.NETWORK_TUNNEL_CARD);
}
///////////////////////////////////////////////////////////////////
@Override
protected Optional<ItemDevice> getItemDevice(final ItemDeviceQuery query) {
return Optional.of(new NetworkTunnelItemDevice(query.getItemStack()));
}
@Override
protected int getItemDeviceEnergyConsumption(final ItemDeviceQuery query) {
return Config.networkTunnelEnergyPerTick;
}
}

View File

@@ -0,0 +1,28 @@
package li.cil.oc2.common.bus.device.provider.item;
import li.cil.oc2.api.bus.device.ItemDevice;
import li.cil.oc2.api.bus.device.provider.ItemDeviceQuery;
import li.cil.oc2.common.Config;
import li.cil.oc2.common.bus.device.item.NetworkTunnelItemDevice;
import li.cil.oc2.common.bus.device.provider.util.AbstractItemDeviceProvider;
import li.cil.oc2.common.item.Items;
import java.util.Optional;
public final class NetworkTunnelModuleItemDeviceProvider extends AbstractItemDeviceProvider {
public NetworkTunnelModuleItemDeviceProvider() {
super(Items.NETWORK_TUNNEL_MODULE);
}
///////////////////////////////////////////////////////////////////
@Override
protected Optional<ItemDevice> getItemDevice(final ItemDeviceQuery query) {
return Optional.of(new NetworkTunnelItemDevice(query.getItemStack()));
}
@Override
protected int getItemDeviceEnergyConsumption(final ItemDeviceQuery query) {
return Config.networkTunnelEnergyPerTick;
}
}

View File

@@ -16,6 +16,7 @@ public final class Containers {
public static final RegistryObject<MenuType<ComputerTerminalContainer>> COMPUTER_TERMINAL = CONTAINERS.register("computer_terminal", () -> IForgeMenuType.create(ComputerTerminalContainer::createClient));
public static final RegistryObject<MenuType<RobotInventoryContainer>> ROBOT = CONTAINERS.register("robot", () -> IForgeMenuType.create(RobotInventoryContainer::createClient));
public static final RegistryObject<MenuType<RobotTerminalContainer>> ROBOT_TERMINAL = CONTAINERS.register("robot_terminal", () -> IForgeMenuType.create(RobotTerminalContainer::createClient));
public static final RegistryObject<MenuType<NetworkTunnelContainer>> NETWORK_TUNNEL = CONTAINERS.register("network_tunnel", () -> IForgeMenuType.create(NetworkTunnelContainer::createClient));
///////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,40 @@
package li.cil.oc2.common.container;
import com.mojang.datafixers.util.Pair;
import li.cil.oc2.api.bus.device.DeviceType;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.Container;
import net.minecraft.world.inventory.InventoryMenu;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
public final class DeviceTypeSlot extends Slot {
private final DeviceType deviceType;
public DeviceTypeSlot(final Container container, final DeviceType deviceType, final int index, final int x, final int y) {
super(container, index, x, y);
this.deviceType = deviceType;
}
public DeviceType getDeviceType() {
return deviceType;
}
@Override
public boolean mayPlace(@NotNull final ItemStack stack) {
return super.mayPlace(stack) && stack.is(deviceType.getTag());
}
@Nullable
@Override
public Pair<ResourceLocation, ResourceLocation> getNoItemIcon() {
if (hasItem()) {
return super.getNoItemIcon();
} else {
return Pair.of(InventoryMenu.BLOCK_ATLAS, deviceType.getBackgroundIcon());
}
}
}

View File

@@ -0,0 +1,116 @@
package li.cil.oc2.common.container;
import li.cil.oc2.api.bus.device.DeviceTypes;
import li.cil.oc2.common.item.NetworkTunnelItem;
import li.cil.oc2.common.tags.ItemTags;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.network.NetworkHooks;
import java.util.UUID;
public final class NetworkTunnelContainer extends AbstractContainer {
public static void createServer(final ServerPlayer player, final InteractionHand hand) {
NetworkHooks.openGui(player, new MenuProvider() {
@Override
public Component getDisplayName() {
return player.getItemInHand(hand).getItem().getDescription();
}
@Override
public AbstractContainerMenu createMenu(final int id, final Inventory inventory, final Player player) {
return new NetworkTunnelContainer(id, player, hand);
}
}, b -> b.writeEnum(hand));
}
public static NetworkTunnelContainer createClient(final int id, final Inventory inventory, final FriendlyByteBuf data) {
final InteractionHand hand = data.readEnum(InteractionHand.class);
final Player player = inventory.player;
return new NetworkTunnelContainer(id, player, hand);
}
///////////////////////////////////////////////////////////////////
private final Player player;
private final InteractionHand hand;
private final Container linkSlot = new SimpleContainer(1);
///////////////////////////////////////////////////////////////////
private NetworkTunnelContainer(final int id, final Player player, final InteractionHand hand) {
super(Containers.NETWORK_TUNNEL.get(), id);
this.player = player;
this.hand = hand;
createPlayerInventoryAndHotbarSlots(player.getInventory(), 8, 115);
addSlot(new LockedSlot(player.getInventory(), getHandSlot(), 80, 25));
addSlot(new DeviceTypeSlot(linkSlot, DeviceTypes.NETWORK_TUNNEL, 0, 80, 51));
}
///////////////////////////////////////////////////////////////////
public boolean hasLinkSlotItem() {
return !linkSlot.getItem(0).isEmpty();
}
public void createTunnel() {
final ItemStack tunnelA = player.getItemInHand(hand);
final ItemStack tunnelB = linkSlot.getItem(0);
if (tunnelA.isEmpty() ||
tunnelB.isEmpty() ||
!tunnelA.is(ItemTags.DEVICES_NETWORK_TUNNEL) ||
!tunnelB.is(ItemTags.DEVICES_NETWORK_TUNNEL)) {
return;
}
final UUID id = UUID.randomUUID();
NetworkTunnelItem.setTunnelId(tunnelA, id);
NetworkTunnelItem.setTunnelId(tunnelB, id);
}
@Override
public void removed(final Player player) {
super.removed(player);
if (!player.getLevel().isClientSide()) {
clearContainer(player, linkSlot);
}
}
@Override
public boolean stillValid(final Player player) {
return player.getItemInHand(hand).is(ItemTags.DEVICES_NETWORK_TUNNEL);
}
///////////////////////////////////////////////////////////////////
@Override
protected boolean isSlotLocked(final Inventory inventory, final int slot) {
return inventory.getItem(slot) == player.getItemInHand(hand);
}
///////////////////////////////////////////////////////////////////
private int getHandSlot() {
final Inventory inventory = player.getInventory();
for (int slot = 0; slot < inventory.getContainerSize(); slot++) {
if (inventory.getItem(slot) == player.getItemInHand(hand)) {
return slot;
}
}
return -1;
}
}

View File

@@ -64,11 +64,13 @@ public final class Items {
public static final RegistryObject<Item> REDSTONE_INTERFACE_CARD = register("redstone_interface_card");
public static final RegistryObject<Item> NETWORK_INTERFACE_CARD = register("network_interface_card", NetworkInterfaceCardItem::new);
public static final RegistryObject<Item> NETWORK_TUNNEL_CARD = register("network_tunnel_card", NetworkTunnelItem::new);
public static final RegistryObject<Item> FILE_IMPORT_EXPORT_CARD = register("file_import_export_card");
public static final RegistryObject<Item> SOUND_CARD = register("sound_card");
public static final RegistryObject<Item> INVENTORY_OPERATIONS_MODULE = register("inventory_operations_module");
public static final RegistryObject<Item> BLOCK_OPERATIONS_MODULE = register("block_operations_module", BlockOperationsModule::new);
public static final RegistryObject<Item> NETWORK_TUNNEL_MODULE = register("network_tunnel_module", NetworkTunnelItem::new);
public static final RegistryObject<Item> TRANSISTOR = register("transistor", ModItem::new);
public static final RegistryObject<Item> CIRCUIT_BOARD = register("circuit_board", ModItem::new);

View File

@@ -0,0 +1,78 @@
package li.cil.oc2.common.item;
import li.cil.oc2.common.container.NetworkTunnelContainer;
import li.cil.oc2.common.util.ItemStackUtils;
import li.cil.oc2.common.util.TextFormatUtils;
import net.minecraft.ChatFormatting;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.StringUtil;
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 org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import static li.cil.oc2.common.util.TranslationUtils.key;
public final class NetworkTunnelItem extends ModItem {
private static final String TUNNEL_ID_TAG_NAME = "tunnel";
private static final String TUNNEL_ID_TEXT = key("tooltip.{mod}.network_tunnel_id");
///////////////////////////////////////////////////////////////////
public NetworkTunnelItem() {
super(createProperties().stacksTo(1));
}
///////////////////////////////////////////////////////////////////
public static Optional<UUID> getTunnelId(final ItemStack stack) {
final CompoundTag tag = ItemStackUtils.getModDataTag(stack);
if (tag.hasUUID(TUNNEL_ID_TAG_NAME)) {
return Optional.of(tag.getUUID(TUNNEL_ID_TAG_NAME));
} else {
return Optional.empty();
}
}
public static void setTunnelId(final ItemStack stack, final UUID value) {
ItemStackUtils.getOrCreateModDataTag(stack).putUUID(TUNNEL_ID_TAG_NAME, value);
}
///////////////////////////////////////////////////////////////////
@Override
public void appendHoverText(final ItemStack stack, @Nullable final Level level, final List<Component> tooltip, final TooltipFlag flag) {
super.appendHoverText(stack, level, tooltip, flag);
getTunnelId(stack).ifPresent(id -> {
final String idString = StringUtil.truncateStringIfNecessary(id.toString(), 8 + 3, true);
final MutableComponent idComponent = TextFormatUtils.withFormat(idString, ChatFormatting.GREEN);
tooltip.add(new TranslatableComponent(TUNNEL_ID_TEXT, idComponent).withStyle(ChatFormatting.GRAY));
});
}
@Override
public InteractionResultHolder<ItemStack> use(final Level level, final Player player, final InteractionHand hand) {
if (!level.isClientSide() && player instanceof ServerPlayer serverPlayer) {
openContainerScreen(serverPlayer, hand);
}
return InteractionResultHolder.sidedSuccess(player.getItemInHand(hand), level.isClientSide());
}
///////////////////////////////////////////////////////////////////
private void openContainerScreen(final ServerPlayer player, final InteractionHand hand) {
NetworkTunnelContainer.createServer(player, hand);
}
}

View File

@@ -68,6 +68,7 @@ public final class Network {
registerMessage(BusCableFacadeMessage.class, BusCableFacadeMessage::new, NetworkDirection.PLAY_TO_CLIENT);
registerMessage(NetworkInterfaceCardConfigurationMessage.class, NetworkInterfaceCardConfigurationMessage::new, NetworkDirection.PLAY_TO_SERVER);
registerMessage(NetworkTunnelLinkMessage.class, NetworkTunnelLinkMessage::new, NetworkDirection.PLAY_TO_SERVER);
}
public static <T> void sendToClientsTrackingChunk(final T message, final LevelChunk chunk) {

View File

@@ -0,0 +1,52 @@
package li.cil.oc2.common.network.message;
import li.cil.oc2.common.container.NetworkTunnelContainer;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraftforge.network.NetworkEvent;
public final class NetworkTunnelLinkMessage extends AbstractMessage {
private int containerId;
///////////////////////////////////////////////////////////////////
public NetworkTunnelLinkMessage(final int containerId) {
this.containerId = containerId;
}
public NetworkTunnelLinkMessage(final FriendlyByteBuf buffer) {
super(buffer);
}
///////////////////////////////////////////////////////////////////
@Override
public void fromBytes(final FriendlyByteBuf buffer) {
containerId = buffer.readVarInt();
}
@Override
public void toBytes(final FriendlyByteBuf buffer) {
buffer.writeVarInt(containerId);
}
///////////////////////////////////////////////////////////////////
@Override
protected void handleMessage(final NetworkEvent.Context context) {
final ServerPlayer player = context.getSender();
if (player == null) {
return;
}
final AbstractContainerMenu container = player.containerMenu;
if (container.containerId != containerId) {
return;
}
if (container instanceof NetworkTunnelContainer networkTunnelContainer) {
networkTunnelContainer.createTunnel();
}
}
}

View File

@@ -15,6 +15,7 @@ public final class ItemTags {
public static final Tags.IOptionalNamedTag<Item> DEVICES_CARD = tag("devices/card");
public static final Tags.IOptionalNamedTag<Item> DEVICES_ROBOT_MODULE = tag("devices/robot_module");
public static final Tags.IOptionalNamedTag<Item> DEVICES_FLOPPY = tag("devices/floppy");
public static final Tags.IOptionalNamedTag<Item> DEVICES_NETWORK_TUNNEL = tag("devices/network_tunnel");
public static final Tags.IOptionalNamedTag<Item> CABLES = tag("cables");
public static final Tags.IOptionalNamedTag<Item> WRENCHES = tag("wrenches");

View File

@@ -43,9 +43,11 @@ public final class ModItemModelProvider extends ItemModelProvider {
simple(Items.NETWORK_INTERFACE_CARD);
simple(Items.FILE_IMPORT_EXPORT_CARD);
simple(Items.SOUND_CARD);
simple(Items.NETWORK_TUNNEL_CARD);
simple(Items.INVENTORY_OPERATIONS_MODULE);
simple(Items.BLOCK_OPERATIONS_MODULE);
simple(Items.NETWORK_TUNNEL_MODULE);
simple(Items.TRANSISTOR);
simple(Items.CIRCUIT_BOARD);

View File

@@ -53,11 +53,17 @@ public final class ModItemTagsProvider extends ItemTagsProvider {
Items.REDSTONE_INTERFACE_CARD.get(),
Items.NETWORK_INTERFACE_CARD.get(),
Items.FILE_IMPORT_EXPORT_CARD.get(),
Items.SOUND_CARD.get()
Items.SOUND_CARD.get(),
Items.NETWORK_TUNNEL_CARD.get()
);
tag(DEVICES_ROBOT_MODULE).add(
Items.INVENTORY_OPERATIONS_MODULE.get(),
Items.BLOCK_OPERATIONS_MODULE.get()
Items.BLOCK_OPERATIONS_MODULE.get(),
Items.NETWORK_TUNNEL_MODULE.get()
);
tag(DEVICES_NETWORK_TUNNEL).add(
Items.NETWORK_TUNNEL_CARD.get(),
Items.NETWORK_TUNNEL_MODULE.get()
);
tag(WRENCHES).add(Items.WRENCH.get());
@@ -73,7 +79,9 @@ public final class ModItemTagsProvider extends ItemTagsProvider {
Items.FLASH_MEMORY.get(),
Items.FLASH_MEMORY_CUSTOM.get(),
Items.NETWORK_INTERFACE_CARD.get(),
Items.DISK_DRIVE.get()
Items.DISK_DRIVE.get(),
Items.NETWORK_TUNNEL_CARD.get(),
Items.NETWORK_TUNNEL_MODULE.get()
);
}
}

View File

@@ -273,6 +273,17 @@ public final class ModRecipesProvider extends RecipeProvider {
.unlockedBy("has_computer", inventoryChange(Items.COMPUTER.get()))
.save(consumer);
ShapedRecipeBuilder
.shaped(Items.NETWORK_TUNNEL_CARD.get())
.pattern("IET")
.pattern(" B ")
.define('E', Tags.Items.ENDER_PEARLS)
.define('I', Tags.Items.INGOTS_IRON)
.define('T', Items.TRANSISTOR.get())
.define('B', Items.CIRCUIT_BOARD.get())
.unlockedBy("has_computer", inventoryChange(Items.COMPUTER.get()))
.save(consumer);
ShapedRecipeBuilder
.shaped(Items.FILE_IMPORT_EXPORT_CARD.get())
.pattern("IET")
@@ -329,6 +340,17 @@ public final class ModRecipesProvider extends RecipeProvider {
.unlockedBy("has_robot", inventoryChange(Items.ROBOT.get()))
.save(consumer);
ShapedRecipeBuilder
.shaped(Items.NETWORK_TUNNEL_MODULE.get())
.pattern("TEG")
.pattern(" B ")
.define('T', Items.TRANSISTOR.get())
.define('E', Tags.Items.ENDER_PEARLS)
.define('G', Tags.Items.INGOTS_GOLD)
.define('B', Items.CIRCUIT_BOARD.get())
.unlockedBy("has_robot", inventoryChange(Items.ROBOT.get()))
.save(consumer);
ShapedRecipeBuilder
.shaped(Items.TRANSISTOR.get(), 8)

View File

@@ -11,7 +11,9 @@ This index lists all documented items. If you're looking for a block, see the [b
- [Memory](memory.md)
- [Network Cable](network_cable.md)
- [Network Interface Card](network_interface_card.md)
- [Network Tunnel Card](network_tunnel_card.md)
- [Network Tunnel Module](network_tunnel_module.md)
- [Redstone Interface Card](redstone_interface_card.md)
- [Robot](robot.md)
- [Scrench](wrench.md)
- [Sound Card](sound_card.md)
- [Sound Card](sound_card.md)

View File

@@ -0,0 +1,12 @@
# Network Tunnel Card
![Tunnel vision?](item:oc2:network_tunnel_card)
The network tunnel card allows [computers](../block/computer.md) to send messages to and receive messages from another tunnel device (tunnel cards and [tunnel modules](network_tunnel_module.md)) linked to the card.
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.
To link two tunnel devices, open their configuration interface (use while holding), and insert the other tunnel device to link to. This allows linking any two network tunnel devices.
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.

View File

@@ -0,0 +1,12 @@
# Network Tunnel Module
![Tunnel vision?](item:oc2:network_tunnel_module)
The network tunnel module allows [robots](robot.md) to send messages to and receive messages from another tunnel device (tunnel modules and [tunnel cards](network_tunnel_card.md)) linked to the module.
Robots *have to be shut down* before installing or removing this component. Installing it while the robot is running will have no effect, removing it may lead to system errors.
To link two tunnel devices, open their configuration interface (use while holding), and insert the other tunnel device to link to. This allows linking any two network tunnel devices.
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.

View File

@@ -40,6 +40,8 @@
"item.oc2.network_interface_card": "Network Interface Card",
"item.oc2.network_interface_card.desc": "Sends and receives network packets via attached Network Connectors.",
"item.oc2.network_interface_card.is_configured": "Has connectivity configuration.",
"item.oc2.network_tunnel_card": "Network Tunnel Card",
"item.oc2.network_tunnel_card.desc": "Sends and receives network packets to a linked device.",
"item.oc2.file_import_export_card": "File Import/Export Card",
"item.oc2.file_import_export_card.desc": "Imports and exports files from and into your real file system.",
"item.oc2.robot": "Robot",
@@ -50,6 +52,8 @@
"item.oc2.block_operations_module.desc": "Breaks and places blocks.",
"item.oc2.sound_card": "Sound Card",
"item.oc2.sound_card.desc": "Plays various sounds from its highly realistic sound bank.",
"item.oc2.network_tunnel_module": "Network Tunnel Module",
"item.oc2.network_tunnel_module.desc": "Sends and receives network packets to a linked device.",
"item.oc2.transistor": "Transistor",
"item.oc2.transistor.desc": "Crafting material.",
@@ -92,6 +96,8 @@
"gui.oc2.network_interface_card.connectivity.disabled": "Disabled",
"gui.oc2.network_interface_card.info": "Drag to rotate. Click faces to toggle connectivity.",
"gui.oc2.network_tunnel.link": "Link",
"manual.oc2.home": "Home",
"manual.oc2.blocks": "Blocks",
"manual.oc2.items": "Items",
@@ -110,6 +116,7 @@
"tooltip.oc2.energyConsumption": "Energy Consumption: %s/t",
"tooltip.oc2.confirm": "Confirm",
"tooltip.oc2.cancel": "Cancel",
"tooltip.oc2.network_tunnel_id": "Tunnel: %s",
"subtitles.oc2.computer": "Computer fans running",
"subtitles.oc2.floppy": "Floppy access",

View File

@@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "oc2:item/network_tunnel_card"
}
}

View File

@@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "oc2:item/network_tunnel_module"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,34 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [
"oc2:network_tunnel_card"
]
},
"criteria": {
"has_computer": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [
{
"items": [
"oc2:computer"
]
}
]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": {
"recipe": "oc2:network_tunnel_card"
}
}
},
"requirements": [
[
"has_computer",
"has_the_recipe"
]
]
}

View File

@@ -0,0 +1,34 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [
"oc2:network_tunnel_module"
]
},
"criteria": {
"has_robot": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [
{
"items": [
"oc2:robot"
]
}
]
}
},
"has_the_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": {
"recipe": "oc2:network_tunnel_module"
}
}
},
"requirements": [
[
"has_robot",
"has_the_recipe"
]
]
}

View File

@@ -0,0 +1,24 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
"IET",
" B "
],
"key": {
"E": {
"tag": "forge:ender_pearls"
},
"I": {
"tag": "forge:ingots/iron"
},
"T": {
"item": "oc2:transistor"
},
"B": {
"item": "oc2:circuit_board"
}
},
"result": {
"item": "oc2:network_tunnel_card"
}
}

View File

@@ -0,0 +1,24 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
"TEG",
" B "
],
"key": {
"T": {
"item": "oc2:transistor"
},
"E": {
"tag": "forge:ender_pearls"
},
"G": {
"tag": "forge:ingots/gold"
},
"B": {
"item": "oc2:circuit_board"
}
},
"result": {
"item": "oc2:network_tunnel_module"
}
}

View File

@@ -11,6 +11,8 @@
"oc2:flash_memory",
"oc2:flash_memory_buildroot",
"oc2:network_interface_card",
"oc2:disk_drive"
"oc2:disk_drive",
"oc2:network_tunnel_card",
"oc2:network_tunnel_module"
]
}

View File

@@ -4,6 +4,7 @@
"oc2:redstone_interface_card",
"oc2:network_interface_card",
"oc2:file_import_export_card",
"oc2:sound_card"
"oc2:sound_card",
"oc2:network_tunnel_card"
]
}

View File

@@ -0,0 +1,7 @@
{
"replace": false,
"values": [
"oc2:network_tunnel_card",
"oc2:network_tunnel_module"
]
}

View File

@@ -2,6 +2,7 @@
"replace": false,
"values": [
"oc2:inventory_operations_module",
"oc2:block_operations_module"
"oc2:block_operations_module",
"oc2:network_tunnel_module"
]
}