diff --git a/src/main/java/li/cil/oc2/api/bus/device/DeviceTypes.java b/src/main/java/li/cil/oc2/api/bus/device/DeviceTypes.java index a87df022..2366f250 100644 --- a/src/main/java/li/cil/oc2/api/bus/device/DeviceTypes.java +++ b/src/main/java/li/cil/oc2/api/bus/device/DeviceTypes.java @@ -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; } diff --git a/src/main/java/li/cil/oc2/client/ClientSetup.java b/src/main/java/li/cil/oc2/client/ClientSetup.java index da369414..be774840 100644 --- a/src/main/java/li/cil/oc2/client/ClientSetup.java +++ b/src/main/java/li/cil/oc2/client/ClientSetup.java @@ -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()); diff --git a/src/main/java/li/cil/oc2/client/gui/NetworkTunnelScreen.java b/src/main/java/li/cil/oc2/client/gui/NetworkTunnelScreen.java new file mode 100644 index 00000000..46a7385b --- /dev/null +++ b/src/main/java/li/cil/oc2/client/gui/NetworkTunnelScreen.java @@ -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 { + 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); + } +} diff --git a/src/main/java/li/cil/oc2/client/gui/Sprites.java b/src/main/java/li/cil/oc2/client/gui/Sprites.java index 771c9938..e20dbc18 100644 --- a/src/main/java/li/cil/oc2/client/gui/Sprites.java +++ b/src/main/java/li/cil/oc2/client/gui/Sprites.java @@ -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); diff --git a/src/main/java/li/cil/oc2/client/gui/Textures.java b/src/main/java/li/cil/oc2/client/gui/Textures.java index 9786d594..957953fa 100644 --- a/src/main/java/li/cil/oc2/client/gui/Textures.java +++ b/src/main/java/li/cil/oc2/client/gui/Textures.java @@ -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); } diff --git a/src/main/java/li/cil/oc2/common/Config.java b/src/main/java/li/cil/oc2/common/Config.java index 1515fc16..6c3ae36a 100644 --- a/src/main/java/li/cil/oc2/common/Config.java +++ b/src/main/java/li/cil/oc2/common/Config.java @@ -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); diff --git a/src/main/java/li/cil/oc2/common/bus/device/DeviceTypes.java b/src/main/java/li/cil/oc2/common/bus/device/DeviceTypes.java index df06c348..231254c4 100644 --- a/src/main/java/li/cil/oc2/common/bus/device/DeviceTypes.java +++ b/src/main/java/li/cil/oc2/common/bus/device/DeviceTypes.java @@ -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); } /////////////////////////////////////////////////////////////////// diff --git a/src/main/java/li/cil/oc2/common/bus/device/item/NetworkTunnelItemDevice.java b/src/main/java/li/cil/oc2/common/bus/device/item/NetworkTunnelItemDevice.java new file mode 100644 index 00000000..ea057546 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/bus/device/item/NetworkTunnelItemDevice.java @@ -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 LazyOptional getCapability(final Capability 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> 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 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> iterator = TUNNELS.values().iterator(); + while (iterator.hasNext()) { + final Set tunnel = iterator.next(); + if (tunnel.isEmpty()) { + iterator.remove(); + } else { + pumpMessages(tunnel); + } + } + } + + private static void pumpMessages(final Collection 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); + } + } + } + } + } + } +} diff --git a/src/main/java/li/cil/oc2/common/bus/device/provider/Providers.java b/src/main/java/li/cil/oc2/common/bus/device/provider/Providers.java index 78cf0c00..69111f39 100644 --- a/src/main/java/li/cil/oc2/common/bus/device/provider/Providers.java +++ b/src/main/java/li/cil/oc2/common/bus/device/provider/Providers.java @@ -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); } } diff --git a/src/main/java/li/cil/oc2/common/bus/device/provider/item/NetworkTunnelCardItemDeviceProvider.java b/src/main/java/li/cil/oc2/common/bus/device/provider/item/NetworkTunnelCardItemDeviceProvider.java new file mode 100644 index 00000000..db2c1e25 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/bus/device/provider/item/NetworkTunnelCardItemDeviceProvider.java @@ -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 getItemDevice(final ItemDeviceQuery query) { + return Optional.of(new NetworkTunnelItemDevice(query.getItemStack())); + } + + @Override + protected int getItemDeviceEnergyConsumption(final ItemDeviceQuery query) { + return Config.networkTunnelEnergyPerTick; + } +} diff --git a/src/main/java/li/cil/oc2/common/bus/device/provider/item/NetworkTunnelModuleItemDeviceProvider.java b/src/main/java/li/cil/oc2/common/bus/device/provider/item/NetworkTunnelModuleItemDeviceProvider.java new file mode 100644 index 00000000..e42586f9 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/bus/device/provider/item/NetworkTunnelModuleItemDeviceProvider.java @@ -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 getItemDevice(final ItemDeviceQuery query) { + return Optional.of(new NetworkTunnelItemDevice(query.getItemStack())); + } + + @Override + protected int getItemDeviceEnergyConsumption(final ItemDeviceQuery query) { + return Config.networkTunnelEnergyPerTick; + } +} diff --git a/src/main/java/li/cil/oc2/common/container/Containers.java b/src/main/java/li/cil/oc2/common/container/Containers.java index 34fb7bfb..670ebbc9 100644 --- a/src/main/java/li/cil/oc2/common/container/Containers.java +++ b/src/main/java/li/cil/oc2/common/container/Containers.java @@ -16,6 +16,7 @@ public final class Containers { public static final RegistryObject> COMPUTER_TERMINAL = CONTAINERS.register("computer_terminal", () -> IForgeMenuType.create(ComputerTerminalContainer::createClient)); public static final RegistryObject> ROBOT = CONTAINERS.register("robot", () -> IForgeMenuType.create(RobotInventoryContainer::createClient)); public static final RegistryObject> ROBOT_TERMINAL = CONTAINERS.register("robot_terminal", () -> IForgeMenuType.create(RobotTerminalContainer::createClient)); + public static final RegistryObject> NETWORK_TUNNEL = CONTAINERS.register("network_tunnel", () -> IForgeMenuType.create(NetworkTunnelContainer::createClient)); /////////////////////////////////////////////////////////////////// diff --git a/src/main/java/li/cil/oc2/common/container/DeviceTypeSlot.java b/src/main/java/li/cil/oc2/common/container/DeviceTypeSlot.java new file mode 100644 index 00000000..1f4a89df --- /dev/null +++ b/src/main/java/li/cil/oc2/common/container/DeviceTypeSlot.java @@ -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 getNoItemIcon() { + if (hasItem()) { + return super.getNoItemIcon(); + } else { + return Pair.of(InventoryMenu.BLOCK_ATLAS, deviceType.getBackgroundIcon()); + } + } +} diff --git a/src/main/java/li/cil/oc2/common/container/NetworkTunnelContainer.java b/src/main/java/li/cil/oc2/common/container/NetworkTunnelContainer.java new file mode 100644 index 00000000..80d92ef6 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/container/NetworkTunnelContainer.java @@ -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; + } +} diff --git a/src/main/java/li/cil/oc2/common/item/Items.java b/src/main/java/li/cil/oc2/common/item/Items.java index 5506705a..c10bbdb8 100644 --- a/src/main/java/li/cil/oc2/common/item/Items.java +++ b/src/main/java/li/cil/oc2/common/item/Items.java @@ -64,11 +64,13 @@ public final class Items { public static final RegistryObject REDSTONE_INTERFACE_CARD = register("redstone_interface_card"); public static final RegistryObject NETWORK_INTERFACE_CARD = register("network_interface_card", NetworkInterfaceCardItem::new); + public static final RegistryObject NETWORK_TUNNEL_CARD = register("network_tunnel_card", NetworkTunnelItem::new); public static final RegistryObject FILE_IMPORT_EXPORT_CARD = register("file_import_export_card"); public static final RegistryObject SOUND_CARD = register("sound_card"); public static final RegistryObject INVENTORY_OPERATIONS_MODULE = register("inventory_operations_module"); public static final RegistryObject BLOCK_OPERATIONS_MODULE = register("block_operations_module", BlockOperationsModule::new); + public static final RegistryObject NETWORK_TUNNEL_MODULE = register("network_tunnel_module", NetworkTunnelItem::new); public static final RegistryObject TRANSISTOR = register("transistor", ModItem::new); public static final RegistryObject CIRCUIT_BOARD = register("circuit_board", ModItem::new); diff --git a/src/main/java/li/cil/oc2/common/item/NetworkTunnelItem.java b/src/main/java/li/cil/oc2/common/item/NetworkTunnelItem.java new file mode 100644 index 00000000..ab525a54 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/item/NetworkTunnelItem.java @@ -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 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 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 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); + } +} 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 f797953a..c671110c 100644 --- a/src/main/java/li/cil/oc2/common/network/Network.java +++ b/src/main/java/li/cil/oc2/common/network/Network.java @@ -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 void sendToClientsTrackingChunk(final T message, final LevelChunk chunk) { diff --git a/src/main/java/li/cil/oc2/common/network/message/NetworkTunnelLinkMessage.java b/src/main/java/li/cil/oc2/common/network/message/NetworkTunnelLinkMessage.java new file mode 100644 index 00000000..019b95b2 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/network/message/NetworkTunnelLinkMessage.java @@ -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(); + } + } +} diff --git a/src/main/java/li/cil/oc2/common/tags/ItemTags.java b/src/main/java/li/cil/oc2/common/tags/ItemTags.java index a2293ed4..4dcb8bd5 100644 --- a/src/main/java/li/cil/oc2/common/tags/ItemTags.java +++ b/src/main/java/li/cil/oc2/common/tags/ItemTags.java @@ -15,6 +15,7 @@ public final class ItemTags { public static final Tags.IOptionalNamedTag DEVICES_CARD = tag("devices/card"); public static final Tags.IOptionalNamedTag DEVICES_ROBOT_MODULE = tag("devices/robot_module"); public static final Tags.IOptionalNamedTag DEVICES_FLOPPY = tag("devices/floppy"); + public static final Tags.IOptionalNamedTag DEVICES_NETWORK_TUNNEL = tag("devices/network_tunnel"); public static final Tags.IOptionalNamedTag CABLES = tag("cables"); public static final Tags.IOptionalNamedTag WRENCHES = tag("wrenches"); diff --git a/src/main/java/li/cil/oc2/data/ModItemModelProvider.java b/src/main/java/li/cil/oc2/data/ModItemModelProvider.java index 5f101913..a46e269a 100644 --- a/src/main/java/li/cil/oc2/data/ModItemModelProvider.java +++ b/src/main/java/li/cil/oc2/data/ModItemModelProvider.java @@ -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); diff --git a/src/main/java/li/cil/oc2/data/ModItemTagsProvider.java b/src/main/java/li/cil/oc2/data/ModItemTagsProvider.java index 649f2556..be4167a6 100644 --- a/src/main/java/li/cil/oc2/data/ModItemTagsProvider.java +++ b/src/main/java/li/cil/oc2/data/ModItemTagsProvider.java @@ -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() ); } } diff --git a/src/main/java/li/cil/oc2/data/ModRecipesProvider.java b/src/main/java/li/cil/oc2/data/ModRecipesProvider.java index 546f04b3..ffdf2f63 100644 --- a/src/main/java/li/cil/oc2/data/ModRecipesProvider.java +++ b/src/main/java/li/cil/oc2/data/ModRecipesProvider.java @@ -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) diff --git a/src/main/resources/assets/oc2/doc/en_us/item/index.md b/src/main/resources/assets/oc2/doc/en_us/item/index.md index 56d68cea..af8fe154 100644 --- a/src/main/resources/assets/oc2/doc/en_us/item/index.md +++ b/src/main/resources/assets/oc2/doc/en_us/item/index.md @@ -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) \ No newline at end of file +- [Sound Card](sound_card.md) diff --git a/src/main/resources/assets/oc2/doc/en_us/item/network_tunnel_card.md b/src/main/resources/assets/oc2/doc/en_us/item/network_tunnel_card.md new file mode 100644 index 00000000..9cbb1ab0 --- /dev/null +++ b/src/main/resources/assets/oc2/doc/en_us/item/network_tunnel_card.md @@ -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. diff --git a/src/main/resources/assets/oc2/doc/en_us/item/network_tunnel_module.md b/src/main/resources/assets/oc2/doc/en_us/item/network_tunnel_module.md new file mode 100644 index 00000000..affbb0e9 --- /dev/null +++ b/src/main/resources/assets/oc2/doc/en_us/item/network_tunnel_module.md @@ -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. diff --git a/src/main/resources/assets/oc2/lang/en_us.json b/src/main/resources/assets/oc2/lang/en_us.json index af6246f0..6784922a 100644 --- a/src/main/resources/assets/oc2/lang/en_us.json +++ b/src/main/resources/assets/oc2/lang/en_us.json @@ -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", diff --git a/src/main/resources/assets/oc2/models/item/network_tunnel_card.json b/src/main/resources/assets/oc2/models/item/network_tunnel_card.json new file mode 100644 index 00000000..b9588a59 --- /dev/null +++ b/src/main/resources/assets/oc2/models/item/network_tunnel_card.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "oc2:item/network_tunnel_card" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/oc2/models/item/network_tunnel_module.json b/src/main/resources/assets/oc2/models/item/network_tunnel_module.json new file mode 100644 index 00000000..c0490936 --- /dev/null +++ b/src/main/resources/assets/oc2/models/item/network_tunnel_module.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "oc2:item/network_tunnel_module" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/oc2/textures/gui/icon/network_tunnel.png b/src/main/resources/assets/oc2/textures/gui/icon/network_tunnel.png new file mode 100644 index 00000000..71170948 Binary files /dev/null and b/src/main/resources/assets/oc2/textures/gui/icon/network_tunnel.png differ diff --git a/src/main/resources/assets/oc2/textures/gui/widget/network_tunnel_link_button.png b/src/main/resources/assets/oc2/textures/gui/widget/network_tunnel_link_button.png new file mode 100644 index 00000000..ad7cf6e4 Binary files /dev/null and b/src/main/resources/assets/oc2/textures/gui/widget/network_tunnel_link_button.png differ diff --git a/src/main/resources/assets/oc2/textures/gui/widget/network_tunnel_screen.png b/src/main/resources/assets/oc2/textures/gui/widget/network_tunnel_screen.png new file mode 100644 index 00000000..10f6d91b Binary files /dev/null and b/src/main/resources/assets/oc2/textures/gui/widget/network_tunnel_screen.png differ diff --git a/src/main/resources/assets/oc2/textures/item/network_tunnel_card.png b/src/main/resources/assets/oc2/textures/item/network_tunnel_card.png new file mode 100644 index 00000000..2beec361 Binary files /dev/null and b/src/main/resources/assets/oc2/textures/item/network_tunnel_card.png differ diff --git a/src/main/resources/assets/oc2/textures/item/network_tunnel_module.png b/src/main/resources/assets/oc2/textures/item/network_tunnel_module.png new file mode 100644 index 00000000..4e76f874 Binary files /dev/null and b/src/main/resources/assets/oc2/textures/item/network_tunnel_module.png differ diff --git a/src/main/resources/data/oc2/advancements/recipes/oc2.common/network_tunnel_card.json b/src/main/resources/data/oc2/advancements/recipes/oc2.common/network_tunnel_card.json new file mode 100644 index 00000000..56708def --- /dev/null +++ b/src/main/resources/data/oc2/advancements/recipes/oc2.common/network_tunnel_card.json @@ -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" + ] + ] +} \ No newline at end of file diff --git a/src/main/resources/data/oc2/advancements/recipes/oc2.common/network_tunnel_module.json b/src/main/resources/data/oc2/advancements/recipes/oc2.common/network_tunnel_module.json new file mode 100644 index 00000000..5a57ec61 --- /dev/null +++ b/src/main/resources/data/oc2/advancements/recipes/oc2.common/network_tunnel_module.json @@ -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" + ] + ] +} \ No newline at end of file diff --git a/src/main/resources/data/oc2/recipes/network_tunnel_card.json b/src/main/resources/data/oc2/recipes/network_tunnel_card.json new file mode 100644 index 00000000..5b2e06bb --- /dev/null +++ b/src/main/resources/data/oc2/recipes/network_tunnel_card.json @@ -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" + } +} \ No newline at end of file diff --git a/src/main/resources/data/oc2/recipes/network_tunnel_module.json b/src/main/resources/data/oc2/recipes/network_tunnel_module.json new file mode 100644 index 00000000..000cd142 --- /dev/null +++ b/src/main/resources/data/oc2/recipes/network_tunnel_module.json @@ -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" + } +} \ No newline at end of file diff --git a/src/main/resources/data/oc2/tags/items/device_needs_reboot.json b/src/main/resources/data/oc2/tags/items/device_needs_reboot.json index 6555864a..f1fcfe8d 100644 --- a/src/main/resources/data/oc2/tags/items/device_needs_reboot.json +++ b/src/main/resources/data/oc2/tags/items/device_needs_reboot.json @@ -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" ] } \ No newline at end of file diff --git a/src/main/resources/data/oc2/tags/items/devices/card.json b/src/main/resources/data/oc2/tags/items/devices/card.json index 76528456..69e8c465 100644 --- a/src/main/resources/data/oc2/tags/items/devices/card.json +++ b/src/main/resources/data/oc2/tags/items/devices/card.json @@ -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" ] } \ No newline at end of file diff --git a/src/main/resources/data/oc2/tags/items/devices/network_tunnel.json b/src/main/resources/data/oc2/tags/items/devices/network_tunnel.json new file mode 100644 index 00000000..67baf038 --- /dev/null +++ b/src/main/resources/data/oc2/tags/items/devices/network_tunnel.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "oc2:network_tunnel_card", + "oc2:network_tunnel_module" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/oc2/tags/items/devices/robot_module.json b/src/main/resources/data/oc2/tags/items/devices/robot_module.json index c7a51a93..7a652ab2 100644 --- a/src/main/resources/data/oc2/tags/items/devices/robot_module.json +++ b/src/main/resources/data/oc2/tags/items/devices/robot_module.json @@ -2,6 +2,7 @@ "replace": false, "values": [ "oc2:inventory_operations_module", - "oc2:block_operations_module" + "oc2:block_operations_module", + "oc2:network_tunnel_module" ] } \ No newline at end of file