From 525c024cee96732e5f08333e9addcf507a1512a9 Mon Sep 17 00:00:00 2001 From: gruetzkopf Date: Mon, 20 Jun 2022 10:03:42 +0200 Subject: [PATCH] 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