1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-12-12 11:10:29 +00:00

Add an API for wired networks

The API is composed of three primary classes:

- IWiredElement: Represents some physical entity in the network. This
   will generally be a block (such as a cable or modem), but it is not
   required to be.

   Each element can provide a series of peripherals, which will be
   exposed to other elements on the network.

- IWiredNode: Every wired element has a unique wired node. This acts
   as a thread-safe proxy for communicating with the rest of the
   network (such as sending packets). Each node is also its own packet
   network.

- IWiredNetwork: This is responsible for keeping track of nodes and
   peripherals in the network. It provides methods for forming and
   breaking connections, correctly joining and splitting networks where
   needed.

Tiles which wish to be part of a wired network should implement
IWiredElementTile or register a custom IWiredProvider. When loaded into
the world, it should connect to adjacent nodes. Similarly, when removed
(either due to being broken or chunk unloads), it should break those
connections.

There is no method to query the layout of the network, as that offers
greater flexibility in changing or extending the implementation later
on.
This commit is contained in:
SquidDev 2018-02-21 15:26:13 +00:00
parent a2e2a5cb37
commit 4651e362c9
9 changed files with 432 additions and 0 deletions

View File

@ -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;
}

View File

@ -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 <em>need</em> to cache the return value.
*
* @return The peripherals this node provides.
* @see IWiredNode#invalidate()
*/
@Nonnull
default Map<String, IPeripheral> 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 )
{
}
}

View File

@ -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 );
}

View File

@ -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 );
}

View File

@ -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<String, IPeripheral> 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<String, IPeripheral> peripheralsAdded();
}

View File

@ -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 );
}
}

View File

@ -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 );
}

View File

@ -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();
}

View File

@ -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;