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;