diff --git a/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java b/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java
index 88dd4bcde..fd00693ed 100644
--- a/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java
+++ b/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java
@@ -12,6 +12,9 @@ import dan200.computercraft.api.lua.ILuaAPIFactory;
import dan200.computercraft.api.media.IMedia;
import dan200.computercraft.api.media.IMediaProvider;
import dan200.computercraft.api.network.IPacketNetwork;
+import dan200.computercraft.api.network.wired.IWiredElement;
+import dan200.computercraft.api.network.wired.IWiredNode;
+import dan200.computercraft.api.network.wired.IWiredProvider;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.IPeripheralProvider;
@@ -21,6 +24,7 @@ import dan200.computercraft.api.redstone.IBundledRedstoneProvider;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
@@ -328,6 +332,81 @@ public final class ComputerCraftAPI
}
}
+ /**
+ * Registers a peripheral handler to convert blocks into {@link IPeripheral} implementations.
+ *
+ * @param handler The peripheral provider to register.
+ * @see dan200.computercraft.api.peripheral.IPeripheral
+ * @see dan200.computercraft.api.peripheral.IPeripheralProvider
+ */
+ public static void registerWiredProvider( @Nonnull IWiredProvider handler )
+ {
+ findCC();
+ if ( computerCraft_registerWiredProvider != null)
+ {
+ try {
+ computerCraft_registerWiredProvider.invoke( null, handler );
+ } catch (Exception e){
+ // It failed
+ }
+ }
+ }
+
+ /**
+ * Construct a new wired node for a given wired element
+ *
+ * @param element The element to construct it for
+ * @return The element's node
+ * @see IWiredElement#getNode()
+ */
+ @Nonnull
+ public static IWiredNode createWiredNodeForElement( @Nonnull IWiredElement element )
+ {
+ findCC();
+ if( computerCraft_createWiredNodeForElement != null )
+ {
+ try
+ {
+ return (IWiredNode) computerCraft_createWiredNodeForElement.invoke( null, element );
+ }
+ catch( ReflectiveOperationException e )
+ {
+ throw new IllegalStateException( "Error creating wired node", e );
+ }
+ }
+ else
+ {
+ throw new IllegalStateException( "ComputerCraft cannot be found" );
+ }
+ }
+
+ /**
+ * Get the wired network element for a block in world
+ *
+ * @param world The world the block exists in
+ * @param pos The position the block exists in
+ * @param side The side to extract the network element from
+ * @return The element's node
+ * @see IWiredElement#getNode()
+ */
+ @Nullable
+ public static IWiredElement getWiredElementAt( @Nonnull IBlockAccess world, @Nonnull BlockPos pos, @Nonnull EnumFacing side )
+ {
+ findCC();
+ if( computerCraft_getWiredElementAt != null )
+ {
+ try
+ {
+ return (IWiredElement) computerCraft_getWiredElementAt.invoke( null, world, pos, side );
+ }
+ catch( ReflectiveOperationException ignored )
+ {
+ }
+ }
+
+ return null;
+ }
+
// The functions below here are private, and are used to interface with the non-API ComputerCraft classes.
// Reflection is used here so you can develop your mod without decompiling ComputerCraft and including
// it in your solution, and so your mod won't crash if ComputerCraft is installed.
@@ -374,6 +453,15 @@ public final class ComputerCraftAPI
computerCraft_registerAPIFactory = findCCMethod( "registerAPIFactory", new Class>[] {
ILuaAPIFactory.class
} );
+ computerCraft_registerWiredProvider = findCCMethod( "registerWiredProvider", new Class>[] {
+ IWiredProvider.class
+ } );
+ computerCraft_createWiredNodeForElement = findCCMethod( "createWiredNodeForElement", new Class>[] {
+ IWiredElement.class
+ } );
+ computerCraft_getWiredElementAt = findCCMethod( "getWiredElementAt", new Class>[]{
+ IBlockAccess.class, BlockPos.class, EnumFacing.class
+ } );
} catch( Exception e ) {
System.out.println( "ComputerCraftAPI: ComputerCraft not found." );
} finally {
@@ -411,4 +499,7 @@ public final class ComputerCraftAPI
private static Method computerCraft_registerPocketUpgrade = null;
private static Method computerCraft_getWirelessNetwork = null;
private static Method computerCraft_registerAPIFactory = null;
+ private static Method computerCraft_registerWiredProvider = null;
+ private static Method computerCraft_createWiredNodeForElement = null;
+ private static Method computerCraft_getWiredElementAt = null;
}
diff --git a/src/main/java/dan200/computercraft/api/network/wired/IWiredElement.java b/src/main/java/dan200/computercraft/api/network/wired/IWiredElement.java
new file mode 100644
index 000000000..8f078bb2f
--- /dev/null
+++ b/src/main/java/dan200/computercraft/api/network/wired/IWiredElement.java
@@ -0,0 +1,51 @@
+package dan200.computercraft.api.network.wired;
+
+import dan200.computercraft.api.ComputerCraftAPI;
+import dan200.computercraft.api.peripheral.IPeripheral;
+
+import javax.annotation.Nonnull;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * An object which may be part of a wired network.
+ *
+ * Elements should construct a node using {@link ComputerCraftAPI#createWiredNodeForElement(IWiredElement)}. This acts
+ * as a proxy for all network objects. Whilst the node may change networks, an element's node should remain constant
+ * for its lifespan.
+ *
+ * Elements are generally tied to a block or tile entity in world. One should either register an {@link IWiredProvider}
+ * or implement {@link IWiredElementTile} on your tile entity.
+ *
+ * @see IWiredProvider
+ * @see ComputerCraftAPI#registerWiredProvider(IWiredProvider)
+ * @see IWiredElementTile
+ */
+public interface IWiredElement extends IWiredSender
+{
+ /**
+ * Fetch the peripherals this network element provides.
+ *
+ * This is only called when initially attaching to a network and after a call to {@link IWiredNode#invalidate()}}, so
+ * one does not need to cache the return value.
+ *
+ * @return The peripherals this node provides.
+ * @see IWiredNode#invalidate()
+ */
+ @Nonnull
+ default Map getPeripherals()
+ {
+ return Collections.emptyMap();
+ }
+
+ /**
+ * Called when objects on the network change. This may occur when network nodes are added or removed, or when
+ * peripherals change.
+ *
+ * @param change The change which occurred.
+ * @see IWiredNetworkChange
+ */
+ default void networkChanged( @Nonnull IWiredNetworkChange change )
+ {
+ }
+}
diff --git a/src/main/java/dan200/computercraft/api/network/wired/IWiredElementTile.java b/src/main/java/dan200/computercraft/api/network/wired/IWiredElementTile.java
new file mode 100644
index 000000000..03e2f2f10
--- /dev/null
+++ b/src/main/java/dan200/computercraft/api/network/wired/IWiredElementTile.java
@@ -0,0 +1,22 @@
+package dan200.computercraft.api.network.wired;
+
+import net.minecraft.util.EnumFacing;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/**
+ * A {@link net.minecraft.tileentity.TileEntity} which provides a {@link IWiredElement}. This acts
+ * as a simpler alternative to a full-blown {@link IWiredProvider}.
+ */
+public interface IWiredElementTile
+{
+ /**
+ * Get the wired element of this tile for a given side.
+ *
+ * @param side The side to get the network element from.
+ * @return A network element, or {@code null} if there is no element here.
+ */
+ @Nullable
+ IWiredElement getWiredElement( @Nonnull EnumFacing side );
+}
diff --git a/src/main/java/dan200/computercraft/api/network/wired/IWiredNetwork.java b/src/main/java/dan200/computercraft/api/network/wired/IWiredNetwork.java
new file mode 100644
index 000000000..3ab387e6c
--- /dev/null
+++ b/src/main/java/dan200/computercraft/api/network/wired/IWiredNetwork.java
@@ -0,0 +1,74 @@
+package dan200.computercraft.api.network.wired;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A wired network is composed of one of more {@link IWiredNode}s, a set of connections between them, and a series
+ * of peripherals.
+ *
+ * Networks from a connected graph. This means there is some path between all nodes on the network. Further more, if
+ * there is some path between two nodes then they must be on the same network. {@link IWiredNetwork} will automatically
+ * handle the merging and splitting of networks (and thus changing of available nodes and peripherals) as connections
+ * change.
+ *
+ * This does mean one can not rely on the network remaining consistent between subsequent operations. Consequently,
+ * it is generally preferred to use the methods provided by {@link IWiredNode}.
+ *
+ * @see IWiredNode#getNetwork()
+ */
+public interface IWiredNetwork
+{
+ /**
+ * Create a connection between two nodes.
+ *
+ * This should only be used on the server thread.
+ *
+ * @param left The first node to connect
+ * @param right The second node to connect
+ * @return {@code true} if a connection was created or {@code false} if the connection already exists.
+ * @throws IllegalStateException If neither node is on the network.
+ * @throws IllegalArgumentException If {@code left} and {@code right} are equal.
+ * @see IWiredNode#connectTo(IWiredNode)
+ * @see IWiredNetwork#connect(IWiredNode, IWiredNode)
+ */
+ boolean connect( @Nonnull IWiredNode left, @Nonnull IWiredNode right );
+
+ /**
+ * Destroy a connection between this node and another.
+ *
+ * This should only be used on the server thread.
+ *
+ * @param left The first node in the connection.
+ * @param right The second node in the connection.
+ * @return {@code true} if a connection was destroyed or {@code false} if no connection exists.
+ * @throws IllegalArgumentException If either node is not on the network.
+ * @throws IllegalArgumentException If {@code left} and {@code right} are equal.
+ * @see IWiredNode#disconnectFrom(IWiredNode)
+ * @see IWiredNetwork#connect(IWiredNode, IWiredNode)
+ */
+ boolean disconnect( @Nonnull IWiredNode left, @Nonnull IWiredNode right );
+
+ /**
+ * Sever all connections this node has, removing it from this network.
+ *
+ * This should only be used on the server thread.
+ *
+ * @param node The node to remove
+ * @return Whether this node was removed from the network. One cannot remove a node from a network where it is the
+ * only element.
+ * @throws IllegalArgumentException If the node is not in the network.
+ * @see IWiredNode#remove()
+ */
+ boolean remove( @Nonnull IWiredNode node );
+
+ /**
+ * Mark this node's peripherals as having changed.
+ *
+ * This should only be used on the server thread.
+ *
+ * @param node The node to mark as invalid.
+ * @throws IllegalArgumentException If the node is not in the network.
+ * @see IWiredElement#getPeripherals()
+ */
+ void invalidate( @Nonnull IWiredNode node );
+}
diff --git a/src/main/java/dan200/computercraft/api/network/wired/IWiredNetworkChange.java b/src/main/java/dan200/computercraft/api/network/wired/IWiredNetworkChange.java
new file mode 100644
index 000000000..1f088b669
--- /dev/null
+++ b/src/main/java/dan200/computercraft/api/network/wired/IWiredNetworkChange.java
@@ -0,0 +1,32 @@
+package dan200.computercraft.api.network.wired;
+
+import dan200.computercraft.api.peripheral.IPeripheral;
+
+import javax.annotation.Nonnull;
+import java.util.Map;
+
+/**
+ * Represents a change to the objects on a wired network.
+ *
+ * @see IWiredElement#networkChanged(IWiredNetworkChange)
+ */
+public interface IWiredNetworkChange
+{
+ /**
+ * A set of peripherals which have been removed. Note that there may be entries with the same name
+ * in the added and removed set, but with a different peripheral.
+ *
+ * @return The set of removed peripherals.
+ */
+ @Nonnull
+ Map peripheralsRemoved();
+
+ /**
+ * A set of peripherals which have been added. Note that there may be entries with the same name
+ * in the added and removed set, but with a different peripheral.
+ *
+ * @return The set of added peripherals.
+ */
+ @Nonnull
+ Map peripheralsAdded();
+}
diff --git a/src/main/java/dan200/computercraft/api/network/wired/IWiredNode.java b/src/main/java/dan200/computercraft/api/network/wired/IWiredNode.java
new file mode 100644
index 000000000..076c7bc03
--- /dev/null
+++ b/src/main/java/dan200/computercraft/api/network/wired/IWiredNode.java
@@ -0,0 +1,98 @@
+package dan200.computercraft.api.network.wired;
+
+import dan200.computercraft.api.network.IPacketNetwork;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Wired nodes act as a layer between {@link IWiredElement}s and {@link IWiredNetwork}s.
+ *
+ * Firstly, a node acts as a packet network, capable of sending and receiving modem messages to connected nodes. These
+ * methods may be safely used on any thread.
+ *
+ * When sending a packet, the system will attempt to find the shortest path between the two nodes based on their
+ * element's position. Note that packet senders and receivers can have different locations from their associated
+ * element: the distance between the two will be added to the total packet's distance.
+ *
+ * Wired nodes also provide several convenience methods for interacting with a wired network. These should only ever
+ * be used on the main server thread.
+ */
+public interface IWiredNode extends IPacketNetwork
+{
+ /**
+ * The associated element for this network node.
+ *
+ * @return This node's element.
+ */
+ @Nonnull
+ IWiredElement getElement();
+
+ /**
+ * The network this node is currently connected to. Note that this may change
+ * after any network operation, so it should not be cached.
+ *
+ * This should only be used on the server thread.
+ *
+ * @return This node's network.
+ */
+ @Nonnull
+ IWiredNetwork getNetwork();
+
+ /**
+ * Create a connection from this node to another.
+ *
+ * This should only be used on the server thread.
+ *
+ * @param node The other node to connect to.
+ * @return {@code true} if a connection was created or {@code false} if the connection already exists.
+ * @see IWiredNetwork#connect(IWiredNode, IWiredNode)
+ * @see IWiredNode#disconnectFrom(IWiredNode)
+ */
+ default boolean connectTo( @Nonnull IWiredNode node )
+ {
+ return getNetwork().connect( this, node );
+ }
+
+ /**
+ * Destroy a connection between this node and another.
+ *
+ * This should only be used on the server thread.
+ *
+ * @param node The other node to disconnect from.
+ * @return {@code true} if a connection was destroyed or {@code false} if no connection exists.
+ * @throws IllegalArgumentException If {@code node} is not on the same network.
+ * @see IWiredNetwork#disconnect(IWiredNode, IWiredNode)
+ * @see IWiredNode#connectTo(IWiredNode)
+ */
+ default boolean disconnectFrom( @Nonnull IWiredNode node )
+ {
+ return getNetwork().disconnect( this, node );
+ }
+
+ /**
+ * Sever all connections this node has, removing it from this network.
+ *
+ * This should only be used on the server thread.
+ *
+ * @return Whether this node was removed from the network. One cannot remove a node from a network where it is the
+ * only element.
+ * @throws IllegalArgumentException If the node is not in the network.
+ * @see IWiredNetwork#remove(IWiredNode)
+ */
+ default boolean remove()
+ {
+ return getNetwork().remove( this );
+ }
+
+ /**
+ * Mark this node's peripherals as having changed.
+ *
+ * This should only be used on the server thread.
+ *
+ * @see IWiredElement#getPeripherals()
+ */
+ default void invalidate()
+ {
+ getNetwork().invalidate( this );
+ }
+}
diff --git a/src/main/java/dan200/computercraft/api/network/wired/IWiredProvider.java b/src/main/java/dan200/computercraft/api/network/wired/IWiredProvider.java
new file mode 100644
index 000000000..10afae3e3
--- /dev/null
+++ b/src/main/java/dan200/computercraft/api/network/wired/IWiredProvider.java
@@ -0,0 +1,29 @@
+package dan200.computercraft.api.network.wired;
+
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.IBlockAccess;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/**
+ * Fetch or create an {@link IWiredElement} for a block at a given position.
+ *
+ * @see dan200.computercraft.api.ComputerCraftAPI#registerWiredProvider(IWiredProvider)
+ * @see IWiredElementTile
+ */
+@FunctionalInterface
+public interface IWiredProvider
+{
+ /**
+ * Extract a wired network element from a block location.
+ *
+ * @param world The world the block is in.
+ * @param pos The position the block is at.
+ * @param side The side to get the network element from.
+ * @return A network element, or {@code null} if there is not an element here you'd like to handle.
+ */
+ @Nullable
+ IWiredElement getElement( @Nonnull IBlockAccess world, @Nonnull BlockPos pos, @Nonnull EnumFacing side );
+}
diff --git a/src/main/java/dan200/computercraft/api/network/wired/IWiredSender.java b/src/main/java/dan200/computercraft/api/network/wired/IWiredSender.java
new file mode 100644
index 000000000..d29316799
--- /dev/null
+++ b/src/main/java/dan200/computercraft/api/network/wired/IWiredSender.java
@@ -0,0 +1,25 @@
+package dan200.computercraft.api.network.wired;
+
+import dan200.computercraft.api.network.IPacketSender;
+
+import javax.annotation.Nonnull;
+
+/**
+ * An object on a {@link IWiredNetwork} capable of sending packets.
+ *
+ * Unlike a regular {@link IPacketSender}, this must be associated with the node you are attempting to
+ * to send the packet from.
+ */
+public interface IWiredSender extends IPacketSender
+{
+ /**
+ * The node in the network representing this object.
+ *
+ * This should be used as a proxy for the main network. One should send packets
+ * and register receivers through this object.
+ *
+ * @return The node for this element.
+ */
+ @Nonnull
+ IWiredNode getNode();
+}
diff --git a/src/main/java/dan200/computercraft/api/network/wired/package-info.java b/src/main/java/dan200/computercraft/api/network/wired/package-info.java
new file mode 100644
index 000000000..3a8185086
--- /dev/null
+++ b/src/main/java/dan200/computercraft/api/network/wired/package-info.java
@@ -0,0 +1,10 @@
+/*
+ * This file is part of the public ComputerCraft API - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only.
+ * For help using the API, and posting your mods, visit the forums at computercraft.info.
+ */
+
+@API( owner="ComputerCraft", provides="ComputerCraft|API|Network|Wired", apiVersion="${version}" )
+package dan200.computercraft.api.network.wired;
+
+import net.minecraftforge.fml.common.API;