From 10ab82095f0d62cc8822eebb128e66fe943d93f0 Mon Sep 17 00:00:00 2001 From: Kilobyte22 Date: Sat, 29 Jan 2022 22:27:22 +0100 Subject: [PATCH 01/28] Re-Implement the switch for 1.18 --- .../java/li/cil/oc2/common/block/Blocks.java | 1 + .../oc2/common/block/NetworkSwitchBlock.java | 61 +++ .../oc2/common/blockentity/BlockEntities.java | 1 + .../blockentity/NetworkSwitchBlockEntity.java | 487 ++++++++++++++++++ .../oc2/blockstates/network_switch.json | 19 + 5 files changed, 569 insertions(+) create mode 100644 src/main/java/li/cil/oc2/common/block/NetworkSwitchBlock.java create mode 100644 src/main/java/li/cil/oc2/common/blockentity/NetworkSwitchBlockEntity.java create mode 100644 src/main/resources/assets/oc2/blockstates/network_switch.json diff --git a/src/main/java/li/cil/oc2/common/block/Blocks.java b/src/main/java/li/cil/oc2/common/block/Blocks.java index 0f9c4e87..9330a681 100644 --- a/src/main/java/li/cil/oc2/common/block/Blocks.java +++ b/src/main/java/li/cil/oc2/common/block/Blocks.java @@ -21,6 +21,7 @@ public final class Blocks { public static final RegistryObject KEYBOARD = BLOCKS.register("keyboard", KeyboardBlock::new); public static final RegistryObject NETWORK_CONNECTOR = BLOCKS.register("network_connector", NetworkConnectorBlock::new); public static final RegistryObject NETWORK_HUB = BLOCKS.register("network_hub", NetworkHubBlock::new); + public static final RegistryObject NETWORK_SWITCH = BLOCKS.register("network_switch", NetworkSwitchBlock::new); public static final RegistryObject PROJECTOR = BLOCKS.register("projector", ProjectorBlock::new); public static final RegistryObject REDSTONE_INTERFACE = BLOCKS.register("redstone_interface", RedstoneInterfaceBlock::new); diff --git a/src/main/java/li/cil/oc2/common/block/NetworkSwitchBlock.java b/src/main/java/li/cil/oc2/common/block/NetworkSwitchBlock.java new file mode 100644 index 00000000..0e3ce881 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/block/NetworkSwitchBlock.java @@ -0,0 +1,61 @@ +package li.cil.oc2.common.block; + +import li.cil.oc2.common.blockentity.BlockEntities; +import li.cil.oc2.common.blockentity.NetworkHubBlockEntity; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.HorizontalDirectionalBlock; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.material.Material; + +import javax.annotation.Nullable; + +public final class NetworkSwitchBlock extends HorizontalDirectionalBlock implements EntityBlock { + public NetworkSwitchBlock() { + super(Properties + .of(Material.METAL) + .sound(SoundType.METAL) + .strength(1.5f, 6.0f)); + registerDefaultState(getStateDefinition().any().setValue(FACING, Direction.NORTH)); + } + + /////////////////////////////////////////////////////////////////// + + @Override + public BlockState getStateForPlacement(final BlockPlaceContext context) { + return super.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()); + } + + @SuppressWarnings("deprecation") + @Override + public void neighborChanged(final BlockState state, final Level level, final BlockPos pos, final Block changedBlock, final BlockPos changedBlockPos, final boolean isMoving) { + final BlockEntity blockEntity = level.getBlockEntity(pos); + if (blockEntity instanceof final NetworkHubBlockEntity networkHub) { + networkHub.handleNeighborChanged(); + } + } + + /////////////////////////////////////////////////////////////////// + // EntityBlock + + @Nullable + @Override + public BlockEntity newBlockEntity(final BlockPos pos, final BlockState state) { + return BlockEntities.NETWORK_HUB.get().create(pos, state); + } + + /////////////////////////////////////////////////////////////////// + + @Override + protected void createBlockStateDefinition(final StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(FACING); + } +} diff --git a/src/main/java/li/cil/oc2/common/blockentity/BlockEntities.java b/src/main/java/li/cil/oc2/common/blockentity/BlockEntities.java index 1c885086..9b0eb2cc 100644 --- a/src/main/java/li/cil/oc2/common/blockentity/BlockEntities.java +++ b/src/main/java/li/cil/oc2/common/blockentity/BlockEntities.java @@ -24,6 +24,7 @@ public final class BlockEntities { public static final RegistryObject> KEYBOARD = register(Blocks.KEYBOARD, KeyboardBlockEntity::new); public static final RegistryObject> NETWORK_CONNECTOR = register(Blocks.NETWORK_CONNECTOR, NetworkConnectorBlockEntity::new); public static final RegistryObject> NETWORK_HUB = register(Blocks.NETWORK_HUB, NetworkHubBlockEntity::new); + public static final RegistryObject> NETWORK_SWITCH = register(Blocks.NETWORK_SWITCH, NetworkSwitchBlockEntity::new); public static final RegistryObject> PROJECTOR = register(Blocks.PROJECTOR, ProjectorBlockEntity::new); public static final RegistryObject> REDSTONE_INTERFACE = register(Blocks.REDSTONE_INTERFACE, RedstoneInterfaceBlockEntity::new); diff --git a/src/main/java/li/cil/oc2/common/blockentity/NetworkSwitchBlockEntity.java b/src/main/java/li/cil/oc2/common/blockentity/NetworkSwitchBlockEntity.java new file mode 100644 index 00000000..d9c5a903 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/blockentity/NetworkSwitchBlockEntity.java @@ -0,0 +1,487 @@ +package li.cil.oc2.common.blockentity; + +import java.util.*; +import java.util.stream.Collectors; + +import com.google.gson.internal.LinkedTreeMap; +import com.mojang.datafixers.util.Pair; +import li.cil.oc2.api.bus.device.object.Callback; +import li.cil.oc2.api.bus.device.object.DocumentedDevice; +import li.cil.oc2.api.bus.device.object.NamedDevice; +import li.cil.oc2.api.capabilities.NetworkInterface; +import li.cil.oc2.common.Constants; +import li.cil.oc2.common.block.NetworkSwitchBlock; +import li.cil.oc2.common.blockentity.BlockEntities; +import li.cil.oc2.common.blockentity.NetworkHubBlockEntity; +import li.cil.oc2.common.capabilities.Capabilities; +import li.cil.oc2.common.util.LazyOptionalUtils; +import li.cil.oc2.common.util.LevelUtils; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.*; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.common.util.LazyOptional; + + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; + +public final class NetworkSwitchBlockEntity extends ModBlockEntity implements NamedDevice, DocumentedDevice, NetworkInterface, TickableBlockEntity { + private final String GET_HOST_TABLE = "getHostTable"; + private final String GET_PORT_CONFIG = "getPortConfig"; + private final String SET_PORT_CONFIG = "setPortConfig"; + + private final long HOST_TTL = 20 * 60 * 2; + private final int TTL_COST = 1; + private final Map hostTable = new HashMap<>(); + private final PortSettings[] portSettings = new PortSettings[Constants.BLOCK_FACE_COUNT]; + private int tickCount = 0; + private final NetworkInterface[] adjacentBlockInterfaces = new NetworkInterface[Constants.BLOCK_FACE_COUNT]; + private boolean haveAdjacentBlocksChanged = true; + + public NetworkSwitchBlockEntity(final BlockPos pos, final BlockState state) { + super(BlockEntities.NETWORK_SWITCH.get(), pos, state); + for (int i = 0; i < portSettings.length; i++) { + portSettings[i] = new PortSettings(); + } + } + + public void writeEthernetFrame(final NetworkInterface source, byte[] frame, final int timeToLive) { + validateAdjacentBlocks(); + long tickTime = getLevel().getGameTime(); + long destMac = macToLong(frame, 0); + long srcMac = macToLong(frame, 6); + short vlan = getVLAN(frame); + Optional optSide = sideReverseLookup(source); + if (!optSide.isPresent()) { + return; + } + int side = optSide.get(); + if (hostTable.size() <= 256) { + hostTable.put(srcMac, new HostEntry(side, tickTime)); + } + PortSettings ingressSettings = portSettings[side]; + SwitchLog log = new SwitchLog(vlan, side, srcMac, destMac); + if (vlan == 0) { + // Untagged packet + Pair pair = removeVLANTag(frame); // Remove tag in case there is a 0-tag + frame = pair.getSecond(); + if (ingressSettings.untagged != 0) { + frame = addVLANTag(frame, ingressSettings.untagged); + vlan = ingressSettings.untagged; + } + } else { + if (!(ingressSettings.trunkAll || ingressSettings.tagged.contains(vlan))) { + // drop packet with disallowed vlan + log.drop("Tag not allowed for ingress"); + return; + } + } + + HostEntry host = hostTable.get(destMac); + if (host != null) { + if (host.iface == side && !ingressSettings.hairpin) { + // if packet is to same port and hairpin is disabled, drop + log.drop("hairpin disabled"); + return; + } + writeToSide(frame, host.iface, vlan, log, timeToLive); + } else { + log.flood(); + for (int i = 0; i < Constants.BLOCK_FACE_COUNT; i++) { + if (i != side) { + writeToSide(frame, i, vlan, log, timeToLive); + } + } + } + } + + @Override + public byte[] readEthernetFrame() { + return null; + } + + private void writeToSide(byte[] frame, int side, short vlan, SwitchLog log, int timeToLive) { + log.egressPort(side); + NetworkInterface iface = adjacentBlockInterfaces[side]; + if (iface != null) { + PortSettings egressSettings = portSettings[side]; + if (egressSettings.untagged != 0 && vlan == 0) { + log.drop("inner tag untagged"); + return; + } + + if (egressSettings.untagged == vlan) { + Pair pair = removeVLANTag(frame); + frame = pair.getSecond(); + log.egressVlan = 0; + } else if (!(egressSettings.trunkAll || egressSettings.tagged.contains(vlan))) { + // Drop packets with wrong tag + log.drop("Tag not allowed for egress"); + return; + } else { + log.egressVlan = vlan; + } + log.emit(); + iface.writeEthernetFrame(this, frame, timeToLive - TTL_COST); + } + } + + private long macToLong(final byte[] mac, int offset) { + long ret = 0; + for (int i = 0; i < 6; i++) { + ret |= ((((long) mac[i + offset]) & 0xff) << (i * 8)); + } + return ret; + } + + @Override + public void clientTick() + { + return; + } + + @Override + public void serverTick() { + if (level == null) { + return; + } + if (tickCount++ % 20 == 0) { + long threshold = getLevel().getGameTime() - HOST_TTL; + if (threshold < 0) { + return; + } + hostTable.entrySet().removeIf(e -> e.getValue().timestamp < threshold); + } + } + + @Override + public void getDeviceDocumentation(final DeviceVisitor visitor) { + visitor.visitCallback(GET_HOST_TABLE) + .description("Returns the MAC address table of the switch") + .returnValueDescription("The MAC table. For each host the mac address, the age (in ticks) and the face is returned"); + } + + @Override + public Collection getDeviceTypeNames() { + return singletonList("switch"); + } + + @Override + public void saveAdditional(final CompoundTag tag) { + super.saveAdditional(tag); + + ListTag hosts = new ListTag(); + for (Map.Entry host : hostTable.entrySet()) { + CompoundTag thisHost = new CompoundTag(); + thisHost.put("mac", LongTag.valueOf(host.getKey())); + thisHost.put("side", IntTag.valueOf(host.getValue().iface)); + thisHost.put("timestamp", LongTag.valueOf(host.getValue().timestamp)); + hosts.add(thisHost); + } + tag.put("hosts", hosts); + + ListTag ports = new ListTag(); + for (PortSettings myPort : portSettings) { + CompoundTag port = new CompoundTag(); + myPort.save(port); + ports.add(port); + } + tag.put("ports", ports); + } + + @Override + public void load(final CompoundTag tag) { + super.load(tag); + + Tag hosts = tag.get("hosts"); + if (hosts != null) { + for (Tag host_ : ((ListTag) hosts)) { + CompoundTag host = (CompoundTag) host_; + hostTable.put( + host.getLong("mac"), + new HostEntry( + tag.getInt("side"), + tag.getLong("timestamp") + ) + ); + } + } + + Tag ports = tag.get("ports"); + if (ports != null) { + int i = 0; + for (Tag port : ((ListTag) ports)) { + portSettings[i++] = PortSettings.load((CompoundTag) port); + } + } + + } + + @Callback(name = GET_HOST_TABLE) + public List getHostTable() { + long now = getLevel().getGameTime(); + return hostTable + .entrySet() + .stream() + .map(e -> new LuaHostEntry(macLongToString(e.getKey()), now - e.getValue().timestamp, e.getValue().iface)) + .collect(Collectors.toList()); + } + + @Callback(name = GET_PORT_CONFIG, synchronize = false) + public PortSettings[] getPortSettings() { + return portSettings; + } + + @Callback(name = SET_PORT_CONFIG) + public void setPortSettings(List settings) { + int max = Math.min(portSettings.length, settings.size()); + for (int i = 0; i < max; i++) { + portSettings[i].untagged = ((Double) settings.get(i).get("untagged")).shortValue(); + } + } + + private Optional sideReverseLookup(NetworkInterface iface) { + for (int i = 0; i < Constants.BLOCK_FACE_COUNT; i++) { + if (iface == adjacentBlockInterfaces[i]) { + return Optional.of(i); + } + } + return Optional.empty(); + } + + private static String macLongToString(long mac) { + StringBuilder ret = new StringBuilder(); + for (int i = 0; i < 6; i++) { + if (i != 0) { + ret.append(":"); + } + ret.append(String.format("%02x", (mac >> (i * 8)) & 0xff)); + } + return ret.toString(); + } + + private byte[] addVLANTag(byte[] packet, short tag) { + if (tag != 0) { + byte[] ret = new byte[packet.length + 4]; + copyBytes(packet, ret, 0, 0, 12); // Copy Ethernet Header + copyBytes(packet, ret, 12, 16, packet.length - 12); + ret[12] = (byte) 0x81; + ret[13] = (byte) 0x00; + ret[14] = (byte) ((tag >> 8) & 0x0f); + ret[15] = (byte) (tag & 0xff); + return ret; + } else { + return packet; + } + } + + private short getVLAN(byte[] packet) { + if (packet[12] == ((byte) 0x81) && packet[13] == 0x00) { + return (short) (packet[15] | ((((short) packet[14]) & 0x0f) << 8)); + } else { + return (short) 0; + } + } + + private Pair removeVLANTag(byte[] packet) { + if (packet[12] == ((byte) 0x81) && packet[13] == 0x00) { + byte[] ret = new byte[packet.length - 4]; + copyBytes(packet, ret, 0, 0, 12); // Copy Ethernet Header + copyBytes(packet, ret, 16, 12, packet.length - 16); // Copy payload + short tag = (short) (packet[15] | ((((short) packet[14]) & 0x0f) << 8)); // Extract vlan tag + return new Pair<>(tag, ret); + } else { + return new Pair<>((short) 0, packet); + } + } + + private void copyBytes(byte[] input, byte[] output, int inputOffset, int outputOffset, int length) { + for (int i = 0; i < length; i++) { + output[outputOffset + i] = input[inputOffset + i]; + } + } + + private void validateAdjacentBlocks() { + if (isRemoved() || !haveAdjacentBlocksChanged) { + return; + } + + for (final Direction side : Constants.DIRECTIONS) { + adjacentBlockInterfaces[side.get3DDataValue()] = null; + } + + haveAdjacentBlocksChanged = false; + + if (level == null || level.isClientSide()) { + return; + } + + final BlockPos pos = getBlockPos(); + for (final Direction side : Constants.DIRECTIONS) { + final BlockEntity neighborBlockEntity = LevelUtils.getBlockEntityIfChunkExists(level, pos.relative(side)); + if (neighborBlockEntity != null) { + final LazyOptional optional = neighborBlockEntity.getCapability(Capabilities.networkInterface(), side.getOpposite()); + optional.ifPresent(adjacentInterface -> { + adjacentBlockInterfaces[side.get3DDataValue()] = adjacentInterface; + LazyOptionalUtils.addWeakListener(optional, this, (hub, unused) -> hub.handleNeighborChanged()); + }); + } + } + } + + private void handleNeighborChanged() { + haveAdjacentBlocksChanged = true; + } + + private static class HostEntry { + public int iface; + public long timestamp; + public HostEntry(int iface, long timestamp) { + this.iface = iface; + this.timestamp = timestamp; + } + } + + public static class LuaHostEntry { + public String mac; + public long age; + public int side; + + public LuaHostEntry(String mac, long age, int iface) { + this.mac = mac; + this.age = age; + this.side = iface; + } + } + + private static class PortSettings { + /** + * The VLAN that is both PVID and untagged vlan. It will be removed on egress and added on ingress. If set to 0 + * this port is put on the global untagged vlan. The global untagged vlan can ever only be used as an untagged vlan + */ + public short untagged; + /** + * A list of tagged vlans that will be accepted on both ingress and egress. 0 (the global untagged vlan) is not a valid + * value + */ + public List tagged; + /** + * If enabled, packets entering on this port may also leave via this port again + */ + public boolean hairpin; + /** + * If this is set, tagged will be ignored. Instead all tagged vlans will be accepted. untagged will still be honored + */ + public boolean trunkAll; + + public PortSettings(final short untagged, final List tagged, final boolean hairpin, final boolean trunkAll) { + this.untagged = untagged; + this.tagged = tagged; + this.hairpin = hairpin; + this.trunkAll = trunkAll; + } + + /** + * Default configuration of an unmanaged switch, which just forwards all tagged vlans as well as the untagged vlan + * straight through + */ + public PortSettings() { + this((short) 0, emptyList(), false, true); + } + + public void save(final CompoundTag tag) { + tag.put("untagged", ShortTag.valueOf(untagged)); + tag.put("tagged", new IntArrayTag(tagged.stream().map(s -> (int) s).collect(Collectors.toList()))); + tag.put("hairpin", ByteTag.valueOf(hairpin)); + tag.put("trunkAll", ByteTag.valueOf(trunkAll)); + } + + public static PortSettings load(final CompoundTag tag) { + short untagged = tag.getShort("untagged"); + List tagged = Arrays.stream(tag.getIntArray("tagged")) + .mapToObj(i -> (short) i) + .collect(Collectors.toList()); + boolean hairpin = tag.getBoolean("hairpin"); + boolean trunkAll = tag.getBoolean("trunkAll"); + + return new PortSettings(untagged, tagged, hairpin, trunkAll); + } + } + + private static class SwitchLog { + private static final boolean ENABLED = true; + private short ingressVlan = 0; + private short egressVlan = 0; + private int ingressSide = 0; + private final long srcMac; + private final long destMac; + private Integer egressSide = null; + + public SwitchLog(short ingressVlan, int ingressSide, long srcMac, long destMac) { + this.ingressVlan = ingressVlan; + this.ingressSide = ingressSide; + this.srcMac = srcMac; + this.destMac = destMac; + } + + public void egressPort(int side) { + egressSide = side; + } + + public void drop(String reason) { + if (!ENABLED) return; + String inMac = NetworkSwitchBlockEntity.macLongToString(srcMac); + String outMac = NetworkSwitchBlockEntity.macLongToString(destMac); + if (egressSide == null) { + System.out.printf( + "Switch Packet %s (Port %s, VLAN %s) -> %s drop (%s)\n", + inMac, + ingressSide, + ingressVlan, + outMac, + reason + ); + } else { + System.out.printf( + "Switch Packet %s (Port %s, VLAN %s) -> %s (Port %s) drop (%s)\n", + inMac, + ingressSide, + ingressVlan, + outMac, + egressSide, + reason + ); + } + } + + public void emit() { + if (!ENABLED) return; + String inMac = NetworkSwitchBlockEntity.macLongToString(srcMac); + String outMac = NetworkSwitchBlockEntity.macLongToString(destMac); + System.out.printf( + "Switch Packet %s (Port %s, VLAN %s) -> %s (Port %s, VLAN %s)\n", + inMac, + ingressSide, + ingressVlan, + outMac, + egressSide, + egressVlan + ); + } + + public void flood() { + if (!ENABLED) return; + String inMac = NetworkSwitchBlockEntity.macLongToString(srcMac); + String outMac = NetworkSwitchBlockEntity.macLongToString(destMac); + System.out.printf( + "Switch Packet %s (Port %s, VLAN %s) -> %s flood\n", + inMac, + ingressSide, + ingressVlan, + outMac + ); + } + } +} diff --git a/src/main/resources/assets/oc2/blockstates/network_switch.json b/src/main/resources/assets/oc2/blockstates/network_switch.json new file mode 100644 index 00000000..99bb9127 --- /dev/null +++ b/src/main/resources/assets/oc2/blockstates/network_switch.json @@ -0,0 +1,19 @@ +{ + "variants": { + "facing=north": { + "model": "oc2:block/network_hub" + }, + "facing=south": { + "model": "oc2:block/network_hub", + "y": 180 + }, + "facing=west": { + "model": "oc2:block/network_hub", + "y": 270 + }, + "facing=east": { + "model": "oc2:block/network_hub", + "y": 90 + } + } +} From c0a3fcce3cc344bb3a38378ec0b17bd92ff0b9e8 Mon Sep 17 00:00:00 2001 From: Kilobyte22 Date: Sat, 29 Jan 2022 22:54:19 +0100 Subject: [PATCH 02/28] Add swconfig script --- src/main/scripts/bin/swconfig.lua | 59 +++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 src/main/scripts/bin/swconfig.lua diff --git a/src/main/scripts/bin/swconfig.lua b/src/main/scripts/bin/swconfig.lua new file mode 100644 index 00000000..6ef47192 --- /dev/null +++ b/src/main/scripts/bin/swconfig.lua @@ -0,0 +1,59 @@ +#!/usr/bin/lua + +function usage() + print("Usage:") + print(" swconfig show_hosts") + print(" swconfig show_ports") + print(" swconfig set_port untagged ") + print(" swconfig set_port trunk_all (on|off)") +end + +if not arg[1] then + usage() + return +end + +local cjson = require("cjson").new() + +local devbus = require('devices') +local switch = devbus:find("switch") + +if not switch then + print("No switch found") + return +end + +if arg[1] == "show_hosts" then + host_table = switch:getHostTable() + + for _, v in ipairs(host_table) do + print(v.mac .. " : " .. v.side .. ", Age: " .. v.age) + end +elseif arg[1] == "show_ports" then + local link_state = switch:getLinkState() + for i, port in ipairs(switch:getPortConfig()) do + print("Port #" .. (i - 1) .. " " .. (link_state[i] and "UP" or "DOWN")) + print(" Untagged VLAN: " .. port.untagged) + print(" Tagged: " .. table.concat(port.tagged, ", ")) + print(" Hairpin: " .. (port.hairpin and "on" or "off")) + print(" Trunk All: " .. (port.trunk_all and "on" or "off")) + end +elseif arg[1] == "set_port" then + if #arg < 4 then + usage() + return + end + local config = switch:getPortConfig() + local port = config[tonumber(arg[2]) + 1] + if not port then + print("Invalid Port Number") + return + end + if arg[3] == "untagged" then + port.untagged = tonumber(arg[4]) + end + switch:setPortConfig(config) +else + usage() + return +end From 7eb0d9efce404c562b855487d762397519effda6 Mon Sep 17 00:00:00 2001 From: Kilobyte22 Date: Sun, 30 Jan 2022 00:24:31 +0100 Subject: [PATCH 03/28] Add initial work on the NetworkPortalBlock --- src/main/java/li/cil/oc2/common/Config.java | 6 ++ src/main/java/li/cil/oc2/common/Main.java | 6 ++ .../oc2/common/block/NetworkPortalBlock.java | 35 ++++++++ .../cil/oc2/common/vxlan/TunnelManager.java | 82 +++++++++++++++++++ 4 files changed, 129 insertions(+) create mode 100644 src/main/java/li/cil/oc2/common/block/NetworkPortalBlock.java create mode 100644 src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java diff --git a/src/main/java/li/cil/oc2/common/Config.java b/src/main/java/li/cil/oc2/common/Config.java index c54f96d2..32f178e8 100644 --- a/src/main/java/li/cil/oc2/common/Config.java +++ b/src/main/java/li/cil/oc2/common/Config.java @@ -45,6 +45,12 @@ public final class Config { @Path("admin.virtual_network") public static int ethernetFrameTimeToLive = 12; @Path("admin.virtual_network") public static int hubEthernetFramesPerTick = 32; + @Path("vxlan") public static boolean enable = false; + @Path("vxlan") public static String remoteHost = "::1"; + @Path("vxlan") public static int remotePort = 4789; + @Path("vxlan") public static String bindHost = "::1"; + @Path("vxlan") public static int bindPort = 4789; + public static boolean computersUseEnergy() { return computerEnergyPerTick > 0 && computerEnergyStorage > 0; } diff --git a/src/main/java/li/cil/oc2/common/Main.java b/src/main/java/li/cil/oc2/common/Main.java index f9a3d9b2..1f94a903 100644 --- a/src/main/java/li/cil/oc2/common/Main.java +++ b/src/main/java/li/cil/oc2/common/Main.java @@ -22,7 +22,11 @@ import li.cil.oc2.common.tags.BlockTags; import li.cil.oc2.common.tags.ItemTags; import li.cil.oc2.common.util.RegistryUtils; import li.cil.oc2.common.util.SoundEvents; +<<<<<<< HEAD import li.cil.oc2.common.vm.provider.DeviceTreeProviders; +======= +import li.cil.oc2.common.vxlan.TunnelManager; +>>>>>>> 7a7b14ef (Add initial work on the NetworkPortalBlock) import li.cil.sedna.Sedna; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.fml.DistExecutor; @@ -54,8 +58,10 @@ public final class Main { ProviderRegistry.initialize(); DeviceTypes.initialize(); + BlockDeviceDataRegistry.initialize(); FirmwareRegistry.initialize(); + TunnelManager.initialize(); DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> Manuals::initialize); diff --git a/src/main/java/li/cil/oc2/common/block/NetworkPortalBlock.java b/src/main/java/li/cil/oc2/common/block/NetworkPortalBlock.java new file mode 100644 index 00000000..de701756 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/block/NetworkPortalBlock.java @@ -0,0 +1,35 @@ +package li.cil.oc2.common.block; + +import li.cil.oc2.common.blockentity.BlockEntities; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.HorizontalDirectionalBlock; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Material; +import org.jetbrains.annotations.Nullable; + +public class NetworkPortalBlock extends HorizontalDirectionalBlock implements EntityBlock { + + public NetworkPortalBlock() { + super(Properties + .of(Material.METAL) + .sound(SoundType.METAL) + .strength(1.5f, 6.0f)); + registerDefaultState(getStateDefinition().any().setValue(FACING, Direction.NORTH)); + } + + @Override + public BlockState getStateForPlacement(final BlockPlaceContext context) { + return super.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()); + } + + @Nullable + @Override + public BlockEntity newBlockEntity(final BlockPos pos, final BlockState state) { + return BlockEntities.NETWORK_HUB.get().create(pos, state); + } +} diff --git a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java new file mode 100644 index 00000000..1b641523 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java @@ -0,0 +1,82 @@ +package li.cil.oc2.common.vxlan; + +import li.cil.oc2.api.capabilities.NetworkInterface; +import li.cil.oc2.common.Config; + +import java.io.IOException; +import java.net.*; +import java.util.HashMap; + +public class TunnelManager { + + private final HashMap tunnels = new HashMap<>(); + private final DatagramSocket socket; + private static TunnelManager INSTANCE; + + public TunnelManager(InetAddress bindHost, short bindPort, InetAddress remoteHost, short remotePort) throws SocketException { + socket = new DatagramSocket(bindPort, bindHost); + socket.connect(remoteHost, remotePort); + } + + public static void initialize() { + if (Config.enable) { + try { + INSTANCE = new TunnelManager( + InetAddress.getByName(Config.bindHost), Config.bindPort, + InetAddress.getByName(Config.remoteHost), Config.remotePort + ); + } catch (SocketException | UnknownHostException e) { + e.printStackTrace(); + } + + new Thread(new Runnable() { + @Override + public void run() { + INSTANCE.listen(); + } + }).start(); + } + } + + public void listen() { + byte[] buffer = new byte[65535]; + try { + while (true) { + DatagramPacket packet = new DatagramPacket(buffer, buffer.length); + socket.receive(packet); + + if (packet.getData().length < 8) { + continue; + } + + byte flags = packet.getData()[0]; + int vni_1 = packet.getData()[4]; + + if ((flags & 0x08) != 0x08) { + continue; + } + + NetworkInterface iface = tunnels.get(vni); + + if (iface != null) { + byte[] inner = new byte[packet.getData().length - 8]; + copyBytes(packet.getData(), inner, 8, 0, packet.getData().length - 8); + + iface.writeEthernetFrame(null, inner, 255); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static TunnelManager instance() { + return INSTANCE; + } + + private void copyBytes(byte[] input, byte[] output, int inputOffset, int outputOffset, int length) { + for (int i = 0; i < length; i++) { + output[outputOffset + i] = input[inputOffset + i]; + } + } +} From 40cfc418d603f5fea12a9bf064843efb29f055ac Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sun, 30 Jan 2022 01:19:05 +0100 Subject: [PATCH 04/28] First prototype vxlan hub --- src/main/java/li/cil/oc2/common/Config.java | 4 +- .../java/li/cil/oc2/common/block/Blocks.java | 1 + .../oc2/common/block/NetworkPortalBlock.java | 35 ----- .../li/cil/oc2/common/block/VxlanBlock.java | 62 +++++++++ .../oc2/common/blockentity/BlockEntities.java | 1 + .../common/blockentity/VxlanBlockEntity.java | 121 ++++++++++++++++++ .../cil/oc2/common/vxlan/TunnelManager.java | 69 ++++++++-- 7 files changed, 242 insertions(+), 51 deletions(-) delete mode 100644 src/main/java/li/cil/oc2/common/block/NetworkPortalBlock.java create mode 100644 src/main/java/li/cil/oc2/common/block/VxlanBlock.java create mode 100644 src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java diff --git a/src/main/java/li/cil/oc2/common/Config.java b/src/main/java/li/cil/oc2/common/Config.java index 32f178e8..a243ffde 100644 --- a/src/main/java/li/cil/oc2/common/Config.java +++ b/src/main/java/li/cil/oc2/common/Config.java @@ -47,9 +47,9 @@ public final class Config { @Path("vxlan") public static boolean enable = false; @Path("vxlan") public static String remoteHost = "::1"; - @Path("vxlan") public static int remotePort = 4789; + @Path("vxlan") public static short remotePort = 4789; @Path("vxlan") public static String bindHost = "::1"; - @Path("vxlan") public static int bindPort = 4789; + @Path("vxlan") public static short bindPort = 4789; public static boolean computersUseEnergy() { return computerEnergyPerTick > 0 && computerEnergyStorage > 0; diff --git a/src/main/java/li/cil/oc2/common/block/Blocks.java b/src/main/java/li/cil/oc2/common/block/Blocks.java index 0f9c4e87..6957abde 100644 --- a/src/main/java/li/cil/oc2/common/block/Blocks.java +++ b/src/main/java/li/cil/oc2/common/block/Blocks.java @@ -23,6 +23,7 @@ public final class Blocks { public static final RegistryObject NETWORK_HUB = BLOCKS.register("network_hub", NetworkHubBlock::new); public static final RegistryObject PROJECTOR = BLOCKS.register("projector", ProjectorBlock::new); public static final RegistryObject REDSTONE_INTERFACE = BLOCKS.register("redstone_interface", RedstoneInterfaceBlock::new); + public static final RegistryObject VXLAN_HUB = BLOCKS.register("vxlan_hub", VxlanBlock::new); /////////////////////////////////////////////////////////////////// diff --git a/src/main/java/li/cil/oc2/common/block/NetworkPortalBlock.java b/src/main/java/li/cil/oc2/common/block/NetworkPortalBlock.java deleted file mode 100644 index de701756..00000000 --- a/src/main/java/li/cil/oc2/common/block/NetworkPortalBlock.java +++ /dev/null @@ -1,35 +0,0 @@ -package li.cil.oc2.common.block; - -import li.cil.oc2.common.blockentity.BlockEntities; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.item.context.BlockPlaceContext; -import net.minecraft.world.level.block.EntityBlock; -import net.minecraft.world.level.block.HorizontalDirectionalBlock; -import net.minecraft.world.level.block.SoundType; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.material.Material; -import org.jetbrains.annotations.Nullable; - -public class NetworkPortalBlock extends HorizontalDirectionalBlock implements EntityBlock { - - public NetworkPortalBlock() { - super(Properties - .of(Material.METAL) - .sound(SoundType.METAL) - .strength(1.5f, 6.0f)); - registerDefaultState(getStateDefinition().any().setValue(FACING, Direction.NORTH)); - } - - @Override - public BlockState getStateForPlacement(final BlockPlaceContext context) { - return super.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()); - } - - @Nullable - @Override - public BlockEntity newBlockEntity(final BlockPos pos, final BlockState state) { - return BlockEntities.NETWORK_HUB.get().create(pos, state); - } -} diff --git a/src/main/java/li/cil/oc2/common/block/VxlanBlock.java b/src/main/java/li/cil/oc2/common/block/VxlanBlock.java new file mode 100644 index 00000000..6a8911f8 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/block/VxlanBlock.java @@ -0,0 +1,62 @@ +package li.cil.oc2.common.block; + +import li.cil.oc2.common.blockentity.BlockEntities; +import li.cil.oc2.common.blockentity.NetworkHubBlockEntity; +import li.cil.oc2.common.blockentity.VxlanBlockEntity; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.HorizontalDirectionalBlock; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.material.Material; + +import javax.annotation.Nullable; + +public final class VxlanBlock extends HorizontalDirectionalBlock implements EntityBlock { + public VxlanBlock() { + super(Properties + .of(Material.METAL) + .sound(SoundType.METAL) + .strength(1.5f, 6.0f)); + registerDefaultState(getStateDefinition().any().setValue(FACING, Direction.NORTH)); + } + + /////////////////////////////////////////////////////////////////// + + @Override + public BlockState getStateForPlacement(final BlockPlaceContext context) { + return super.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()); + } + + @SuppressWarnings("deprecation") + @Override + public void neighborChanged(final BlockState state, final Level level, final BlockPos pos, final Block changedBlock, final BlockPos changedBlockPos, final boolean isMoving) { + final BlockEntity blockEntity = level.getBlockEntity(pos); + if (blockEntity instanceof final VxlanBlockEntity vxlanEntity) { + vxlanEntity.handleNeighborChanged(); + } + } + + /////////////////////////////////////////////////////////////////// + // EntityBlock + + @Nullable + @Override + public BlockEntity newBlockEntity(final BlockPos pos, final BlockState state) { + return BlockEntities.VXLAN_HUB.get().create(pos, state); + } + + /////////////////////////////////////////////////////////////////// + + @Override + protected void createBlockStateDefinition(final StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(FACING); + } +} diff --git a/src/main/java/li/cil/oc2/common/blockentity/BlockEntities.java b/src/main/java/li/cil/oc2/common/blockentity/BlockEntities.java index 1c885086..460c3475 100644 --- a/src/main/java/li/cil/oc2/common/blockentity/BlockEntities.java +++ b/src/main/java/li/cil/oc2/common/blockentity/BlockEntities.java @@ -26,6 +26,7 @@ public final class BlockEntities { public static final RegistryObject> NETWORK_HUB = register(Blocks.NETWORK_HUB, NetworkHubBlockEntity::new); public static final RegistryObject> PROJECTOR = register(Blocks.PROJECTOR, ProjectorBlockEntity::new); public static final RegistryObject> REDSTONE_INTERFACE = register(Blocks.REDSTONE_INTERFACE, RedstoneInterfaceBlockEntity::new); + public static final RegistryObject> VXLAN_HUB = register(Blocks.VXLAN_HUB, VxlanBlockEntity::new); /////////////////////////////////////////////////////////////////// diff --git a/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java b/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java new file mode 100644 index 00000000..c2931962 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java @@ -0,0 +1,121 @@ +package li.cil.oc2.common.blockentity; + +import li.cil.oc2.api.capabilities.NetworkInterface; +import li.cil.oc2.common.Constants; +import li.cil.oc2.common.capabilities.Capabilities; +import li.cil.oc2.common.util.LazyOptionalUtils; +import li.cil.oc2.common.util.LevelUtils; +import li.cil.oc2.common.vxlan.TunnelManager; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.common.util.LazyOptional; + +import javax.annotation.Nullable; +import java.util.Arrays; +import java.util.Objects; +import java.util.stream.Stream; + +public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInterface { + private static final int TTL_COST = 1; + private int vti = ((int) (Math.random() * Integer.MAX_VALUE)) & 0x00ff_ffff; + private boolean initialized = false; + + /////////////////////////////////////////////////////////////////// + + // Each face and the default TunnelInterface connecting to the outernet + private final NetworkInterface[] adjacentBlockInterfaces = new NetworkInterface[Constants.BLOCK_FACE_COUNT + 1]; + private boolean haveAdjacentBlocksChanged = true; + + /////////////////////////////////////////////////////////////////// + + public VxlanBlockEntity(final BlockPos pos, final BlockState state) { + super(BlockEntities.NETWORK_HUB.get(), pos, state); + } + + /////////////////////////////////////////////////////////////////// + + public void handleNeighborChanged() { + haveAdjacentBlocksChanged = true; + } + + @Override + public byte[] readEthernetFrame() { + return null; + } + + @Override + public void writeEthernetFrame(final NetworkInterface source, final byte[] frame, final int timeToLive) { + getAdjacentInterfaces().forEach(adjacentInterface -> { + if (adjacentInterface != source) { + adjacentInterface.writeEthernetFrame(this, frame, timeToLive - TTL_COST); + } + }); + } + + /////////////////////////////////////////////////////////////////// + + @Override + public void load(CompoundTag tag) { + super.load(tag); + vti = tag.getInt("vti"); + } + + @Override + protected void onUnload(final boolean isRemove) { + TunnelManager.instance().unregisterVti(vti); + + super.onUnload(isRemove); + } + + @Override + public void onLoad() { + super.onLoad(); + + TunnelManager.instance().registerVti(vti, this); + } + + /////////////////////////////////////////////////////////////////// + + @Override + protected void collectCapabilities(final CapabilityCollector collector, @Nullable final Direction direction) { + collector.offer(Capabilities.NETWORK_INTERFACE, this); + } + + /////////////////////////////////////////////////////////////////// + + private Stream getAdjacentInterfaces() { + validateAdjacentBlocks(); + return Arrays.stream(adjacentBlockInterfaces).filter(Objects::nonNull); + } + + private void validateAdjacentBlocks() { + if (isRemoved() || !haveAdjacentBlocksChanged) { + return; + } + + for (final Direction side : Constants.DIRECTIONS) { + adjacentBlockInterfaces[side.get3DDataValue() + 1] = null; + } + + haveAdjacentBlocksChanged = false; + + if (level == null || level.isClientSide()) { + return; + } + + final BlockPos pos = getBlockPos(); + for (final Direction side : Constants.DIRECTIONS) { + final BlockEntity neighborBlockEntity = LevelUtils.getBlockEntityIfChunkExists(level, pos.relative(side)); + if (neighborBlockEntity != null) { + final LazyOptional optional = neighborBlockEntity.getCapability(Capabilities.NETWORK_INTERFACE, side.getOpposite()); + optional.ifPresent(adjacentInterface -> { + adjacentBlockInterfaces[side.get3DDataValue() + 1] = adjacentInterface; + LazyOptionalUtils.addWeakListener(optional, this, (hub, unused) -> hub.handleNeighborChanged()); + }); + } + } + } +} diff --git a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java index 1b641523..3d9b7770 100644 --- a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java +++ b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java @@ -2,6 +2,7 @@ package li.cil.oc2.common.vxlan; import li.cil.oc2.api.capabilities.NetworkInterface; import li.cil.oc2.common.Config; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.net.*; @@ -9,11 +10,19 @@ import java.util.HashMap; public class TunnelManager { - private final HashMap tunnels = new HashMap<>(); + private final HashMap tunnels = new HashMap<>(); private final DatagramSocket socket; private static TunnelManager INSTANCE; + private final InetAddress remoteHost; + private final short remotePort; + private final InetAddress bindHost; + private final short bindPort; public TunnelManager(InetAddress bindHost, short bindPort, InetAddress remoteHost, short remotePort) throws SocketException { + this.remoteHost = remoteHost; + this.remotePort = remotePort; + this.bindHost = bindHost; + this.bindPort = bindPort; socket = new DatagramSocket(bindPort, bindHost); socket.connect(remoteHost, remotePort); } @@ -29,12 +38,7 @@ public class TunnelManager { e.printStackTrace(); } - new Thread(new Runnable() { - @Override - public void run() { - INSTANCE.listen(); - } - }).start(); + new Thread(() -> INSTANCE.listen()).start(); } } @@ -50,19 +54,21 @@ public class TunnelManager { } byte flags = packet.getData()[0]; - int vni_1 = packet.getData()[4]; + int vni = packet.getData()[6] + + packet.getData()[5] << 8 + + packet.getData()[4] << 16; if ((flags & 0x08) != 0x08) { continue; } - NetworkInterface iface = tunnels.get(vni); + TunnelInterface iface = tunnels.get(vni); if (iface != null) { byte[] inner = new byte[packet.getData().length - 8]; - copyBytes(packet.getData(), inner, 8, 0, packet.getData().length - 8); + System.arraycopy(packet.getData(), 8, inner, 0, packet.getData().length - 8); - iface.writeEthernetFrame(null, inner, 255); + iface.target.writeEthernetFrame(iface, inner, 255); } } } catch (IOException e) { @@ -74,9 +80,44 @@ public class TunnelManager { return INSTANCE; } - private void copyBytes(byte[] input, byte[] output, int inputOffset, int outputOffset, int length) { - for (int i = 0; i < length; i++) { - output[outputOffset + i] = input[inputOffset + i]; + public void sendToVti(int vti, byte[] payload) { + byte[] buffer = new byte[payload.length + 8]; + + System.arraycopy(payload, 0, buffer, 8, payload.length); + + buffer[0] = 0x08; + buffer[4] = (byte) ((vti >> 16) & 0xff); + buffer[5] = (byte) ((vti >> 8) & 0xff); + buffer[6] = (byte) (vti & 0xff); + + DatagramPacket packet = new DatagramPacket(buffer, buffer.length, this.remoteHost, this.remotePort); + } + + public void registerVti(int vti, NetworkInterface iface) { + tunnels.put(vti, new TunnelInterface(vti, iface)); + } + + public void unregisterVti(int vti) { + tunnels.remove(vti); + } + + public class TunnelInterface implements NetworkInterface { + final NetworkInterface target; + private final int vti; + + public TunnelInterface(int vti, NetworkInterface iface) { + this.vti = vti; + this.target = iface; + } + + @Override + public byte[] readEthernetFrame() { + return new byte[0]; + } + + @Override + public void writeEthernetFrame(final NetworkInterface source, final byte[] frame, final int timeToLive) { + TunnelManager.this.sendToVti(vti, frame); } } } From 6b9b51f6c1b9b6bf86aa03e79cb21e8fc269ca32 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sun, 30 Jan 2022 01:57:46 +0100 Subject: [PATCH 05/28] Prototype commit 2 --- .../li/cil/oc2/common/blockentity/VxlanBlockEntity.java | 4 +++- src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java | 6 ++++-- src/main/resources/META-INF/mods.toml | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java b/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java index c2931962..2ea8fba6 100644 --- a/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java +++ b/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java @@ -65,6 +65,7 @@ public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInt @Override protected void onUnload(final boolean isRemove) { + adjacentBlockInterfaces[0] = null; TunnelManager.instance().unregisterVti(vti); super.onUnload(isRemove); @@ -74,7 +75,8 @@ public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInt public void onLoad() { super.onLoad(); - TunnelManager.instance().registerVti(vti, this); + System.out.println("Tunnel VTI: " + vti); + adjacentBlockInterfaces[0] = TunnelManager.instance().registerVti(vti, this); } /////////////////////////////////////////////////////////////////// diff --git a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java index 3d9b7770..88fbb3ec 100644 --- a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java +++ b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java @@ -93,8 +93,10 @@ public class TunnelManager { DatagramPacket packet = new DatagramPacket(buffer, buffer.length, this.remoteHost, this.remotePort); } - public void registerVti(int vti, NetworkInterface iface) { - tunnels.put(vti, new TunnelInterface(vti, iface)); + public NetworkInterface registerVti(int vti, NetworkInterface iface) { + TunnelInterface tuniface = new TunnelInterface(vti, iface); + tunnels.put(vti, tuniface); + return tuniface; } public void unregisterVti(int vti) { diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index e46ae304..204ca3bf 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -17,7 +17,7 @@ This mod uses the Terminus Font under the Open Font License. The full license ca [[dependencies.oc2]] modId = "forge" mandatory = true -versionRange = "[39.0.63,)" +versionRange = "[39.0.59,)" ordering = "NONE" side = "BOTH" [[dependencies.oc2]] From 5eead9a1ee0c40f1b2096a62edff7996ca8715bd Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sun, 30 Jan 2022 02:02:16 +0100 Subject: [PATCH 06/28] Prototype commit #3 --- src/main/java/li/cil/oc2/common/Config.java | 4 +- .../common/blockentity/VxlanBlockEntity.java | 6 +++ .../java/li/cil/oc2/common/item/Items.java | 1 + .../cil/oc2/common/vxlan/TunnelManager.java | 49 ++++++++++++------- 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/main/java/li/cil/oc2/common/Config.java b/src/main/java/li/cil/oc2/common/Config.java index a243ffde..32f178e8 100644 --- a/src/main/java/li/cil/oc2/common/Config.java +++ b/src/main/java/li/cil/oc2/common/Config.java @@ -47,9 +47,9 @@ public final class Config { @Path("vxlan") public static boolean enable = false; @Path("vxlan") public static String remoteHost = "::1"; - @Path("vxlan") public static short remotePort = 4789; + @Path("vxlan") public static int remotePort = 4789; @Path("vxlan") public static String bindHost = "::1"; - @Path("vxlan") public static short bindPort = 4789; + @Path("vxlan") public static int bindPort = 4789; public static boolean computersUseEnergy() { return computerEnergyPerTick > 0 && computerEnergyStorage > 0; diff --git a/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java b/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java index 2ea8fba6..fc350339 100644 --- a/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java +++ b/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java @@ -63,6 +63,12 @@ public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInt vti = tag.getInt("vti"); } + @Override + public void saveAdditional(CompoundTag tag) { + super.saveAdditional(tag); + tag.putInt("vti", vti); + } + @Override protected void onUnload(final boolean isRemove) { adjacentBlockInterfaces[0] = null; 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 09a630d8..97a19376 100644 --- a/src/main/java/li/cil/oc2/common/item/Items.java +++ b/src/main/java/li/cil/oc2/common/item/Items.java @@ -33,6 +33,7 @@ public final class Items { public static final RegistryObject NETWORK_HUB = register(Blocks.NETWORK_HUB); public static final RegistryObject PROJECTOR = register(Blocks.PROJECTOR); public static final RegistryObject REDSTONE_INTERFACE = register(Blocks.REDSTONE_INTERFACE); + public static final RegistryObject VXLAN_HUB = register(Blocks.VXLAN_HUB); /////////////////////////////////////////////////////////////////// diff --git a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java index 88fbb3ec..7c7dc375 100644 --- a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java +++ b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java @@ -23,21 +23,26 @@ public class TunnelManager { this.remotePort = remotePort; this.bindHost = bindHost; this.bindPort = bindPort; - socket = new DatagramSocket(bindPort, bindHost); - socket.connect(remoteHost, remotePort); + if (Config.enable) { + socket = new DatagramSocket(bindPort, bindHost); + socket.connect(remoteHost, remotePort); + } else { + socket = null; + } } public static void initialize() { - if (Config.enable) { - try { - INSTANCE = new TunnelManager( - InetAddress.getByName(Config.bindHost), Config.bindPort, - InetAddress.getByName(Config.remoteHost), Config.remotePort - ); - } catch (SocketException | UnknownHostException e) { - e.printStackTrace(); - } + try { + INSTANCE = new TunnelManager( + InetAddress.getByName(Config.bindHost), (short) Config.bindPort, + InetAddress.getByName(Config.remoteHost), (short) Config.remotePort + ); + } catch (SocketException | UnknownHostException e) { + e.printStackTrace(); + } + + if (Config.enable) { new Thread(() -> INSTANCE.listen()).start(); } } @@ -81,16 +86,24 @@ public class TunnelManager { } public void sendToVti(int vti, byte[] payload) { - byte[] buffer = new byte[payload.length + 8]; + if (socket != null) { + byte[] buffer = new byte[payload.length + 8]; - System.arraycopy(payload, 0, buffer, 8, payload.length); + System.arraycopy(payload, 0, buffer, 8, payload.length); - buffer[0] = 0x08; - buffer[4] = (byte) ((vti >> 16) & 0xff); - buffer[5] = (byte) ((vti >> 8) & 0xff); - buffer[6] = (byte) (vti & 0xff); + buffer[0] = 0x08; + buffer[4] = (byte) ((vti >> 16) & 0xff); + buffer[5] = (byte) ((vti >> 8) & 0xff); + buffer[6] = (byte) (vti & 0xff); - DatagramPacket packet = new DatagramPacket(buffer, buffer.length, this.remoteHost, this.remotePort); + DatagramPacket packet = new DatagramPacket(buffer, buffer.length, this.remoteHost, this.remotePort); + + try { + socket.send(packet); + } catch (IOException e) { + e.printStackTrace(); + } + } } public NetworkInterface registerVti(int vti, NetworkInterface iface) { From 646a66f895a0aaf2bd4db9cfe526283f211b2b23 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sun, 30 Jan 2022 03:06:29 +0100 Subject: [PATCH 07/28] Debugging --- .../cil/oc2/common/vxlan/TunnelManager.java | 83 ++++++++++--------- 1 file changed, 45 insertions(+), 38 deletions(-) diff --git a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java index 7c7dc375..e20ccbb9 100644 --- a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java +++ b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java @@ -23,61 +23,68 @@ public class TunnelManager { this.remotePort = remotePort; this.bindHost = bindHost; this.bindPort = bindPort; - if (Config.enable) { - socket = new DatagramSocket(bindPort, bindHost); - socket.connect(remoteHost, remotePort); - } else { - socket = null; - } } public static void initialize() { - try { INSTANCE = new TunnelManager( InetAddress.getByName(Config.bindHost), (short) Config.bindPort, InetAddress.getByName(Config.remoteHost), (short) Config.remotePort ); } catch (SocketException | UnknownHostException e) { + System.out.println("Failed to bind host: " + e.getMessage()); e.printStackTrace(); } - if (Config.enable) { - new Thread(() -> INSTANCE.listen()).start(); - } + //if (Config.enable) { + new Thread(() -> { + try { + INSTANCE.listen(); + } catch (IOException e) { + e.printStackTrace(); + } + }).start(); + //} } - public void listen() { + public void listen() throws IOException { + System.out.printf("Binding %s:%s\n", bindHost, bindPort); + + //if (Config.enable) { + socket = new DatagramSocket(bindPort/*, bindHost*/); + socket.connect(remoteHost, remotePort); + //} else { + // socket = null; + //} + System.out.printf("Bind successful: connected=%s bound=%s\n", socket.isConnected(), socket.isBound()); + byte[] buffer = new byte[65535]; - try { - while (true) { - DatagramPacket packet = new DatagramPacket(buffer, buffer.length); - socket.receive(packet); + while (true) { + DatagramPacket packet = new DatagramPacket(buffer, buffer.length); + socket.receive(packet); + System.out.println("Received message"); - if (packet.getData().length < 8) { - continue; - } - - byte flags = packet.getData()[0]; - int vni = packet.getData()[6] - + packet.getData()[5] << 8 - + packet.getData()[4] << 16; - - if ((flags & 0x08) != 0x08) { - continue; - } - - TunnelInterface iface = tunnels.get(vni); - - if (iface != null) { - byte[] inner = new byte[packet.getData().length - 8]; - System.arraycopy(packet.getData(), 8, inner, 0, packet.getData().length - 8); - - iface.target.writeEthernetFrame(iface, inner, 255); - } + if (packet.getData().length < 8) { + continue; + } + + byte flags = packet.getData()[0]; + int vni = packet.getData()[6] + + packet.getData()[5] << 8 + + packet.getData()[4] << 16; + + if ((flags & 0x08) != 0x08) { + continue; + } + + TunnelInterface iface = tunnels.get(vni); + + if (iface != null) { + byte[] inner = new byte[packet.getData().length - 8]; + System.arraycopy(packet.getData(), 8, inner, 0, packet.getData().length - 8); + + iface.target.writeEthernetFrame(iface, inner, 255); } - } catch (IOException e) { - e.printStackTrace(); } } From 9f68c9c320e0d9475ef61ba730e9719b4fc49241 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sun, 30 Jan 2022 03:06:49 +0100 Subject: [PATCH 08/28] More debugging! --- .../li/cil/oc2/common/blockentity/VxlanBlockEntity.java | 4 +++- src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java b/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java index fc350339..cbddbc1b 100644 --- a/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java +++ b/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java @@ -81,7 +81,9 @@ public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInt public void onLoad() { super.onLoad(); - System.out.println("Tunnel VTI: " + vti); + if (!level.isClientSide()) { + System.out.println("Tunnel VTI: " + vti); + } adjacentBlockInterfaces[0] = TunnelManager.instance().registerVti(vti, this); } diff --git a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java index e20ccbb9..e6f028be 100644 --- a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java +++ b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java @@ -11,7 +11,7 @@ import java.util.HashMap; public class TunnelManager { private final HashMap tunnels = new HashMap<>(); - private final DatagramSocket socket; + private DatagramSocket socket; private static TunnelManager INSTANCE; private final InetAddress remoteHost; private final short remotePort; @@ -28,8 +28,8 @@ public class TunnelManager { public static void initialize() { try { INSTANCE = new TunnelManager( - InetAddress.getByName(Config.bindHost), (short) Config.bindPort, - InetAddress.getByName(Config.remoteHost), (short) Config.remotePort + InetAddress.getByName("2001:16b8:4908:5700:d22e:ecd:e75b:f5a8"), (short) Config.bindPort, + InetAddress.getByName("2001:470:7398::a"), (short) Config.remotePort ); } catch (SocketException | UnknownHostException e) { System.out.println("Failed to bind host: " + e.getMessage()); From 4c3ef28236835e04c1aebce7eb08914d90629316 Mon Sep 17 00:00:00 2001 From: gruetzkopf Date: Sun, 30 Jan 2022 21:10:14 +0100 Subject: [PATCH 09/28] This should work --- .../li/cil/oc2/common/block/VxlanBlock.java | 9 ++++ .../common/blockentity/VxlanBlockEntity.java | 48 ++++++++++++++--- .../cil/oc2/common/vxlan/TunnelManager.java | 53 +++++++++++-------- 3 files changed, 81 insertions(+), 29 deletions(-) diff --git a/src/main/java/li/cil/oc2/common/block/VxlanBlock.java b/src/main/java/li/cil/oc2/common/block/VxlanBlock.java index 6a8911f8..79f0cea6 100644 --- a/src/main/java/li/cil/oc2/common/block/VxlanBlock.java +++ b/src/main/java/li/cil/oc2/common/block/VxlanBlock.java @@ -2,6 +2,7 @@ package li.cil.oc2.common.block; import li.cil.oc2.common.blockentity.BlockEntities; import li.cil.oc2.common.blockentity.NetworkHubBlockEntity; +import li.cil.oc2.common.blockentity.TickableBlockEntity; import li.cil.oc2.common.blockentity.VxlanBlockEntity; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -12,6 +13,8 @@ import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.HorizontalDirectionalBlock; import net.minecraft.world.level.block.SoundType; import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.material.Material; @@ -52,6 +55,12 @@ public final class VxlanBlock extends HorizontalDirectionalBlock implements Enti return BlockEntities.VXLAN_HUB.get().create(pos, state); } + @Nullable + @Override + public BlockEntityTicker getTicker(final Level level, final BlockState state, final BlockEntityType type) { + return TickableBlockEntity.createServerTicker(level, type, BlockEntities.VXLAN_HUB.get()); + } + /////////////////////////////////////////////////////////////////// @Override diff --git a/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java b/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java index cbddbc1b..d43ffcaf 100644 --- a/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java +++ b/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java @@ -16,13 +16,19 @@ import net.minecraftforge.common.util.LazyOptional; import javax.annotation.Nullable; import java.util.Arrays; import java.util.Objects; +import java.util.Queue; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; import java.util.stream.Stream; -public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInterface { +public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInterface, TickableBlockEntity { private static final int TTL_COST = 1; - private int vti = ((int) (Math.random() * Integer.MAX_VALUE)) & 0x00ff_ffff; + //private int vti = ((int) (Math.random() * Integer.MAX_VALUE)) & 0x00ff_ffff; + private int vti = 4037973; private boolean initialized = false; + private BlockingQueue packetQueue = new ArrayBlockingQueue(32); + /////////////////////////////////////////////////////////////////// // Each face and the default TunnelInterface connecting to the outernet @@ -32,9 +38,10 @@ public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInt /////////////////////////////////////////////////////////////////// public VxlanBlockEntity(final BlockPos pos, final BlockState state) { - super(BlockEntities.NETWORK_HUB.get(), pos, state); + super(BlockEntities.VXLAN_HUB.get(), pos, state); } + /////////////////////////////////////////////////////////////////// public void handleNeighborChanged() { @@ -55,24 +62,47 @@ public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInt }); } + @Override + public void serverTick() { + if (level == null) { + return; + } + + if (adjacentBlockInterfaces[0] != null) { + packetQueue.forEach(packet -> { + writeEthernetFrame(adjacentBlockInterfaces[0], packet, 255); + }); + packetQueue.clear(); + } else { + System.out.printf("VXLAN block is unregistered upstream: VTI=%d\n", vti); + } + } + /////////////////////////////////////////////////////////////////// @Override public void load(CompoundTag tag) { super.load(tag); - vti = tag.getInt("vti"); + if (!level.isClientSide()) { + // vti = tag.getInt("vti"); + adjacentBlockInterfaces[0] = TunnelManager.instance().registerVti(vti, packetQueue); + } } @Override public void saveAdditional(CompoundTag tag) { super.saveAdditional(tag); - tag.putInt("vti", vti); + if (!level.isClientSide()) { + tag.putInt("vti", vti); + } } @Override protected void onUnload(final boolean isRemove) { - adjacentBlockInterfaces[0] = null; - TunnelManager.instance().unregisterVti(vti); + if (!level.isClientSide()) { + adjacentBlockInterfaces[0] = null; + TunnelManager.instance().unregisterVti(vti); + } super.onUnload(isRemove); } @@ -83,12 +113,14 @@ public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInt if (!level.isClientSide()) { System.out.println("Tunnel VTI: " + vti); + adjacentBlockInterfaces[0] = TunnelManager.instance().registerVti(vti, this.packetQueue); } - adjacentBlockInterfaces[0] = TunnelManager.instance().registerVti(vti, this); } /////////////////////////////////////////////////////////////////// + + @Override protected void collectCapabilities(final CapabilityCollector collector, @Nullable final Direction direction) { collector.offer(Capabilities.NETWORK_INTERFACE, this); diff --git a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java index e6f028be..e9d8939f 100644 --- a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java +++ b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java @@ -2,11 +2,11 @@ package li.cil.oc2.common.vxlan; import li.cil.oc2.api.capabilities.NetworkInterface; import li.cil.oc2.common.Config; -import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.net.*; import java.util.HashMap; +import java.util.concurrent.BlockingQueue; public class TunnelManager { @@ -28,8 +28,8 @@ public class TunnelManager { public static void initialize() { try { INSTANCE = new TunnelManager( - InetAddress.getByName("2001:16b8:4908:5700:d22e:ecd:e75b:f5a8"), (short) Config.bindPort, - InetAddress.getByName("2001:470:7398::a"), (short) Config.remotePort + InetAddress.getByName("2001:470:7398::a"), (short) Config.bindPort, + InetAddress.getByName("2001:470:7398::10"), (short) Config.remotePort ); } catch (SocketException | UnknownHostException e) { System.out.println("Failed to bind host: " + e.getMessage()); @@ -37,13 +37,15 @@ public class TunnelManager { } //if (Config.enable) { - new Thread(() -> { + Thread bgThread = new Thread(() -> { try { INSTANCE.listen(); } catch (IOException e) { e.printStackTrace(); } - }).start(); + }); + bgThread.setName("VXLAN Background Thread"); + bgThread.start(); //} } @@ -52,7 +54,7 @@ public class TunnelManager { //if (Config.enable) { socket = new DatagramSocket(bindPort/*, bindHost*/); - socket.connect(remoteHost, remotePort); + //socket.connect(remoteHost, remotePort); //} else { // socket = null; //} @@ -62,28 +64,33 @@ public class TunnelManager { while (true) { DatagramPacket packet = new DatagramPacket(buffer, buffer.length); socket.receive(packet); - System.out.println("Received message"); - if (packet.getData().length < 8) { + if (packet.getLength() < 8) { continue; } byte flags = packet.getData()[0]; - int vni = packet.getData()[6] - + packet.getData()[5] << 8 - + packet.getData()[4] << 16; + int vni = (packet.getData()[6] & 0xFF ) + | ( ( packet.getData()[5] & 0xFF ) << 8 ) + | ( ( packet.getData()[4] & 0xFF ) << 16 ); if ((flags & 0x08) != 0x08) { continue; } + System.out.println(vni); + TunnelInterface iface = tunnels.get(vni); if (iface != null) { - byte[] inner = new byte[packet.getData().length - 8]; - System.arraycopy(packet.getData(), 8, inner, 0, packet.getData().length - 8); + byte[] inner = new byte[packet.getLength() - 8]; + System.arraycopy(packet.getData(), 8, inner, 0, packet.getLength() - 8); - iface.target.writeEthernetFrame(iface, inner, 255); + try { + iface.packetQueue.add(inner); + } catch (IllegalStateException ignored) { + System.err.println("Queue full"); + } } } } @@ -92,8 +99,9 @@ public class TunnelManager { return INSTANCE; } - public void sendToVti(int vti, byte[] payload) { + public void sendToOuternet(int vti, byte[] payload) { if (socket != null) { + byte[] buffer = new byte[payload.length + 8]; System.arraycopy(payload, 0, buffer, 8, payload.length); @@ -110,11 +118,13 @@ public class TunnelManager { } catch (IOException e) { e.printStackTrace(); } + } else { + System.out.printf("No socket in TunnelManager\n"); } } - public NetworkInterface registerVti(int vti, NetworkInterface iface) { - TunnelInterface tuniface = new TunnelInterface(vti, iface); + public NetworkInterface registerVti(int vti, BlockingQueue packetQueue) { + TunnelInterface tuniface = new TunnelInterface(vti, packetQueue); tunnels.put(vti, tuniface); return tuniface; } @@ -124,14 +134,15 @@ public class TunnelManager { } public class TunnelInterface implements NetworkInterface { - final NetworkInterface target; + final BlockingQueue packetQueue; private final int vti; - public TunnelInterface(int vti, NetworkInterface iface) { + public TunnelInterface(int vti, BlockingQueue packetQueue) { this.vti = vti; - this.target = iface; + this.packetQueue = packetQueue; } + @Override public byte[] readEthernetFrame() { return new byte[0]; @@ -139,7 +150,7 @@ public class TunnelManager { @Override public void writeEthernetFrame(final NetworkInterface source, final byte[] frame, final int timeToLive) { - TunnelManager.this.sendToVti(vti, frame); + TunnelManager.this.sendToOuternet(vti, frame); } } } From 8dc69011aa89d61adebcdb7dea77495c1cc20b1d Mon Sep 17 00:00:00 2001 From: Kilobyte22 Date: Fri, 18 Feb 2022 00:20:31 +0100 Subject: [PATCH 10/28] Add switch to item list --- src/main/java/li/cil/oc2/common/item/Items.java | 1 + 1 file changed, 1 insertion(+) 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 09a630d8..8cfdbd1e 100644 --- a/src/main/java/li/cil/oc2/common/item/Items.java +++ b/src/main/java/li/cil/oc2/common/item/Items.java @@ -31,6 +31,7 @@ public final class Items { public static final RegistryObject KEYBOARD = register(Blocks.KEYBOARD); public static final RegistryObject NETWORK_CONNECTOR = register(Blocks.NETWORK_CONNECTOR); public static final RegistryObject NETWORK_HUB = register(Blocks.NETWORK_HUB); + public static final RegistryObject NETWORK_SWITCH = register(Blocks.NETWORK_SWITCH); public static final RegistryObject PROJECTOR = register(Blocks.PROJECTOR); public static final RegistryObject REDSTONE_INTERFACE = register(Blocks.REDSTONE_INTERFACE); From 55bd407aef65e3894c65c4a0b0349d5a1ae69b87 Mon Sep 17 00:00:00 2001 From: Kilobyte22 Date: Fri, 18 Feb 2022 00:43:22 +0100 Subject: [PATCH 11/28] Initialize item block states for Switch Block --- src/main/java/li/cil/oc2/data/ModBlockStateProvider.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/li/cil/oc2/data/ModBlockStateProvider.java b/src/main/java/li/cil/oc2/data/ModBlockStateProvider.java index 722e43ff..30d6964f 100644 --- a/src/main/java/li/cil/oc2/data/ModBlockStateProvider.java +++ b/src/main/java/li/cil/oc2/data/ModBlockStateProvider.java @@ -54,6 +54,7 @@ public final class ModBlockStateProvider extends BlockStateProvider { .end() .end(); horizontalBlock(Blocks.NETWORK_HUB, Items.NETWORK_HUB, NETWORK_HUB_MODEL); + horizontalBlock(Blocks.NETWORK_SWITCH, Items.NETWORK_SWITCH, NETWORK_HUB_MODEL); horizontalBlock(Blocks.PROJECTOR, Items.PROJECTOR, PROJECTOR_MODEL); horizontalBlock(Blocks.REDSTONE_INTERFACE, Items.REDSTONE_INTERFACE, REDSTONE_INTERFACE_MODEL); From d12bc2df691e46af070ac4df5608e7015408a93d Mon Sep 17 00:00:00 2001 From: Kilobyte22 Date: Mon, 28 Feb 2022 01:16:25 +0100 Subject: [PATCH 12/28] Add configurability to vxlan block --- .../java/li/cil/oc2/common/ConfigManager.java | 20 +++++++++++++++++++ src/main/java/li/cil/oc2/common/Main.java | 3 --- .../common/blockentity/VxlanBlockEntity.java | 18 +++++------------ .../cil/oc2/common/vxlan/TunnelManager.java | 19 +++++++++--------- 4 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/main/java/li/cil/oc2/common/ConfigManager.java b/src/main/java/li/cil/oc2/common/ConfigManager.java index cd6f8359..8c31a184 100644 --- a/src/main/java/li/cil/oc2/common/ConfigManager.java +++ b/src/main/java/li/cil/oc2/common/ConfigManager.java @@ -65,8 +65,10 @@ public final class ConfigManager { static { PARSERS.put(int.class, ConfigManager::parseIntField); + PARSERS.put(short.class, ConfigManager::parseShortField); PARSERS.put(long.class, ConfigManager::parseLongField); PARSERS.put(double.class, ConfigManager::parseDoubleField); + PARSERS.put(boolean.class, ConfigManager::parseBooleanField); PARSERS.put(String.class, ConfigManager::parseStringField); PARSERS.put(UUID.class, ConfigManager::parseUUIDField); PARSERS.put(ResourceLocation.class, ConfigManager::parseResourceLocationField); @@ -134,6 +136,16 @@ public final class ConfigManager { return new ConfigFieldPair<>(field, configValue); } + private static ConfigFieldPair parseShortField(final Object instance, final Field field, final String path, final ForgeConfigSpec.Builder builder) throws IllegalAccessException { + final short defaultValue = field.getShort(instance); + final short minValue = (short) Math.max(getMin(field), Short.MIN_VALUE); + final short maxValue = (short) Math.min(getMax(field), Short.MAX_VALUE); + + final ForgeConfigSpec.IntValue configValue = builder.defineInRange(path, defaultValue, minValue, maxValue); + + return new ConfigFieldPair<>(field, configValue); + } + private static ConfigFieldPair parseLongField(final Object instance, final Field field, final String path, final ForgeConfigSpec.Builder builder) throws IllegalAccessException { final long defaultValue = field.getLong(instance); final long minValue = (long) Math.max(getMin(field), Long.MIN_VALUE); @@ -170,6 +182,14 @@ public final class ConfigManager { return new ConfigFieldPair<>(field, configValue, UUID::fromString); } + private static ConfigFieldPair parseBooleanField(final Object instance, final Field field, final String path, final ForgeConfigSpec.Builder builder) throws IllegalAccessException { + final boolean defaultValue = (boolean) field.get(instance); + + final ForgeConfigSpec.ConfigValue configValue = builder.define(path, defaultValue); + + return new ConfigFieldPair<>(field, configValue); + } + private static ConfigFieldPair parseResourceLocationField(final Object instance, final Field field, final String path, final ForgeConfigSpec.Builder builder) throws IllegalAccessException { final ResourceLocation defaultValue = (ResourceLocation) field.get(instance); diff --git a/src/main/java/li/cil/oc2/common/Main.java b/src/main/java/li/cil/oc2/common/Main.java index 1f94a903..5a3a7cf4 100644 --- a/src/main/java/li/cil/oc2/common/Main.java +++ b/src/main/java/li/cil/oc2/common/Main.java @@ -22,11 +22,8 @@ import li.cil.oc2.common.tags.BlockTags; import li.cil.oc2.common.tags.ItemTags; import li.cil.oc2.common.util.RegistryUtils; import li.cil.oc2.common.util.SoundEvents; -<<<<<<< HEAD import li.cil.oc2.common.vm.provider.DeviceTreeProviders; -======= import li.cil.oc2.common.vxlan.TunnelManager; ->>>>>>> 7a7b14ef (Add initial work on the NetworkPortalBlock) import li.cil.sedna.Sedna; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.fml.DistExecutor; diff --git a/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java b/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java index d43ffcaf..9ce8f71a 100644 --- a/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java +++ b/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java @@ -83,10 +83,6 @@ public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInt @Override public void load(CompoundTag tag) { super.load(tag); - if (!level.isClientSide()) { - // vti = tag.getInt("vti"); - adjacentBlockInterfaces[0] = TunnelManager.instance().registerVti(vti, packetQueue); - } } @Override @@ -108,13 +104,9 @@ public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInt } @Override - public void onLoad() { - super.onLoad(); - - if (!level.isClientSide()) { - System.out.println("Tunnel VTI: " + vti); - adjacentBlockInterfaces[0] = TunnelManager.instance().registerVti(vti, this.packetQueue); - } + public void loadServer() { + System.out.println("Tunnel VTI: " + vti); + adjacentBlockInterfaces[0] = TunnelManager.instance().registerVti(vti, this.packetQueue); } /////////////////////////////////////////////////////////////////// @@ -123,7 +115,7 @@ public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInt @Override protected void collectCapabilities(final CapabilityCollector collector, @Nullable final Direction direction) { - collector.offer(Capabilities.NETWORK_INTERFACE, this); + collector.offer(Capabilities.networkInterface(), this); } /////////////////////////////////////////////////////////////////// @@ -152,7 +144,7 @@ public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInt for (final Direction side : Constants.DIRECTIONS) { final BlockEntity neighborBlockEntity = LevelUtils.getBlockEntityIfChunkExists(level, pos.relative(side)); if (neighborBlockEntity != null) { - final LazyOptional optional = neighborBlockEntity.getCapability(Capabilities.NETWORK_INTERFACE, side.getOpposite()); + final LazyOptional optional = neighborBlockEntity.getCapability(Capabilities.networkInterface(), side.getOpposite()); optional.ifPresent(adjacentInterface -> { adjacentBlockInterfaces[side.get3DDataValue() + 1] = adjacentInterface; LazyOptionalUtils.addWeakListener(optional, this, (hub, unused) -> hub.handleNeighborChanged()); diff --git a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java index e9d8939f..b0702087 100644 --- a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java +++ b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java @@ -28,15 +28,15 @@ public class TunnelManager { public static void initialize() { try { INSTANCE = new TunnelManager( - InetAddress.getByName("2001:470:7398::a"), (short) Config.bindPort, - InetAddress.getByName("2001:470:7398::10"), (short) Config.remotePort + InetAddress.getByName(Config.bindHost), (short) Config.bindPort, + InetAddress.getByName(Config.remoteHost), (short) Config.remotePort ); } catch (SocketException | UnknownHostException e) { System.out.println("Failed to bind host: " + e.getMessage()); e.printStackTrace(); } - //if (Config.enable) { + if (Config.enable) { Thread bgThread = new Thread(() -> { try { INSTANCE.listen(); @@ -46,18 +46,17 @@ public class TunnelManager { }); bgThread.setName("VXLAN Background Thread"); bgThread.start(); - //} + } } public void listen() throws IOException { System.out.printf("Binding %s:%s\n", bindHost, bindPort); - //if (Config.enable) { - socket = new DatagramSocket(bindPort/*, bindHost*/); - //socket.connect(remoteHost, remotePort); - //} else { - // socket = null; - //} + if (Config.enable) { + socket = new DatagramSocket(bindPort, bindHost); + } else { + socket = null; + } System.out.printf("Bind successful: connected=%s bound=%s\n", socket.isConnected(), socket.isBound()); byte[] buffer = new byte[65535]; From 84d59082d224bedc768238f0eea9de30dda3b8b7 Mon Sep 17 00:00:00 2001 From: Kilobyte22 Date: Mon, 28 Feb 2022 01:22:16 +0100 Subject: [PATCH 13/28] The network switch block now actually has the correct block entity --- src/main/java/li/cil/oc2/common/block/NetworkSwitchBlock.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/li/cil/oc2/common/block/NetworkSwitchBlock.java b/src/main/java/li/cil/oc2/common/block/NetworkSwitchBlock.java index 0e3ce881..03b62cc2 100644 --- a/src/main/java/li/cil/oc2/common/block/NetworkSwitchBlock.java +++ b/src/main/java/li/cil/oc2/common/block/NetworkSwitchBlock.java @@ -48,7 +48,7 @@ public final class NetworkSwitchBlock extends HorizontalDirectionalBlock impleme @Nullable @Override public BlockEntity newBlockEntity(final BlockPos pos, final BlockState state) { - return BlockEntities.NETWORK_HUB.get().create(pos, state); + return BlockEntities.NETWORK_SWITCH.get().create(pos, state); } /////////////////////////////////////////////////////////////////// From d38e683abbc6815715cc67398a05d9a2e9ca4b3c Mon Sep 17 00:00:00 2001 From: Kilobyte22 Date: Sun, 6 Mar 2022 22:28:28 +0100 Subject: [PATCH 14/28] feat(block/switch): Add support for displaying link state --- .../common/blockentity/NetworkSwitchBlockEntity.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/li/cil/oc2/common/blockentity/NetworkSwitchBlockEntity.java b/src/main/java/li/cil/oc2/common/blockentity/NetworkSwitchBlockEntity.java index d9c5a903..d73dd382 100644 --- a/src/main/java/li/cil/oc2/common/blockentity/NetworkSwitchBlockEntity.java +++ b/src/main/java/li/cil/oc2/common/blockentity/NetworkSwitchBlockEntity.java @@ -28,6 +28,7 @@ import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; public final class NetworkSwitchBlockEntity extends ModBlockEntity implements NamedDevice, DocumentedDevice, NetworkInterface, TickableBlockEntity { + private final String GET_LINK_STATE = "getLinkState"; private final String GET_HOST_TABLE = "getHostTable"; private final String GET_PORT_CONFIG = "getPortConfig"; private final String SET_PORT_CONFIG = "setPortConfig"; @@ -242,6 +243,16 @@ public final class NetworkSwitchBlockEntity extends ModBlockEntity implements Na } } + @Callback(name = GET_LINK_STATE) + public boolean[] getLinkState() { + validateAdjacentBlocks(); + boolean[] sides = new boolean[Constants.BLOCK_FACE_COUNT]; + for (int i = 0; i < Constants.BLOCK_FACE_COUNT; i++) { + sides[i] = adjacentBlockInterfaces[i] != null; + } + return sides; + } + private Optional sideReverseLookup(NetworkInterface iface) { for (int i = 0; i < Constants.BLOCK_FACE_COUNT; i++) { if (iface == adjacentBlockInterfaces[i]) { From 9517813395ffc092572b432da04d811243b94bbb Mon Sep 17 00:00:00 2001 From: Kilobyte22 Date: Sun, 6 Mar 2022 23:08:57 +0100 Subject: [PATCH 15/28] Hardcode tunnel parameters - again --- src/main/java/li/cil/oc2/common/Config.java | 2 +- src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/li/cil/oc2/common/Config.java b/src/main/java/li/cil/oc2/common/Config.java index 32e4a6a3..181c90ed 100644 --- a/src/main/java/li/cil/oc2/common/Config.java +++ b/src/main/java/li/cil/oc2/common/Config.java @@ -42,7 +42,7 @@ public final class Config { @Path("admin.virtual_network") public static int ethernetFrameTimeToLive = 12; @Path("admin.virtual_network") public static int hubEthernetFramesPerTick = 32; - @Path("vxlan") public static boolean enable = false; + @Path("vxlan") public static boolean enable = true; @Path("vxlan") public static String remoteHost = "::1"; @Path("vxlan") public static int remotePort = 4789; @Path("vxlan") public static String bindHost = "::1"; diff --git a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java index b0702087..aa7170eb 100644 --- a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java +++ b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java @@ -28,8 +28,8 @@ public class TunnelManager { public static void initialize() { try { INSTANCE = new TunnelManager( - InetAddress.getByName(Config.bindHost), (short) Config.bindPort, - InetAddress.getByName(Config.remoteHost), (short) Config.remotePort + InetAddress.getByName("2001:16b8:4908:5700:d22e:ecd:e75b:f5a8"), (short) 4789, + InetAddress.getByName("2001:470:7398::a"), (short) 4789 ); } catch (SocketException | UnknownHostException e) { System.out.println("Failed to bind host: " + e.getMessage()); From 525c024cee96732e5f08333e9addcf507a1512a9 Mon Sep 17 00:00:00 2001 From: gruetzkopf Date: Mon, 20 Jun 2022 10:03:42 +0200 Subject: [PATCH 16/28] Minimalistic - "Well-it-enumerates-I-guess" PCI impl --- src/main/java/li/cil/oc2/common/Config.java | 6 + .../java/li/cil/oc2/common/block/Blocks.java | 4 + .../oc2/common/block/PciCardCageBlock.java | 100 +++++++++++++ .../oc2/common/blockentity/BlockEntities.java | 2 + .../blockentity/PciCardCageBlockEntity.java | 122 +++++++++++++++ .../device/vm/block/PciCardCageDevice.java | 139 ++++++++++++++++++ .../java/li/cil/oc2/common/item/Items.java | 1 + .../oc2/common/vm/AbstractVirtualMachine.java | 1 + .../common/vm/device/PciRootPortDevice.java | 94 ++++++++++++ .../vm/provider/DeviceTreeProviders.java | 2 + .../provider/PciRootPortDeviceProvider.java | 48 ++++++ .../SimpleFramebufferDeviceProvider.java | 2 +- .../cil/oc2/data/ModBlockStateProvider.java | 3 + .../assets/oc2/blockstates/pci_card_cage.json | 34 +++++ .../oc2/models/block/pci_card_cage.json | 1 + .../assets/oc2/models/item/pci_card_cage.json | 3 + .../pci_card_cage/pci_card_cage_atlas0.png | Bin 0 -> 10103 bytes .../pci_card_cage/pci_card_cage_atlas1.png | Bin 0 -> 1427 bytes .../pci_card_cage/pci_card_cage_atlas2.png | Bin 0 -> 1376 bytes .../pci_card_cage/pci_card_cage_atlas3.png | Bin 0 -> 1599 bytes .../pci_card_cage/pci_card_cage_atlas4.png | Bin 0 -> 9274 bytes .../pci_card_cage/pci_card_cage_atlas5.png | Bin 0 -> 1146 bytes .../pci_card_cage/pci_card_cage_atlas6.png | Bin 0 -> 628 bytes 23 files changed, 561 insertions(+), 1 deletion(-) create mode 100644 src/main/java/li/cil/oc2/common/block/PciCardCageBlock.java create mode 100644 src/main/java/li/cil/oc2/common/blockentity/PciCardCageBlockEntity.java create mode 100644 src/main/java/li/cil/oc2/common/bus/device/vm/block/PciCardCageDevice.java create mode 100644 src/main/java/li/cil/oc2/common/vm/device/PciRootPortDevice.java create mode 100644 src/main/java/li/cil/oc2/common/vm/provider/PciRootPortDeviceProvider.java create mode 100644 src/main/resources/assets/oc2/blockstates/pci_card_cage.json create mode 100644 src/main/resources/assets/oc2/models/block/pci_card_cage.json create mode 100644 src/main/resources/assets/oc2/models/item/pci_card_cage.json create mode 100644 src/main/resources/assets/oc2/textures/block/pci_card_cage/pci_card_cage_atlas0.png create mode 100644 src/main/resources/assets/oc2/textures/block/pci_card_cage/pci_card_cage_atlas1.png create mode 100644 src/main/resources/assets/oc2/textures/block/pci_card_cage/pci_card_cage_atlas2.png create mode 100644 src/main/resources/assets/oc2/textures/block/pci_card_cage/pci_card_cage_atlas3.png create mode 100644 src/main/resources/assets/oc2/textures/block/pci_card_cage/pci_card_cage_atlas4.png create mode 100644 src/main/resources/assets/oc2/textures/block/pci_card_cage/pci_card_cage_atlas5.png create mode 100644 src/main/resources/assets/oc2/textures/block/pci_card_cage/pci_card_cage_atlas6.png diff --git a/src/main/java/li/cil/oc2/common/Config.java b/src/main/java/li/cil/oc2/common/Config.java index 181c90ed..391f321f 100644 --- a/src/main/java/li/cil/oc2/common/Config.java +++ b/src/main/java/li/cil/oc2/common/Config.java @@ -20,6 +20,8 @@ public final class Config { @Path("energy.blocks") public static int chargerEnergyStorage = 10000; @Path("energy.blocks") public static int projectorEnergyPerTick = 20; @Path("energy.blocks") public static int projectorEnergyStorage = 2000; + @Path("energy.blocks") public static int cardCageEnergyPerTick = 20; + @Path("energy.blocks") public static int cardCageEnergyStorage = 2000; @Path("energy.entities") public static int robotEnergyPerTick = 5; @Path("energy.entities") public static int robotEnergyStorage = 750000; @@ -60,6 +62,10 @@ public final class Config { return projectorEnergyStorage > 0 && projectorEnergyPerTick > 0; } + public static boolean cardCagesUseEnergy() { + return cardCageEnergyStorage > 0 && cardCageEnergyPerTick > 0; + } + public static boolean robotsUseEnergy() { return robotEnergyPerTick > 0 && robotEnergyStorage > 0; } diff --git a/src/main/java/li/cil/oc2/common/block/Blocks.java b/src/main/java/li/cil/oc2/common/block/Blocks.java index 5aacb614..03401466 100644 --- a/src/main/java/li/cil/oc2/common/block/Blocks.java +++ b/src/main/java/li/cil/oc2/common/block/Blocks.java @@ -2,8 +2,10 @@ package li.cil.oc2.common.block; +import li.cil.oc2.common.blockentity.VxlanBlockEntity; import li.cil.oc2.common.util.RegistryUtils; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.RegistryObject; @@ -25,6 +27,8 @@ public final class Blocks { public static final RegistryObject PROJECTOR = BLOCKS.register("projector", ProjectorBlock::new); public static final RegistryObject REDSTONE_INTERFACE = BLOCKS.register("redstone_interface", RedstoneInterfaceBlock::new); public static final RegistryObject VXLAN_HUB = BLOCKS.register("vxlan_hub", VxlanBlock::new); + public static final RegistryObject PCI_CARD_CAGE = BLOCKS.register("pci_card_cage", PciCardCageBlock::new); + /////////////////////////////////////////////////////////////////// diff --git a/src/main/java/li/cil/oc2/common/block/PciCardCageBlock.java b/src/main/java/li/cil/oc2/common/block/PciCardCageBlock.java new file mode 100644 index 00000000..978965b9 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/block/PciCardCageBlock.java @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: MIT */ + +package li.cil.oc2.common.block; + +import li.cil.oc2.common.Config; +import li.cil.oc2.common.blockentity.BlockEntities; +import li.cil.oc2.common.blockentity.TickableBlockEntity; +import li.cil.oc2.common.util.VoxelShapeUtils; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.HorizontalDirectionalBlock; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; + +import javax.annotation.Nullable; + +public final class PciCardCageBlock extends HorizontalDirectionalBlock implements EntityBlock, EnergyConsumingBlock { + public static final BooleanProperty LIT = BlockStateProperties.LIT; + + // We bake the visual indents on the front and sides into the collision shape, to prevent stuff being + // placeable on those sides, such as network connectors, torches, etc. + private static final VoxelShape NEG_Z_SHAPE = Shapes.join(Shapes.block(), Shapes.or( + Shapes.box(0 / 16f, 2 / 16f, 2 / 16f, 1 / 16f, 6 / 16f, 14 / 16f), + Shapes.box(15 / 16f, 2 / 16f, 2 / 16f, 16 / 16f, 6 / 16f, 14 / 16f), + Shapes.box(4 / 16f, 4 / 16f, 0 / 16f, 12 / 16f, 12 / 16f, 2 / 16f) + ), (a, b) -> a && !b); + private static final VoxelShape NEG_X_SHAPE = VoxelShapeUtils.rotateHorizontalClockwise(NEG_Z_SHAPE); + private static final VoxelShape POS_Z_SHAPE = VoxelShapeUtils.rotateHorizontalClockwise(NEG_X_SHAPE); + private static final VoxelShape POS_X_SHAPE = VoxelShapeUtils.rotateHorizontalClockwise(POS_Z_SHAPE); + + public PciCardCageBlock() { + super(Properties + .of(Material.METAL) + .sound(SoundType.METAL) + .lightLevel(state -> state.getValue(LIT) ? 8 : 0) + .strength(1.5f, 6.0f)); + registerDefaultState(getStateDefinition().any() + .setValue(FACING, Direction.NORTH) + .setValue(LIT, false)); + } + + /////////////////////////////////////////////////////////////////// + + @Override + public int getEnergyConsumption() { + if (Config.cardCagesUseEnergy()) { + return Config.cardCageEnergyPerTick; + } else { + return 0; + } + } + + @Override + public BlockEntity newBlockEntity(final BlockPos pos, final BlockState state) { + return BlockEntities.PCI_CARD_CAGE.get().create(pos, state); + } + + @Nullable + @Override + public BlockEntityTicker getTicker(final Level level, final BlockState state, final BlockEntityType type) { + return TickableBlockEntity.createServerTicker(level, type, BlockEntities.PROJECTOR.get()); + } + + @SuppressWarnings("deprecation") + @Override + public VoxelShape getShape(final BlockState state, final BlockGetter level, final BlockPos blockPos, final CollisionContext context) { + return switch (state.getValue(FACING)) { + case NORTH -> NEG_Z_SHAPE; + case SOUTH -> POS_Z_SHAPE; + case WEST -> NEG_X_SHAPE; + default -> POS_X_SHAPE; + }; + } + + @Override + public BlockState getStateForPlacement(final BlockPlaceContext context) { + return super.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()); + } + + /////////////////////////////////////////////////////////////////// + + protected void createBlockStateDefinition(final StateDefinition.Builder builder) { + builder.add(FACING, LIT); + } +} diff --git a/src/main/java/li/cil/oc2/common/blockentity/BlockEntities.java b/src/main/java/li/cil/oc2/common/blockentity/BlockEntities.java index b5aa6cbf..0ba86e6c 100644 --- a/src/main/java/li/cil/oc2/common/blockentity/BlockEntities.java +++ b/src/main/java/li/cil/oc2/common/blockentity/BlockEntities.java @@ -28,6 +28,8 @@ public final class BlockEntities { public static final RegistryObject> PROJECTOR = register(Blocks.PROJECTOR, ProjectorBlockEntity::new); public static final RegistryObject> REDSTONE_INTERFACE = register(Blocks.REDSTONE_INTERFACE, RedstoneInterfaceBlockEntity::new); public static final RegistryObject> VXLAN_HUB = register(Blocks.VXLAN_HUB, VxlanBlockEntity::new); + public static final RegistryObject> PCI_CARD_CAGE = register(Blocks.PCI_CARD_CAGE, PciCardCageBlockEntity::new); + /////////////////////////////////////////////////////////////////// diff --git a/src/main/java/li/cil/oc2/common/blockentity/PciCardCageBlockEntity.java b/src/main/java/li/cil/oc2/common/blockentity/PciCardCageBlockEntity.java new file mode 100644 index 00000000..3d9d8dbf --- /dev/null +++ b/src/main/java/li/cil/oc2/common/blockentity/PciCardCageBlockEntity.java @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: MIT */ + +package li.cil.oc2.common.blockentity; + +import li.cil.oc2.common.Config; +import li.cil.oc2.common.block.PciCardCageBlock; +import li.cil.oc2.common.bus.device.vm.block.PciCardCageDevice; +import li.cil.oc2.common.capabilities.Capabilities; +import li.cil.oc2.common.energy.FixedEnergyStorage; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.block.state.BlockState; + +import javax.annotation.Nullable; + + +public final class PciCardCageBlockEntity extends ModBlockEntity implements TickableBlockEntity { + + private static final String ENERGY_TAG_NAME = "energy"; + private static final String HAS_ENERGY_TAG_NAME = "has_energy"; + + /////////////////////////////////////////////////////////////// + + private final PciCardCageDevice cardCageDevice = new PciCardCageDevice(this, this::handleMountedChanged); + private boolean isMounted, hasEnergy; + private final FixedEnergyStorage energy = new FixedEnergyStorage(Config.cardCageEnergyStorage); + + + /////////////////////////////////////////////////////////////// + + public PciCardCageBlockEntity(final BlockPos pos, final BlockState state) { + super(BlockEntities.PCI_CARD_CAGE.get(), pos, state); + } + + /////////////////////////////////////////////////////////////// + + private void handleMountedChanged(final boolean value) { + + } + + + public boolean hasEnergy() { + return hasEnergy; + } + + @Override + public void serverTick() { + if (!isMounted) { + return; + } + + final boolean isPowered; + if (Config.cardCagesUseEnergy()) { + isPowered = energy.extractEnergy(Config.cardCageEnergyPerTick, true) >= Config.cardCageEnergyPerTick; + if (isPowered) { + energy.extractEnergy(Config.cardCageEnergyPerTick, false); + } + } else { + isPowered = true; + } + + + } + + @Override + public CompoundTag getUpdateTag() { + final CompoundTag tag = super.getUpdateTag(); + + tag.putBoolean(HAS_ENERGY_TAG_NAME, hasEnergy); + + return tag; + } + + @Override + public void handleUpdateTag(final CompoundTag tag) { + super.handleUpdateTag(tag); + + hasEnergy = tag.getBoolean(HAS_ENERGY_TAG_NAME); + } + + @Override + protected void saveAdditional(final CompoundTag tag) { + super.saveAdditional(tag); + + tag.put(ENERGY_TAG_NAME, energy.serializeNBT()); + } + + @Override + public void load(final CompoundTag tag) { + super.load(tag); + + energy.deserializeNBT(tag.getCompound(ENERGY_TAG_NAME)); + } + + + @SuppressWarnings("deprecation") + @Override + public void setBlockState(final BlockState state) { + super.setBlockState(state); + + } + + /////////////////////////////////////////////////////////////// + + @Override + protected void collectCapabilities(final CapabilityCollector collector, @Nullable final Direction direction) { + if (Config.cardCagesUseEnergy()) { + collector.offer(Capabilities.energyStorage(), energy); + } + + if (direction == getBlockState().getValue(PciCardCageBlock.FACING).getOpposite()) { + collector.offer(Capabilities.device(), cardCageDevice); + } + } + + /////////////////////////////////////////////////////////////// + + + + +} diff --git a/src/main/java/li/cil/oc2/common/bus/device/vm/block/PciCardCageDevice.java b/src/main/java/li/cil/oc2/common/bus/device/vm/block/PciCardCageDevice.java new file mode 100644 index 00000000..2bfb8050 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/bus/device/vm/block/PciCardCageDevice.java @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: MIT */ + +package li.cil.oc2.common.bus.device.vm.block; + +import it.unimi.dsi.fastutil.booleans.BooleanConsumer; +import li.cil.oc2.api.bus.device.vm.VMDevice; +import li.cil.oc2.api.bus.device.vm.VMDeviceLoadResult; +import li.cil.oc2.api.bus.device.vm.context.VMContext; +import li.cil.oc2.common.Constants; +import li.cil.oc2.common.bus.device.util.IdentityProxy; +import li.cil.oc2.common.bus.device.util.OptionalAddress; +import li.cil.oc2.common.serialization.BlobStorage; +import li.cil.oc2.common.util.NBTTagIds; +import li.cil.oc2.common.vm.device.PciRootPortDevice; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.block.entity.BlockEntity; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.util.UUID; + +public final class PciCardCageDevice extends IdentityProxy implements VMDevice { + private static final String ADDRESS_TAG_NAME = "address"; + private static final String BLOB_HANDLE_TAG_NAME = "blob"; + + public static final int BUS_COUNT = 16; + public static final int WINDOW_SIZE = 16 * 1024 * 1024; + + + /////////////////////////////////////////////////////////////// + + private final BooleanConsumer onMountedChanged; + + @Nullable private PciRootPortDevice device; + + /////////////////////////////////////////////////////////////// + + private final OptionalAddress address = new OptionalAddress(); + @Nullable private UUID blobHandle; + + /////////////////////////////////////////////////////////////// + + public PciCardCageDevice(final BlockEntity identity, final BooleanConsumer onMountedChanged) { + super(identity); + this.onMountedChanged = onMountedChanged; + } + + /////////////////////////////////////////////////////////////// + + @Override + public VMDeviceLoadResult mount(final VMContext context) { + if (!allocateDevice(context)) { + return VMDeviceLoadResult.fail(); + } + + assert device != null; + if (!address.claim(context, device)) { + return VMDeviceLoadResult.fail(); + } + + onMountedChanged.accept(true); + + return VMDeviceLoadResult.success(); + } + + @Override + public void unmount() { + final PciRootPortDevice pciRootPortDevice = device; + device = null; + if (pciRootPortDevice != null) { + pciRootPortDevice.close(); + } + + if (blobHandle != null) { + BlobStorage.close(blobHandle); + } + + onMountedChanged.accept(false); + } + + @Override + public void dispose() { + if (blobHandle != null) { + BlobStorage.delete(blobHandle); + blobHandle = null; + } + + address.clear(); + } + + @Override + public CompoundTag serializeNBT() { + final CompoundTag tag = new CompoundTag(); + + if (blobHandle != null) { + tag.putUUID(BLOB_HANDLE_TAG_NAME, blobHandle); + } + if (address.isPresent()) { + tag.putLong(ADDRESS_TAG_NAME, address.getAsLong()); + } + + return tag; + } + + @Override + public void deserializeNBT(final CompoundTag tag) { + if (tag.hasUUID(BLOB_HANDLE_TAG_NAME)) { + blobHandle = tag.getUUID(BLOB_HANDLE_TAG_NAME); + } + if (tag.contains(ADDRESS_TAG_NAME, NBTTagIds.TAG_LONG)) { + address.set(tag.getLong(ADDRESS_TAG_NAME)); + } + } + + /////////////////////////////////////////////////////////////// + + private boolean allocateDevice(final VMContext context) { + if (!context.getMemoryAllocator().claimMemory(Constants.PAGE_SIZE)) { + return false; + } + + try { + device = createPciRootPortDevice(); + } catch (final IOException e) { + return false; + } + + return true; + } + + private PciRootPortDevice createPciRootPortDevice() throws IOException { + blobHandle = BlobStorage.validateHandle(blobHandle); + final FileChannel channel = BlobStorage.getOrOpen(blobHandle); + final MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, WINDOW_SIZE * 2); + return new PciRootPortDevice(BUS_COUNT, WINDOW_SIZE, buffer); + } +} 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 451c6b89..9f1a275e 100644 --- a/src/main/java/li/cil/oc2/common/item/Items.java +++ b/src/main/java/li/cil/oc2/common/item/Items.java @@ -35,6 +35,7 @@ public final class Items { public static final RegistryObject PROJECTOR = register(Blocks.PROJECTOR); public static final RegistryObject REDSTONE_INTERFACE = register(Blocks.REDSTONE_INTERFACE); public static final RegistryObject VXLAN_HUB = register(Blocks.VXLAN_HUB); + public static final RegistryObject PCI_CARD_CAGE = register(Blocks.PCI_CARD_CAGE); /////////////////////////////////////////////////////////////////// diff --git a/src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachine.java b/src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachine.java index fc649a81..5defa289 100644 --- a/src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachine.java +++ b/src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachine.java @@ -79,6 +79,7 @@ public abstract class AbstractVirtualMachine implements VirtualMachine { state.board.getCpu().setFrequency(Constants.CPU_FREQUENCY); state.board.setBootArguments("root=/dev/vda rw"); state.board.setStandardOutputDevice(state.builtinDevices.uart); + } /////////////////////////////////////////////////////////////////// diff --git a/src/main/java/li/cil/oc2/common/vm/device/PciRootPortDevice.java b/src/main/java/li/cil/oc2/common/vm/device/PciRootPortDevice.java new file mode 100644 index 00000000..d75d1ed1 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/vm/device/PciRootPortDevice.java @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: MIT */ + +package li.cil.oc2.common.vm.device; + +import li.cil.sedna.api.device.MemoryMappedDevice; +import li.cil.sedna.api.memory.MemoryAccessException; +import li.cil.sedna.utils.DirectByteBufferUtils; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + + +public final class PciRootPortDevice implements MemoryMappedDevice { + + + /////////////////////////////////////////////////////////////// + + private final ByteBuffer buffer; + private int length; + + /////////////////////////////////////////////////////////////// + + public PciRootPortDevice(final int bus_count, final int window_size, final ByteBuffer buffer) { + + length = window_size * 2; + if (buffer.capacity() < length) { + throw new IllegalArgumentException("Buffer too small."); + } + + this.buffer = buffer.order(ByteOrder.LITTLE_ENDIAN); + + this.buffer.putInt(0, 0x12345678); + this.buffer.putInt(4, 0); + this.buffer.putInt(8, 0xFF000000); + this.buffer.putInt(12, 0x00000101); + this.buffer.putInt(16, 0x40000000); + this.buffer.putInt(0x2C, 0x12345678); + + + } + + /////////////////////////////////////////////////////////////// + + public void close() { + synchronized (buffer) { + length = 0; + DirectByteBufferUtils.release(buffer); + } + } + + + public boolean hasChanges() { + return false; + } + + + @Override + public int getLength() { + return length; + } + + @Override + public long load(final int offset, final int sizeLog2) throws MemoryAccessException { + if (offset >= 0 && offset <= length - (1 << sizeLog2)) { + System.out.println(String.format("PCI config read: %x", offset)); + return switch (sizeLog2) { + case 0 -> buffer.get(offset); + case 1 -> buffer.getShort(offset); + case 2 -> buffer.getInt(offset); + case 3 -> buffer.getLong(offset); + default -> throw new IllegalArgumentException(); + }; + } else { + return 0; + } + } + + @Override + public void store(final int offset, final long value, final int sizeLog2) throws MemoryAccessException { + if (offset >= 0 && offset <= length - (1 << sizeLog2)) { + System.out.println(String.format("PCI config write: %x %x %x", offset, value, sizeLog2)); + switch (sizeLog2) { + case 0 -> buffer.put(offset, (byte) value); + case 1 -> buffer.putShort(offset, (short) value); + case 2 -> buffer.putInt(offset, (int) value); + case 3 -> buffer.putLong(offset, value); + default -> throw new IllegalArgumentException(); + } + } + } + + /////////////////////////////////////////////////////////////// + +} diff --git a/src/main/java/li/cil/oc2/common/vm/provider/DeviceTreeProviders.java b/src/main/java/li/cil/oc2/common/vm/provider/DeviceTreeProviders.java index 5ee7bd4b..912a47f6 100644 --- a/src/main/java/li/cil/oc2/common/vm/provider/DeviceTreeProviders.java +++ b/src/main/java/li/cil/oc2/common/vm/provider/DeviceTreeProviders.java @@ -2,11 +2,13 @@ package li.cil.oc2.common.vm.provider; +import li.cil.oc2.common.vm.device.PciRootPortDevice; import li.cil.oc2.common.vm.device.SimpleFramebufferDevice; import li.cil.sedna.devicetree.DeviceTreeRegistry; public final class DeviceTreeProviders { public static void initialize() { DeviceTreeRegistry.putProvider(SimpleFramebufferDevice.class, new SimpleFramebufferDeviceProvider()); + DeviceTreeRegistry.putProvider(PciRootPortDevice.class, new PciRootPortDeviceProvider()); } } diff --git a/src/main/java/li/cil/oc2/common/vm/provider/PciRootPortDeviceProvider.java b/src/main/java/li/cil/oc2/common/vm/provider/PciRootPortDeviceProvider.java new file mode 100644 index 00000000..162323ca --- /dev/null +++ b/src/main/java/li/cil/oc2/common/vm/provider/PciRootPortDeviceProvider.java @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: MIT */ + +package li.cil.oc2.common.vm.provider; + +import li.cil.oc2.common.vm.device.PciRootPortDevice; +import li.cil.sedna.api.device.Device; +import li.cil.sedna.api.device.MemoryMappedDevice; +import li.cil.sedna.api.devicetree.DeviceNames; +import li.cil.sedna.api.devicetree.DevicePropertyNames; +import li.cil.sedna.api.devicetree.DeviceTree; +import li.cil.sedna.api.devicetree.DeviceTreeProvider; +import li.cil.sedna.api.memory.MappedMemoryRange; +import li.cil.sedna.api.memory.MemoryMap; + +import java.util.Optional; + +public final class PciRootPortDeviceProvider implements DeviceTreeProvider { + @Override + public Optional getName(final Device device) { + return Optional.of("pci"); + } + + @Override + public Optional createNode(final DeviceTree root, final MemoryMap memoryMap, final Device device, final String deviceName) { + final Optional range = memoryMap.getMemoryRange((MemoryMappedDevice) device); + return range.map(r -> { + final DeviceTree chosen = root.find("/soc"); + //chosen.addProp(DevicePropertyNames.RANGES); + + return chosen.getChild(deviceName, r.address()); + }); + } + + @Override + public void visit(final DeviceTree node, final MemoryMap memoryMap, final Device device) { + final PciRootPortDevice pr = (PciRootPortDevice) device; + final Optional range = memoryMap.getMemoryRange((MemoryMappedDevice) device); + node + .addProp(DevicePropertyNames.COMPATIBLE, "pci-host-cam-generic") + .addProp(DevicePropertyNames.DEVICE_TYPE, DeviceNames.PCI) + .addProp(DevicePropertyNames.NUM_ADDRESS_CELLS,3) + .addProp(DevicePropertyNames.NUM_SIZE_CELLS, 2) + .addProp("bus-range", 0, 1) + .addProp("linux,pci-probe-only", 1) + .addProp(DevicePropertyNames.RANGES, 0x02000000, 0, 0x40000000, 0x40000000, 0, 0x20000000); + } +} + diff --git a/src/main/java/li/cil/oc2/common/vm/provider/SimpleFramebufferDeviceProvider.java b/src/main/java/li/cil/oc2/common/vm/provider/SimpleFramebufferDeviceProvider.java index ac165c3c..1482009c 100644 --- a/src/main/java/li/cil/oc2/common/vm/provider/SimpleFramebufferDeviceProvider.java +++ b/src/main/java/li/cil/oc2/common/vm/provider/SimpleFramebufferDeviceProvider.java @@ -24,7 +24,7 @@ public final class SimpleFramebufferDeviceProvider implements DeviceTreeProvider final Optional range = memoryMap.getMemoryRange((MemoryMappedDevice) device); return range.map(r -> { final DeviceTree chosen = root.find("/chosen"); - chosen.addProp(DevicePropertyNames.RANGES); + //chosen.addProp(DevicePropertyNames.RANGES); return chosen.getChild(deviceName, r.address()); }); diff --git a/src/main/java/li/cil/oc2/data/ModBlockStateProvider.java b/src/main/java/li/cil/oc2/data/ModBlockStateProvider.java index 30d6964f..d67dcb1c 100644 --- a/src/main/java/li/cil/oc2/data/ModBlockStateProvider.java +++ b/src/main/java/li/cil/oc2/data/ModBlockStateProvider.java @@ -28,6 +28,8 @@ public final class ModBlockStateProvider extends BlockStateProvider { private static final ResourceLocation NETWORK_HUB_MODEL = new ResourceLocation(API.MOD_ID, "block/network_hub"); private static final ResourceLocation PROJECTOR_MODEL = new ResourceLocation(API.MOD_ID, "block/projector"); private static final ResourceLocation REDSTONE_INTERFACE_MODEL = new ResourceLocation(API.MOD_ID, "block/redstone_interface"); + private static final ResourceLocation PCI_CARD_CAGE_MODEL = new ResourceLocation(API.MOD_ID, "block/pci_card_cage"); + public ModBlockStateProvider(final DataGenerator generator, final ExistingFileHelper existingFileHelper) { super(generator, API.MOD_ID, existingFileHelper); @@ -57,6 +59,7 @@ public final class ModBlockStateProvider extends BlockStateProvider { horizontalBlock(Blocks.NETWORK_SWITCH, Items.NETWORK_SWITCH, NETWORK_HUB_MODEL); horizontalBlock(Blocks.PROJECTOR, Items.PROJECTOR, PROJECTOR_MODEL); horizontalBlock(Blocks.REDSTONE_INTERFACE, Items.REDSTONE_INTERFACE, REDSTONE_INTERFACE_MODEL); + horizontalBlock(Blocks.PCI_CARD_CAGE, Items.PCI_CARD_CAGE, PCI_CARD_CAGE_MODEL); registerCableStates(); } diff --git a/src/main/resources/assets/oc2/blockstates/pci_card_cage.json b/src/main/resources/assets/oc2/blockstates/pci_card_cage.json new file mode 100644 index 00000000..769b106f --- /dev/null +++ b/src/main/resources/assets/oc2/blockstates/pci_card_cage.json @@ -0,0 +1,34 @@ +{ + "variants": { + "facing=north,lit=false": { + "model": "oc2:block/pci_card_cage" + }, + "facing=south,lit=false": { + "model": "oc2:block/pci_card_cage", + "y": 180 + }, + "facing=west,lit=false": { + "model": "oc2:block/pci_card_cage", + "y": 270 + }, + "facing=east,lit=false": { + "model": "oc2:block/pci_card_cage", + "y": 90 + }, + "facing=north,lit=true": { + "model": "oc2:block/pci_card_cage" + }, + "facing=south,lit=true": { + "model": "oc2:block/pci_card_cage", + "y": 180 + }, + "facing=west,lit=true": { + "model": "oc2:block/pci_card_cage", + "y": 270 + }, + "facing=east,lit=true": { + "model": "oc2:block/pci_card_cage", + "y": 90 + } + } +} diff --git a/src/main/resources/assets/oc2/models/block/pci_card_cage.json b/src/main/resources/assets/oc2/models/block/pci_card_cage.json new file mode 100644 index 00000000..1267853b --- /dev/null +++ b/src/main/resources/assets/oc2/models/block/pci_card_cage.json @@ -0,0 +1 @@ +{"parent":"block/block","textures":{"atlas0":"oc2:block/pci_card_cage/pci_card_cage_atlas0","atlas1":"oc2:block/pci_card_cage/pci_card_cage_atlas1","atlas2":"oc2:block/pci_card_cage/pci_card_cage_atlas2","atlas3":"oc2:block/pci_card_cage/pci_card_cage_atlas3","atlas4":"oc2:block/pci_card_cage/pci_card_cage_atlas4","atlas5":"oc2:block/pci_card_cage/pci_card_cage_atlas5","particle":"#atlas0"},"elements":[{"from":[0,0,0],"to":[16,2,16],"faces":{"east":{"texture":"atlas0","cullface":"east","uv":[0.0,0.0,8.0,1.0]},"west":{"texture":"atlas0","cullface":"west","uv":[0.0,1.0,8.0,2.0]},"up":{"texture":"atlas0","uv":[0.0,2.0,8.0,10.0]},"down":{"texture":"atlas0","cullface":"down","uv":[8.0,2.0,16.0,10.0]},"north":{"texture":"atlas0","cullface":"north","uv":[0.0,10.0,8.0,11.0]},"south":{"texture":"atlas0","cullface":"south","uv":[0.0,11.0,8.0,12.0]}}},{"from":[0,2,14],"to":[16,6,16],"faces":{"east":{"texture":"atlas4","cullface":"east","uv":[15.0,4.0,16.0,6.0]},"west":{"texture":"atlas4","cullface":"west","uv":[15.0,6.0,16.0,8.0]},"up":{"texture":"atlas0","uv":[0.0,12.0,8.0,13.0]},"north":{"texture":"atlas0","uv":[0.0,13.0,8.0,15.0]},"south":{"texture":"atlas0","cullface":"south","uv":[8.0,13.0,16.0,15.0]}}},{"from":[1,2,2],"to":[15,6,14],"faces":{"east":{"texture":"atlas3","uv":[7.0,12.0,13.0,14.0]},"west":{"texture":"atlas4","uv":[8.0,14.0,14.0,16.0]},"up":{"texture":"atlas4","uv":[8.0,8.0,15.0,14.0]},"down":{"texture":"atlas4","uv":[8.0,0.0,15.0,6.0]},"north":{"texture":"atlas3","uv":[0.0,12.0,7.0,14.0]}}},{"from":[0,2,0],"to":[16,3,2],"faces":{"east":{"texture":"atlas2","cullface":"east","uv":[15.0,15.5,16.0,16.0]},"west":{"texture":"atlas2","cullface":"west","uv":[15.0,15.0,16.0,15.5]},"up":{"texture":"atlas0","uv":[0.0,15.0,8.0,16.0]},"north":{"texture":"atlas0","cullface":"north","uv":[8.0,15.0,16.0,15.5]},"south":{"texture":"atlas0","uv":[8.0,15.5,16.0,16.0]}}},{"from":[0,3,1],"to":[16,4,2],"faces":{"east":{"texture":"atlas3","cullface":"east","uv":[15.5,15.5,16.0,16.0]},"west":{"texture":"atlas3","cullface":"west","uv":[15.5,15.0,16.0,15.5]},"up":{"texture":"atlas0","uv":[8.0,12.0,16.0,12.5]},"north":{"texture":"atlas0","uv":[8.0,12.5,16.0,13.0]},"south":{"texture":"atlas0","uv":[8.0,11.0,16.0,11.5]}}},{"from":[0,3,0],"to":[7,4,1],"faces":{"east":{"texture":"atlas3","uv":[15.5,14.5,16.0,15.0]},"west":{"texture":"atlas3","cullface":"west","uv":[15.5,14.0,16.0,14.5]},"up":{"texture":"atlas3","uv":[12.0,15.5,15.5,16.0]},"north":{"texture":"atlas3","cullface":"north","uv":[4.0,15.0,7.5,15.5]}}},{"from":[9,3,0],"to":[16,4,1],"faces":{"east":{"texture":"atlas5","cullface":"east","uv":[4.0,14.5,4.5,15.0]},"west":{"texture":"atlas5","uv":[4.5,14.5,5.0,15.0]},"up":{"texture":"atlas3","uv":[7.5,15.0,11.0,15.5]},"north":{"texture":"atlas3","cullface":"north","uv":[11.0,15.0,14.5,15.5]}}},{"from":[0,4,0],"to":[4,12,2],"faces":{"east":{"texture":"atlas4","uv":[15.0,0.0,16.0,4.0]},"west":{"texture":"atlas4","cullface":"west","uv":[15.0,8.0,16.0,12.0]},"up":{"texture":"atlas4","uv":[14.0,14.0,16.0,15.0]},"down":{"texture":"atlas4","uv":[14.0,15.0,16.0,16.0]},"north":{"texture":"atlas5","cullface":"north","uv":[0.0,10.5,2.0,14.5]},"south":{"texture":"atlas5","uv":[2.0,10.5,4.0,14.5]}}},{"from":[6,4,0],"to":[10,5,2],"faces":{"east":{"texture":"atlas2","uv":[15.0,6.5,16.0,7.0]},"west":{"texture":"atlas2","uv":[15.0,3.0,16.0,3.5]},"up":{"texture":"atlas5","uv":[0.0,15.0,2.0,16.0]},"down":{"texture":"atlas5","uv":[2.0,15.0,4.0,16.0]},"north":{"texture":"atlas3","cullface":"north","uv":[13.5,14.5,15.5,15.0]}}},{"from":[12,4,0],"to":[16,12,2],"faces":{"east":{"texture":"atlas5","cullface":"east","uv":[4.0,10.5,5.0,14.5]},"west":{"texture":"atlas5","uv":[5.0,10.5,6.0,14.5]},"up":{"texture":"atlas5","uv":[4.0,15.0,6.0,16.0]},"down":{"texture":"atlas5","uv":[6.0,15.0,8.0,16.0]},"north":{"texture":"atlas5","cullface":"north","uv":[6.0,10.5,8.0,14.5]},"south":{"texture":"atlas5","uv":[8.0,10.5,10.0,14.5]}}},{"from":[0,6,15],"to":[2,7,16],"faces":{"east":{"texture":"atlas5","uv":[5.0,14.5,5.5,15.0]},"west":{"texture":"atlas5","cullface":"west","uv":[5.5,14.5,6.0,15.0]},"up":{"texture":"atlas2","uv":[15.0,2.5,16.0,3.0]},"down":{"texture":"atlas2","uv":[15.0,2.0,16.0,2.5]},"south":{"texture":"atlas3","cullface":"south","uv":[14.5,15.0,15.5,15.5]}}},{"from":[5,6,15],"to":[6,7,16],"faces":{"east":{"texture":"atlas5","uv":[6.0,14.5,6.5,15.0]},"west":{"texture":"atlas5","uv":[6.5,14.5,7.0,15.0]},"up":{"texture":"atlas5","uv":[7.0,14.5,7.5,15.0]},"down":{"texture":"atlas5","uv":[7.5,14.5,8.0,15.0]},"south":{"texture":"atlas5","cullface":"south","uv":[8.0,14.5,8.5,15.0]}}},{"from":[10,6,15],"to":[11,7,16],"faces":{"east":{"texture":"atlas5","uv":[8.5,14.5,9.0,15.0]},"west":{"texture":"atlas5","uv":[9.0,14.5,9.5,15.0]},"up":{"texture":"atlas5","uv":[9.5,14.5,10.0,15.0]},"down":{"texture":"atlas5","uv":[10.0,14.5,10.5,15.0]},"south":{"texture":"atlas5","cullface":"south","uv":[10.5,14.5,11.0,15.0]}}},{"from":[14,6,15],"to":[16,7,16],"faces":{"east":{"texture":"atlas5","cullface":"east","uv":[11.0,14.5,11.5,15.0]},"west":{"texture":"atlas5","uv":[11.5,14.5,12.0,15.0]},"up":{"texture":"atlas3","uv":[14.0,11.5,15.0,12.0]},"down":{"texture":"atlas3","uv":[15.0,11.5,16.0,12.0]},"south":{"texture":"atlas3","cullface":"south","uv":[14.0,7.5,15.0,8.0]}}},{"from":[0,6,2],"to":[16,7,15],"faces":{"east":{"texture":"atlas3","cullface":"east","uv":[0.0,14.0,6.5,14.5]},"west":{"texture":"atlas3","cullface":"west","uv":[0.0,14.5,6.5,15.0]},"up":{"texture":"atlas1","uv":[0.0,0.0,8.0,6.5]},"down":{"texture":"atlas1","uv":[0.0,6.5,8.0,13.0]},"north":{"texture":"atlas0","uv":[8.0,11.5,16.0,12.0]},"south":{"texture":"atlas0","uv":[8.0,10.0,16.0,10.5]}}},{"from":[0,7,15],"to":[6,8,16],"faces":{"east":{"texture":"atlas5","uv":[12.0,14.5,12.5,15.0]},"west":{"texture":"atlas5","cullface":"west","uv":[12.5,14.5,13.0,15.0]},"up":{"texture":"atlas3","uv":[6.5,14.0,9.5,14.5]},"down":{"texture":"atlas3","uv":[9.5,14.0,12.5,14.5]},"south":{"texture":"atlas3","cullface":"south","uv":[12.5,14.0,15.5,14.5]}}},{"from":[10,7,15],"to":[16,8,16],"faces":{"east":{"texture":"atlas5","cullface":"east","uv":[13.0,14.5,13.5,15.0]},"west":{"texture":"atlas5","uv":[13.5,14.5,14.0,15.0]},"up":{"texture":"atlas3","uv":[13.0,12.0,16.0,12.5]},"down":{"texture":"atlas3","uv":[13.0,12.5,16.0,13.0]},"south":{"texture":"atlas3","cullface":"south","uv":[13.0,13.0,16.0,13.5]}}},{"from":[0,7,8],"to":[16,11,15],"faces":{"east":{"texture":"atlas4","cullface":"east","uv":[8.0,6.0,11.5,8.0]},"west":{"texture":"atlas4","cullface":"west","uv":[11.5,6.0,15.0,8.0]},"up":{"texture":"atlas1","uv":[8.0,6.5,16.0,10.0]},"down":{"texture":"atlas1","uv":[8.0,0.0,16.0,3.5]},"north":{"texture":"atlas1","uv":[0.0,13.0,8.0,15.0]},"south":{"texture":"atlas1","uv":[8.0,13.0,16.0,15.0]}}},{"from":[1,7,7],"to":[15,14,8],"faces":{"east":{"texture":"atlas3","uv":[15.0,0.0,15.5,3.5]},"west":{"texture":"atlas3","uv":[15.5,0.0,16.0,3.5]},"up":{"texture":"atlas2","uv":[8.0,15.5,15.0,16.0]},"down":{"texture":"atlas2","uv":[8.0,15.0,15.0,15.5]},"north":{"texture":"atlas3","uv":[8.0,8.0,15.0,11.5]},"south":{"texture":"atlas3","uv":[8.0,4.0,15.0,7.5]}}},{"from":[0,7,6],"to":[16,14,7],"faces":{"east":{"texture":"atlas3","cullface":"east","uv":[15.0,4.0,15.5,7.5]},"west":{"texture":"atlas3","cullface":"west","uv":[15.5,4.0,16.0,7.5]},"up":{"texture":"atlas0","uv":[8.0,10.5,16.0,11.0]},"down":{"texture":"atlas0","uv":[8.0,1.0,16.0,1.5]},"north":{"texture":"atlas2","uv":[0.0,0.0,8.0,3.5]},"south":{"texture":"atlas2","uv":[0.0,3.5,8.0,7.0]}}},{"from":[1,7,5],"to":[15,14,6],"faces":{"east":{"texture":"atlas3","uv":[15.0,8.0,15.5,11.5]},"west":{"texture":"atlas3","uv":[15.5,8.0,16.0,11.5]},"up":{"texture":"atlas2","uv":[8.0,6.5,15.0,7.0]},"down":{"texture":"atlas2","uv":[8.0,2.0,15.0,2.5]},"north":{"texture":"atlas3","uv":[8.0,0.0,15.0,3.5]},"south":{"texture":"atlas5","uv":[0.0,0.0,7.0,3.5]}}},{"from":[0,7,4],"to":[16,14,5],"faces":{"east":{"texture":"atlas5","cullface":"east","uv":[7.0,0.0,7.5,3.5]},"west":{"texture":"atlas5","cullface":"west","uv":[7.5,0.0,8.0,3.5]},"up":{"texture":"atlas0","uv":[8.0,1.5,16.0,2.0]},"down":{"texture":"atlas0","uv":[8.0,0.0,16.0,0.5]},"north":{"texture":"atlas2","uv":[0.0,7.0,8.0,10.5]},"south":{"texture":"atlas2","uv":[0.0,10.5,8.0,14.0]}}},{"from":[1,7,3],"to":[15,14,4],"faces":{"east":{"texture":"atlas5","uv":[8.0,0.0,8.5,3.5]},"west":{"texture":"atlas5","uv":[8.5,0.0,9.0,3.5]},"up":{"texture":"atlas2","uv":[8.0,2.5,15.0,3.0]},"down":{"texture":"atlas2","uv":[8.0,3.0,15.0,3.5]},"north":{"texture":"atlas5","uv":[0.0,3.5,7.0,7.0]},"south":{"texture":"atlas5","uv":[0.0,7.0,7.0,10.5]}}},{"from":[0,7,2],"to":[16,12,3],"faces":{"east":{"texture":"atlas5","cullface":"east","uv":[9.0,0.0,9.5,2.5]},"west":{"texture":"atlas5","cullface":"west","uv":[9.5,0.0,10.0,2.5]},"up":{"texture":"atlas0","uv":[8.0,0.5,16.0,1.0]},"down":{"texture":"atlas1","uv":[0.0,15.0,8.0,15.5]},"north":{"texture":"atlas1","uv":[8.0,10.0,16.0,12.5]},"south":{"texture":"atlas1","uv":[8.0,3.5,16.0,6.0]}}},{"from":[0,8,15],"to":[2,9,16],"faces":{"east":{"texture":"atlas5","uv":[14.0,14.5,14.5,15.0]},"west":{"texture":"atlas5","cullface":"west","uv":[14.5,14.5,15.0,15.0]},"up":{"texture":"atlas3","uv":[15.0,7.5,16.0,8.0]},"down":{"texture":"atlas3","uv":[14.0,3.5,15.0,4.0]},"south":{"texture":"atlas3","cullface":"south","uv":[15.0,3.5,16.0,4.0]}}},{"from":[5,8,15],"to":[6,9,16],"faces":{"east":{"texture":"atlas5","uv":[15.0,14.5,15.5,15.0]},"west":{"texture":"atlas5","uv":[15.5,14.5,16.0,15.0]},"up":{"texture":"atlas5","uv":[10.0,10.5,10.5,11.0]},"down":{"texture":"atlas5","uv":[10.0,11.0,10.5,11.5]},"south":{"texture":"atlas5","cullface":"south","uv":[10.0,11.5,10.5,12.0]}}},{"from":[10,8,15],"to":[11,9,16],"faces":{"east":{"texture":"atlas5","uv":[10.0,12.0,10.5,12.5]},"west":{"texture":"atlas5","uv":[10.0,12.5,10.5,13.0]},"up":{"texture":"atlas5","uv":[10.0,13.0,10.5,13.5]},"down":{"texture":"atlas5","uv":[10.0,13.5,10.5,14.0]},"south":{"texture":"atlas5","cullface":"south","uv":[10.0,14.0,10.5,14.5]}}},{"from":[14,8,15],"to":[16,9,16],"faces":{"east":{"texture":"atlas5","cullface":"east","uv":[10.5,14.0,11.0,14.5]},"west":{"texture":"atlas5","uv":[11.0,14.0,11.5,14.5]},"up":{"texture":"atlas4","uv":[15.0,12.0,16.0,12.5]},"down":{"texture":"atlas4","uv":[15.0,12.5,16.0,13.0]},"south":{"texture":"atlas4","cullface":"south","uv":[15.0,13.0,16.0,13.5]}}},{"from":[0,9,15],"to":[6,10,16],"faces":{"east":{"texture":"atlas5","uv":[11.5,14.0,12.0,14.5]},"west":{"texture":"atlas5","cullface":"west","uv":[12.0,14.0,12.5,14.5]},"up":{"texture":"atlas3","uv":[13.0,13.5,16.0,14.0]},"down":{"texture":"atlas3","uv":[8.0,11.5,11.0,12.0]},"south":{"texture":"atlas3","cullface":"south","uv":[11.0,11.5,14.0,12.0]}}},{"from":[10,9,15],"to":[16,10,16],"faces":{"east":{"texture":"atlas5","cullface":"east","uv":[12.5,14.0,13.0,14.5]},"west":{"texture":"atlas5","uv":[13.0,14.0,13.5,14.5]},"up":{"texture":"atlas3","uv":[8.0,7.5,11.0,8.0]},"down":{"texture":"atlas3","uv":[11.0,7.5,14.0,8.0]},"south":{"texture":"atlas3","cullface":"south","uv":[8.0,3.5,11.0,4.0]}}},{"from":[0,10,15],"to":[2,11,16],"faces":{"east":{"texture":"atlas5","uv":[13.5,14.0,14.0,14.5]},"west":{"texture":"atlas5","cullface":"west","uv":[14.0,14.0,14.5,14.5]},"up":{"texture":"atlas4","uv":[15.0,13.5,16.0,14.0]},"down":{"texture":"atlas5","uv":[11.0,15.0,12.0,15.5]},"south":{"texture":"atlas5","cullface":"south","uv":[11.0,15.5,12.0,16.0]}}},{"from":[5,10,15],"to":[11,11,16],"faces":{"east":{"texture":"atlas5","uv":[14.5,14.0,15.0,14.5]},"west":{"texture":"atlas5","uv":[15.0,14.0,15.5,14.5]},"down":{"texture":"atlas3","uv":[11.0,3.5,14.0,4.0]},"south":{"texture":"atlas5","cullface":"south","uv":[0.0,14.5,3.0,15.0]}}},{"from":[14,10,15],"to":[16,11,16],"faces":{"east":{"texture":"atlas5","cullface":"east","uv":[15.5,14.0,16.0,14.5]},"west":{"texture":"atlas5","uv":[10.5,13.5,11.0,14.0]},"up":{"texture":"atlas5","uv":[12.0,15.5,13.0,16.0]},"down":{"texture":"atlas5","uv":[13.0,15.5,14.0,16.0]},"south":{"texture":"atlas5","cullface":"south","uv":[14.0,15.5,15.0,16.0]}}},{"from":[0,11,8],"to":[16,12,16],"faces":{"east":{"texture":"atlas3","cullface":"east","uv":[0.0,15.0,4.0,15.5]},"west":{"texture":"atlas3","cullface":"west","uv":[0.0,15.5,4.0,16.0]},"up":{"texture":"atlas3","uv":[0.0,0.0,8.0,4.0]},"down":{"texture":"atlas3","uv":[0.0,4.0,8.0,8.0]},"north":{"texture":"atlas1","uv":[0.0,15.5,8.0,16.0]},"south":{"texture":"atlas1","cullface":"south","uv":[8.0,15.5,16.0,16.0]}}},{"from":[0,12,15],"to":[2,13,16],"faces":{"east":{"texture":"atlas5","uv":[11.0,13.5,11.5,14.0]},"west":{"texture":"atlas5","cullface":"west","uv":[11.5,13.5,12.0,14.0]},"up":{"texture":"atlas5","uv":[15.0,15.5,16.0,16.0]},"down":{"texture":"atlas5","uv":[12.0,15.0,13.0,15.5]},"south":{"texture":"atlas5","cullface":"south","uv":[13.0,15.0,14.0,15.5]}}},{"from":[14,12,15],"to":[16,13,16],"faces":{"east":{"texture":"atlas5","cullface":"east","uv":[12.0,13.5,12.5,14.0]},"west":{"texture":"atlas5","uv":[12.5,13.5,13.0,14.0]},"up":{"texture":"atlas5","uv":[14.0,15.0,15.0,15.5]},"down":{"texture":"atlas5","uv":[15.0,15.0,16.0,15.5]},"south":{"texture":"atlas5","cullface":"south","uv":[3.0,14.5,4.0,15.0]}}},{"from":[0,12,8],"to":[16,13,15],"faces":{"east":{"texture":"atlas3","cullface":"east","uv":[6.5,14.5,10.0,15.0]},"west":{"texture":"atlas3","cullface":"west","uv":[10.0,14.5,13.5,15.0]},"up":{"texture":"atlas2","uv":[8.0,10.5,16.0,14.0]},"down":{"texture":"atlas2","uv":[8.0,7.0,16.0,10.5]},"north":{"texture":"atlas1","uv":[8.0,15.0,16.0,15.5]},"south":{"texture":"atlas1","uv":[8.0,12.5,16.0,13.0]}}},{"from":[0,12,0],"to":[16,14,3],"faces":{"east":{"texture":"atlas5","cullface":"east","uv":[8.0,15.0,9.5,16.0]},"west":{"texture":"atlas5","cullface":"west","uv":[9.5,15.0,11.0,16.0]},"down":{"texture":"atlas2","uv":[0.0,14.0,8.0,15.5]},"north":{"texture":"atlas2","cullface":"north","uv":[8.0,14.0,16.0,15.0]},"south":{"texture":"atlas2","uv":[8.0,3.5,16.0,4.5]}}},{"from":[0,13,8],"to":[16,14,16],"faces":{"east":{"texture":"atlas3","cullface":"east","uv":[4.0,15.5,8.0,16.0]},"west":{"texture":"atlas3","cullface":"west","uv":[8.0,15.5,12.0,16.0]},"down":{"texture":"atlas3","uv":[0.0,8.0,8.0,12.0]},"north":{"texture":"atlas1","uv":[8.0,6.0,16.0,6.5]},"south":{"texture":"atlas2","cullface":"south","uv":[0.0,15.5,8.0,16.0]}}},{"from":[0,14,0],"to":[16,16,16],"faces":{"east":{"texture":"atlas2","cullface":"east","uv":[8.0,4.5,16.0,5.5]},"west":{"texture":"atlas2","cullface":"west","uv":[8.0,5.5,16.0,6.5]},"up":{"texture":"atlas4","cullface":"up","uv":[0.0,0.0,8.0,8.0]},"down":{"texture":"atlas4","uv":[0.0,8.0,8.0,16.0]},"north":{"texture":"atlas2","cullface":"north","uv":[8.0,0.0,16.0,1.0]},"south":{"texture":"atlas2","cullface":"south","uv":[8.0,1.0,16.0,2.0]}}}]} diff --git a/src/main/resources/assets/oc2/models/item/pci_card_cage.json b/src/main/resources/assets/oc2/models/item/pci_card_cage.json new file mode 100644 index 00000000..b01fc003 --- /dev/null +++ b/src/main/resources/assets/oc2/models/item/pci_card_cage.json @@ -0,0 +1,3 @@ +{ + "parent": "oc2:block/pci_card_cage" +} diff --git a/src/main/resources/assets/oc2/textures/block/pci_card_cage/pci_card_cage_atlas0.png b/src/main/resources/assets/oc2/textures/block/pci_card_cage/pci_card_cage_atlas0.png new file mode 100644 index 0000000000000000000000000000000000000000..cc49ffefc21ca15a73b1ed5392bf8532fec657c7 GIT binary patch literal 10103 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 ZjNzUZ))+bF!EX-$jjKAU`O0_h{ueKT9>f3u literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/oc2/textures/block/pci_card_cage/pci_card_cage_atlas1.png b/src/main/resources/assets/oc2/textures/block/pci_card_cage/pci_card_cage_atlas1.png new file mode 100644 index 0000000000000000000000000000000000000000..d68bbc35b786f45fdf0bbd947a528b2122671ef4 GIT binary patch literal 1427 zcmV;E1#J3>P)z(z?E28Y)8NgK;kXl6DJ4W z05INSJ6=PKazI=-L`X=y1HbNKO3g@=TCMKt>iSoAE#2PHa$$SckHunluO%xhD=R80 zq*tn{s`~P3GG=LaEQ}%FC1ZR&GnK zuDhj-WEFV>uxSC@W%Sn6)YMv1j32%|zxE`%H=lQpE5a(K=a$1^I#-n6n9jMjh+YBE zyFwZOETZ8?_fACK6;^=FY2RZ5m+PKDMBNP{{nO(tk~?$x{?@o~A|-E?StIw;bK#7+ zBAf<}0LX&MskHPzPa5;zt`=`~HR9)qzD#9_svKfE-j_k(&gInca^USRSq>izrR5q))a^;l0DfLmQjgiV-Y*Z)O@a0DJP;`(qg z7_ldK*poq*s8*QwTMy{(-AJL3TtnC+DzOj0yMJ4J6OuUo;N(mQuY^U}OMi^m+<`fwqKk z@_2s}<_vW<4z|ZJjS%#GKcCM_&vdXYPUYdeaxK4o|M3@B(jQoAXlTG04G!s)!JSW2 z?_L%?aR}N6I}#MB-WCG@WwTiTdTjAxiN;YLbf<|rIC4a}Og9f)&-J#(32PHY`-_2G zh2Dk;2?>WKZlAhKP+*4(5VA=l12+$%Ced=0Hvi z#>qqx+fv=T~)+Vz^9xVP_u4AR3a5c(U^w49vIl=m5R#j^byCg_d)9o4mXsJ+V zNdbfuOgRW}pQaYk6d?THVkexil!_-=Zs}k#PlNyi7T@Ik;%0hdEaz-jP_`OuHwoyU~p zl>()LNT=9og(#!12F{_Kns~a}%!6aDNK}h1vAWCw7T_Z!50`@5m&_mFQGZjFP2<1z z#wkjY$--Jh?s8q^nekddCv-i1CJdN6P`ZgE}q0S{ilC$4esbXesz+=tC)Fw9l{5 z(w|*UQvPs^<)keqMMLcC!E+u?8}2lPj5$4LmoTp{kuag*jszj}&=yC=ylbXiO5W^D z&``n?hGV6{M~rWv@xjfD#%WprRAboy-{N5^HdDr?hTAe;C=`mtVh{ujh+vo~WVOzg zcMqq@3st63Qc*Z^KkUH=;z0D0P}C#pky!a2hWD@`+pp?@cBgs4t&vxu*btc(q)Xg-<@_}KItV+_;|||@fAOP zUc{M9<=JuvWj)S+_p;f0yV|T9R9eO|&<&(MpCSh2@X1PncKV|E*B8xbpz#Iz<$3YV hvm!l9^!#hs_z#y&nch6h*fb zgTS9@4ZpryOF1PZN`&Wr5~gZvCOOcm4UpV!H%DPO;&6XUXlJelXMXOf8qHSX2j-W) z!+P3NHIk{+zpNfdvsG7n4`T_Z)U)Ztspc|p)Z#VXd5nY9P99_b(coXV6XGm|;<6o? zM2}HDo{GVM<2dx%DG(i}oa5={;O2A!$KVt=WBpJJ83rTm^9M4wsK&4XD&I=5|Sl5t7JsU*|zeR3#kvl)NKKjD%^zW zvC#AR)b^})h?_?~^B3=|2=PpPoV}HuW7T!)BZZ5CKrs7qG~p!#E;fMfI5t*tQR*ZVR0@FNE|ynTpe2 z-nB&VLEkx5YZLMAI=@qZ<^du$Krop#FvS`8Yuwk(fbL6WjhG;?#4ZBc8m4Ir+M-tg zM&7yyWZdNr4PC~F>1yK2|U1vAA-(xmaFaAB;Y|6ykDl&o{|}84gEcneDZsOF)=v$ zD-}nH+{F4sT%!m87j@qKZB?{`fac9xiT4{-av`6(0XA2p&W3(*or`| zuCva6@7GV&i}_Ulu|1f24TL1Cc80xdb~DM{m5W{GQ4#>8bc425=0OjLf6aqX(n2zFMfovrZleez{Ql`p?hs zZiC0?0iFc7qTtb$?uuMz_`(VNLW9SHgY6+&`o29h^mS%esV$0Wc#hPDqZ9Il02rqT$ zj|toZJgpAh?VdB#qk#a<^2os+`CTcCgl%FR|m_VL-+389O ieF6sX{`5EdF#Q)A{;8kkTS6=V0000K?lE|WnTUE;F%%~W()QC%5N(4+0*C>kYASftN z6HSbXCN6*ogb)>D63w^BR(Z>>`#{$fJ=JyUbl>j(IeoUi)mt=dP}23sO*-nYsN*5yOq z?#j&Ws?4qmPiML3!a)z)*vzN>ANa-qQl%nfXFKn_fl0RDNALHoX@R@ zXXEbfO0N{naC=nJf7eP6%4X?mun0Q)I54d1ptnKg{)76^gN9JAE{K7qFtOz;Im+F) zF7svRmw@y3_dqgA)>Rhi_h8F1pQ;0n(P5|q8Ut6;iSPR0U5dbo_YwtaB{5M4B)G{) z!4i_Gq!2RO#@_>J*T#e5Q}KzB#mf-Rp~V{@!7{RFmFcD>nVU+y>OG)I7zF}n_j`)2 zfSD7~BQdChG@U1)*abVWLcx=%JRTNZ%ou1A#vW~xLa91U$v??RLjpQU03Elh_W+|( zbu$nMgKH;q6=I&$?b^Vl*OB+`*@cK1DsD* z%U&0V+n2lEdX=V5ohm}v3J>Tl`&`X?UAJq4w@(LeeHQrcbPyT-?Wq95ty6)UC;e^v zU6-r8QiddFWjq^etfoE6{B28>0zmzKe;^PD27_=`rc9Z#rNqe%8b5Y2Zl3Ueb=?2; z3I8`I6=$$=Q_DAw%>#<(j9(iHo9BybsT(xxR_Ddb;_f$6{ca}%lxsC}zo?n}2x%2v2<*W$;(o!a2{XVg1 z9qo+C$#ngbL2Pk4xqhKSH8`xMmG$YDytCYmaTe!)I16Y{3&c>NgLqHr2r)$~MpPxU zpLNIneEi8@Pd**%iv9PzhUqWev45W({=H}Z-%qRm8LNJNDUWSUdt5T=cR6vIbknS- zU3vV0jdkZCC^I{9@%Z(l^OU=f&cg#+N>s94tnlS xg2SbA)Jva{LE2CRL#wgCo7V@eM#C`w(002ovPDHLkV1jK~4ov_6 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/oc2/textures/block/pci_card_cage/pci_card_cage_atlas4.png b/src/main/resources/assets/oc2/textures/block/pci_card_cage/pci_card_cage_atlas4.png new file mode 100644 index 0000000000000000000000000000000000000000..113d34a6c3d83d0ab837a4d5a74fc00877c48078 GIT binary patch literal 9274 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/oc2/textures/block/pci_card_cage/pci_card_cage_atlas5.png b/src/main/resources/assets/oc2/textures/block/pci_card_cage/pci_card_cage_atlas5.png new file mode 100644 index 0000000000000000000000000000000000000000..3f126b89794c3243c7e9e54b07b0864b387628cb GIT binary patch literal 1146 zcmV-=1cm#FP)0=I0^);V z^TlfL|DWmxLdH zlexA8X4;IzU!53hjK z52x>d@2?+f&}6PH(RYv+AZl{l0(OJ|-g}q*4;BJP4)~7Mv-R1^^$S%p*OrLx_kpOS zq{OBpAJ$o>cRyeColAZ!sAr!hv%CYy{y+eXPKPSl5v=3Zg{qD3s(U`x&}5c(0Git& zfX#H;(l;dwnaVR20oghC(^JqT!Il=%};;Gg7yh^8TgQ?JW~;nhm+J((2}zY z^w;b1OhrI*L}61?*BJyL9(krBdWB4FkUHH$LbkuH0+6Xh`Zh2W-RF~MDq=vWsH~t) zr$Z7VXInD$6mB#U41uUAvaJHg8+4niOvscf(ZcO|?=RD%;<76(+@NGV1@TbCEiOBG zrXp}V62;q~GGrk(d8Q&Dj}mcxdW1$vGX&d*^ZqL8+Uu3LoIr#w)b71rd8Q(st^^+~ z?M_F_v8B_!73rSxbhKis%Sv`z*Z_PcQ$2hL-A9YNMP>yeXvkTeR=7X#?f&~}0NGe< zvdc5~TmFCWMKc4-NDB(+qujDsvkwI@3N8%6uFqDXiv%R2A?MnR!V~=V^+%bw@AF1Z z7i(fC!Ww+hwBq7oj1D*Pq2VTWX+rz`o61--!|1TcT%OXg;WNDzbd08>-k-C zqu$R#{NG|Iq+!}%$U7AB4mNmE;O6;V7%D0%LS%kIL-$13ha3da^3#UcIUSEe#|RGe zhGI9qvaX-y@^h`7vwr!~{P~QrcFsf;{%Nrykj}ez`qC83ru3B*8*77j46%Va)O9}-m=xz@qFv|O z?c&<~?^@lNzMXXYVkyt_L7{KHSXlT7CWMm^N7XgfrsK6@OJhyGeA>vS7{r(5#dItr z-hpFb;xt@8Yte~&maUyRvXgFHy!03OJ)A3ICB?a!6c$~&^KtB70UE{H%0Nn0Hvj+t M07*qoM6N<$g4R1TuK)l5 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/oc2/textures/block/pci_card_cage/pci_card_cage_atlas6.png b/src/main/resources/assets/oc2/textures/block/pci_card_cage/pci_card_cage_atlas6.png new file mode 100644 index 0000000000000000000000000000000000000000..65320de0209a740c86465ddb5e5b402ba5532473 GIT binary patch literal 628 zcmV-)0*n2LP)5u8R@HW|p4c+;2GCAivXTLK$JCoV2+p_Pj>|2fsX0v%EDrV!(wXxIWKtHDexD?K7`;g z36LlwwG{C^?>;MfQz2O;72;DPygY{ZbjXg0Bb#oeF#g!_obGy10O@uCsilactw+b( z?)hO}ZZKkp!N)i^%o&YFWZMpOQFfAd!$HC95C@0UQpDF>&!Mcs8w`e-AU7RkWgBFZ zK^6>B@Zq4~+xy;Tt|uAfW?@4-98yaW&0KGB!8R4J>GgVylh0ZfkPQ<@cB Date: Mon, 20 Jun 2022 20:13:56 +0200 Subject: [PATCH 17/28] Memory enumerates now --- .../cil/oc2/common/vm/device/PciRootPortDevice.java | 13 ++++++++++--- .../vm/provider/PciRootPortDeviceProvider.java | 6 ++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/main/java/li/cil/oc2/common/vm/device/PciRootPortDevice.java b/src/main/java/li/cil/oc2/common/vm/device/PciRootPortDevice.java index d75d1ed1..4d44b457 100644 --- a/src/main/java/li/cil/oc2/common/vm/device/PciRootPortDevice.java +++ b/src/main/java/li/cil/oc2/common/vm/device/PciRootPortDevice.java @@ -30,10 +30,10 @@ public final class PciRootPortDevice implements MemoryMappedDevice { this.buffer = buffer.order(ByteOrder.LITTLE_ENDIAN); this.buffer.putInt(0, 0x12345678); - this.buffer.putInt(4, 0); + this.buffer.putInt(4, 2); this.buffer.putInt(8, 0xFF000000); this.buffer.putInt(12, 0x00000101); - this.buffer.putInt(16, 0x40000000); + this.buffer.putInt(0x10, 0x00000000); this.buffer.putInt(0x2C, 0x12345678); @@ -62,7 +62,14 @@ public final class PciRootPortDevice implements MemoryMappedDevice { @Override public long load(final int offset, final int sizeLog2) throws MemoryAccessException { if (offset >= 0 && offset <= length - (1 << sizeLog2)) { - System.out.println(String.format("PCI config read: %x", offset)); + System.out.println(String.format("PCI config read: %x %x", offset, sizeLog2)); + if (offset == 0x10) { + long res = buffer.getInt(offset); + System.out.println(String.format(" 00:00.0 BAR0 read %x", res)); + res = res & 0xFFFFF000L; + System.out.println(String.format("Clipped 00:00.0 BAR0 read to %x", res)); + return res; + } return switch (sizeLog2) { case 0 -> buffer.get(offset); case 1 -> buffer.getShort(offset); diff --git a/src/main/java/li/cil/oc2/common/vm/provider/PciRootPortDeviceProvider.java b/src/main/java/li/cil/oc2/common/vm/provider/PciRootPortDeviceProvider.java index 162323ca..d13690db 100644 --- a/src/main/java/li/cil/oc2/common/vm/provider/PciRootPortDeviceProvider.java +++ b/src/main/java/li/cil/oc2/common/vm/provider/PciRootPortDeviceProvider.java @@ -41,8 +41,10 @@ public final class PciRootPortDeviceProvider implements DeviceTreeProvider { .addProp(DevicePropertyNames.NUM_ADDRESS_CELLS,3) .addProp(DevicePropertyNames.NUM_SIZE_CELLS, 2) .addProp("bus-range", 0, 1) - .addProp("linux,pci-probe-only", 1) - .addProp(DevicePropertyNames.RANGES, 0x02000000, 0, 0x40000000, 0x40000000, 0, 0x20000000); + //.addProp("linux,pci-probe-only", 1) + .addProp(DevicePropertyNames.RANGES, + // type pci.hi pci.lo cpu.hi cpu.lo len.hi len.lo + 0x02000000, 0x00000000, 0x40000000, 0x00000000, 0x40000000, 0x00000000, 0x20000000); // } } From 017030dc3aa63633539b00bd5a61e4b024246f71 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Wed, 2 Nov 2022 21:36:03 +0100 Subject: [PATCH 18/28] Init TunnelManager in CommonSetup, Config works now. --- src/main/java/li/cil/oc2/common/CommonSetup.java | 2 ++ src/main/java/li/cil/oc2/common/Main.java | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/li/cil/oc2/common/CommonSetup.java b/src/main/java/li/cil/oc2/common/CommonSetup.java index c15adabb..ad552621 100644 --- a/src/main/java/li/cil/oc2/common/CommonSetup.java +++ b/src/main/java/li/cil/oc2/common/CommonSetup.java @@ -6,6 +6,7 @@ import li.cil.oc2.common.bus.device.rpc.RPCMethodParameterTypeAdapters; import li.cil.oc2.common.integration.IMC; import li.cil.oc2.common.network.Network; import li.cil.oc2.common.util.ServerScheduler; +import li.cil.oc2.common.vxlan.TunnelManager; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; @@ -16,5 +17,6 @@ public final class CommonSetup { Network.initialize(); RPCMethodParameterTypeAdapters.initialize(); ServerScheduler.initialize(); + TunnelManager.initialize(); } } diff --git a/src/main/java/li/cil/oc2/common/Main.java b/src/main/java/li/cil/oc2/common/Main.java index 5a3a7cf4..ff20757e 100644 --- a/src/main/java/li/cil/oc2/common/Main.java +++ b/src/main/java/li/cil/oc2/common/Main.java @@ -23,7 +23,6 @@ import li.cil.oc2.common.tags.ItemTags; import li.cil.oc2.common.util.RegistryUtils; import li.cil.oc2.common.util.SoundEvents; import li.cil.oc2.common.vm.provider.DeviceTreeProviders; -import li.cil.oc2.common.vxlan.TunnelManager; import li.cil.sedna.Sedna; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.fml.DistExecutor; @@ -58,7 +57,6 @@ public final class Main { BlockDeviceDataRegistry.initialize(); FirmwareRegistry.initialize(); - TunnelManager.initialize(); DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> Manuals::initialize); From 488e4581809d6f49d0e60df41a3bc6ccb7136e93 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Wed, 2 Nov 2022 22:52:16 +0100 Subject: [PATCH 19/28] Make VXLAN configurable --- .../li/cil/oc2/common/block/VxlanBlock.java | 1 - .../common/blockentity/VxlanBlockEntity.java | 5 +--- .../cil/oc2/common/vxlan/TunnelManager.java | 24 ++++++++++++------- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/main/java/li/cil/oc2/common/block/VxlanBlock.java b/src/main/java/li/cil/oc2/common/block/VxlanBlock.java index 79f0cea6..57c4b364 100644 --- a/src/main/java/li/cil/oc2/common/block/VxlanBlock.java +++ b/src/main/java/li/cil/oc2/common/block/VxlanBlock.java @@ -1,7 +1,6 @@ package li.cil.oc2.common.block; import li.cil.oc2.common.blockentity.BlockEntities; -import li.cil.oc2.common.blockentity.NetworkHubBlockEntity; import li.cil.oc2.common.blockentity.TickableBlockEntity; import li.cil.oc2.common.blockentity.VxlanBlockEntity; import net.minecraft.core.BlockPos; diff --git a/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java b/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java index 9ce8f71a..ac2323ff 100644 --- a/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java +++ b/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java @@ -69,9 +69,7 @@ public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInt } if (adjacentBlockInterfaces[0] != null) { - packetQueue.forEach(packet -> { - writeEthernetFrame(adjacentBlockInterfaces[0], packet, 255); - }); + packetQueue.forEach(packet -> writeEthernetFrame(adjacentBlockInterfaces[0], packet, 255)); packetQueue.clear(); } else { System.out.printf("VXLAN block is unregistered upstream: VTI=%d\n", vti); @@ -105,7 +103,6 @@ public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInt @Override public void loadServer() { - System.out.println("Tunnel VTI: " + vti); adjacentBlockInterfaces[0] = TunnelManager.instance().registerVti(vti, this.packetQueue); } diff --git a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java index aa7170eb..db9c928a 100644 --- a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java +++ b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java @@ -2,6 +2,9 @@ package li.cil.oc2.common.vxlan; import li.cil.oc2.api.capabilities.NetworkInterface; import li.cil.oc2.common.Config; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import java.io.IOException; import java.net.*; @@ -9,6 +12,7 @@ import java.util.HashMap; import java.util.concurrent.BlockingQueue; public class TunnelManager { + private static final Logger LOGGER = LogManager.getLogger(); private final HashMap tunnels = new HashMap<>(); private DatagramSocket socket; @@ -26,14 +30,16 @@ public class TunnelManager { } public static void initialize() { + LOGGER.info("Initializing outernet tunnel manager"); + try { INSTANCE = new TunnelManager( - InetAddress.getByName("2001:16b8:4908:5700:d22e:ecd:e75b:f5a8"), (short) 4789, - InetAddress.getByName("2001:470:7398::a"), (short) 4789 + InetAddress.getByName(Config.bindHost), (short) Config.bindPort, + InetAddress.getByName(Config.remoteHost), (short) Config.remotePort ); } catch (SocketException | UnknownHostException e) { - System.out.println("Failed to bind host: " + e.getMessage()); - e.printStackTrace(); + LOGGER.error("Failed to bind to configured address: " + e.getMessage()); + LOGGER.error(e); } if (Config.enable) { @@ -41,7 +47,7 @@ public class TunnelManager { try { INSTANCE.listen(); } catch (IOException e) { - e.printStackTrace(); + LOGGER.error(e); } }); bgThread.setName("VXLAN Background Thread"); @@ -50,14 +56,14 @@ public class TunnelManager { } public void listen() throws IOException { - System.out.printf("Binding %s:%s\n", bindHost, bindPort); + LOGGER.printf(Level.INFO, "Binding %s:%s\n", bindHost, bindPort); if (Config.enable) { socket = new DatagramSocket(bindPort, bindHost); } else { socket = null; } - System.out.printf("Bind successful: connected=%s bound=%s\n", socket.isConnected(), socket.isBound()); + LOGGER.printf(Level.INFO, "Bind successful: connected=%s bound=%s\n", socket.isConnected(), socket.isBound()); byte[] buffer = new byte[65535]; while (true) { @@ -88,7 +94,7 @@ public class TunnelManager { try { iface.packetQueue.add(inner); } catch (IllegalStateException ignored) { - System.err.println("Queue full"); + LOGGER.error("Queue full"); } } } @@ -118,7 +124,7 @@ public class TunnelManager { e.printStackTrace(); } } else { - System.out.printf("No socket in TunnelManager\n"); + LOGGER.error("No socket in TunnelManager\n"); } } From dd3160ab31a157739ef9f1ea6b0ba7974b7246d9 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Thu, 3 Nov 2022 18:50:48 +0100 Subject: [PATCH 20/28] Less printf, more logging --- src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java index db9c928a..af3eae92 100644 --- a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java +++ b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java @@ -121,7 +121,7 @@ public class TunnelManager { try { socket.send(packet); } catch (IOException e) { - e.printStackTrace(); + LOGGER.error(e); } } else { LOGGER.error("No socket in TunnelManager\n"); From a751efcc1835ad4f514a6659a7fe00f035a6a5c3 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Thu, 3 Nov 2022 18:50:57 +0100 Subject: [PATCH 21/28] New network switch texture --- .../oc2/blockstates/network_switch.json | 8 +- .../oc2/models/block/network_switch.json | 293 ++++++++++++++++++ .../oc2/models/item/network_switch.json | 3 + .../network_switch/network_switch_atlas0.png | Bin 0 -> 1993 bytes .../network_switch/network_switch_atlas1.png | Bin 0 -> 2027 bytes .../network_switch/network_switch_atlas2.png | Bin 0 -> 2113 bytes .../network_switch/network_switch_atlas3.png | Bin 0 -> 1558 bytes 7 files changed, 300 insertions(+), 4 deletions(-) create mode 100644 src/main/resources/assets/oc2/models/block/network_switch.json create mode 100644 src/main/resources/assets/oc2/models/item/network_switch.json create mode 100644 src/main/resources/assets/oc2/textures/block/network_switch/network_switch_atlas0.png create mode 100644 src/main/resources/assets/oc2/textures/block/network_switch/network_switch_atlas1.png create mode 100644 src/main/resources/assets/oc2/textures/block/network_switch/network_switch_atlas2.png create mode 100644 src/main/resources/assets/oc2/textures/block/network_switch/network_switch_atlas3.png diff --git a/src/main/resources/assets/oc2/blockstates/network_switch.json b/src/main/resources/assets/oc2/blockstates/network_switch.json index 99bb9127..cb558822 100644 --- a/src/main/resources/assets/oc2/blockstates/network_switch.json +++ b/src/main/resources/assets/oc2/blockstates/network_switch.json @@ -1,18 +1,18 @@ { "variants": { "facing=north": { - "model": "oc2:block/network_hub" + "model": "oc2:block/network_switch" }, "facing=south": { - "model": "oc2:block/network_hub", + "model": "oc2:block/network_switch", "y": 180 }, "facing=west": { - "model": "oc2:block/network_hub", + "model": "oc2:block/network_switch", "y": 270 }, "facing=east": { - "model": "oc2:block/network_hub", + "model": "oc2:block/network_switch", "y": 90 } } diff --git a/src/main/resources/assets/oc2/models/block/network_switch.json b/src/main/resources/assets/oc2/models/block/network_switch.json new file mode 100644 index 00000000..36b365b8 --- /dev/null +++ b/src/main/resources/assets/oc2/models/block/network_switch.json @@ -0,0 +1,293 @@ +{ + "parent": "block/block", + "textures": { + "particle": "oc2:block/network_switch/network_switch_atlas0", + "atlas0": "oc2:block/network_switch/network_switch_atlas0", + "atlas1": "oc2:block/network_switch/network_switch_atlas1", + "atlas2": "oc2:block/network_switch/network_switch_atlas2", + "atlas3": "oc2:block/network_switch/network_switch_atlas3" + }, + "elements": [ + { + "from": [0, 0, 10], + "to": [16, 1, 16], + "faces": { + "north": {"uv": [0, 3, 8, 3.5], "texture": "#atlas0"}, + "east": {"uv": [8, 7, 11, 7.5], "texture": "#atlas1", "cullface": "east"}, + "south": {"uv": [0, 3.5, 8, 4], "texture": "#atlas0", "cullface": "south"}, + "west": {"uv": [8, 7.5, 11, 8], "texture": "#atlas1", "cullface": "west"}, + "down": {"uv": [0, 0, 8, 3], "texture": "#atlas0", "cullface": "down"} + } + }, + { + "from": [0, 0, 6], + "to": [6, 1, 10], + "faces": { + "north": {"uv": [11, 7.5, 14, 8], "texture": "#atlas1"}, + "east": {"uv": [14, 7.5, 16, 8], "texture": "#atlas1"}, + "south": {"uv": [11, 7, 14, 7.5], "texture": "#atlas1"}, + "west": {"uv": [14, 7, 16, 7.5], "texture": "#atlas1", "cullface": "west"}, + "down": {"uv": [0, 0, 3, 2], "texture": "#atlas3", "cullface": "down"} + } + }, + { + "from": [10, 0, 6], + "to": [16, 1, 10], + "faces": { + "north": {"uv": [0, 4, 3, 4.5], "texture": "#atlas3"}, + "east": {"uv": [9, 14.5, 11, 15], "texture": "#atlas3", "cullface": "east"}, + "south": {"uv": [0, 4.5, 3, 5], "texture": "#atlas3"}, + "west": {"uv": [11, 14.5, 13, 15], "texture": "#atlas3"}, + "down": {"uv": [0, 2, 3, 4], "texture": "#atlas3", "cullface": "down"} + } + }, + { + "from": [0, 0, 0], + "to": [16, 1, 6], + "faces": { + "north": {"uv": [0, 7, 8, 7.5], "texture": "#atlas0", "cullface": "north"}, + "east": {"uv": [0, 5, 3, 5.5], "texture": "#atlas3", "cullface": "east"}, + "south": {"uv": [0, 7.5, 8, 8], "texture": "#atlas0"}, + "west": {"uv": [0, 5.5, 3, 6], "texture": "#atlas3", "cullface": "west"}, + "down": {"uv": [0, 4, 8, 7], "texture": "#atlas0", "cullface": "down"} + } + }, + { + "from": [0, 1, 0], + "to": [16, 6, 16], + "faces": { + "north": {"uv": [0, 13, 8, 15.5], "texture": "#atlas0", "cullface": "north"}, + "east": {"uv": [0, 8, 8, 10.5], "texture": "#atlas0", "cullface": "east"}, + "south": {"uv": [8, 13, 16, 15.5], "texture": "#atlas0", "cullface": "south"}, + "west": {"uv": [0, 10.5, 8, 13], "texture": "#atlas0", "cullface": "west"}, + "up": {"uv": [0, 0, 8, 8], "texture": "#atlas1"}, + "down": {"uv": [0, 8, 8, 16], "texture": "#atlas1"} + } + }, + { + "from": [0, 6, 15], + "to": [6, 10, 16], + "faces": { + "east": {"uv": [15, 0, 15.5, 2], "texture": "#atlas2"}, + "south": {"uv": [0, 7, 3, 9], "texture": "#atlas3", "cullface": "south"}, + "west": {"uv": [15.5, 0, 16, 2], "texture": "#atlas2", "cullface": "west"}, + "up": {"uv": [0, 6, 3, 6.5], "texture": "#atlas3"}, + "down": {"uv": [0, 6.5, 3, 7], "texture": "#atlas3"} + } + }, + { + "from": [10, 6, 15], + "to": [16, 10, 16], + "faces": { + "east": {"uv": [15, 2, 15.5, 4], "texture": "#atlas2", "cullface": "east"}, + "south": {"uv": [0, 10, 3, 12], "texture": "#atlas3", "cullface": "south"}, + "west": {"uv": [15.5, 2, 16, 4], "texture": "#atlas2"}, + "up": {"uv": [0, 9, 3, 9.5], "texture": "#atlas3"}, + "down": {"uv": [0, 9.5, 3, 10], "texture": "#atlas3"} + } + }, + { + "from": [1, 6, 6], + "to": [15, 10, 10], + "faces": { + "north": {"uv": [8, 4, 15, 6], "texture": "#atlas2"}, + "east": {"uv": [13, 10, 15, 12], "texture": "#atlas3"}, + "south": {"uv": [8, 6, 15, 8], "texture": "#atlas2"}, + "west": {"uv": [3, 7, 5, 9], "texture": "#atlas3"}, + "up": {"uv": [8, 0, 15, 2], "texture": "#atlas2"}, + "down": {"uv": [8, 2, 15, 4], "texture": "#atlas2"} + } + }, + { + "from": [0, 6, 1], + "to": [16, 10, 6], + "faces": { + "north": {"uv": [8, 13, 16, 15], "texture": "#atlas1"}, + "east": {"uv": [8, 10, 10.5, 12], "texture": "#atlas3", "cullface": "east"}, + "south": {"uv": [8, 0, 16, 2], "texture": "#atlas1"}, + "west": {"uv": [10.5, 10, 13, 12], "texture": "#atlas3", "cullface": "west"}, + "up": {"uv": [8, 8, 16, 10.5], "texture": "#atlas1"}, + "down": {"uv": [8, 10.5, 16, 13], "texture": "#atlas1"} + } + }, + { + "from": [0, 6, 0], + "to": [6, 10, 1], + "faces": { + "north": {"uv": [0, 13, 3, 15], "texture": "#atlas3", "cullface": "north"}, + "east": {"uv": [15, 4, 15.5, 6], "texture": "#atlas2"}, + "west": {"uv": [15.5, 4, 16, 6], "texture": "#atlas2", "cullface": "west"}, + "up": {"uv": [0, 12, 3, 12.5], "texture": "#atlas3"}, + "down": {"uv": [0, 12.5, 3, 13], "texture": "#atlas3"} + } + }, + { + "from": [10, 6, 0], + "to": [16, 10, 1], + "faces": { + "north": {"uv": [3, 13, 6, 15], "texture": "#atlas3", "cullface": "north"}, + "east": {"uv": [15, 6, 15.5, 8], "texture": "#atlas2", "cullface": "east"}, + "west": {"uv": [15.5, 6, 16, 8], "texture": "#atlas2"}, + "up": {"uv": [0, 15, 3, 15.5], "texture": "#atlas3"}, + "down": {"uv": [0, 15.5, 3, 16], "texture": "#atlas3"} + } + }, + { + "from": [0, 10, 0], + "to": [16, 15, 16], + "faces": { + "north": {"uv": [8, 8, 16, 10.5], "texture": "#atlas2", "cullface": "north"}, + "east": {"uv": [8, 2, 16, 4.5], "texture": "#atlas1", "cullface": "east"}, + "south": {"uv": [8, 10.5, 16, 13], "texture": "#atlas2", "cullface": "south"}, + "west": {"uv": [8, 4.5, 16, 7], "texture": "#atlas1", "cullface": "west"}, + "up": {"uv": [0, 0, 8, 8], "texture": "#atlas2"}, + "down": {"uv": [0, 8, 8, 16], "texture": "#atlas2"} + } + }, + { + "from": [0, 15, 13], + "to": [16, 16, 16], + "faces": { + "north": {"uv": [0, 15.5, 8, 16], "texture": "#atlas0"}, + "east": {"uv": [11, 13, 12.5, 13.5], "texture": "#atlas3", "cullface": "east"}, + "south": {"uv": [8, 15.5, 16, 16], "texture": "#atlas0", "cullface": "south"}, + "west": {"uv": [12.5, 13, 14, 13.5], "texture": "#atlas3", "cullface": "west"}, + "up": {"uv": [8, 13, 16, 14.5], "texture": "#atlas2", "cullface": "up"} + } + }, + { + "from": [0, 15, 12], + "to": [6, 16, 13], + "faces": { + "north": {"uv": [6, 15.5, 9, 16], "texture": "#atlas3"}, + "east": {"uv": [15.5, 13, 16, 13.5], "texture": "#atlas3"}, + "south": {"uv": [9, 15.5, 12, 16], "texture": "#atlas3"}, + "west": {"uv": [13.5, 12.5, 14, 13], "texture": "#atlas3", "cullface": "west"}, + "up": {"uv": [3, 15.5, 6, 16], "texture": "#atlas3", "cullface": "up"} + } + }, + { + "from": [10, 15, 12], + "to": [16, 16, 13], + "faces": { + "north": {"uv": [3, 15, 6, 15.5], "texture": "#atlas3"}, + "east": {"uv": [14, 12.5, 14.5, 13], "texture": "#atlas3", "cullface": "east"}, + "south": {"uv": [6, 15, 9, 15.5], "texture": "#atlas3"}, + "west": {"uv": [14.5, 12.5, 15, 13], "texture": "#atlas3"}, + "up": {"uv": [12, 15.5, 15, 16], "texture": "#atlas3", "cullface": "up"} + } + }, + { + "from": [0, 15, 10], + "to": [16, 16, 12], + "faces": { + "north": {"uv": [8, 7.5, 16, 8], "texture": "#atlas0"}, + "east": {"uv": [15, 15.5, 16, 16], "texture": "#atlas3", "cullface": "east"}, + "south": {"uv": [8, 7, 16, 7.5], "texture": "#atlas0"}, + "west": {"uv": [15, 15, 16, 15.5], "texture": "#atlas3", "cullface": "west"}, + "up": {"uv": [8, 6, 16, 7], "texture": "#atlas0", "cullface": "up"} + } + }, + { + "from": [0, 15, 6], + "to": [3, 16, 10], + "faces": { + "north": {"uv": [14, 13, 15.5, 13.5], "texture": "#atlas3"}, + "east": {"uv": [13, 14.5, 15, 15], "texture": "#atlas3"}, + "south": {"uv": [3, 12.5, 4.5, 13], "texture": "#atlas3"}, + "west": {"uv": [9, 14, 11, 14.5], "texture": "#atlas3", "cullface": "west"}, + "up": {"uv": [3, 0, 4.5, 2], "texture": "#atlas3", "cullface": "up"} + } + }, + { + "from": [4, 15, 6], + "to": [6, 16, 10], + "faces": { + "north": {"uv": [15, 14.5, 16, 15], "texture": "#atlas3"}, + "east": {"uv": [11, 14, 13, 14.5], "texture": "#atlas3"}, + "south": {"uv": [15, 14, 16, 14.5], "texture": "#atlas3"}, + "west": {"uv": [13, 14, 15, 14.5], "texture": "#atlas3"}, + "up": {"uv": [4.5, 0, 5.5, 2], "texture": "#atlas3", "cullface": "up"} + } + }, + { + "from": [10, 15, 6], + "to": [12, 16, 10], + "faces": { + "north": {"uv": [15, 13.5, 16, 14], "texture": "#atlas3"}, + "east": {"uv": [9, 13.5, 11, 14], "texture": "#atlas3"}, + "south": {"uv": [10.5, 12.5, 11.5, 13], "texture": "#atlas3"}, + "west": {"uv": [11, 13.5, 13, 14], "texture": "#atlas3"}, + "up": {"uv": [5.5, 0, 6.5, 2], "texture": "#atlas3", "cullface": "up"} + } + }, + { + "from": [13, 15, 6], + "to": [16, 16, 10], + "faces": { + "north": {"uv": [4.5, 12.5, 6, 13], "texture": "#atlas3"}, + "east": {"uv": [13, 13.5, 15, 14], "texture": "#atlas3", "cullface": "east"}, + "south": {"uv": [6, 12.5, 7.5, 13], "texture": "#atlas3"}, + "west": {"uv": [9, 13, 11, 13.5], "texture": "#atlas3"}, + "up": {"uv": [6.5, 0, 8, 2], "texture": "#atlas3", "cullface": "up"} + } + }, + { + "from": [0, 15, 4], + "to": [16, 16, 6], + "faces": { + "north": {"uv": [8, 3.5, 16, 4], "texture": "#atlas0"}, + "east": {"uv": [11.5, 12.5, 12.5, 13], "texture": "#atlas3", "cullface": "east"}, + "south": {"uv": [8, 3, 16, 3.5], "texture": "#atlas0"}, + "west": {"uv": [12.5, 12.5, 13.5, 13], "texture": "#atlas3", "cullface": "west"}, + "up": {"uv": [8, 2, 16, 3], "texture": "#atlas0", "cullface": "up"} + } + }, + { + "from": [0, 15, 3], + "to": [6, 16, 4], + "faces": { + "north": {"uv": [12, 15, 15, 15.5], "texture": "#atlas3"}, + "east": {"uv": [15, 12.5, 15.5, 13], "texture": "#atlas3"}, + "south": {"uv": [6, 13, 9, 13.5], "texture": "#atlas3"}, + "west": {"uv": [15.5, 12.5, 16, 13], "texture": "#atlas3", "cullface": "west"}, + "up": {"uv": [9, 15, 12, 15.5], "texture": "#atlas3", "cullface": "up"} + } + }, + { + "from": [10, 15, 3], + "to": [16, 16, 4], + "faces": { + "north": {"uv": [6, 14, 9, 14.5], "texture": "#atlas3"}, + "east": {"uv": [3, 12, 3.5, 12.5], "texture": "#atlas3", "cullface": "east"}, + "south": {"uv": [6, 14.5, 9, 15], "texture": "#atlas3"}, + "west": {"uv": [3.5, 12, 4, 12.5], "texture": "#atlas3"}, + "up": {"uv": [6, 13.5, 9, 14], "texture": "#atlas3", "cullface": "up"} + } + }, + { + "from": [0, 15, 0], + "to": [16, 16, 3], + "faces": { + "north": {"uv": [8, 15, 16, 15.5], "texture": "#atlas1", "cullface": "north"}, + "east": {"uv": [7.5, 12.5, 9, 13], "texture": "#atlas3", "cullface": "east"}, + "south": {"uv": [8, 15.5, 16, 16], "texture": "#atlas1"}, + "west": {"uv": [9, 12.5, 10.5, 13], "texture": "#atlas3", "cullface": "west"}, + "up": {"uv": [8, 14.5, 16, 16], "texture": "#atlas2", "cullface": "up"} + } + }, + { + "from": [0, 6, 10], + "to": [16, 10, 15], + "faces": { + "north": {"uv": [8, 4, 16, 6], "texture": "#atlas0"}, + "east": {"uv": [3, 10, 5.5, 12], "texture": "#atlas3", "cullface": "east"}, + "south": {"uv": [8, 0, 16, 2], "texture": "#atlas0"}, + "west": {"uv": [5.5, 10, 8, 12], "texture": "#atlas3", "cullface": "west"}, + "up": {"uv": [8, 10.5, 16, 13], "texture": "#atlas0"}, + "down": {"uv": [8, 8, 16, 10.5], "texture": "#atlas0"} + } + } + ], + "display": {} +} diff --git a/src/main/resources/assets/oc2/models/item/network_switch.json b/src/main/resources/assets/oc2/models/item/network_switch.json new file mode 100644 index 00000000..657bd289 --- /dev/null +++ b/src/main/resources/assets/oc2/models/item/network_switch.json @@ -0,0 +1,3 @@ +{ + "parent": "oc2:block/network_switch" +} diff --git a/src/main/resources/assets/oc2/textures/block/network_switch/network_switch_atlas0.png b/src/main/resources/assets/oc2/textures/block/network_switch/network_switch_atlas0.png new file mode 100644 index 0000000000000000000000000000000000000000..4271a0a7a7e63edc6d728fe0859073e73ef2f232 GIT binary patch literal 1993 zcmV;)2R8VLP)zQ$Fn(}8OM9#nQ6YS>b>?jf_6qd^ZLD7zxt{=!NS5q)ai622m?e)67IV(o)e*5?xDW0M>;!e63oxfM=GSbwX!bv=W`N{6m%OraRvnPT(czKUO$kxKTbezB+JRH$#<9T-7T2` z_ZRXKok~%(MD4Z;N%sw_BJ)0w%A+mZLNzMr>6HkFC;nNJIt-A(4Ay#1II#4fqG!MR z>`31FXh4>SXGFNi$)}EJ2T_`xosBkOKrob%?7+Bs!B3~%lHNCdS9{yvKMT(-5JA{0 zJBl$A{1 zD6ED7NC)?7m|Si<@TLbIM0iknXa-E)GbNiipGlsv{hhW7Z^Kh6S9u-FJ%5|PP|D3a zGP!~jXapEW8t}xZ=DK`PhY>ByzXuM}GLY2pmk%2v`&F4h1ZWhakexawoueaFSUqf7 z>J$?Y;|xeA#x+VIWUM#tXbOXLIvs80(-I8M$n+Bo+K@ydAsbK4Nq%xkh4WfFtXrrN1zmICDa?f+CwHq?^*NOylb92%CIn=SHU<`-bQaJ?T zardCHPzI9H6!e_HK>gy&<{=fieTs0RUDNIIZeTJDaKAgF7^p86Bubj2@pG%mbkf<0Eer&R3My>e$@H@FRi2w{K-qin=2ml=^k9+BUGpaOPqtR^?)2%6h0g?a^t@d;i)1lboA1tr7 zwzZsePY=oq5tv39Zk)O73}ERZ{}EFc%J{E8O5^;C`fTF2pJnsxWs9I1+qQs_H`ohm zsN<;Ra^{R6QVn!4AaYdPi?z1nb1Y5WT~~R;PpJWxA5D|0ENg6Q`&>&lr1gs2=k@WHkBMrRnM@{X=2H?5ra(kS#T$>d9jdMQc;HO;gDI7$w!q_6NtS9#M9Kr17YwjaSbWG7 zSj$xax~p(%%Eww(H^2jb-jyJq&qwuHbTc#~QY95uk5Fc;V}iujG`^=v)$9en3RcU~ zczRyKVU#fq)sKO8sabZN)Ed&wk(8cgOIOYy!1vLNve|6Z1eXVBsVR6!WX5$0&Zbb` z=MB07t_M}48I8k$!B<|F(*CAJ%;87z#MZ7AVH}~+aFnc-)Y~IDP;-#W<)Q{7fO&us z;3j&*Dp2o99Y%{^G1gQtzFo`l6Mg+=iPV^R~%n-9H`qXLdtM!!YzT-{< zIDcpg8aYfWZjT?gY{5n{n~b`~GZ&$;wX$qqzG8V-?spC#YGb#5k>)cORZ;SfWoewb zWaY?aMVa6OWyctV!=Pd|8%#9i1BBG&E~a=0%Jc)I@TQEAD-{C+P1lTU7&!)F4E=7y zIno&?8zBK>20{*&=mqR{BoWP17hvM=j!G*WSoSJ1&J+Lx#l8E60j7uwNJyTx9dHf# zu4_7cWfK!-z!v8^1tGhk!7av)rp94_%*b|(GNb`XL_k3ZgUoxLvqq*7aU5bPWKiK4 z=SnS;Kw<-Y75MRq5%3J=OVTRAP03V*DwtTS$k3}-rGybpF`^SMsn}*oo#^rB=~!hj zwaPCnLl=QY4kNi*$@1`YY6GQ>RQm4(C`ql!Vnw#ko(73wsO1hfQBwzrh!;tS1L`fl z7sNWOEh`ae8G;8wf{;TaCsx+ApK#or=uKf})PV$JIY(*^r8In#Jc7}jAm&v;S;m*L zAG>H9@+TM`oJVWlDakm`D-i($G$4hc(;PkK@wkWE6ZMiKTeJ@hI0Nd1wDgSRWrjno z$zR3^15D8K_XfX#?4m~Gkaex~=WngY6+kn5y!Nz7;jHQ%m> zxWr)%t(c$d?Y5;cn6TIip3~_W00000NkvXXu0mjf{I8>k literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/oc2/textures/block/network_switch/network_switch_atlas1.png b/src/main/resources/assets/oc2/textures/block/network_switch/network_switch_atlas1.png new file mode 100644 index 0000000000000000000000000000000000000000..80f9ddee27b04df97197f9c659b82b67e4fff12f GIT binary patch literal 2027 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 IM6N<$f)*m+p8x;= literal 0 HcmV?d00001 From 1157bfe91babd15cb5c8f93fe3e2498bad513fef Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sat, 5 Nov 2022 17:57:17 +0100 Subject: [PATCH 22/28] Small improvements --- .../common/blockentity/VxlanBlockEntity.java | 22 +++++++++++++++++-- .../cil/oc2/common/vxlan/TunnelManager.java | 14 +++++------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java b/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java index ac2323ff..4b97fd52 100644 --- a/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java +++ b/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java @@ -1,6 +1,7 @@ package li.cil.oc2.common.blockentity; import li.cil.oc2.api.capabilities.NetworkInterface; +import li.cil.oc2.common.Config; import li.cil.oc2.common.Constants; import li.cil.oc2.common.capabilities.Capabilities; import li.cil.oc2.common.util.LazyOptionalUtils; @@ -16,7 +17,6 @@ import net.minecraftforge.common.util.LazyOptional; import javax.annotation.Nullable; import java.util.Arrays; import java.util.Objects; -import java.util.Queue; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.stream.Stream; @@ -25,7 +25,8 @@ public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInt private static final int TTL_COST = 1; //private int vti = ((int) (Math.random() * Integer.MAX_VALUE)) & 0x00ff_ffff; private int vti = 4037973; - private boolean initialized = false; + private int frameCount; + private long lastGameTime; private BlockingQueue packetQueue = new ArrayBlockingQueue(32); @@ -55,6 +56,20 @@ public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInt @Override public void writeEthernetFrame(final NetworkInterface source, final byte[] frame, final int timeToLive) { + if (level == null) { + return; + } + + final long gameTime = level.getGameTime(); + if (gameTime > lastGameTime) { + lastGameTime = gameTime; + frameCount = 1; + } else if (frameCount > Config.hubEthernetFramesPerTick) { + return; + } else { + frameCount++; + } + getAdjacentInterfaces().forEach(adjacentInterface -> { if (adjacentInterface != source) { adjacentInterface.writeEthernetFrame(this, frame, timeToLive - TTL_COST); @@ -81,6 +96,9 @@ public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInt @Override public void load(CompoundTag tag) { super.load(tag); + if (!level.isClientSide() && tag.contains("vti")) { + vti = tag.getInt("vti"); + } } @Override diff --git a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java index af3eae92..3e875e02 100644 --- a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java +++ b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java @@ -61,7 +61,7 @@ public class TunnelManager { if (Config.enable) { socket = new DatagramSocket(bindPort, bindHost); } else { - socket = null; + return; } LOGGER.printf(Level.INFO, "Bind successful: connected=%s bound=%s\n", socket.isConnected(), socket.isBound()); @@ -83,7 +83,7 @@ public class TunnelManager { continue; } - System.out.println(vni); + LOGGER.debug("recv on vti " + vni); TunnelInterface iface = tunnels.get(vni); @@ -91,11 +91,8 @@ public class TunnelManager { byte[] inner = new byte[packet.getLength() - 8]; System.arraycopy(packet.getData(), 8, inner, 0, packet.getLength() - 8); - try { - iface.packetQueue.add(inner); - } catch (IllegalStateException ignored) { - LOGGER.error("Queue full"); - } + // Ignore failed inserts + iface.packetQueue.offer(inner); } } } @@ -147,10 +144,9 @@ public class TunnelManager { this.packetQueue = packetQueue; } - @Override public byte[] readEthernetFrame() { - return new byte[0]; + return null; } @Override From 455c6da39ded41c53ef88f9dc85f06c031083386 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sat, 5 Nov 2022 18:05:37 +0100 Subject: [PATCH 23/28] Switch to a ring buffer for vxlan packets --- build.gradle | 1 + .../common/blockentity/VxlanBlockEntity.java | 20 +++++++++++-------- .../cil/oc2/common/vxlan/TunnelManager.java | 19 +++++++++++------- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/build.gradle b/build.gradle index e559622e..1ba5419c 100644 --- a/build.gradle +++ b/build.gradle @@ -82,6 +82,7 @@ dependencies { compileOnly "li.cil.sedna:sedna-buildroot:0.0.8" } implementation "curse.maven:sedna-511276:3885542" + implementation "org.apache.commons:commons-collections4:4.4" implementation fg.deobf("curse.maven:markdownmanual-502485:3738124") diff --git a/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java b/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java index 4b97fd52..54241ae3 100644 --- a/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java +++ b/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java @@ -13,12 +13,13 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraftforge.common.util.LazyOptional; +import org.apache.commons.collections4.QueueUtils; +import org.apache.commons.collections4.queue.CircularFifoQueue; import javax.annotation.Nullable; import java.util.Arrays; import java.util.Objects; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; +import java.util.Queue; import java.util.stream.Stream; public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInterface, TickableBlockEntity { @@ -28,7 +29,7 @@ public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInt private int frameCount; private long lastGameTime; - private BlockingQueue packetQueue = new ArrayBlockingQueue(32); + private final Queue packetQueue = QueueUtils.synchronizedQueue(new CircularFifoQueue<>(32)); /////////////////////////////////////////////////////////////////// @@ -84,8 +85,11 @@ public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInt } if (adjacentBlockInterfaces[0] != null) { - packetQueue.forEach(packet -> writeEthernetFrame(adjacentBlockInterfaces[0], packet, 255)); - packetQueue.clear(); + // CircularFifoQueue isn't thread-safe, so we have to synchronize on it. + synchronized (packetQueue) { + packetQueue.forEach(packet -> writeEthernetFrame(adjacentBlockInterfaces[0], packet, 255)); + packetQueue.clear(); + } } else { System.out.printf("VXLAN block is unregistered upstream: VTI=%d\n", vti); } @@ -96,7 +100,7 @@ public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInt @Override public void load(CompoundTag tag) { super.load(tag); - if (!level.isClientSide() && tag.contains("vti")) { + if (level != null && !level.isClientSide() && tag.contains("vti")) { vti = tag.getInt("vti"); } } @@ -104,14 +108,14 @@ public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInt @Override public void saveAdditional(CompoundTag tag) { super.saveAdditional(tag); - if (!level.isClientSide()) { + if (level != null && !level.isClientSide()) { tag.putInt("vti", vti); } } @Override protected void onUnload(final boolean isRemove) { - if (!level.isClientSide()) { + if (level != null && !level.isClientSide()) { adjacentBlockInterfaces[0] = null; TunnelManager.instance().unregisterVti(vti); } diff --git a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java index 3e875e02..def122ef 100644 --- a/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java +++ b/src/main/java/li/cil/oc2/common/vxlan/TunnelManager.java @@ -5,11 +5,12 @@ import li.cil.oc2.common.Config; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.net.*; import java.util.HashMap; -import java.util.concurrent.BlockingQueue; +import java.util.Queue; public class TunnelManager { private static final Logger LOGGER = LogManager.getLogger(); @@ -66,6 +67,8 @@ public class TunnelManager { LOGGER.printf(Level.INFO, "Bind successful: connected=%s bound=%s\n", socket.isConnected(), socket.isBound()); byte[] buffer = new byte[65535]; + // TODO shut this thread down more cleanly on server shutdown? + //noinspection InfiniteLoopStatement while (true) { DatagramPacket packet = new DatagramPacket(buffer, buffer.length); socket.receive(packet); @@ -91,8 +94,10 @@ public class TunnelManager { byte[] inner = new byte[packet.getLength() - 8]; System.arraycopy(packet.getData(), 8, inner, 0, packet.getLength() - 8); - // Ignore failed inserts - iface.packetQueue.offer(inner); + // CircularFifoQueue isn't thread-safe, so we have to synchronize on it. + synchronized (iface.packetQueue) { + iface.packetQueue.offer(inner); + } } } } @@ -125,7 +130,7 @@ public class TunnelManager { } } - public NetworkInterface registerVti(int vti, BlockingQueue packetQueue) { + public NetworkInterface registerVti(int vti, Queue packetQueue) { TunnelInterface tuniface = new TunnelInterface(vti, packetQueue); tunnels.put(vti, tuniface); return tuniface; @@ -136,10 +141,10 @@ public class TunnelManager { } public class TunnelInterface implements NetworkInterface { - final BlockingQueue packetQueue; + final Queue packetQueue; private final int vti; - public TunnelInterface(int vti, BlockingQueue packetQueue) { + public TunnelInterface(int vti, Queue packetQueue) { this.vti = vti; this.packetQueue = packetQueue; } @@ -150,7 +155,7 @@ public class TunnelManager { } @Override - public void writeEthernetFrame(final NetworkInterface source, final byte[] frame, final int timeToLive) { + public void writeEthernetFrame(final @NotNull NetworkInterface source, final byte @NotNull [] frame, final int timeToLive) { TunnelManager.this.sendToOuternet(vti, frame); } } From 184bb02fe3415e08937e1810e9531248e29867a9 Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sat, 5 Nov 2022 22:46:00 +0100 Subject: [PATCH 24/28] Make those ring buffers actually work with MC --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 1ba5419c..07de11c4 100644 --- a/build.gradle +++ b/build.gradle @@ -82,7 +82,7 @@ dependencies { compileOnly "li.cil.sedna:sedna-buildroot:0.0.8" } implementation "curse.maven:sedna-511276:3885542" - implementation "org.apache.commons:commons-collections4:4.4" + minecraftLibrary "org.apache.commons:commons-collections4:4.4" implementation fg.deobf("curse.maven:markdownmanual-502485:3738124") From a462b5fe45bdd389784552ca51d67a8e761bbbfa Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sat, 5 Nov 2022 22:46:08 +0100 Subject: [PATCH 25/28] Switch to VTI 1000, finally --- .../java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java b/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java index 54241ae3..7ef030d8 100644 --- a/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java +++ b/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java @@ -25,7 +25,7 @@ import java.util.stream.Stream; public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInterface, TickableBlockEntity { private static final int TTL_COST = 1; //private int vti = ((int) (Math.random() * Integer.MAX_VALUE)) & 0x00ff_ffff; - private int vti = 4037973; + private int vti = 1000; private int frameCount; private long lastGameTime; From 662ff76f06047a62815875816971aadc8287b80f Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Sat, 5 Nov 2022 23:37:04 +0100 Subject: [PATCH 26/28] That part is important to make the fb show up --- .../oc2/common/vm/provider/SimpleFramebufferDeviceProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/li/cil/oc2/common/vm/provider/SimpleFramebufferDeviceProvider.java b/src/main/java/li/cil/oc2/common/vm/provider/SimpleFramebufferDeviceProvider.java index 1482009c..ac165c3c 100644 --- a/src/main/java/li/cil/oc2/common/vm/provider/SimpleFramebufferDeviceProvider.java +++ b/src/main/java/li/cil/oc2/common/vm/provider/SimpleFramebufferDeviceProvider.java @@ -24,7 +24,7 @@ public final class SimpleFramebufferDeviceProvider implements DeviceTreeProvider final Optional range = memoryMap.getMemoryRange((MemoryMappedDevice) device); return range.map(r -> { final DeviceTree chosen = root.find("/chosen"); - //chosen.addProp(DevicePropertyNames.RANGES); + chosen.addProp(DevicePropertyNames.RANGES); return chosen.getChild(deviceName, r.address()); }); From be481775c34ba0b376a057ba641b3e2ebf46516c Mon Sep 17 00:00:00 2001 From: Nadja Reitzenstein Date: Mon, 7 Nov 2022 16:47:49 +0100 Subject: [PATCH 27/28] VXLAN hub texture foo --- .../assets/oc2/blockstates/vxlan_hub.json | 19 ++ .../assets/oc2/models/block/vxlan_hub.json | 292 ++++++++++++++++++ .../assets/oc2/models/item/vxlan_hub.json | 3 + .../block/vxlan_hub/vxlan_hub_atlas0.png | Bin 0 -> 2086 bytes .../block/vxlan_hub/vxlan_hub_atlas1.png | Bin 0 -> 2112 bytes .../block/vxlan_hub/vxlan_hub_atlas2.png | Bin 0 -> 2169 bytes .../block/vxlan_hub/vxlan_hub_atlas3.png | Bin 0 -> 1521 bytes 7 files changed, 314 insertions(+) create mode 100644 src/main/resources/assets/oc2/blockstates/vxlan_hub.json create mode 100644 src/main/resources/assets/oc2/models/block/vxlan_hub.json create mode 100644 src/main/resources/assets/oc2/models/item/vxlan_hub.json create mode 100644 src/main/resources/assets/oc2/textures/block/vxlan_hub/vxlan_hub_atlas0.png create mode 100644 src/main/resources/assets/oc2/textures/block/vxlan_hub/vxlan_hub_atlas1.png create mode 100644 src/main/resources/assets/oc2/textures/block/vxlan_hub/vxlan_hub_atlas2.png create mode 100644 src/main/resources/assets/oc2/textures/block/vxlan_hub/vxlan_hub_atlas3.png diff --git a/src/main/resources/assets/oc2/blockstates/vxlan_hub.json b/src/main/resources/assets/oc2/blockstates/vxlan_hub.json new file mode 100644 index 00000000..c951dc4e --- /dev/null +++ b/src/main/resources/assets/oc2/blockstates/vxlan_hub.json @@ -0,0 +1,19 @@ +{ + "variants": { + "facing=north": { + "model": "oc2:block/vxlan_hub" + }, + "facing=south": { + "model": "oc2:block/vxlan_hub", + "y": 180 + }, + "facing=west": { + "model": "oc2:block/vxlan_hub", + "y": 270 + }, + "facing=east": { + "model": "oc2:block/vxlan_hub", + "y": 90 + } + } +} diff --git a/src/main/resources/assets/oc2/models/block/vxlan_hub.json b/src/main/resources/assets/oc2/models/block/vxlan_hub.json new file mode 100644 index 00000000..622cc9b3 --- /dev/null +++ b/src/main/resources/assets/oc2/models/block/vxlan_hub.json @@ -0,0 +1,292 @@ +{ + "parent": "block/block", + "textures": { + "particle": "oc2:block/vxlan_hub/vxlan_hub_atlas0", + "atlas0": "oc2:block/vxlan_hub/vxlan_hub_atlas0", + "atlas1": "oc2:block/vxlan_hub/vxlan_hub_atlas1", + "atlas2": "oc2:block/vxlan_hub/vxlan_hub_atlas2", + "atlas3": "oc2:block/vxlan_hub/vxlan_hub_atlas3" + }, + "elements": [ + { + "from": [0, 0, 10], + "to": [16, 1, 16], + "faces": { + "north": {"uv": [0, 3, 8, 3.5], "texture": "#atlas0"}, + "east": {"uv": [8, 7, 11, 7.5], "texture": "#atlas1", "cullface": "east"}, + "south": {"uv": [0, 3.5, 8, 4], "texture": "#atlas0", "cullface": "south"}, + "west": {"uv": [8, 7.5, 11, 8], "texture": "#atlas1", "cullface": "west"}, + "down": {"uv": [0, 0, 8, 3], "texture": "#atlas0", "cullface": "down"} + } + }, + { + "from": [0, 0, 6], + "to": [6, 1, 10], + "faces": { + "north": {"uv": [11, 7.5, 14, 8], "texture": "#atlas1"}, + "east": {"uv": [14, 7.5, 16, 8], "texture": "#atlas1"}, + "south": {"uv": [11, 7, 14, 7.5], "texture": "#atlas1"}, + "west": {"uv": [14, 7, 16, 7.5], "texture": "#atlas1", "cullface": "west"}, + "down": {"uv": [0, 0, 3, 2], "texture": "#atlas3", "cullface": "down"} + } + }, + { + "from": [10, 0, 6], + "to": [16, 1, 10], + "faces": { + "north": {"uv": [0, 4, 3, 4.5], "texture": "#atlas3"}, + "east": {"uv": [9, 14.5, 11, 15], "texture": "#atlas3", "cullface": "east"}, + "south": {"uv": [0, 4.5, 3, 5], "texture": "#atlas3"}, + "west": {"uv": [11, 14.5, 13, 15], "texture": "#atlas3"}, + "down": {"uv": [0, 2, 3, 4], "texture": "#atlas3", "cullface": "down"} + } + }, + { + "from": [0, 0, 0], + "to": [16, 1, 6], + "faces": { + "north": {"uv": [0, 7, 8, 7.5], "texture": "#atlas0", "cullface": "north"}, + "east": {"uv": [0, 5, 3, 5.5], "texture": "#atlas3", "cullface": "east"}, + "south": {"uv": [0, 7.5, 8, 8], "texture": "#atlas0"}, + "west": {"uv": [0, 5.5, 3, 6], "texture": "#atlas3", "cullface": "west"}, + "down": {"uv": [0, 4, 8, 7], "texture": "#atlas0", "cullface": "down"} + } + }, + { + "from": [0, 1, 0], + "to": [16, 6, 16], + "faces": { + "north": {"uv": [0, 13, 8, 15.5], "texture": "#atlas0", "cullface": "north"}, + "east": {"uv": [0, 8, 8, 10.5], "texture": "#atlas0", "cullface": "east"}, + "south": {"uv": [8, 13, 16, 15.5], "texture": "#atlas0", "cullface": "south"}, + "west": {"uv": [0, 10.5, 8, 13], "texture": "#atlas0", "cullface": "west"}, + "up": {"uv": [0, 0, 8, 8], "texture": "#atlas1"}, + "down": {"uv": [0, 8, 8, 16], "texture": "#atlas1"} + } + }, + { + "from": [0, 6, 15], + "to": [6, 10, 16], + "faces": { + "east": {"uv": [15, 0, 15.5, 2], "texture": "#atlas2"}, + "south": {"uv": [0, 7, 3, 9], "texture": "#atlas3", "cullface": "south"}, + "west": {"uv": [15.5, 0, 16, 2], "texture": "#atlas2", "cullface": "west"}, + "up": {"uv": [0, 6, 3, 6.5], "texture": "#atlas3"}, + "down": {"uv": [0, 6.5, 3, 7], "texture": "#atlas3"} + } + }, + { + "from": [10, 6, 15], + "to": [16, 10, 16], + "faces": { + "east": {"uv": [15, 2, 15.5, 4], "texture": "#atlas2", "cullface": "east"}, + "south": {"uv": [0, 10, 3, 12], "texture": "#atlas3", "cullface": "south"}, + "west": {"uv": [15.5, 2, 16, 4], "texture": "#atlas2"}, + "up": {"uv": [0, 9, 3, 9.5], "texture": "#atlas3"}, + "down": {"uv": [0, 9.5, 3, 10], "texture": "#atlas3"} + } + }, + { + "from": [0, 6, 10], + "to": [16, 10, 15], + "faces": { + "north": {"uv": [8, 4, 16, 6], "texture": "#atlas0"}, + "east": {"uv": [3, 10, 5.5, 12], "texture": "#atlas3", "cullface": "east"}, + "south": {"uv": [8, 0, 16, 2], "texture": "#atlas0"}, + "west": {"uv": [5.5, 10, 8, 12], "texture": "#atlas3", "cullface": "west"}, + "up": {"uv": [8, 10.5, 16, 13], "texture": "#atlas0"}, + "down": {"uv": [8, 8, 16, 10.5], "texture": "#atlas0"} + } + }, + { + "from": [1, 6, 6], + "to": [15, 10, 10], + "faces": { + "north": {"uv": [8, 4, 15, 6], "texture": "#atlas2"}, + "east": {"uv": [13, 10, 15, 12], "texture": "#atlas3"}, + "south": {"uv": [8, 6, 15, 8], "texture": "#atlas2"}, + "west": {"uv": [3, 7, 5, 9], "texture": "#atlas3"}, + "up": {"uv": [8, 0, 15, 2], "texture": "#atlas2"}, + "down": {"uv": [8, 2, 15, 4], "texture": "#atlas2"} + } + }, + { + "from": [0, 6, 1], + "to": [16, 10, 6], + "faces": { + "north": {"uv": [8, 13, 16, 15], "texture": "#atlas1"}, + "east": {"uv": [8, 10, 10.5, 12], "texture": "#atlas3", "cullface": "east"}, + "south": {"uv": [8, 0, 16, 2], "texture": "#atlas1"}, + "west": {"uv": [10.5, 10, 13, 12], "texture": "#atlas3", "cullface": "west"}, + "up": {"uv": [8, 8, 16, 10.5], "texture": "#atlas1"}, + "down": {"uv": [8, 10.5, 16, 13], "texture": "#atlas1"} + } + }, + { + "from": [0, 6, 0], + "to": [6, 10, 1], + "faces": { + "north": {"uv": [0, 13, 3, 15], "texture": "#atlas3", "cullface": "north"}, + "east": {"uv": [15, 4, 15.5, 6], "texture": "#atlas2"}, + "west": {"uv": [15.5, 4, 16, 6], "texture": "#atlas2", "cullface": "west"}, + "up": {"uv": [0, 12, 3, 12.5], "texture": "#atlas3"}, + "down": {"uv": [0, 12.5, 3, 13], "texture": "#atlas3"} + } + }, + { + "from": [10, 6, 0], + "to": [16, 10, 1], + "faces": { + "north": {"uv": [3, 13, 6, 15], "texture": "#atlas3", "cullface": "north"}, + "east": {"uv": [15, 6, 15.5, 8], "texture": "#atlas2", "cullface": "east"}, + "west": {"uv": [15.5, 6, 16, 8], "texture": "#atlas2"}, + "up": {"uv": [0, 15, 3, 15.5], "texture": "#atlas3"}, + "down": {"uv": [0, 15.5, 3, 16], "texture": "#atlas3"} + } + }, + { + "from": [0, 10, 0], + "to": [16, 15, 16], + "faces": { + "north": {"uv": [8, 8, 16, 10.5], "texture": "#atlas2", "cullface": "north"}, + "east": {"uv": [8, 2, 16, 4.5], "texture": "#atlas1", "cullface": "east"}, + "south": {"uv": [8, 10.5, 16, 13], "texture": "#atlas2", "cullface": "south"}, + "west": {"uv": [8, 4.5, 16, 7], "texture": "#atlas1", "cullface": "west"}, + "up": {"uv": [0, 0, 8, 8], "texture": "#atlas2"}, + "down": {"uv": [0, 8, 8, 16], "texture": "#atlas2"} + } + }, + { + "from": [0, 15, 13], + "to": [16, 16, 16], + "faces": { + "north": {"uv": [0, 15.5, 8, 16], "texture": "#atlas0"}, + "east": {"uv": [11, 13, 12.5, 13.5], "texture": "#atlas3", "cullface": "east"}, + "south": {"uv": [8, 15.5, 16, 16], "texture": "#atlas0", "cullface": "south"}, + "west": {"uv": [12.5, 13, 14, 13.5], "texture": "#atlas3", "cullface": "west"}, + "up": {"uv": [8, 13, 16, 14.5], "texture": "#atlas2", "cullface": "up"} + } + }, + { + "from": [0, 15, 12], + "to": [6, 16, 13], + "faces": { + "north": {"uv": [6, 15.5, 9, 16], "texture": "#atlas3"}, + "east": {"uv": [15.5, 13, 16, 13.5], "texture": "#atlas3"}, + "south": {"uv": [9, 15.5, 12, 16], "texture": "#atlas3"}, + "west": {"uv": [13.5, 12.5, 14, 13], "texture": "#atlas3", "cullface": "west"}, + "up": {"uv": [3, 15.5, 6, 16], "texture": "#atlas3", "cullface": "up"} + } + }, + { + "from": [10, 15, 12], + "to": [16, 16, 13], + "faces": { + "north": {"uv": [3, 15, 6, 15.5], "texture": "#atlas3"}, + "east": {"uv": [14, 12.5, 14.5, 13], "texture": "#atlas3", "cullface": "east"}, + "south": {"uv": [6, 15, 9, 15.5], "texture": "#atlas3"}, + "west": {"uv": [14.5, 12.5, 15, 13], "texture": "#atlas3"}, + "up": {"uv": [12, 15.5, 15, 16], "texture": "#atlas3", "cullface": "up"} + } + }, + { + "from": [0, 15, 10], + "to": [16, 16, 12], + "faces": { + "north": {"uv": [8, 7.5, 16, 8], "texture": "#atlas0"}, + "east": {"uv": [15, 15.5, 16, 16], "texture": "#atlas3", "cullface": "east"}, + "south": {"uv": [8, 7, 16, 7.5], "texture": "#atlas0"}, + "west": {"uv": [15, 15, 16, 15.5], "texture": "#atlas3", "cullface": "west"}, + "up": {"uv": [8, 6, 16, 7], "texture": "#atlas0", "cullface": "up"} + } + }, + { + "from": [0, 15, 6], + "to": [3, 16, 10], + "faces": { + "north": {"uv": [14, 13, 15.5, 13.5], "texture": "#atlas3"}, + "east": {"uv": [13, 14.5, 15, 15], "texture": "#atlas3"}, + "south": {"uv": [3, 12.5, 4.5, 13], "texture": "#atlas3"}, + "west": {"uv": [9, 14, 11, 14.5], "texture": "#atlas3", "cullface": "west"}, + "up": {"uv": [3, 0, 4.5, 2], "texture": "#atlas3", "cullface": "up"} + } + }, + { + "from": [4, 15, 6], + "to": [6, 16, 10], + "faces": { + "north": {"uv": [15, 14.5, 16, 15], "texture": "#atlas3"}, + "east": {"uv": [11, 14, 13, 14.5], "texture": "#atlas3"}, + "south": {"uv": [15, 14, 16, 14.5], "texture": "#atlas3"}, + "west": {"uv": [13, 14, 15, 14.5], "texture": "#atlas3"}, + "up": {"uv": [4.5, 0, 5.5, 2], "texture": "#atlas3", "cullface": "up"} + } + }, + { + "from": [10, 15, 6], + "to": [12, 16, 10], + "faces": { + "north": {"uv": [15, 13.5, 16, 14], "texture": "#atlas3"}, + "east": {"uv": [9, 13.5, 11, 14], "texture": "#atlas3"}, + "south": {"uv": [10.5, 12.5, 11.5, 13], "texture": "#atlas3"}, + "west": {"uv": [11, 13.5, 13, 14], "texture": "#atlas3"}, + "up": {"uv": [5.5, 0, 6.5, 2], "texture": "#atlas3", "cullface": "up"} + } + }, + { + "from": [13, 15, 6], + "to": [16, 16, 10], + "faces": { + "north": {"uv": [4.5, 12.5, 6, 13], "texture": "#atlas3"}, + "east": {"uv": [13, 13.5, 15, 14], "texture": "#atlas3", "cullface": "east"}, + "south": {"uv": [6, 12.5, 7.5, 13], "texture": "#atlas3"}, + "west": {"uv": [9, 13, 11, 13.5], "texture": "#atlas3"}, + "up": {"uv": [6.5, 0, 8, 2], "texture": "#atlas3", "cullface": "up"} + } + }, + { + "from": [0, 15, 4], + "to": [16, 16, 6], + "faces": { + "north": {"uv": [8, 3.5, 16, 4], "texture": "#atlas0"}, + "east": {"uv": [11.5, 12.5, 12.5, 13], "texture": "#atlas3", "cullface": "east"}, + "south": {"uv": [8, 3, 16, 3.5], "texture": "#atlas0"}, + "west": {"uv": [12.5, 12.5, 13.5, 13], "texture": "#atlas3", "cullface": "west"}, + "up": {"uv": [8, 2, 16, 3], "texture": "#atlas0", "cullface": "up"} + } + }, + { + "from": [0, 15, 3], + "to": [6, 16, 4], + "faces": { + "north": {"uv": [12, 15, 15, 15.5], "texture": "#atlas3"}, + "east": {"uv": [15, 12.5, 15.5, 13], "texture": "#atlas3"}, + "south": {"uv": [6, 13, 9, 13.5], "texture": "#atlas3"}, + "west": {"uv": [15.5, 12.5, 16, 13], "texture": "#atlas3", "cullface": "west"}, + "up": {"uv": [9, 15, 12, 15.5], "texture": "#atlas3", "cullface": "up"} + } + }, + { + "from": [10, 15, 3], + "to": [16, 16, 4], + "faces": { + "north": {"uv": [6, 14, 9, 14.5], "texture": "#atlas3"}, + "east": {"uv": [3, 12, 3.5, 12.5], "texture": "#atlas3", "cullface": "east"}, + "south": {"uv": [6, 14.5, 9, 15], "texture": "#atlas3"}, + "west": {"uv": [3.5, 12, 4, 12.5], "texture": "#atlas3"}, + "up": {"uv": [6, 13.5, 9, 14], "texture": "#atlas3", "cullface": "up"} + } + }, + { + "from": [0, 15, 0], + "to": [16, 16, 3], + "faces": { + "north": {"uv": [8, 15, 16, 15.5], "texture": "#atlas1", "cullface": "north"}, + "east": {"uv": [7.5, 12.5, 9, 13], "texture": "#atlas3", "cullface": "east"}, + "south": {"uv": [8, 15.5, 16, 16], "texture": "#atlas1"}, + "west": {"uv": [9, 12.5, 10.5, 13], "texture": "#atlas3", "cullface": "west"}, + "up": {"uv": [8, 14.5, 16, 16], "texture": "#atlas2", "cullface": "up"} + } + } + ] +} diff --git a/src/main/resources/assets/oc2/models/item/vxlan_hub.json b/src/main/resources/assets/oc2/models/item/vxlan_hub.json new file mode 100644 index 00000000..eee4e7ff --- /dev/null +++ b/src/main/resources/assets/oc2/models/item/vxlan_hub.json @@ -0,0 +1,3 @@ +{ + "parent": "oc2:block/vxlan_hub" +} diff --git a/src/main/resources/assets/oc2/textures/block/vxlan_hub/vxlan_hub_atlas0.png b/src/main/resources/assets/oc2/textures/block/vxlan_hub/vxlan_hub_atlas0.png new file mode 100644 index 0000000000000000000000000000000000000000..b2e4afe9661840474831d14bc7302a9d5ee5bd5a GIT binary patch literal 2086 zcmV+>2-)|EP)45875?6gE|-JjIIvgOb>Uz3nS1;z5C|aI+6XL&>TCU9evT8ss?I(#so45cckx019%I`rsd#ynDykK#`Gau|8``?MNLl{gnAW+^UfEO zVX7B%Ynf>rslTLxjQ0-OhZ6b! zcr!HBi({z+7#0Ii5`_~C7I?T~tWVFr|Ccm2{waj6vKs*I$!1LyD0@Pp)ZEq{hnv-aqaz=yQ8!nt!0| zKWIWca+}F5(p=gtp&joQ=Q1_L*iuR(L=jbI8~In>r~>e?=wh6B}S zRYCh;0+%NKs~DKKsssJm%S4-|FOP7dUDNI5T^~FiVP(m$aY3!Y06`QfO$Z2+ddPL< zFohWjM576vc*SyP>yMysvKuGIKSS~A_2|z`&Xl0_l!_I}UhR7Xq~+(yZYryv0ijkw zY|uL+%0$q%_$IaC+3t7qWkw|5aj`bf18P8eqEM0X!gGKM#UU2LETih@3FhMQxJwk5 z&#TEJo*_S6-=l}3tzY8#S8JFk{J$Y%E5zPE%!CjEX%NR?BVD=cdF2m&HkS}FtDf@@%q(<-!090 z{iy04P$E(uh~BRIsj%nj$lOFEW#=Ii=aa$%r_RA?Y;1IYvh$zl-1~|mw`1dGb3e!G z*zh}azPC#~<^4xn?#IZI1OjDEYB+&N8H~b;)FrQznuhdEdA(`X0jV!m0|9;+B2outP6*V2&j{Ti)u5M`GF*{_! z<0FSqCK0&A=EVBXko#q{ldh=7(-79aS}%s!d%4np7ZYVb#Z zpkmYUUuh8E5rpC5(t_^-zCSj65aY`h!6}uG6NcWKsHuZQ#6=R~fO?eQ1ztLBTUH`s z8UA$FzqGaw)^;lh4}S1EGSgR36k1SlCGa};?38bW7Uc|rkRIK?<0E|f;k&pxHX``g zk_WNHjLrkH%=sOdUr~d#5Bwbe*!<%eA|ew~IG366Ioo-4JOgRRWi>xz|jny^-34jpc1Z$!m{%bZEU#_LzR|joNb!GWLvrs!}?p4sUSzKe=UQ)O)|a Q3;+NC07*qoM6N<$f^z!@tN;K2 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/oc2/textures/block/vxlan_hub/vxlan_hub_atlas1.png b/src/main/resources/assets/oc2/textures/block/vxlan_hub/vxlan_hub_atlas1.png new file mode 100644 index 0000000000000000000000000000000000000000..ccc13cb4b04079db1956c3646cc1b5a8a0e559f3 GIT binary patch literal 2112 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 Date: Sun, 24 Mar 2024 16:52:46 +0100 Subject: [PATCH 28/28] status commit --- build.gradle | 5 +++-- gradle.properties | 4 ++-- minecraft.gradle | 2 +- .../li/cil/oc2/common/blockentity/VxlanBlockEntity.java | 3 ++- .../oc2/common/vm/provider/PciRootPortDeviceProvider.java | 6 ++---- .../META-INF/libraries/commons-collections4-4.4.jar.meta | 1 + 6 files changed, 11 insertions(+), 10 deletions(-) create mode 100644 src/main/resources/META-INF/libraries/commons-collections4-4.4.jar.meta diff --git a/build.gradle b/build.gradle index 07de11c4..a50982c9 100644 --- a/build.gradle +++ b/build.gradle @@ -86,8 +86,8 @@ dependencies { implementation fg.deobf("curse.maven:markdownmanual-502485:3738124") - compileOnly fg.deobf("mezz.jei:jei-${minecraft_version}:9.7.0.180:api") - runtimeOnly fg.deobf("mezz.jei:jei-${minecraft_version}:9.7.0.180") + compileOnly fg.deobf("mezz.jei:jei-${minecraft_version}:13.1.0.6:api") + runtimeOnly fg.deobf("mezz.jei:jei-${minecraft_version}:13.1.0.6") testImplementation "org.mockito:mockito-inline:4.3.1" testImplementation "org.junit.jupiter:junit-jupiter-api:5.8.2" @@ -176,6 +176,7 @@ jar { "Implementation-Vendor" : "Sangar", "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), "MixinConfigs" : "mixins.oc2.json", + "ContainedDeps" : "commons-collections4-4.4.jar" ]) } } diff --git a/gradle.properties b/gradle.properties index 3dc993a9..8af4161c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,8 +3,8 @@ org.gradle.jvmargs=-Xmx3G org.gradle.daemon=false -forge_version=40.1.68 +forge_version=43.2.0 -semver=0.0.0 +semver=1.0.15 curse_project_id=437654 diff --git a/minecraft.gradle b/minecraft.gradle index 03af5590..1cdd335a 100644 --- a/minecraft.gradle +++ b/minecraft.gradle @@ -1,4 +1,4 @@ ext { - minecraft_version = '1.18.2' + minecraft_version = '1.19.4' minecraft_sdk = 'forge' } diff --git a/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java b/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java index 7ef030d8..63778158 100644 --- a/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java +++ b/src/main/java/li/cil/oc2/common/blockentity/VxlanBlockEntity.java @@ -20,6 +20,7 @@ import javax.annotation.Nullable; import java.util.Arrays; import java.util.Objects; import java.util.Queue; +import java.util.concurrent.ArrayBlockingQueue; import java.util.stream.Stream; public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInterface, TickableBlockEntity { @@ -29,7 +30,7 @@ public final class VxlanBlockEntity extends ModBlockEntity implements NetworkInt private int frameCount; private long lastGameTime; - private final Queue packetQueue = QueueUtils.synchronizedQueue(new CircularFifoQueue<>(32)); + private final Queue packetQueue = new ArrayBlockingQueue(32); /////////////////////////////////////////////////////////////////// diff --git a/src/main/java/li/cil/oc2/common/vm/provider/PciRootPortDeviceProvider.java b/src/main/java/li/cil/oc2/common/vm/provider/PciRootPortDeviceProvider.java index d13690db..514af517 100644 --- a/src/main/java/li/cil/oc2/common/vm/provider/PciRootPortDeviceProvider.java +++ b/src/main/java/li/cil/oc2/common/vm/provider/PciRootPortDeviceProvider.java @@ -24,10 +24,8 @@ public final class PciRootPortDeviceProvider implements DeviceTreeProvider { public Optional createNode(final DeviceTree root, final MemoryMap memoryMap, final Device device, final String deviceName) { final Optional range = memoryMap.getMemoryRange((MemoryMappedDevice) device); return range.map(r -> { - final DeviceTree chosen = root.find("/soc"); - //chosen.addProp(DevicePropertyNames.RANGES); - - return chosen.getChild(deviceName, r.address()); + final DeviceTree pci = root.find("/pci"); + return pci.getChild(deviceName, r.address()); }); } diff --git a/src/main/resources/META-INF/libraries/commons-collections4-4.4.jar.meta b/src/main/resources/META-INF/libraries/commons-collections4-4.4.jar.meta new file mode 100644 index 00000000..7f6c74b1 --- /dev/null +++ b/src/main/resources/META-INF/libraries/commons-collections4-4.4.jar.meta @@ -0,0 +1 @@ +Maven-Artifact: org.apache.commons:commons-collections4:4.4