Added network card, connectors and cables.
@@ -0,0 +1,50 @@
|
||||
package li.cil.oc2.api.bus.device.capabilities;
|
||||
|
||||
import li.cil.oc2.api.bus.device.ItemDevice;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* This interface provides interaction with the network bus.
|
||||
* <p>
|
||||
* Network connectors will check for this capability on blocks they are placed on.
|
||||
* If found, they will actively poll frames via {@link #readEthernetFrame()} and push
|
||||
* forwarded frames via {@link #writeEthernetFrame(NetworkInterface, byte[], int)}.
|
||||
* <p>
|
||||
* As with all capabilities, this capability can be provided by {@link ItemDevice}s.
|
||||
*/
|
||||
public interface NetworkInterface {
|
||||
/**
|
||||
* Tries to read an ethernet frame from this network interface.
|
||||
* <p>
|
||||
* The frame <em>should</em>> be a Layer 2 Ethernet frame.
|
||||
* <p>
|
||||
* When no data is available, {@code null} should be returned.
|
||||
*
|
||||
* @return a pending frame or {@code null}.
|
||||
*/
|
||||
@Nullable
|
||||
byte[] readEthernetFrame();
|
||||
|
||||
/**
|
||||
* Tries to write an ethernet frame to this network interface.
|
||||
* <p>
|
||||
* The frame <em>should</em> be a Layer 2 Ethernet frame, but this is
|
||||
* not guaranteed. Implementations should not rely on this, and if relying
|
||||
* on this at least add appropriate validation and discard the frame otherwise.
|
||||
* <p>
|
||||
* If the device is not ready to receive data, it may ignore the call.
|
||||
* <p>
|
||||
* The {@code timeToLive} parameter is not to be confused with the IP protocol's
|
||||
* TTL field. This parameter is used when pushing frames through the network bus
|
||||
* to prevent infinite loops in case of cycles. Pure consumers can ignore this
|
||||
* argument. Any implementation forwarding directly (by pushing it to some other
|
||||
* {@link NetworkInterface} implementation) should call {@code writeEthernetFrame}
|
||||
* with the time to live reduced by some value, usually by one.
|
||||
*
|
||||
* @param source the device that last forwarded the frame.
|
||||
* @param frame the frame offered to the network interface.
|
||||
* @param timeToLive the number of hops remaining before the frame should be discarded.
|
||||
*/
|
||||
void writeEthernetFrame(NetworkInterface source, byte[] frame, final int timeToLive);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package li.cil.oc2.client;
|
||||
|
||||
import li.cil.oc2.client.gui.ComputerContainerScreen;
|
||||
import li.cil.oc2.client.renderer.NetworkCableRenderer;
|
||||
import li.cil.oc2.client.renderer.tileentity.ComputerTileEntityRenderer;
|
||||
import li.cil.oc2.common.container.Containers;
|
||||
import li.cil.oc2.common.tileentity.TileEntities;
|
||||
@@ -10,6 +11,8 @@ import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
|
||||
|
||||
public final class ClientSetup {
|
||||
public static void run(final FMLClientSetupEvent event) {
|
||||
NetworkCableRenderer.initialize();
|
||||
|
||||
ScreenManager.registerFactory(Containers.COMPUTER_CONTAINER.get(), ComputerContainerScreen::new);
|
||||
|
||||
ClientRegistry.bindTileEntityRenderer(TileEntities.COMPUTER_TILE_ENTITY.get(), ComputerTileEntityRenderer::new);
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
package li.cil.oc2.client.renderer;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.mojang.blaze3d.vertex.IVertexBuilder;
|
||||
import li.cil.oc2.common.tileentity.NetworkConnectorTileEntity;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.ActiveRenderInfo;
|
||||
import net.minecraft.client.renderer.IRenderTypeBuffer;
|
||||
import net.minecraft.client.renderer.LightTexture;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.culling.ClippingHelper;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.vector.Matrix4f;
|
||||
import net.minecraft.util.math.vector.Vector3d;
|
||||
import net.minecraft.util.math.vector.Vector3f;
|
||||
import net.minecraft.world.LightType;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.event.RenderWorldLastEvent;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class NetworkCableRenderer {
|
||||
private static final int MAX_RENDER_DISTANCE = 100;
|
||||
private static final int CABLE_VERTEX_COUNT = 9;
|
||||
private static final float CABLE_THICKNESS = 0.025f;
|
||||
private static final float CABLE_HANG_MIN = 0.1f;
|
||||
private static final float CABLE_HANG_MAX = 0.5f;
|
||||
private static final float CABLE_MAX_LENGTH = 8f;
|
||||
private static final Vector3f CABLE_COLOR = new Vector3f(0.0f, 0.33f, 0.4f);
|
||||
|
||||
private static final ArrayList<NetworkConnectorTileEntity> connectors = new ArrayList<>();
|
||||
private static final ArrayList<Connection> connections = new ArrayList<>();
|
||||
private static boolean isDirty;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void initialize() {
|
||||
MinecraftForge.EVENT_BUS.addListener(NetworkCableRenderer::handleRenderWorld);
|
||||
}
|
||||
|
||||
public static void addNetworkConnector(final NetworkConnectorTileEntity connector) {
|
||||
connectors.add(connector);
|
||||
invalidateConnections();
|
||||
}
|
||||
|
||||
public static void invalidateConnections() {
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void handleRenderWorld(final RenderWorldLastEvent event) {
|
||||
validateConnectors();
|
||||
validatePairs();
|
||||
|
||||
if (connections.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final World world = Minecraft.getInstance().world;
|
||||
if (world == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final MatrixStack matrixStack = event.getMatrixStack();
|
||||
|
||||
final ActiveRenderInfo activeRenderInfo = Minecraft.getInstance().gameRenderer.getActiveRenderInfo();
|
||||
final Vector3d eye = activeRenderInfo.getProjectedView();
|
||||
|
||||
final ClippingHelper frustum = new ClippingHelper(matrixStack.getLast().getMatrix(), event.getProjectionMatrix());
|
||||
frustum.setCameraPosition(eye.getX(), eye.getY(), eye.getZ());
|
||||
|
||||
matrixStack.push();
|
||||
matrixStack.translate(-eye.getX(), -eye.getY(), -eye.getZ());
|
||||
|
||||
final Matrix4f viewMatrix = matrixStack.getLast().getMatrix();
|
||||
|
||||
final RenderType renderType = OpenComputersRenderType.getNetworkCable();
|
||||
final IRenderTypeBuffer.Impl bufferSource = Minecraft.getInstance().getRenderTypeBuffers().getBufferSource();
|
||||
|
||||
final float r = CABLE_COLOR.getX();
|
||||
final float g = CABLE_COLOR.getY();
|
||||
final float b = CABLE_COLOR.getZ();
|
||||
|
||||
for (final Connection connection : connections) {
|
||||
final Vector3d p0 = connection.from;
|
||||
final Vector3d p1 = connection.to;
|
||||
|
||||
if (!p0.isWithinDistanceOf(eye, MAX_RENDER_DISTANCE) && !p1.isWithinDistanceOf(eye, MAX_RENDER_DISTANCE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We may easily get false positives here for diagonal cables, but it's good enough for now.
|
||||
if (!frustum.isBoundingBoxInFrustum(connection.bounds)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final Vector3d p2 = addNoisyMovement(lerp(p0, p1, 0.5f).subtract(0, computeCableHang(p0, p1), 0), connection.hashCode());
|
||||
|
||||
final IVertexBuilder buffer = bufferSource.getBuffer(renderType);
|
||||
|
||||
for (int i = 0; i < CABLE_VERTEX_COUNT; i++) {
|
||||
final float t = i / (CABLE_VERTEX_COUNT - 1f);
|
||||
final Vector3d p = quadraticBezier(p0, p1, p2, t);
|
||||
final Vector3d n = getExtrusionVector(eye, p, connection.forward);
|
||||
|
||||
final BlockPos blockPos = new BlockPos(p);
|
||||
final int blockLight = world.getLightFor(LightType.BLOCK, blockPos);
|
||||
final int skyLight = world.getLightFor(LightType.SKY, blockPos);
|
||||
final int packedLight = LightTexture.packLight(blockLight, skyLight);
|
||||
|
||||
final Vector3f v0 = new Vector3f(p.subtract(n));
|
||||
final Vector3f v1 = new Vector3f(p.add(n));
|
||||
|
||||
buffer.pos(viewMatrix, v0.getX(), v0.getY(), v0.getZ())
|
||||
.color(r, g, b, 1f)
|
||||
.lightmap(packedLight)
|
||||
.endVertex();
|
||||
buffer.pos(viewMatrix, v1.getX(), v1.getY(), v1.getZ())
|
||||
.color(r, g, b, 1f)
|
||||
.lightmap(packedLight)
|
||||
.endVertex();
|
||||
}
|
||||
|
||||
bufferSource.finish(renderType);
|
||||
}
|
||||
|
||||
matrixStack.pop();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private static Vector3d lerp(final Vector3d a, final Vector3d b, final float t) {
|
||||
return a.add(b.subtract(a).scale(t)); // a + (b - a)*t = a*(1-t) + b*t
|
||||
}
|
||||
|
||||
private static Vector3d quadraticBezier(final Vector3d a, final Vector3d b, final Vector3d c, final float t) {
|
||||
final Vector3d a1 = lerp(a, c, t);
|
||||
final Vector3d b1 = lerp(c, b, t);
|
||||
return lerp(a1, b1, t);
|
||||
}
|
||||
|
||||
private static Vector3d getExtrusionVector(final Vector3d eye, final Vector3d v, final Vector3d forward) {
|
||||
return forward.crossProduct(eye.subtract(v)).normalize().scale(CABLE_THICKNESS);
|
||||
}
|
||||
|
||||
private static float computeCableHang(final Vector3d a, final Vector3d b) {
|
||||
final double length = a.distanceTo(b);
|
||||
final double hangFactor = MathHelper.clamp(length / CABLE_MAX_LENGTH, 0, 1);
|
||||
return (float) (CABLE_HANG_MIN + (CABLE_HANG_MAX - CABLE_HANG_MIN) * hangFactor);
|
||||
}
|
||||
|
||||
private static Vector3d addNoisyMovement(final Vector3d c, final int seed) {
|
||||
final float relTime = ((System.currentTimeMillis() + seed) % 10000) / 10000f;
|
||||
final float relRadialTime = relTime * 2 * (float) Math.PI;
|
||||
|
||||
return c.add(0.1f * MathHelper.cos(relRadialTime), 0.025f + 0.025f * MathHelper.sin(relRadialTime), 0.1f * MathHelper.cos(relRadialTime));
|
||||
}
|
||||
|
||||
private static void validateConnectors() {
|
||||
for (int i = connectors.size() - 1; i >= 0; i--) {
|
||||
final NetworkConnectorTileEntity connector = connectors.get(i);
|
||||
if (connector.isRemoved()) {
|
||||
connectors.remove(i);
|
||||
invalidateConnections();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void validatePairs() {
|
||||
if (!isDirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
isDirty = false;
|
||||
connections.clear();
|
||||
|
||||
final HashSet<Connection> seen = new HashSet<>();
|
||||
for (final NetworkConnectorTileEntity connector : connectors) {
|
||||
final BlockPos position = connector.getPos();
|
||||
for (final BlockPos connectedPosition : connector.getConnectedPositions()) {
|
||||
final Connection connection = new Connection(position, connectedPosition);
|
||||
if (seen.add(connection)) {
|
||||
connections.add(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private static final class Connection {
|
||||
public final BlockPos fromPos, toPos;
|
||||
public final Vector3d from, to, forward;
|
||||
public final AxisAlignedBB bounds;
|
||||
|
||||
private Connection(final BlockPos fromPos, final BlockPos toPos) {
|
||||
if (fromPos.compareTo(toPos) > 0) {
|
||||
this.fromPos = toPos;
|
||||
this.toPos = fromPos;
|
||||
} else {
|
||||
this.fromPos = fromPos;
|
||||
this.toPos = toPos;
|
||||
}
|
||||
|
||||
from = Vector3d.copyCentered(fromPos);
|
||||
to = Vector3d.copyCentered(toPos);
|
||||
forward = to.subtract(from).normalize();
|
||||
bounds = new AxisAlignedBB(from, to).grow(0, CABLE_HANG_MAX, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final Connection that = (Connection) o;
|
||||
return fromPos.equals(that.fromPos) && toPos.equals(that.toPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(fromPos, toPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package li.cil.oc2.client.renderer;
|
||||
|
||||
import li.cil.oc2.api.API;
|
||||
import net.minecraft.client.renderer.RenderState;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
@@ -23,6 +24,19 @@ public abstract class OpenComputersRenderType extends RenderType {
|
||||
state);
|
||||
}
|
||||
|
||||
public static RenderType getNetworkCable() {
|
||||
final State state = State.getBuilder()
|
||||
.transparency(RenderState.NO_TRANSPARENCY)
|
||||
.diffuseLighting(RenderState.DIFFUSE_LIGHTING_ENABLED)
|
||||
.lightmap(RenderState.LIGHTMAP_ENABLED)
|
||||
.build(false);
|
||||
return RenderType.makeType(API.MOD_ID + ":network_cable",
|
||||
DefaultVertexFormats.POSITION_COLOR_LIGHTMAP,
|
||||
GL11.GL_QUAD_STRIP,
|
||||
256,
|
||||
state);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private OpenComputersRenderType(final String name, final VertexFormat format, final int drawMode, final int bufferSize, final boolean useDelegate, final boolean needsSorting, final Runnable setupTask, final Runnable clearTask) {
|
||||
|
||||
@@ -16,17 +16,22 @@ public final class Constants {
|
||||
|
||||
public static final String COMPUTER_BLOCK_NAME = "computer";
|
||||
public static final String BUS_CABLE_BLOCK_NAME = "bus_cable";
|
||||
public static final String NETWORK_CONNECTOR_BLOCK_NAME = "network_connector";
|
||||
public static final String REDSTONE_INTERFACE_BLOCK_NAME = "redstone_interface";
|
||||
public static final String SCREEN_BLOCK_NAME = "screen";
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static final String WRENCH_ITEM_NAME = "wrench";
|
||||
|
||||
public static final String BUS_INTERFACE_ITEM_NAME = "bus_interface";
|
||||
public static final String NETWORK_CABLE_NAME = "network_cable";
|
||||
|
||||
public static final String FLASH_MEMORY_ITEM_NAME = "flash_memory";
|
||||
public static final String MEMORY_ITEM_NAME = "memory";
|
||||
public static final String HARD_DRIVE_ITEM_NAME = "hard_drive";
|
||||
public static final String REDSTONE_INTERFACE_CARD_NAME = "redstone_interface_card";
|
||||
public static final String NETWORK_INTERFACE_CARD_NAME = "network_interface_card";
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -46,4 +51,10 @@ public final class Constants {
|
||||
public static final String COMPUTER_BUS_STATE_INCOMPLETE = "gui.oc2.computer.bus_state.incomplete";
|
||||
public static final String COMPUTER_BUS_STATE_TOO_COMPLEX = "gui.oc2.computer.bus_state.too_complex";
|
||||
public static final String COMPUTER_BUS_STATE_MULTIPLE_CONTROLLERS = "gui.oc2.computer.bus_state.multiple_controllers";
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static final String CONNECTOR_ERROR_FULL = "message.oc2.connector.error.full";
|
||||
public static final String CONNECTOR_ERROR_TOO_FAR = "message.oc2.connector.error.too_far";
|
||||
public static final String CONNECTOR_ERROR_OBSTRUCTED = "message.oc2.connector.error.obstructed";
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ public final class Blocks {
|
||||
|
||||
public static final RegistryObject<ComputerBlock> COMPUTER_BLOCK = BLOCKS.register(Constants.COMPUTER_BLOCK_NAME, ComputerBlock::new);
|
||||
public static final RegistryObject<BusCableBlock> BUS_CABLE_BLOCK = BLOCKS.register(Constants.BUS_CABLE_BLOCK_NAME, BusCableBlock::new);
|
||||
public static final RegistryObject<NetworkConnectorBlock> NETWORK_CONNECTOR_BLOCK = BLOCKS.register(Constants.NETWORK_CONNECTOR_BLOCK_NAME, NetworkConnectorBlock::new);
|
||||
public static final RegistryObject<RedstoneInterfaceBlock> REDSTONE_INTERFACE_BLOCK = BLOCKS.register(Constants.REDSTONE_INTERFACE_BLOCK_NAME, RedstoneInterfaceBlock::new);
|
||||
public static final RegistryObject<ScreenBlock> SCREEN_BLOCK = BLOCKS.register(Constants.SCREEN_BLOCK_NAME, ScreenBlock::new);
|
||||
|
||||
|
||||
101
src/main/java/li/cil/oc2/common/block/NetworkConnectorBlock.java
Normal file
@@ -0,0 +1,101 @@
|
||||
package li.cil.oc2.common.block;
|
||||
|
||||
import li.cil.oc2.common.tileentity.NetworkConnectorTileEntity;
|
||||
import li.cil.oc2.common.tileentity.TileEntities;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.HorizontalFaceBlock;
|
||||
import net.minecraft.block.SoundType;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.state.StateContainer;
|
||||
import net.minecraft.state.properties.AttachFace;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.shapes.ISelectionContext;
|
||||
import net.minecraft.util.math.shapes.VoxelShape;
|
||||
import net.minecraft.world.IBlockReader;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class NetworkConnectorBlock extends HorizontalFaceBlock {
|
||||
private static final VoxelShape NEG_Z_SHAPE = Block.makeCuboidShape(5, 5, 7, 11, 11, 16);
|
||||
private static final VoxelShape POS_Z_SHAPE = Block.makeCuboidShape(5, 5, 0, 11, 11, 9);
|
||||
private static final VoxelShape NEG_X_SHAPE = Block.makeCuboidShape(7, 5, 5, 16, 11, 11);
|
||||
private static final VoxelShape POS_X_SHAPE = Block.makeCuboidShape(0, 5, 5, 9, 11, 11);
|
||||
private static final VoxelShape NEG_Y_SHAPE = Block.makeCuboidShape(5, 0, 5, 11, 9, 11);
|
||||
private static final VoxelShape POS_Y_SHAPE = Block.makeCuboidShape(5, 7, 5, 11, 16, 11);
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public NetworkConnectorBlock() {
|
||||
super(Properties
|
||||
.create(Material.IRON)
|
||||
.sound(SoundType.METAL)
|
||||
.hardnessAndResistance(1.5f, 6.0f));
|
||||
setDefaultState(getStateContainer().getBaseState()
|
||||
.with(HORIZONTAL_FACING, Direction.NORTH)
|
||||
.with(FACE, AttachFace.WALL));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static Direction getFacing(final BlockState state) {
|
||||
return HorizontalFaceBlock.getFacing(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTileEntity(final BlockState state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public TileEntity createTileEntity(final BlockState state, final IBlockReader world) {
|
||||
return TileEntities.NETWORK_CONNECTOR_TILE_ENTITY.get().create();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void neighborChanged(final BlockState state, final World world, final BlockPos pos, final Block changedBlock, final BlockPos changedBlockPos, final boolean isMoving) {
|
||||
if (Objects.equals(changedBlockPos, pos.offset(getFacing(state).getOpposite()))) {
|
||||
final TileEntity tileEntity = world.getTileEntity(pos);
|
||||
if (tileEntity instanceof NetworkConnectorTileEntity) {
|
||||
final NetworkConnectorTileEntity connector = (NetworkConnectorTileEntity) tileEntity;
|
||||
connector.setLocalInterfaceChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public VoxelShape getShape(final BlockState state, final IBlockReader world, final BlockPos pos, final ISelectionContext context) {
|
||||
switch (state.get(FACE)) {
|
||||
case WALL:
|
||||
switch (state.get(HORIZONTAL_FACING)) {
|
||||
case EAST:
|
||||
return POS_X_SHAPE;
|
||||
case WEST:
|
||||
return NEG_X_SHAPE;
|
||||
case SOUTH:
|
||||
return POS_Z_SHAPE;
|
||||
case NORTH:
|
||||
default:
|
||||
return NEG_Z_SHAPE;
|
||||
}
|
||||
case CEILING:
|
||||
return POS_Y_SHAPE;
|
||||
case FLOOR:
|
||||
default:
|
||||
return NEG_Y_SHAPE;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
protected void fillStateContainer(final StateContainer.Builder<Block, BlockState> builder) {
|
||||
builder.add(FACE, HORIZONTAL_FACING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package li.cil.oc2.common.bus.device.item;
|
||||
|
||||
import li.cil.oc2.api.bus.device.ItemDevice;
|
||||
import li.cil.oc2.api.bus.device.capabilities.NetworkInterface;
|
||||
import li.cil.oc2.api.bus.device.vm.*;
|
||||
import li.cil.oc2.common.bus.device.util.IdentityProxy;
|
||||
import li.cil.oc2.common.bus.device.util.OptionalAddress;
|
||||
import li.cil.oc2.common.bus.device.util.OptionalInterrupt;
|
||||
import li.cil.oc2.common.capabilities.Capabilities;
|
||||
import li.cil.oc2.common.serialization.NBTSerialization;
|
||||
import li.cil.oc2.common.util.NBTTagIds;
|
||||
import li.cil.sedna.device.virtio.VirtIONetworkDevice;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.capabilities.ICapabilityProvider;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class NetworkInterfaceCardItemDevice extends IdentityProxy<ItemStack> implements VMDevice, VMDeviceLifecycleListener, ItemDevice, ICapabilityProvider {
|
||||
private static final String DEVICE_TAG_NAME = "device";
|
||||
private static final String ADDRESS_NBT_TAG_NAME = "address";
|
||||
private static final String INTERRUPT_NBT_TAG_NAME = "interrupt";
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
|
||||
private VirtIONetworkDevice device;
|
||||
private final NetworkInterface networkInterface = new NetworkInterfaceImpl();
|
||||
private boolean isRunning;
|
||||
|
||||
private final OptionalAddress address = new OptionalAddress();
|
||||
private final OptionalInterrupt interrupt = new OptionalInterrupt();
|
||||
private CompoundNBT deviceNbt;
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
|
||||
public NetworkInterfaceCardItemDevice(final ItemStack identity) {
|
||||
super(identity);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <T> LazyOptional<T> getCapability(@NotNull final Capability<T> cap, @Nullable final Direction side) {
|
||||
if (cap == Capabilities.NETWORK_INTERFACE && side != null) {
|
||||
return LazyOptional.of(() -> networkInterface).cast();
|
||||
}
|
||||
|
||||
return LazyOptional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VMDeviceLoadResult load(final VMContext context) {
|
||||
device = new VirtIONetworkDevice(context.getMemoryMap());
|
||||
|
||||
if (!address.claim(context, device)) {
|
||||
return VMDeviceLoadResult.fail();
|
||||
}
|
||||
|
||||
if (interrupt.claim(context)) {
|
||||
device.getInterrupt().set(interrupt.getAsInt(), context.getInterruptController());
|
||||
} else {
|
||||
return VMDeviceLoadResult.fail();
|
||||
}
|
||||
|
||||
if (deviceNbt != null) {
|
||||
NBTSerialization.deserialize(deviceNbt, device);
|
||||
}
|
||||
|
||||
return VMDeviceLoadResult.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleLifecycleEvent(final VMDeviceLifecycleEventType event) {
|
||||
switch (event) {
|
||||
case RESUMED_RUNNING:
|
||||
isRunning = true;
|
||||
break;
|
||||
case UNLOAD:
|
||||
unload();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT serializeNBT() {
|
||||
final CompoundNBT tag = new CompoundNBT();
|
||||
|
||||
if (device != null) {
|
||||
deviceNbt = NBTSerialization.serialize(device);
|
||||
}
|
||||
if (deviceNbt != null) {
|
||||
tag.put(DEVICE_TAG_NAME, deviceNbt);
|
||||
}
|
||||
if (address.isPresent()) {
|
||||
tag.putLong(ADDRESS_NBT_TAG_NAME, address.getAsLong());
|
||||
}
|
||||
if (interrupt.isPresent()) {
|
||||
tag.putInt(INTERRUPT_NBT_TAG_NAME, interrupt.getAsInt());
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeNBT(final CompoundNBT tag) {
|
||||
if (tag.contains(DEVICE_TAG_NAME, NBTTagIds.TAG_COMPOUND)) {
|
||||
deviceNbt = tag.getCompound(DEVICE_TAG_NAME);
|
||||
}
|
||||
if (tag.contains(ADDRESS_NBT_TAG_NAME, NBTTagIds.TAG_LONG)) {
|
||||
address.set(tag.getLong(ADDRESS_NBT_TAG_NAME));
|
||||
}
|
||||
if (tag.contains(INTERRUPT_NBT_TAG_NAME, NBTTagIds.TAG_INT)) {
|
||||
interrupt.set(tag.getInt(INTERRUPT_NBT_TAG_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
|
||||
private void unload() {
|
||||
device = null;
|
||||
isRunning = false;
|
||||
address.clear();
|
||||
interrupt.clear();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
|
||||
private final class NetworkInterfaceImpl implements NetworkInterface {
|
||||
@Override
|
||||
public byte[] readEthernetFrame() {
|
||||
if (device != null && isRunning) {
|
||||
return device.readEthernetFrame();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeEthernetFrame(final NetworkInterface source, final byte[] frame, final int timeToLive) {
|
||||
if (device != null && isRunning) {
|
||||
device.writeEthernetFrame(frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,7 @@ import li.cil.oc2.api.API;
|
||||
import li.cil.oc2.api.bus.device.provider.BlockDeviceProvider;
|
||||
import li.cil.oc2.api.bus.device.provider.ItemDeviceProvider;
|
||||
import li.cil.oc2.common.bus.device.provider.block.*;
|
||||
import li.cil.oc2.common.bus.device.provider.item.FlashMemoryItemDeviceProvider;
|
||||
import li.cil.oc2.common.bus.device.provider.item.HardDriveItemDeviceProvider;
|
||||
import li.cil.oc2.common.bus.device.provider.item.MemoryItemDeviceProvider;
|
||||
import li.cil.oc2.common.bus.device.provider.item.RedstoneInterfaceCardItemDeviceProvider;
|
||||
import li.cil.oc2.common.bus.device.provider.item.*;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
import net.minecraftforge.registries.IForgeRegistry;
|
||||
@@ -38,6 +35,7 @@ public final class Providers {
|
||||
ITEM_DEVICE_PROVIDERS.register("item_hard_drive", HardDriveItemDeviceProvider::new);
|
||||
ITEM_DEVICE_PROVIDERS.register("item_flash_memory", FlashMemoryItemDeviceProvider::new);
|
||||
ITEM_DEVICE_PROVIDERS.register("item_redstone_interface_card", RedstoneInterfaceCardItemDeviceProvider::new);
|
||||
ITEM_DEVICE_PROVIDERS.register("item_network_interface_card", NetworkInterfaceCardItemDeviceProvider::new);
|
||||
|
||||
BLOCK_DEVICE_PROVIDERS.register(FMLJavaModLoadingContext.get().getModEventBus());
|
||||
ITEM_DEVICE_PROVIDERS.register(FMLJavaModLoadingContext.get().getModEventBus());
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package li.cil.oc2.common.bus.device.provider.item;
|
||||
|
||||
import li.cil.oc2.api.bus.device.ItemDevice;
|
||||
import li.cil.oc2.api.bus.device.provider.ItemDeviceQuery;
|
||||
import li.cil.oc2.common.bus.device.item.NetworkInterfaceCardItemDevice;
|
||||
import li.cil.oc2.common.bus.device.provider.util.AbstractItemDeviceProvider;
|
||||
import li.cil.oc2.common.item.Items;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public final class NetworkInterfaceCardItemDeviceProvider extends AbstractItemDeviceProvider {
|
||||
public NetworkInterfaceCardItemDeviceProvider() {
|
||||
super(Items.NETWORK_INTERFACE_CARD_ITEM);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected Optional<ItemDevice> getItemDevice(final ItemDeviceQuery query) {
|
||||
return query.getContainerTileEntity().map(tileEntity ->
|
||||
new NetworkInterfaceCardItemDevice(query.getItemStack()));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package li.cil.oc2.common.capabilities;
|
||||
|
||||
import li.cil.oc2.api.bus.DeviceBusElement;
|
||||
import li.cil.oc2.api.bus.device.capabilities.NetworkInterface;
|
||||
import li.cil.oc2.api.bus.device.capabilities.RedstoneEmitter;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.capabilities.CapabilityInject;
|
||||
@@ -25,11 +26,15 @@ public final class Capabilities {
|
||||
@CapabilityInject(RedstoneEmitter.class)
|
||||
public static Capability<RedstoneEmitter> REDSTONE_EMITTER = null;
|
||||
|
||||
@CapabilityInject(NetworkInterface.class)
|
||||
public static Capability<NetworkInterface> NETWORK_INTERFACE = null;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void initialize() {
|
||||
register(DeviceBusElement.class);
|
||||
register(RedstoneEmitter.class);
|
||||
register(NetworkInterface.class);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -20,17 +20,22 @@ public final class Items {
|
||||
|
||||
public static final RegistryObject<Item> COMPUTER_ITEM = register(Constants.COMPUTER_BLOCK_NAME, Blocks.COMPUTER_BLOCK);
|
||||
public static final RegistryObject<Item> BUS_CABLE_ITEM = register(Constants.BUS_CABLE_BLOCK_NAME, Blocks.BUS_CABLE_BLOCK);
|
||||
public static final RegistryObject<Item> NETWORK_CONNECTOR_ITEM = register(Constants.NETWORK_CONNECTOR_BLOCK_NAME, Blocks.NETWORK_CONNECTOR_BLOCK);
|
||||
public static final RegistryObject<Item> REDSTONE_INTERFACE_ITEM = register(Constants.REDSTONE_INTERFACE_BLOCK_NAME, Blocks.REDSTONE_INTERFACE_BLOCK);
|
||||
public static final RegistryObject<Item> SCREEN_ITEM = register(Constants.SCREEN_BLOCK_NAME, Blocks.SCREEN_BLOCK);
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static final RegistryObject<Item> BUS_INTERFACE_ITEM = register(Constants.BUS_INTERFACE_ITEM_NAME, BusInterfaceItem::new);
|
||||
public static final RegistryObject<Item> WRENCH_ITEM = register(Constants.WRENCH_ITEM_NAME, WrenchItem::new);
|
||||
|
||||
public static final RegistryObject<Item> BUS_INTERFACE_ITEM = register(Constants.BUS_INTERFACE_ITEM_NAME, BusInterfaceItem::new);
|
||||
public static final RegistryObject<Item> NETWORK_CABLE_ITEM = register(Constants.NETWORK_CABLE_NAME, NetworkCableItem::new);
|
||||
|
||||
public static final RegistryObject<Item> MEMORY_ITEM = register(Constants.MEMORY_ITEM_NAME, MemoryItem::new, new Item.Properties());
|
||||
public static final RegistryObject<Item> HARD_DRIVE_ITEM = register(Constants.HARD_DRIVE_ITEM_NAME, HardDriveItem::new, new Item.Properties());
|
||||
public static final RegistryObject<Item> FLASH_MEMORY_ITEM = register(Constants.FLASH_MEMORY_ITEM_NAME, FlashMemoryItem::new, new Item.Properties());
|
||||
public static final RegistryObject<Item> REDSTONE_INTERFACE_CARD_ITEM = register(Constants.REDSTONE_INTERFACE_CARD_NAME);
|
||||
public static final RegistryObject<Item> NETWORK_INTERFACE_CARD_ITEM = register(Constants.NETWORK_INTERFACE_CARD_NAME);
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
113
src/main/java/li/cil/oc2/common/item/NetworkCableItem.java
Normal file
@@ -0,0 +1,113 @@
|
||||
package li.cil.oc2.common.item;
|
||||
|
||||
import li.cil.oc2.common.Constants;
|
||||
import li.cil.oc2.common.tileentity.NetworkConnectorTileEntity;
|
||||
import li.cil.oc2.common.tileentity.NetworkConnectorTileEntity.ConnectionResult;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.player.ServerPlayerEntity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.ItemUseContext;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.ActionResultType;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.text.TranslationTextComponent;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
public final class NetworkCableItem extends Item {
|
||||
private static final WeakHashMap<ServerPlayerEntity, BlockPos> LINK_STARTS = new WeakHashMap<>();
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public NetworkCableItem(final Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public ActionResult<ItemStack> onItemRightClick(final World world, final PlayerEntity player, final Hand hand) {
|
||||
if (player.isSneaking()) {
|
||||
if (player instanceof ServerPlayerEntity) {
|
||||
LINK_STARTS.remove(player);
|
||||
}
|
||||
|
||||
return ActionResult.resultSuccess(player.getHeldItem(hand));
|
||||
}
|
||||
|
||||
return super.onItemRightClick(world, player, hand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResultType onItemUse(final ItemUseContext context) {
|
||||
final PlayerEntity player = context.getPlayer();
|
||||
if (player == null) {
|
||||
return super.onItemUse(context);
|
||||
}
|
||||
|
||||
final ItemStack stack = player.getHeldItem(context.getHand());
|
||||
if (stack.isEmpty() || stack.getItem() != this) {
|
||||
return super.onItemUse(context);
|
||||
}
|
||||
|
||||
final World world = context.getWorld();
|
||||
final BlockPos currentPos = context.getPos();
|
||||
|
||||
final TileEntity currentTileEntity = world.getTileEntity(currentPos);
|
||||
if (!(currentTileEntity instanceof NetworkConnectorTileEntity)) {
|
||||
return super.onItemUse(context);
|
||||
}
|
||||
|
||||
if (!world.isRemote() && player instanceof ServerPlayerEntity) {
|
||||
final BlockPos startPos = LINK_STARTS.remove(player);
|
||||
if (startPos == null || Objects.equals(startPos, currentPos)) {
|
||||
if (((NetworkConnectorTileEntity) currentTileEntity).canConnectMore()) {
|
||||
LINK_STARTS.put((ServerPlayerEntity) player, currentPos);
|
||||
} else {
|
||||
player.sendStatusMessage(new TranslationTextComponent(Constants.CONNECTOR_ERROR_FULL), true);
|
||||
}
|
||||
} else {
|
||||
final TileEntity startTileEntity = world.getTileEntity(startPos);
|
||||
if (!(startTileEntity instanceof NetworkConnectorTileEntity)) {
|
||||
// Starting connector was removed in the meantime.
|
||||
return super.onItemUse(context);
|
||||
}
|
||||
|
||||
final NetworkConnectorTileEntity connectorA = (NetworkConnectorTileEntity) startTileEntity;
|
||||
final NetworkConnectorTileEntity connectorB = (NetworkConnectorTileEntity) currentTileEntity;
|
||||
|
||||
final ConnectionResult connectionResult = NetworkConnectorTileEntity.connect(connectorA, connectorB);
|
||||
switch (connectionResult) {
|
||||
case SUCCESS:
|
||||
if (!player.isCreative()) {
|
||||
stack.shrink(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case FAILURE:
|
||||
LINK_STARTS.put((ServerPlayerEntity) player, startPos);
|
||||
break;
|
||||
case FAILURE_FULL:
|
||||
LINK_STARTS.put((ServerPlayerEntity) player, startPos);
|
||||
player.sendStatusMessage(new TranslationTextComponent(Constants.CONNECTOR_ERROR_FULL), true);
|
||||
break;
|
||||
case FAILURE_TOO_FAR:
|
||||
LINK_STARTS.put((ServerPlayerEntity) player, startPos);
|
||||
player.sendStatusMessage(new TranslationTextComponent(Constants.CONNECTOR_ERROR_TOO_FAR), true);
|
||||
break;
|
||||
case FAILURE_OBSTRUCTED:
|
||||
LINK_STARTS.put((ServerPlayerEntity) player, startPos);
|
||||
player.sendStatusMessage(new TranslationTextComponent(Constants.CONNECTOR_ERROR_OBSTRUCTED), true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ActionResultType.SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,12 @@ public final class Network {
|
||||
.decoder(ComputerPowerMessage::new)
|
||||
.consumer(ComputerPowerMessage::handleMessage)
|
||||
.add();
|
||||
|
||||
INSTANCE.messageBuilder(NetworkConnectorConnectionsMessage.class, getNextPacketId(), NetworkDirection.PLAY_TO_CLIENT)
|
||||
.encoder(NetworkConnectorConnectionsMessage::toBytes)
|
||||
.decoder(NetworkConnectorConnectionsMessage::new)
|
||||
.consumer(NetworkConnectorConnectionsMessage::handleMessage)
|
||||
.add();
|
||||
}
|
||||
|
||||
public static <T> void sendToClientsTrackingChunk(final T message, final Chunk chunk) {
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package li.cil.oc2.common.network.message;
|
||||
|
||||
import li.cil.oc2.common.network.MessageUtils;
|
||||
import li.cil.oc2.common.tileentity.NetworkConnectorTileEntity;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraftforge.fml.network.NetworkEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class NetworkConnectorConnectionsMessage {
|
||||
private BlockPos pos;
|
||||
private ArrayList<BlockPos> connectedPositions;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public NetworkConnectorConnectionsMessage(final NetworkConnectorTileEntity connector) {
|
||||
this.pos = connector.getPos();
|
||||
this.connectedPositions = new ArrayList<>(connector.getConnectedPositions());
|
||||
}
|
||||
|
||||
public NetworkConnectorConnectionsMessage(final PacketBuffer buffer) {
|
||||
fromBytes(buffer);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static boolean handleMessage(final NetworkConnectorConnectionsMessage message, final Supplier<NetworkEvent.Context> context) {
|
||||
context.get().enqueueWork(() -> MessageUtils.withClientTileEntityAt(message.pos, NetworkConnectorTileEntity.class,
|
||||
(tileEntity) -> tileEntity.setConnectedPositionsClient(message.connectedPositions)));
|
||||
return true;
|
||||
}
|
||||
|
||||
public void fromBytes(final PacketBuffer buffer) {
|
||||
pos = buffer.readBlockPos();
|
||||
connectedPositions = new ArrayList<>();
|
||||
final int positionCount = buffer.readVarInt();
|
||||
for (int i = 0; i < positionCount; i++) {
|
||||
final BlockPos pos = buffer.readBlockPos();
|
||||
connectedPositions.add(pos);
|
||||
}
|
||||
}
|
||||
|
||||
public static void toBytes(final NetworkConnectorConnectionsMessage message, final PacketBuffer buffer) {
|
||||
buffer.writeBlockPos(message.pos);
|
||||
buffer.writeVarInt(message.connectedPositions.size());
|
||||
for (final BlockPos pos : message.connectedPositions) {
|
||||
buffer.writeBlockPos(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,452 @@
|
||||
package li.cil.oc2.common.tileentity;
|
||||
|
||||
import li.cil.oc2.api.bus.device.capabilities.NetworkInterface;
|
||||
import li.cil.oc2.client.renderer.NetworkCableRenderer;
|
||||
import li.cil.oc2.common.Constants;
|
||||
import li.cil.oc2.common.block.NetworkConnectorBlock;
|
||||
import li.cil.oc2.common.capabilities.Capabilities;
|
||||
import li.cil.oc2.common.item.Items;
|
||||
import li.cil.oc2.common.network.Network;
|
||||
import li.cil.oc2.common.network.message.NetworkConnectorConnectionsMessage;
|
||||
import li.cil.oc2.common.util.ItemStackUtils;
|
||||
import li.cil.oc2.common.util.NBTTagIds;
|
||||
import li.cil.oc2.common.util.ServerScheduler;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.nbt.ListNBT;
|
||||
import net.minecraft.nbt.NBTUtil;
|
||||
import net.minecraft.tileentity.ITickableTileEntity;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.*;
|
||||
import net.minecraft.util.math.vector.Vector3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
public final class NetworkConnectorTileEntity extends AbstractTileEntity implements ITickableTileEntity {
|
||||
public enum ConnectionResult {
|
||||
SUCCESS,
|
||||
FAILURE,
|
||||
FAILURE_FULL,
|
||||
FAILURE_TOO_FAR,
|
||||
FAILURE_OBSTRUCTED,
|
||||
ALREADY_CONNECTED
|
||||
}
|
||||
|
||||
private static final String CONNECTIONS_TAG_NAME = "connections";
|
||||
private static final String IS_OWNER_TAG_NAME = "is_owner";
|
||||
|
||||
private static final int RETRY_UNLOADED_CHUNK_INTERVAL = 5 * Constants.TICK_SECONDS;
|
||||
private static final int MAX_CONNECTION_COUNT = 2;
|
||||
private static final int MAX_CONNECTION_DISTANCE = 16;
|
||||
private static final int INITIAL_PACKET_TIME_TO_LIVE = 8;
|
||||
private static final int BYTES_PER_SECOND = 64 * 1024;
|
||||
private static final int BYTES_PER_TICK = BYTES_PER_SECOND / Constants.TICK_SECONDS;
|
||||
private static final int MIN_ETHERNET_FRAME_SIZE = 42;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private final NetworkConnectorNetworkInterface networkInterface = new NetworkConnectorNetworkInterface();
|
||||
|
||||
private LazyOptional<NetworkInterface> localInterface = LazyOptional.empty();
|
||||
private boolean isLocalConnectionDirty = true;
|
||||
|
||||
private final HashSet<BlockPos> connectorPositions = new HashSet<>();
|
||||
private final HashSet<BlockPos> ownedCables = new HashSet<>();
|
||||
private final HashSet<BlockPos> dirtyConnectors = new HashSet<>();
|
||||
private final HashMap<BlockPos, NetworkConnectorTileEntity> connectors = new HashMap<>();
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public NetworkConnectorTileEntity() {
|
||||
super(TileEntities.NETWORK_CONNECTOR_TILE_ENTITY.get());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static ConnectionResult connect(final NetworkConnectorTileEntity connectorA, final NetworkConnectorTileEntity connectorB) {
|
||||
if (connectorA == connectorB || connectorA.isRemoved() || connectorB.isRemoved()) {
|
||||
return ConnectionResult.FAILURE;
|
||||
}
|
||||
|
||||
final World world = connectorA.getWorld();
|
||||
if (world == null || world.isRemote()) {
|
||||
return ConnectionResult.FAILURE;
|
||||
}
|
||||
|
||||
if (connectorB.getWorld() != world) {
|
||||
return ConnectionResult.FAILURE;
|
||||
}
|
||||
|
||||
if (!connectorA.canConnectMore() || !connectorB.canConnectMore()) {
|
||||
return ConnectionResult.FAILURE_FULL;
|
||||
}
|
||||
|
||||
final BlockPos posA = connectorA.getPos();
|
||||
final BlockPos posB = connectorB.getPos();
|
||||
|
||||
if (!posA.withinDistance(posB, MAX_CONNECTION_DISTANCE)) {
|
||||
return ConnectionResult.FAILURE_TOO_FAR;
|
||||
}
|
||||
|
||||
if (isObstructed(world, posA, posB)) {
|
||||
return ConnectionResult.FAILURE_OBSTRUCTED;
|
||||
}
|
||||
|
||||
if (connectorA.connectorPositions.add(posB)) {
|
||||
connectorA.dirtyConnectors.add(posB);
|
||||
connectorA.onConnectedPositionsChanged();
|
||||
}
|
||||
|
||||
if (connectorB.connectorPositions.add(posA)) {
|
||||
connectorB.dirtyConnectors.add(posA);
|
||||
connectorB.onConnectedPositionsChanged();
|
||||
}
|
||||
|
||||
final ConnectionResult result;
|
||||
if (connectorA.ownedCables.contains(posB) || connectorB.ownedCables.contains(posA)) {
|
||||
connectorA.ownedCables.add(posB);
|
||||
connectorB.ownedCables.remove(posA);
|
||||
result = ConnectionResult.ALREADY_CONNECTED;
|
||||
} else {
|
||||
connectorA.ownedCables.add(posB);
|
||||
result = ConnectionResult.SUCCESS;
|
||||
}
|
||||
|
||||
connectorA.markDirty();
|
||||
connectorB.markDirty();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void disconnectFrom(final BlockPos pos) {
|
||||
dirtyConnectors.remove(pos);
|
||||
connectors.remove(pos);
|
||||
|
||||
if (ownedCables.remove(pos)) {
|
||||
final World world = getWorld();
|
||||
if (world != null) {
|
||||
final Vector3d middle = Vector3d.copyCentered(getPos().add(pos)).scale(0.5f);
|
||||
ItemStackUtils.spawnAsEntity(world, middle, new ItemStack(Items.NETWORK_CABLE_ITEM.get()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!isRemoved()) {
|
||||
if (connectorPositions.remove(pos)) {
|
||||
onConnectedPositionsChanged();
|
||||
}
|
||||
|
||||
markDirty();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canConnectMore() {
|
||||
return connectorPositions.size() < MAX_CONNECTION_COUNT;
|
||||
}
|
||||
|
||||
public Collection<BlockPos> getConnectedPositions() {
|
||||
return connectorPositions;
|
||||
}
|
||||
|
||||
public void setLocalInterfaceChanged() {
|
||||
isLocalConnectionDirty = true;
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public void setConnectedPositionsClient(final ArrayList<BlockPos> positions) {
|
||||
connectorPositions.clear();
|
||||
connectorPositions.addAll(positions);
|
||||
NetworkCableRenderer.invalidateConnections();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
if (isLocalConnectionDirty) {
|
||||
isLocalConnectionDirty = false;
|
||||
resolveLocalInterface();
|
||||
}
|
||||
|
||||
if (!dirtyConnectors.isEmpty()) {
|
||||
final ArrayList<BlockPos> list = new ArrayList<>(dirtyConnectors);
|
||||
dirtyConnectors.clear();
|
||||
for (final BlockPos connectedPosition : list) {
|
||||
resolveConnectedInterface(connectedPosition);
|
||||
}
|
||||
}
|
||||
|
||||
final NetworkInterface src = localInterface.orElse(NullNetworkInterface.INSTANCE);
|
||||
|
||||
int byteBudget = BYTES_PER_TICK;
|
||||
byte[] frame;
|
||||
while ((frame = src.readEthernetFrame()) != null && byteBudget > 0) {
|
||||
byteBudget -= Math.max(frame.length, MIN_ETHERNET_FRAME_SIZE); // Avoid bogus packets messing with us.
|
||||
networkInterface.writeEthernetFrame(src, frame, INITIAL_PACKET_TIME_TO_LIVE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT getUpdateTag() {
|
||||
final CompoundNBT tag = super.getUpdateTag();
|
||||
|
||||
final ListNBT connections = new ListNBT();
|
||||
for (final BlockPos position : connectorPositions) {
|
||||
final CompoundNBT connectionTag = NBTUtil.writeBlockPos(position);
|
||||
connections.add(connectionTag);
|
||||
}
|
||||
tag.put(CONNECTIONS_TAG_NAME, connections);
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleUpdateTag(final BlockState state, final CompoundNBT tag) {
|
||||
super.handleUpdateTag(state, tag);
|
||||
|
||||
final ListNBT connections = tag.getList(CONNECTIONS_TAG_NAME, NBTTagIds.TAG_COMPOUND);
|
||||
for (int i = 0; i < Math.min(connections.size(), MAX_CONNECTION_COUNT); i++) {
|
||||
final CompoundNBT connectionTag = connections.getCompound(i);
|
||||
final BlockPos position = NBTUtil.readBlockPos(connectionTag);
|
||||
connectorPositions.add(position);
|
||||
dirtyConnectors.add(position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT write(CompoundNBT tag) {
|
||||
tag = super.write(tag);
|
||||
|
||||
final ListNBT connections = new ListNBT();
|
||||
for (final BlockPos position : connectorPositions) {
|
||||
final CompoundNBT connectionTag = NBTUtil.writeBlockPos(position);
|
||||
if (ownedCables.contains(position)) {
|
||||
connectionTag.putBoolean(IS_OWNER_TAG_NAME, true);
|
||||
}
|
||||
connections.add(connectionTag);
|
||||
}
|
||||
tag.put(CONNECTIONS_TAG_NAME, connections);
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(final BlockState state, final CompoundNBT tag) {
|
||||
super.read(state, tag);
|
||||
|
||||
final ListNBT connections = tag.getList(CONNECTIONS_TAG_NAME, NBTTagIds.TAG_COMPOUND);
|
||||
for (int i = 0; i < Math.min(connections.size(), MAX_CONNECTION_COUNT); i++) {
|
||||
final CompoundNBT connectionTag = connections.getCompound(i);
|
||||
final BlockPos position = NBTUtil.readBlockPos(connectionTag);
|
||||
connectorPositions.add(position);
|
||||
dirtyConnectors.add(position);
|
||||
if (connectionTag.getBoolean(IS_OWNER_TAG_NAME)) {
|
||||
ownedCables.add(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadClient() {
|
||||
super.loadClient();
|
||||
|
||||
NetworkCableRenderer.addNetworkConnector(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
super.remove();
|
||||
|
||||
// When we're being removed we want to break the actual link to any connected
|
||||
// connectors. This will also cause cables to be dropped.
|
||||
final ArrayList<NetworkConnectorTileEntity> list = new ArrayList<>(connectors.values());
|
||||
connectors.clear();
|
||||
for (final NetworkConnectorTileEntity connector : list) {
|
||||
disconnectFrom(connector.getPos());
|
||||
connector.disconnectFrom(getPos());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unloadServer() {
|
||||
super.unloadServer();
|
||||
|
||||
// When unloading, we just want to remove the reference to this tile entity
|
||||
// from connected connectors; we don't want to actually break the link.
|
||||
final BlockPos pos = getPos();
|
||||
for (final NetworkConnectorTileEntity connector : connectors.values()) {
|
||||
connector.connectors.remove(pos);
|
||||
if (connector.connectorPositions.contains(pos)) {
|
||||
connector.dirtyConnectors.add(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected void collectCapabilities(final CapabilityCollector collector, @org.jetbrains.annotations.Nullable final Direction direction) {
|
||||
collector.offer(Capabilities.NETWORK_INTERFACE, networkInterface);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private void resolveLocalInterface() {
|
||||
localInterface = LazyOptional.empty();
|
||||
|
||||
if (isRemoved()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final World world = getWorld();
|
||||
if (world == null || world.isRemote()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Direction facing = NetworkConnectorBlock.getFacing(getBlockState());
|
||||
final BlockPos sourcePos = getPos().offset(facing.getOpposite());
|
||||
|
||||
final ChunkPos sourceChunk = new ChunkPos(sourcePos);
|
||||
if (!world.chunkExists(sourceChunk.x, sourceChunk.z)) {
|
||||
ServerScheduler.schedule(world, this::setLocalInterfaceChanged, RETRY_UNLOADED_CHUNK_INTERVAL);
|
||||
return;
|
||||
}
|
||||
|
||||
final TileEntity tileEntity = world.getTileEntity(sourcePos);
|
||||
if (tileEntity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
localInterface = tileEntity.getCapability(Capabilities.NETWORK_INTERFACE, facing);
|
||||
if (localInterface.isPresent()) {
|
||||
localInterface.addListener(unused -> setLocalInterfaceChanged());
|
||||
}
|
||||
}
|
||||
|
||||
private void resolveConnectedInterface(final BlockPos connectedPosition) {
|
||||
connectors.remove(connectedPosition);
|
||||
|
||||
if (isRemoved()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final World world = getWorld();
|
||||
if (world == null || world.isRemote()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ChunkPos destinationChunk = new ChunkPos(connectedPosition);
|
||||
if (!world.chunkExists(destinationChunk.x, destinationChunk.z)) {
|
||||
ServerScheduler.schedule(world, () -> dirtyConnectors.add(connectedPosition), RETRY_UNLOADED_CHUNK_INTERVAL);
|
||||
return;
|
||||
}
|
||||
|
||||
final TileEntity tileEntity = world.getTileEntity(connectedPosition);
|
||||
if (!(tileEntity instanceof NetworkConnectorTileEntity)) {
|
||||
disconnectFrom(connectedPosition);
|
||||
return;
|
||||
}
|
||||
|
||||
final NetworkConnectorTileEntity connector = (NetworkConnectorTileEntity) tileEntity;
|
||||
|
||||
if (!connectedPosition.withinDistance(getPos(), MAX_CONNECTION_DISTANCE)) {
|
||||
disconnectFrom(connectedPosition);
|
||||
connector.disconnectFrom(getPos());
|
||||
return;
|
||||
}
|
||||
|
||||
if (isObstructed(world, getPos(), connectedPosition)) {
|
||||
disconnectFrom(connectedPosition);
|
||||
connector.disconnectFrom(getPos());
|
||||
return;
|
||||
}
|
||||
|
||||
connectors.put(connectedPosition, connector);
|
||||
}
|
||||
|
||||
private static boolean isObstructed(final World world, final BlockPos a, final BlockPos b) {
|
||||
final Vector3d va = Vector3d.copyCentered(a);
|
||||
final Vector3d vb = Vector3d.copyCentered(b);
|
||||
final Vector3d ab = vb.subtract(va).normalize().scale(0.5);
|
||||
|
||||
// Because of floating point inaccuracies the raytrace is not necessarily
|
||||
// symmetric. In particular when grazing corners perfectly, e.g. two connectors
|
||||
// attached to the same block at a 90 degree angle. So we check both ways.
|
||||
final BlockRayTraceResult hitAB = world.rayTraceBlocks(new RayTraceContext(
|
||||
va.add(ab),
|
||||
vb.subtract(ab),
|
||||
RayTraceContext.BlockMode.COLLIDER,
|
||||
RayTraceContext.FluidMode.NONE,
|
||||
null
|
||||
));
|
||||
final BlockRayTraceResult hitBA = world.rayTraceBlocks(new RayTraceContext(
|
||||
vb.subtract(ab),
|
||||
va.add(ab),
|
||||
RayTraceContext.BlockMode.COLLIDER,
|
||||
RayTraceContext.FluidMode.NONE,
|
||||
null
|
||||
));
|
||||
|
||||
return hitAB.getType() != RayTraceResult.Type.MISS ||
|
||||
hitBA.getType() != RayTraceResult.Type.MISS;
|
||||
}
|
||||
|
||||
private void onConnectedPositionsChanged() {
|
||||
final World world = getWorld();
|
||||
if (world != null && !world.isRemote()) {
|
||||
final NetworkConnectorConnectionsMessage message = new NetworkConnectorConnectionsMessage(this);
|
||||
final Chunk chunk = world.getChunkAt(getPos());
|
||||
Network.sendToClientsTrackingChunk(message, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private static final class NullNetworkInterface implements NetworkInterface {
|
||||
public static final NetworkInterface INSTANCE = new NullNetworkInterface();
|
||||
|
||||
@Override
|
||||
public byte[] readEthernetFrame() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeEthernetFrame(final NetworkInterface source, final byte[] frame, final int timeToLive) {
|
||||
}
|
||||
}
|
||||
|
||||
private final class NetworkConnectorNetworkInterface implements NetworkInterface {
|
||||
@Override
|
||||
public byte[] readEthernetFrame() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeEthernetFrame(final NetworkInterface source, final byte[] frame, final int timeToLive) {
|
||||
if (timeToLive <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
localInterface.ifPresent(dst -> {
|
||||
if (dst == source) {
|
||||
return;
|
||||
}
|
||||
dst.writeEthernetFrame(this, frame, timeToLive - 1);
|
||||
});
|
||||
|
||||
for (final NetworkConnectorTileEntity dst : connectors.values()) {
|
||||
if (dst.isRemoved() || dst.networkInterface == source) {
|
||||
continue;
|
||||
}
|
||||
dst.networkInterface.writeEthernetFrame(this, frame, timeToLive - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package li.cil.oc2.common.tileentity;
|
||||
|
||||
import li.cil.oc2.common.Constants;
|
||||
import li.cil.oc2.api.API;
|
||||
import li.cil.oc2.common.Constants;
|
||||
import li.cil.oc2.common.block.Blocks;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
@@ -21,6 +21,7 @@ public final class TileEntities {
|
||||
public static final RegistryObject<TileEntityType<RedstoneInterfaceTileEntity>> REDSTONE_INTERFACE_TILE_ENTITY = register(Constants.REDSTONE_INTERFACE_BLOCK_NAME, Blocks.REDSTONE_INTERFACE_BLOCK, RedstoneInterfaceTileEntity::new);
|
||||
public static final RegistryObject<TileEntityType<BusCableTileEntity>> BUS_CABLE_TILE_ENTITY = register(Constants.BUS_CABLE_BLOCK_NAME, Blocks.BUS_CABLE_BLOCK, BusCableTileEntity::new);
|
||||
public static final RegistryObject<TileEntityType<ComputerTileEntity>> COMPUTER_TILE_ENTITY = register(Constants.COMPUTER_BLOCK_NAME, Blocks.COMPUTER_BLOCK, ComputerTileEntity::new);
|
||||
public static final RegistryObject<TileEntityType<NetworkConnectorTileEntity>> NETWORK_CONNECTOR_TILE_ENTITY = register(Constants.NETWORK_CONNECTOR_BLOCK_NAME, Blocks.NETWORK_CONNECTOR_BLOCK, NetworkConnectorTileEntity::new);
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -31,6 +31,18 @@ public class BlockStates extends BlockStateProvider {
|
||||
horizontalBlock(Blocks.COMPUTER_BLOCK, Items.COMPUTER_ITEM);
|
||||
horizontalBlock(Blocks.REDSTONE_INTERFACE_BLOCK, Items.REDSTONE_INTERFACE_ITEM);
|
||||
horizontalBlock(Blocks.SCREEN_BLOCK, Items.SCREEN_ITEM);
|
||||
horizontalFaceBlock(Blocks.NETWORK_CONNECTOR_BLOCK, Items.NETWORK_CONNECTOR_ITEM)
|
||||
.transforms()
|
||||
.transform(ModelBuilder.Perspective.GUI)
|
||||
.rotation(30, 315, 0)
|
||||
.translation(0, 2, 0)
|
||||
.scale(0.75f, 0.75f, 0.75f)
|
||||
.end()
|
||||
.transform(ModelBuilder.Perspective.FIXED)
|
||||
.rotation(270, 0, 0)
|
||||
.translation(0, 0, -5)
|
||||
.end()
|
||||
.end();
|
||||
|
||||
registerCableStates();
|
||||
}
|
||||
@@ -187,8 +199,13 @@ public class BlockStates extends BlockStateProvider {
|
||||
.end();
|
||||
}
|
||||
|
||||
private <T extends Block> void horizontalBlock(final RegistryObject<T> block, final RegistryObject<Item> item) {
|
||||
private <T extends Block> ItemModelBuilder horizontalBlock(final RegistryObject<T> block, final RegistryObject<Item> item) {
|
||||
horizontalBlock(block.get(), models().getBuilder(block.getId().getPath()));
|
||||
itemModels().getBuilder(item.getId().getPath()).parent(models().getExistingFile(block.getId()));
|
||||
return itemModels().getBuilder(item.getId().getPath()).parent(models().getExistingFile(block.getId()));
|
||||
}
|
||||
|
||||
private <T extends Block> ItemModelBuilder horizontalFaceBlock(final RegistryObject<T> block, final RegistryObject<Item> item) {
|
||||
horizontalFaceBlock(block.get(), models().getBuilder(block.getId().getPath()));
|
||||
return itemModels().getBuilder(item.getId().getPath()).parent(models().getExistingFile(block.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package li.cil.oc2.data;
|
||||
|
||||
import li.cil.oc2.common.Constants;
|
||||
import li.cil.oc2.api.API;
|
||||
import li.cil.oc2.common.Constants;
|
||||
import li.cil.oc2.common.item.HardDriveItem;
|
||||
import li.cil.oc2.common.item.Items;
|
||||
import li.cil.oc2.common.item.MemoryItem;
|
||||
@@ -22,6 +22,8 @@ public final class ItemModels extends ItemModelProvider {
|
||||
protected void registerModels() {
|
||||
simple(Items.WRENCH_ITEM, "items/wrench");
|
||||
|
||||
simple(Items.NETWORK_CABLE_ITEM, "items/network_cable");
|
||||
|
||||
simple(Items.MEMORY_ITEM, "items/memory1")
|
||||
.override()
|
||||
.predicate(MemoryItem.CAPACITY_PROPERTY, 4 * Constants.MEGABYTE)
|
||||
@@ -31,7 +33,6 @@ public final class ItemModels extends ItemModelProvider {
|
||||
.predicate(MemoryItem.CAPACITY_PROPERTY, 8 * Constants.MEGABYTE)
|
||||
.model(simple(Items.MEMORY_ITEM, "items/memory3", "3"))
|
||||
.end();
|
||||
|
||||
simple(Items.HARD_DRIVE_ITEM, "items/hard_drive1")
|
||||
.override()
|
||||
.predicate(HardDriveItem.CAPACITY_PROPERTY, 4 * Constants.MEGABYTE)
|
||||
@@ -41,9 +42,9 @@ public final class ItemModels extends ItemModelProvider {
|
||||
.predicate(HardDriveItem.CAPACITY_PROPERTY, 8 * Constants.MEGABYTE)
|
||||
.model(simple(Items.HARD_DRIVE_ITEM, "items/hard_drive3", "3"))
|
||||
.end();
|
||||
|
||||
simple(Items.FLASH_MEMORY_ITEM, "items/flash_memory");
|
||||
simple(Items.REDSTONE_INTERFACE_CARD_ITEM, "items/redstone_interface_card");
|
||||
simple(Items.NETWORK_INTERFACE_CARD_ITEM, "items/network_interface_card");
|
||||
}
|
||||
|
||||
private <T extends Item> ItemModelBuilder simple(final RegistryObject<T> item, final String texturePath) {
|
||||
|
||||
@@ -45,6 +45,7 @@ public final class LootTables extends LootTableProvider {
|
||||
registerDropSelfLootTable(Blocks.BUS_CABLE_BLOCK.get());
|
||||
registerDropSelfLootTable(Blocks.REDSTONE_INTERFACE_BLOCK.get());
|
||||
registerDropSelfLootTable(Blocks.SCREEN_BLOCK.get());
|
||||
registerDropSelfLootTable(Blocks.NETWORK_CONNECTOR_BLOCK.get());
|
||||
|
||||
registerLootTable(Blocks.COMPUTER_BLOCK.get(), ModBlockLootTables::droppingWithInventory);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"variants": {
|
||||
"face=floor,facing=north": {
|
||||
"model": "oc2:block/network_connector"
|
||||
},
|
||||
"face=wall,facing=north": {
|
||||
"model": "oc2:block/network_connector",
|
||||
"x": 90
|
||||
},
|
||||
"face=ceiling,facing=north": {
|
||||
"model": "oc2:block/network_connector",
|
||||
"x": 180,
|
||||
"y": 180
|
||||
},
|
||||
"face=floor,facing=south": {
|
||||
"model": "oc2:block/network_connector",
|
||||
"y": 180
|
||||
},
|
||||
"face=wall,facing=south": {
|
||||
"model": "oc2:block/network_connector",
|
||||
"x": 90,
|
||||
"y": 180
|
||||
},
|
||||
"face=ceiling,facing=south": {
|
||||
"model": "oc2:block/network_connector",
|
||||
"x": 180
|
||||
},
|
||||
"face=floor,facing=west": {
|
||||
"model": "oc2:block/network_connector",
|
||||
"y": 270
|
||||
},
|
||||
"face=wall,facing=west": {
|
||||
"model": "oc2:block/network_connector",
|
||||
"x": 90,
|
||||
"y": 270
|
||||
},
|
||||
"face=ceiling,facing=west": {
|
||||
"model": "oc2:block/network_connector",
|
||||
"x": 180,
|
||||
"y": 90
|
||||
},
|
||||
"face=floor,facing=east": {
|
||||
"model": "oc2:block/network_connector",
|
||||
"y": 90
|
||||
},
|
||||
"face=wall,facing=east": {
|
||||
"model": "oc2:block/network_connector",
|
||||
"x": 90,
|
||||
"y": 90
|
||||
},
|
||||
"face=ceiling,facing=east": {
|
||||
"model": "oc2:block/network_connector",
|
||||
"x": 180,
|
||||
"y": 270
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,15 +3,18 @@
|
||||
|
||||
"block.oc2.computer": "Computer",
|
||||
"block.oc2.bus_cable": "Bus Cable",
|
||||
"block.oc2.network_connector": "Network Connector",
|
||||
"block.oc2.redstone_interface": "Redstone Interface",
|
||||
"block.oc2.screen": "Screen",
|
||||
|
||||
"item.oc2.wrench": "Scrench",
|
||||
"item.oc2.bus_interface": "Bus Interface",
|
||||
"item.oc2.network_cable": "Network Cable",
|
||||
"item.oc2.memory": "Memory",
|
||||
"item.oc2.hard_drive": "Hard Drive",
|
||||
"item.oc2.flash_memory": "Flash Memory",
|
||||
"item.oc2.redstone_interface_card": "Redstone Interface Card",
|
||||
"item.oc2.network_interface_card": "Network Interface Card",
|
||||
|
||||
"gui.oc2.computer.boot_error.unknown": "Unknown Error",
|
||||
"gui.oc2.computer.boot_error.no_memory": "Insufficient Memory",
|
||||
@@ -27,5 +30,9 @@
|
||||
"gui.oc2.device_type.memory": "Memory",
|
||||
"gui.oc2.device_type.hard_drive": "Hard Drive",
|
||||
"gui.oc2.device_type.flash_memory": "Flash Memory",
|
||||
"gui.oc2.device_type.card": "Card"
|
||||
"gui.oc2.device_type.card": "Card",
|
||||
|
||||
"message.oc2.connector.error.full": "Cannot attach more cables.",
|
||||
"message.oc2.connector.error.too_far": "Distance between connectors is too large.",
|
||||
"message.oc2.connector.error.obstructed": "No clear line of sight between connectors."
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"parent":"block/block","textures":{"east":"oc2:blocks/network_connector/network_connector_east","west":"oc2:blocks/network_connector/network_connector_west","up":"oc2:blocks/network_connector/network_connector_up","down":"oc2:blocks/network_connector/network_connector_down","north":"oc2:blocks/network_connector/network_connector_north","south":"oc2:blocks/network_connector/network_connector_south","atlas0":"oc2:blocks/network_connector/network_connector_atlas0","particle":"#north"},"elements":[{"from":[5,0,5],"to":[11,1,11],"faces":{"east":{"texture":"east"},"west":{"texture":"west"},"up":{"texture":"up"},"down":{"texture":"down","cullface":"down"},"north":{"texture":"north"},"south":{"texture":"south"}}},{"from":[6,1,10],"to":[10,3,11],"faces":{"east":{"texture":"east"},"west":{"texture":"west"},"up":{"texture":"atlas0","uv":[0.0,2.5,1.0,2.75]},"south":{"texture":"south"}}},{"from":[5,1,6],"to":[11,3,10],"faces":{"east":{"texture":"east"},"west":{"texture":"west"},"up":{"texture":"atlas0","uv":[0.0,0.0,1.5,1.0]},"north":{"texture":"north"},"south":{"texture":"south"}}},{"from":[6,1,5],"to":[10,3,6],"faces":{"east":{"texture":"east"},"west":{"texture":"west"},"up":{"texture":"atlas0","uv":[0.0,2.75,1.0,3.0]},"north":{"texture":"north"}}},{"from":[6,3,10],"to":[7,4,11],"faces":{"east":{"texture":"atlas0","uv":[0.0,3.5,0.25,3.75]},"west":{"texture":"west"},"up":{"texture":"up"},"down":{"texture":"down"},"south":{"texture":"south"}}},{"from":[9,3,10],"to":[10,4,11],"faces":{"east":{"texture":"east"},"west":{"texture":"atlas0","uv":[0.0,3.75,0.25,4.0]},"up":{"texture":"up"},"down":{"texture":"down"},"south":{"texture":"south"}}},{"from":[5,3,9],"to":[11,4,10],"faces":{"east":{"texture":"east"},"west":{"texture":"west"},"up":{"texture":"up"},"down":{"texture":"down"},"north":{"texture":"atlas0","uv":[0.0,1.0,1.5,1.25]},"south":{"texture":"south"}}},{"from":[6,3,7],"to":[10,4,9],"faces":{"east":{"texture":"east"},"west":{"texture":"west"},"up":{"texture":"up"},"down":{"texture":"down"},"north":{"texture":"north"},"south":{"texture":"south"}}},{"from":[5,3,6],"to":[11,4,7],"faces":{"east":{"texture":"east"},"west":{"texture":"west"},"up":{"texture":"up"},"down":{"texture":"down"},"north":{"texture":"north"},"south":{"texture":"atlas0","uv":[0.0,1.25,1.5,1.5]}}},{"from":[6,3,5],"to":[7,4,6],"faces":{"east":{"texture":"atlas0","uv":[0.0,4.0,0.25,4.25]},"west":{"texture":"west"},"up":{"texture":"up"},"down":{"texture":"down"},"north":{"texture":"north"}}},{"from":[9,3,5],"to":[10,4,6],"faces":{"east":{"texture":"east"},"west":{"texture":"atlas0","uv":[0.0,4.25,0.25,4.5]},"up":{"texture":"up"},"down":{"texture":"down"},"north":{"texture":"north"}}},{"from":[6,4,10],"to":[10,7,11],"faces":{"east":{"texture":"east"},"west":{"texture":"west"},"up":{"texture":"up"},"down":{"texture":"atlas0","uv":[0.0,3.0,1.0,3.25]},"south":{"texture":"south"}}},{"from":[5,4,6],"to":[11,7,10],"faces":{"east":{"texture":"east"},"west":{"texture":"west"},"up":{"texture":"up"},"down":{"texture":"atlas0","uv":[0.0,1.5,1.5,2.5]},"north":{"texture":"north"},"south":{"texture":"south"}}},{"from":[6,4,5],"to":[10,7,6],"faces":{"east":{"texture":"east"},"west":{"texture":"west"},"up":{"texture":"up"},"down":{"texture":"atlas0","uv":[0.0,3.25,1.0,3.5]},"north":{"texture":"north"}}},{"from":[6,7,6],"to":[10,8,10],"faces":{"east":{"texture":"east"},"west":{"texture":"west"},"up":{"texture":"up"},"north":{"texture":"north"},"south":{"texture":"south"}}},{"from":[7,8,7],"to":[9,9,9],"faces":{"east":{"texture":"east"},"west":{"texture":"west"},"up":{"texture":"up"},"north":{"texture":"north"},"south":{"texture":"south"}}}]}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/handheld",
|
||||
"textures": {
|
||||
"layer0": "oc2:items/network_cable"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"parent": "oc2:block/network_connector",
|
||||
"display": {
|
||||
"gui": {
|
||||
"rotation": [
|
||||
30,
|
||||
315,
|
||||
0
|
||||
],
|
||||
"translation": [
|
||||
0, 2, 0
|
||||
],
|
||||
"scale": [
|
||||
0.75,
|
||||
0.75,
|
||||
0.75
|
||||
]
|
||||
},
|
||||
"fixed": {
|
||||
"rotation": [
|
||||
270,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"translation": [
|
||||
0,
|
||||
0,
|
||||
-5
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/handheld",
|
||||
"textures": {
|
||||
"layer0": "oc2:items/network_interface_card"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 307 B |
|
After Width: | Height: | Size: 137 B |
|
After Width: | Height: | Size: 190 B |
|
After Width: | Height: | Size: 183 B |
|
After Width: | Height: | Size: 174 B |
|
After Width: | Height: | Size: 161 B |
|
After Width: | Height: | Size: 189 B |
BIN
src/main/resources/assets/oc2/textures/items/network_cable.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 351 B |
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"type": "minecraft:block",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "oc2:network_connector"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:survives_explosion"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||