mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-10-31 05:33:00 +00:00 
			
		
		
		
	Add the default implementation of wired networks
This commit is contained in:
		| @@ -59,6 +59,8 @@ dependencies { | |||||||
|     deobfProvided "mezz.jei:jei_1.12:4.7.5.86:api" |     deobfProvided "mezz.jei:jei_1.12:4.7.5.86:api" | ||||||
|     runtime "mezz.jei:jei_1.12:4.7.5.86" |     runtime "mezz.jei:jei_1.12:4.7.5.86" | ||||||
|     shade 'org.squiddev:Cobalt:0.3.1' |     shade 'org.squiddev:Cobalt:0.3.1' | ||||||
|  |  | ||||||
|  |     testCompile 'junit:junit:4.11' | ||||||
| } | } | ||||||
|  |  | ||||||
| javadoc { | javadoc { | ||||||
|   | |||||||
| @@ -14,6 +14,9 @@ import dan200.computercraft.api.lua.ILuaAPIFactory; | |||||||
| import dan200.computercraft.api.media.IMedia; | import dan200.computercraft.api.media.IMedia; | ||||||
| import dan200.computercraft.api.media.IMediaProvider; | import dan200.computercraft.api.media.IMediaProvider; | ||||||
| import dan200.computercraft.api.network.IPacketNetwork; | 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.IPeripheral; | import dan200.computercraft.api.peripheral.IPeripheral; | ||||||
| import dan200.computercraft.api.peripheral.IPeripheralProvider; | import dan200.computercraft.api.peripheral.IPeripheralProvider; | ||||||
| import dan200.computercraft.api.permissions.ITurtlePermissionProvider; | import dan200.computercraft.api.permissions.ITurtlePermissionProvider; | ||||||
| @@ -57,6 +60,7 @@ import dan200.computercraft.shared.turtle.blocks.BlockTurtle; | |||||||
| import dan200.computercraft.shared.turtle.blocks.TileTurtle; | import dan200.computercraft.shared.turtle.blocks.TileTurtle; | ||||||
| import dan200.computercraft.shared.turtle.upgrades.*; | import dan200.computercraft.shared.turtle.upgrades.*; | ||||||
| import dan200.computercraft.shared.util.*; | import dan200.computercraft.shared.util.*; | ||||||
|  | import dan200.computercraft.shared.wired.WiredNode; | ||||||
| import io.netty.buffer.Unpooled; | import io.netty.buffer.Unpooled; | ||||||
| import net.minecraft.entity.Entity; | import net.minecraft.entity.Entity; | ||||||
| import net.minecraft.entity.player.EntityPlayer; | import net.minecraft.entity.player.EntityPlayer; | ||||||
| @@ -69,6 +73,7 @@ import net.minecraft.util.EnumHand; | |||||||
| import net.minecraft.util.NonNullList; | import net.minecraft.util.NonNullList; | ||||||
| import net.minecraft.util.SoundEvent; | import net.minecraft.util.SoundEvent; | ||||||
| import net.minecraft.util.math.BlockPos; | import net.minecraft.util.math.BlockPos; | ||||||
|  | import net.minecraft.world.IBlockAccess; | ||||||
| import net.minecraft.world.World; | import net.minecraft.world.World; | ||||||
| import net.minecraftforge.common.config.ConfigCategory; | import net.minecraftforge.common.config.ConfigCategory; | ||||||
| import net.minecraftforge.common.config.Configuration; | import net.minecraftforge.common.config.Configuration; | ||||||
| @@ -259,6 +264,7 @@ public class ComputerCraft | |||||||
|     private static List<ITurtlePermissionProvider> permissionProviders = new ArrayList<>(); |     private static List<ITurtlePermissionProvider> permissionProviders = new ArrayList<>(); | ||||||
|     private static final Map<String, IPocketUpgrade> pocketUpgrades = new HashMap<>(); |     private static final Map<String, IPocketUpgrade> pocketUpgrades = new HashMap<>(); | ||||||
|     private static final Set<ILuaAPIFactory> apiFactories = new LinkedHashSet<>(); |     private static final Set<ILuaAPIFactory> apiFactories = new LinkedHashSet<>(); | ||||||
|  |     private static final Set<IWiredProvider> wiredProviders = new LinkedHashSet<>(); | ||||||
|  |  | ||||||
|     // Implementation |     // Implementation | ||||||
|     @Mod.Instance( value = ComputerCraft.MOD_ID ) |     @Mod.Instance( value = ComputerCraft.MOD_ID ) | ||||||
| @@ -730,6 +736,16 @@ public class ComputerCraft | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public static void registerWiredProvider( IWiredProvider provider ) | ||||||
|  |     { | ||||||
|  |         if( provider != null ) wiredProviders.add( provider ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static IWiredNode createWiredNodeForElement( IWiredElement element ) | ||||||
|  |     { | ||||||
|  |         return new WiredNode( element ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public static IPeripheral getPeripheralAt( World world, BlockPos pos, EnumFacing side ) |     public static IPeripheral getPeripheralAt( World world, BlockPos pos, EnumFacing side ) | ||||||
|     { |     { | ||||||
|         // Try the handlers in order: |         // Try the handlers in order: | ||||||
| @@ -751,6 +767,24 @@ public class ComputerCraft | |||||||
|         return null; |         return null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public static IWiredElement getWiredElementAt( IBlockAccess world, BlockPos pos, EnumFacing side ) | ||||||
|  |     { | ||||||
|  |         // Try the handlers in order: | ||||||
|  |         for( IWiredProvider provider : wiredProviders ) | ||||||
|  |         { | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 IWiredElement element = provider.getElement( world, pos, side ); | ||||||
|  |                 if( element != null ) return element; | ||||||
|  |             } | ||||||
|  |             catch( Exception e ) | ||||||
|  |             { | ||||||
|  |                 ComputerCraft.log.error( "Wired element provider " + provider + " errored.", e ); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public static int getDefaultBundledRedstoneOutput( World world, BlockPos pos, EnumFacing side ) |     public static int getDefaultBundledRedstoneOutput( World world, BlockPos pos, EnumFacing side ) | ||||||
|     { |     { | ||||||
|         if( WorldUtil.isBlockInWorld( world, pos ) ) |         if( WorldUtil.isBlockInWorld( world, pos ) ) | ||||||
|   | |||||||
| @@ -52,6 +52,7 @@ import dan200.computercraft.shared.pocket.recipes.PocketComputerUpgradeRecipe; | |||||||
| import dan200.computercraft.shared.turtle.blocks.TileTurtle; | import dan200.computercraft.shared.turtle.blocks.TileTurtle; | ||||||
| import dan200.computercraft.shared.turtle.inventory.ContainerTurtle; | import dan200.computercraft.shared.turtle.inventory.ContainerTurtle; | ||||||
| import dan200.computercraft.shared.util.*; | import dan200.computercraft.shared.util.*; | ||||||
|  | import dan200.computercraft.shared.wired.DefaultWiredProvider; | ||||||
| import net.minecraft.block.Block; | import net.minecraft.block.Block; | ||||||
| import net.minecraft.creativetab.CreativeTabs; | import net.minecraft.creativetab.CreativeTabs; | ||||||
| import net.minecraft.entity.player.EntityPlayer; | import net.minecraft.entity.player.EntityPlayer; | ||||||
| @@ -291,7 +292,7 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy | |||||||
|         // Command Computer |         // Command Computer | ||||||
|         registry.register( new ItemCommandComputer( ComputerCraft.Blocks.commandComputer ).setRegistryName( new ResourceLocation( ComputerCraft.MOD_ID, "command_computer" ) ) ); |         registry.register( new ItemCommandComputer( ComputerCraft.Blocks.commandComputer ).setRegistryName( new ResourceLocation( ComputerCraft.MOD_ID, "command_computer" ) ) ); | ||||||
|  |  | ||||||
|         // Command Computer |         // Advanced modem | ||||||
|         registry.register( new ItemAdvancedModem( ComputerCraft.Blocks.advancedModem ).setRegistryName( new ResourceLocation( ComputerCraft.MOD_ID, "advanced_modem" ) ) ); |         registry.register( new ItemAdvancedModem( ComputerCraft.Blocks.advancedModem ).setRegistryName( new ResourceLocation( ComputerCraft.MOD_ID, "advanced_modem" ) ) ); | ||||||
|  |  | ||||||
|         // Items |         // Items | ||||||
| @@ -482,6 +483,9 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy | |||||||
|  |  | ||||||
|         // Register media providers |         // Register media providers | ||||||
|         ComputerCraftAPI.registerMediaProvider( new DefaultMediaProvider() ); |         ComputerCraftAPI.registerMediaProvider( new DefaultMediaProvider() ); | ||||||
|  |  | ||||||
|  |         // Register network providers | ||||||
|  |         ComputerCraftAPI.registerWiredProvider( new DefaultWiredProvider() ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void registerForgeHandlers() |     private void registerForgeHandlers() | ||||||
|   | |||||||
| @@ -0,0 +1,23 @@ | |||||||
|  | package dan200.computercraft.shared.wired; | ||||||
|  |  | ||||||
|  | import dan200.computercraft.api.network.wired.IWiredElement; | ||||||
|  | import dan200.computercraft.api.network.wired.IWiredElementTile; | ||||||
|  | import dan200.computercraft.api.network.wired.IWiredProvider; | ||||||
|  | import net.minecraft.tileentity.TileEntity; | ||||||
|  | import net.minecraft.util.EnumFacing; | ||||||
|  | import net.minecraft.util.math.BlockPos; | ||||||
|  | import net.minecraft.world.IBlockAccess; | ||||||
|  |  | ||||||
|  | import javax.annotation.Nonnull; | ||||||
|  | import javax.annotation.Nullable; | ||||||
|  |  | ||||||
|  | public class DefaultWiredProvider implements IWiredProvider | ||||||
|  | { | ||||||
|  |     @Nullable | ||||||
|  |     @Override | ||||||
|  |     public IWiredElement getElement( @Nonnull IBlockAccess world, @Nonnull BlockPos pos, @Nonnull EnumFacing side ) | ||||||
|  |     { | ||||||
|  |         TileEntity te = world.getTileEntity( pos ); | ||||||
|  |         return te instanceof IWiredElementTile ? ((IWiredElementTile) te).getWiredElement( side ) : null; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,46 @@ | |||||||
|  | package dan200.computercraft.shared.wired; | ||||||
|  |  | ||||||
|  | import dan200.computercraft.ComputerCraft; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Verifies certain elements of a network are "well formed". | ||||||
|  |  * | ||||||
|  |  * This adds substantial overhead to network modification, and so should only be enabled | ||||||
|  |  * in a development environment. | ||||||
|  |  */ | ||||||
|  | public class InvariantChecker | ||||||
|  | { | ||||||
|  |     private static final boolean ENABLED = false; | ||||||
|  |  | ||||||
|  |     public static void checkNode( WiredNode node ) | ||||||
|  |     { | ||||||
|  |         if( !ENABLED ) return; | ||||||
|  |  | ||||||
|  |         WiredNetwork network = node.network; | ||||||
|  |         if( network == null ) | ||||||
|  |         { | ||||||
|  |             ComputerCraft.log.error( "Node's network is null", new Exception() ); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if( network.nodes == null || !network.nodes.contains( node ) ) | ||||||
|  |         { | ||||||
|  |             ComputerCraft.log.error( "Node's network does not contain node", new Exception() ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for( WiredNode neighbour : node.neighbours ) | ||||||
|  |         { | ||||||
|  |             if( !neighbour.neighbours.contains( node ) ) | ||||||
|  |             { | ||||||
|  |                 ComputerCraft.log.error( "Neighbour is missing node", new Exception() ); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static void checkNetwork( WiredNetwork network ) | ||||||
|  |     { | ||||||
|  |         if( !ENABLED ) return; | ||||||
|  |  | ||||||
|  |         for( WiredNode node : network.nodes ) checkNode( node ); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,458 @@ | |||||||
|  | package dan200.computercraft.shared.wired; | ||||||
|  |  | ||||||
|  | import dan200.computercraft.api.network.Packet; | ||||||
|  | import dan200.computercraft.api.network.wired.IWiredNetwork; | ||||||
|  | import dan200.computercraft.api.network.wired.IWiredNode; | ||||||
|  | import dan200.computercraft.api.peripheral.IPeripheral; | ||||||
|  | import net.minecraft.util.math.Vec3d; | ||||||
|  | import net.minecraft.world.World; | ||||||
|  |  | ||||||
|  | import javax.annotation.Nonnull; | ||||||
|  | import java.util.*; | ||||||
|  | import java.util.concurrent.locks.ReadWriteLock; | ||||||
|  | import java.util.concurrent.locks.ReentrantReadWriteLock; | ||||||
|  |  | ||||||
|  | public final class WiredNetwork implements IWiredNetwork | ||||||
|  | { | ||||||
|  |     final ReadWriteLock lock = new ReentrantReadWriteLock(); | ||||||
|  |     HashSet<WiredNode> nodes; | ||||||
|  |     private HashMap<String, IPeripheral> peripherals = new HashMap<>(); | ||||||
|  |  | ||||||
|  |     public WiredNetwork( WiredNode node ) | ||||||
|  |     { | ||||||
|  |         nodes = new HashSet<>( 1 ); | ||||||
|  |         nodes.add( node ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private WiredNetwork( HashSet<WiredNode> nodes ) | ||||||
|  |     { | ||||||
|  |         this.nodes = nodes; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean connect( @Nonnull IWiredNode nodeU, @Nonnull IWiredNode nodeV ) | ||||||
|  |     { | ||||||
|  |         WiredNode wiredU = checkNode( nodeU ); | ||||||
|  |         WiredNode wiredV = checkNode( nodeV ); | ||||||
|  |         if( nodeU == nodeV ) throw new IllegalArgumentException( "Cannot add a connection to oneself." ); | ||||||
|  |  | ||||||
|  |         lock.writeLock().lock(); | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             if( nodes == null ) throw new IllegalStateException( "Cannot add a connection to an empty network." ); | ||||||
|  |  | ||||||
|  |             boolean hasU = wiredU.network == this; | ||||||
|  |             boolean hasV = wiredV.network == this; | ||||||
|  |             if( !hasU && !hasV ) throw new IllegalArgumentException( "Neither node is in the network." ); | ||||||
|  |  | ||||||
|  |             // We're going to assimilate a node. Copy across all edges and vertices. | ||||||
|  |             if( !hasU || !hasV ) | ||||||
|  |             { | ||||||
|  |                 WiredNetwork other = hasU ? wiredV.network : wiredU.network; | ||||||
|  |                 other.lock.writeLock().lock(); | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     // Cache several properties for iterating over later | ||||||
|  |                     Map<String, IPeripheral> otherPeripherals = other.peripherals; | ||||||
|  |                     Map<String, IPeripheral> thisPeripherals = otherPeripherals.isEmpty() ? peripherals : new HashMap<>( peripherals ); | ||||||
|  |  | ||||||
|  |                     Collection<WiredNode> thisNodes = otherPeripherals.isEmpty() ? nodes : new ArrayList<>( this.nodes ); | ||||||
|  |                     Collection<WiredNode> otherNodes = other.nodes; | ||||||
|  |  | ||||||
|  |                     // Move all nodes across into this network, destroying the original nodes. | ||||||
|  |                     nodes.addAll( otherNodes ); | ||||||
|  |                     for( WiredNode node : otherNodes ) node.network = this; | ||||||
|  |                     other.nodes = null; | ||||||
|  |  | ||||||
|  |                     // Move all peripherals across,  | ||||||
|  |                     other.peripherals = null; | ||||||
|  |                     peripherals.putAll( otherPeripherals ); | ||||||
|  |  | ||||||
|  |                     if( !thisPeripherals.isEmpty() ) | ||||||
|  |                     { | ||||||
|  |                         WiredNetworkChange.added( thisPeripherals ).broadcast( otherNodes ); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     if( !otherPeripherals.isEmpty() ) | ||||||
|  |                     { | ||||||
|  |                         WiredNetworkChange.added( otherPeripherals ).broadcast( thisNodes ); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 finally | ||||||
|  |                 { | ||||||
|  |                     other.lock.writeLock().unlock(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             boolean added = wiredU.neighbours.add( wiredV ); | ||||||
|  |             if( added ) wiredV.neighbours.add( wiredU ); | ||||||
|  |  | ||||||
|  |             InvariantChecker.checkNetwork( this ); | ||||||
|  |             InvariantChecker.checkNode( wiredU ); | ||||||
|  |             InvariantChecker.checkNode( wiredV ); | ||||||
|  |  | ||||||
|  |             return added; | ||||||
|  |         } | ||||||
|  |         finally | ||||||
|  |         { | ||||||
|  |             lock.writeLock().unlock(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean disconnect( @Nonnull IWiredNode nodeU, @Nonnull IWiredNode nodeV ) | ||||||
|  |     { | ||||||
|  |         WiredNode wiredU = checkNode( nodeU ); | ||||||
|  |         WiredNode wiredV = checkNode( nodeV ); | ||||||
|  |         if( nodeU == nodeV ) throw new IllegalArgumentException( "Cannot remove a connection to oneself." ); | ||||||
|  |  | ||||||
|  |         lock.writeLock().lock(); | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             boolean hasU = wiredU.network == this; | ||||||
|  |             boolean hasV = wiredV.network == this; | ||||||
|  |             if( !hasU || !hasV ) throw new IllegalArgumentException( "One node is not in the network." ); | ||||||
|  |  | ||||||
|  |             // If there was no connection to remove then split. | ||||||
|  |             if( !wiredU.neighbours.remove( wiredV ) ) return false; | ||||||
|  |             wiredV.neighbours.remove( wiredU ); | ||||||
|  |  | ||||||
|  |             // Determine if there is still some connection from u to v. | ||||||
|  |             // Note this is an inlining of reachableNodes which short-circuits | ||||||
|  |             // if all nodes are reachable. | ||||||
|  |             Queue<WiredNode> enqueued = new ArrayDeque<>(); | ||||||
|  |             HashSet<WiredNode> reachableU = new HashSet<>(); | ||||||
|  |  | ||||||
|  |             reachableU.add( wiredU ); | ||||||
|  |             enqueued.add( wiredU ); | ||||||
|  |  | ||||||
|  |             while( !enqueued.isEmpty() ) | ||||||
|  |             { | ||||||
|  |                 WiredNode node = enqueued.remove(); | ||||||
|  |                 for( WiredNode neighbour : node.neighbours ) | ||||||
|  |                 { | ||||||
|  |                     // If we can reach wiredV from wiredU then abort.  | ||||||
|  |                     if( neighbour == wiredV ) return true; | ||||||
|  |  | ||||||
|  |                     // Otherwise attempt to enqueue this neighbour as well. | ||||||
|  |                     if( reachableU.add( neighbour ) ) enqueued.add( neighbour ); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // Create a new network with all U-reachable nodes/edges and remove them | ||||||
|  |             // from the existing graph. | ||||||
|  |             WiredNetwork networkU = new WiredNetwork( reachableU ); | ||||||
|  |             networkU.lock.writeLock().lock(); | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 // Remove nodes from this network | ||||||
|  |                 nodes.removeAll( reachableU ); | ||||||
|  |  | ||||||
|  |                 // Set network and transfer peripherals | ||||||
|  |                 for( WiredNode node : reachableU ) | ||||||
|  |                 { | ||||||
|  |                     node.network = networkU; | ||||||
|  |                     networkU.peripherals.putAll( node.peripherals ); | ||||||
|  |                     peripherals.keySet().removeAll( node.peripherals.keySet() ); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 // Broadcast changes | ||||||
|  |                 if( peripherals.size() != 0 ) WiredNetworkChange.removed( peripherals ).broadcast( networkU.nodes ); | ||||||
|  |                 if( networkU.peripherals.size() != 0 ) | ||||||
|  |                 { | ||||||
|  |                     WiredNetworkChange.removed( networkU.peripherals ).broadcast( nodes ); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 InvariantChecker.checkNetwork( this ); | ||||||
|  |                 InvariantChecker.checkNetwork( networkU ); | ||||||
|  |                 InvariantChecker.checkNode( wiredU ); | ||||||
|  |                 InvariantChecker.checkNode( wiredV ); | ||||||
|  |  | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |             finally | ||||||
|  |             { | ||||||
|  |                 networkU.lock.writeLock().unlock(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         finally | ||||||
|  |         { | ||||||
|  |             lock.writeLock().unlock(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean remove( @Nonnull IWiredNode node ) | ||||||
|  |     { | ||||||
|  |         WiredNode wired = checkNode( node ); | ||||||
|  |  | ||||||
|  |         lock.writeLock().lock(); | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             // If we're the empty graph then just abort: nodes must have _some_ network. | ||||||
|  |             if( nodes == null ) return false; | ||||||
|  |             if( nodes.size() <= 1 ) return false; | ||||||
|  |             if( wired.network != this ) return false; | ||||||
|  |  | ||||||
|  |             HashSet<WiredNode> neighbours = wired.neighbours; | ||||||
|  |  | ||||||
|  |             // Remove this node and move into a separate network. | ||||||
|  |             nodes.remove( wired ); | ||||||
|  |             for( WiredNode neighbour : neighbours ) neighbour.neighbours.remove( wired ); | ||||||
|  |  | ||||||
|  |             WiredNetwork wiredNetwork = new WiredNetwork( wired ); | ||||||
|  |  | ||||||
|  |             // If we're a leaf node in the graph (only one neighbour) then we don't need to  | ||||||
|  |             // check for network splitting | ||||||
|  |             if( neighbours.size() == 1 ) | ||||||
|  |             { | ||||||
|  |                 // Broadcast our simple peripheral changes | ||||||
|  |                 removeSingleNode( wired, wiredNetwork ); | ||||||
|  |                 InvariantChecker.checkNode( wired ); | ||||||
|  |                 InvariantChecker.checkNetwork( wiredNetwork ); | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             HashSet<WiredNode> reachable = reachableNodes( neighbours.iterator().next() ); | ||||||
|  |  | ||||||
|  |             // If all nodes are reachable then exit. | ||||||
|  |             if( reachable.size() == nodes.size() ) | ||||||
|  |             { | ||||||
|  |                 // Broadcast our simple peripheral changes | ||||||
|  |                 removeSingleNode( wired, wiredNetwork ); | ||||||
|  |                 InvariantChecker.checkNode( wired ); | ||||||
|  |                 InvariantChecker.checkNetwork( wiredNetwork ); | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // A split may cause 2..neighbours.size() separate networks, so we  | ||||||
|  |             // iterate through our neighbour list, generating child networks. | ||||||
|  |             neighbours.removeAll( reachable ); | ||||||
|  |             ArrayList<WiredNetwork> maximals = new ArrayList<>( neighbours.size() + 1 ); | ||||||
|  |             maximals.add( wiredNetwork ); | ||||||
|  |             maximals.add( new WiredNetwork( reachable ) ); | ||||||
|  |  | ||||||
|  |             while( neighbours.size() > 0 ) | ||||||
|  |             { | ||||||
|  |                 reachable = reachableNodes( neighbours.iterator().next() ); | ||||||
|  |                 neighbours.removeAll( reachable ); | ||||||
|  |                 maximals.add( new WiredNetwork( reachable ) ); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             for( WiredNetwork network : maximals ) network.lock.writeLock().lock(); | ||||||
|  |  | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 // We special case the original node: detaching all peripherals when needed.  | ||||||
|  |                 wired.network = wiredNetwork; | ||||||
|  |                 wired.peripherals = Collections.emptyMap(); | ||||||
|  |  | ||||||
|  |                 // Ensure every network is finalised | ||||||
|  |                 for( WiredNetwork network : maximals ) | ||||||
|  |                 { | ||||||
|  |                     for( WiredNode child : network.nodes ) | ||||||
|  |                     { | ||||||
|  |                         child.network = network; | ||||||
|  |                         network.peripherals.putAll( child.peripherals ); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 for( WiredNetwork network : maximals ) InvariantChecker.checkNetwork( network ); | ||||||
|  |                 InvariantChecker.checkNode( wired ); | ||||||
|  |  | ||||||
|  |                 // Then broadcast network changes once all nodes are finalised | ||||||
|  |                 for( WiredNetwork network : maximals ) | ||||||
|  |                 { | ||||||
|  |                     WiredNetworkChange.changeOf( peripherals, network.peripherals ).broadcast( network.nodes ); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             finally | ||||||
|  |             { | ||||||
|  |                 for( WiredNetwork network : maximals ) network.lock.writeLock().unlock(); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             nodes.clear(); | ||||||
|  |             peripherals.clear(); | ||||||
|  |  | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |         finally | ||||||
|  |         { | ||||||
|  |             lock.writeLock().unlock(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void invalidate( @Nonnull IWiredNode node ) | ||||||
|  |     { | ||||||
|  |         WiredNode wired = checkNode( node ); | ||||||
|  |  | ||||||
|  |         lock.writeLock().lock(); | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             if( wired.network != this ) throw new IllegalStateException( "Node is not on this network" ); | ||||||
|  |  | ||||||
|  |             Map<String, IPeripheral> oldPeripherals = wired.peripherals; | ||||||
|  |             Map<String, IPeripheral> newPeripherals = wired.element.getPeripherals(); | ||||||
|  |             WiredNetworkChange change = WiredNetworkChange.changeOf( oldPeripherals, newPeripherals ); | ||||||
|  |             if( change.isEmpty() ) return; | ||||||
|  |  | ||||||
|  |             wired.peripherals = newPeripherals; | ||||||
|  |  | ||||||
|  |             // Detach the old peripherals then remove them. | ||||||
|  |             peripherals.keySet().removeAll( change.peripheralsRemoved().keySet() ); | ||||||
|  |  | ||||||
|  |             // Add the new peripherals and attach them | ||||||
|  |             peripherals.putAll( change.peripheralsAdded() ); | ||||||
|  |  | ||||||
|  |             change.broadcast( nodes ); | ||||||
|  |         } | ||||||
|  |         finally | ||||||
|  |         { | ||||||
|  |             lock.writeLock().unlock(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void transmitPacket( WiredNode start, Packet packet, double range, boolean interdimensional ) | ||||||
|  |     { | ||||||
|  |         Map<WiredNode, TransmitPoint> points = new HashMap<>(); | ||||||
|  |         TreeSet<TransmitPoint> transmitTo = new TreeSet<>(); | ||||||
|  |  | ||||||
|  |         { | ||||||
|  |             TransmitPoint startEntry = start.element.getWorld() != packet.getSender().getWorld() | ||||||
|  |                 ? new TransmitPoint( start, Double.POSITIVE_INFINITY, true ) | ||||||
|  |                 : new TransmitPoint( start, start.element.getPosition().distanceTo( packet.getSender().getPosition() ), false ); | ||||||
|  |             points.put( start, startEntry ); | ||||||
|  |             transmitTo.add( startEntry ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         { | ||||||
|  |             TransmitPoint point; | ||||||
|  |             while( (point = transmitTo.pollFirst()) != null ) | ||||||
|  |             { | ||||||
|  |                 World world = point.node.element.getWorld(); | ||||||
|  |                 Vec3d position = point.node.element.getPosition(); | ||||||
|  |                 for( WiredNode neighbour : point.node.neighbours ) | ||||||
|  |                 { | ||||||
|  |                     TransmitPoint neighbourPoint = points.get( neighbour ); | ||||||
|  |  | ||||||
|  |                     boolean newInterdimensional; | ||||||
|  |                     double newDistance; | ||||||
|  |                     if( world != neighbour.element.getWorld() ) | ||||||
|  |                     { | ||||||
|  |                         newInterdimensional = true; | ||||||
|  |                         newDistance = Double.POSITIVE_INFINITY; | ||||||
|  |                     } | ||||||
|  |                     else | ||||||
|  |                     { | ||||||
|  |                         newInterdimensional = false; | ||||||
|  |                         newDistance = point.distance + position.distanceTo( neighbour.element.getPosition() ); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     if( neighbourPoint == null ) | ||||||
|  |                     { | ||||||
|  |                         neighbourPoint = new TransmitPoint( neighbour, newDistance, newInterdimensional ); | ||||||
|  |                         points.put( neighbour, neighbourPoint ); | ||||||
|  |                         transmitTo.add( neighbourPoint ); | ||||||
|  |                     } | ||||||
|  |                     else if( newDistance < neighbourPoint.distance ) | ||||||
|  |                     { | ||||||
|  |                         transmitTo.remove( neighbourPoint ); | ||||||
|  |                         neighbourPoint.distance = newDistance; | ||||||
|  |                         neighbourPoint.interdimensional = newInterdimensional; | ||||||
|  |                         transmitTo.add( neighbourPoint ); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for( TransmitPoint point : points.values() ) | ||||||
|  |         { | ||||||
|  |             point.node.tryTransmit( packet, point.distance, point.interdimensional, range, interdimensional ); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void removeSingleNode( WiredNode wired, WiredNetwork wiredNetwork ) | ||||||
|  |     { | ||||||
|  |         wiredNetwork.lock.writeLock().lock(); | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             // Cache all the old nodes. | ||||||
|  |             Map<String, IPeripheral> wiredPeripherals = new HashMap<>( wired.peripherals ); | ||||||
|  |  | ||||||
|  |             // Setup the new node's network | ||||||
|  |             // Detach the old peripherals then remove them from the old network | ||||||
|  |             wired.network = wiredNetwork; | ||||||
|  |             wired.neighbours.clear(); | ||||||
|  |             wired.peripherals = Collections.emptyMap(); | ||||||
|  |  | ||||||
|  |             // Broadcast the change | ||||||
|  |             if( !peripherals.isEmpty() ) WiredNetworkChange.removed( peripherals ).broadcast( wired ); | ||||||
|  |  | ||||||
|  |             // Now remove all peripherals from this network and broadcast the change. | ||||||
|  |             peripherals.keySet().removeAll( wiredPeripherals.keySet() ); | ||||||
|  |             if( !wiredPeripherals.isEmpty() ) WiredNetworkChange.removed( wiredPeripherals ).broadcast( nodes ); | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         finally | ||||||
|  |         { | ||||||
|  |             wiredNetwork.lock.writeLock().unlock(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static class TransmitPoint implements Comparable<TransmitPoint> | ||||||
|  |     { | ||||||
|  |         final WiredNode node; | ||||||
|  |         double distance; | ||||||
|  |         boolean interdimensional; | ||||||
|  |  | ||||||
|  |         TransmitPoint( WiredNode node, double distance, boolean interdimensional ) | ||||||
|  |         { | ||||||
|  |             this.node = node; | ||||||
|  |             this.distance = distance; | ||||||
|  |             this.interdimensional = interdimensional; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Override | ||||||
|  |         public int compareTo( @Nonnull TransmitPoint o ) | ||||||
|  |         { | ||||||
|  |             // Objects with the same distance are not the same object, so we must add an additional layer of ordering. | ||||||
|  |             return distance == o.distance | ||||||
|  |                 ? Integer.compare( node.hashCode(), o.node.hashCode() ) | ||||||
|  |                 : Double.compare( distance, o.distance ); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static WiredNode checkNode( IWiredNode node ) | ||||||
|  |     { | ||||||
|  |         if( node instanceof WiredNode ) | ||||||
|  |         { | ||||||
|  |             return (WiredNode) node; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             throw new IllegalArgumentException( "Unknown implementation of IWiredNode: " + node ); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static HashSet<WiredNode> reachableNodes( WiredNode start ) | ||||||
|  |     { | ||||||
|  |         Queue<WiredNode> enqueued = new ArrayDeque<>(); | ||||||
|  |         HashSet<WiredNode> reachable = new HashSet<>(); | ||||||
|  |  | ||||||
|  |         reachable.add( start ); | ||||||
|  |         enqueued.add( start ); | ||||||
|  |  | ||||||
|  |         WiredNode node; | ||||||
|  |         while( (node = enqueued.poll()) != null ) | ||||||
|  |         { | ||||||
|  |             for( WiredNode neighbour : node.neighbours ) | ||||||
|  |             { | ||||||
|  |                 // Otherwise attempt to enqueue this neighbour as well. | ||||||
|  |                 if( reachable.add( neighbour ) ) enqueued.add( neighbour ); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return reachable; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,101 @@ | |||||||
|  | package dan200.computercraft.shared.wired; | ||||||
|  |  | ||||||
|  | import dan200.computercraft.api.network.wired.IWiredNetworkChange; | ||||||
|  | import dan200.computercraft.api.peripheral.IPeripheral; | ||||||
|  |  | ||||||
|  | import javax.annotation.Nonnull; | ||||||
|  | import java.util.Collections; | ||||||
|  | import java.util.HashMap; | ||||||
|  | import java.util.Map; | ||||||
|  |  | ||||||
|  | public class WiredNetworkChange implements IWiredNetworkChange | ||||||
|  | { | ||||||
|  |     private final Map<String, IPeripheral> removed; | ||||||
|  |     private final Map<String, IPeripheral> added; | ||||||
|  |  | ||||||
|  |     private WiredNetworkChange( Map<String, IPeripheral> removed, Map<String, IPeripheral> added ) | ||||||
|  |     { | ||||||
|  |         this.removed = removed; | ||||||
|  |         this.added = added; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static WiredNetworkChange changed( Map<String, IPeripheral> removed, Map<String, IPeripheral> added ) | ||||||
|  |     { | ||||||
|  |         return new WiredNetworkChange( Collections.unmodifiableMap( removed ), Collections.unmodifiableMap( added ) ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static WiredNetworkChange added( Map<String, IPeripheral> added ) | ||||||
|  |     { | ||||||
|  |         return new WiredNetworkChange( Collections.emptyMap(), Collections.unmodifiableMap( added ) ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static WiredNetworkChange removed( Map<String, IPeripheral> removed ) | ||||||
|  |     { | ||||||
|  |         return new WiredNetworkChange( Collections.unmodifiableMap( removed ), Collections.emptyMap() ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static WiredNetworkChange changeOf( Map<String, IPeripheral> oldPeripherals, Map<String, IPeripheral> newPeripherals ) | ||||||
|  |     { | ||||||
|  |         Map<String, IPeripheral> added = new HashMap<>( newPeripherals ); | ||||||
|  |         Map<String, IPeripheral> removed = new HashMap<>(); | ||||||
|  |  | ||||||
|  |         for( Map.Entry<String, IPeripheral> entry : oldPeripherals.entrySet() ) | ||||||
|  |         { | ||||||
|  |             String oldKey = entry.getKey(); | ||||||
|  |             IPeripheral oldValue = entry.getValue(); | ||||||
|  |             if( newPeripherals.containsKey( oldKey ) ) | ||||||
|  |             { | ||||||
|  |                 IPeripheral rightValue = added.get( oldKey ); | ||||||
|  |                 if( oldValue.equals( rightValue ) ) | ||||||
|  |                 { | ||||||
|  |                     added.remove( oldKey ); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     removed.put( oldKey, oldValue ); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 removed.put( oldKey, oldValue ); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return changed( removed, added ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Nonnull | ||||||
|  |     @Override | ||||||
|  |     public Map<String, IPeripheral> peripheralsAdded() | ||||||
|  |     { | ||||||
|  |         return added; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Nonnull | ||||||
|  |     @Override | ||||||
|  |     public Map<String, IPeripheral> peripheralsRemoved() | ||||||
|  |     { | ||||||
|  |         return removed; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public boolean isEmpty() | ||||||
|  |     { | ||||||
|  |         return added.isEmpty() && removed.isEmpty(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void broadcast( Iterable<WiredNode> nodes ) | ||||||
|  |     { | ||||||
|  |         if( !isEmpty() ) | ||||||
|  |         { | ||||||
|  |             for( WiredNode node : nodes ) node.element.networkChanged( this ); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void broadcast( WiredNode node ) | ||||||
|  |     { | ||||||
|  |         if( !isEmpty() ) | ||||||
|  |         { | ||||||
|  |             node.element.networkChanged( this ); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										151
									
								
								src/main/java/dan200/computercraft/shared/wired/WiredNode.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								src/main/java/dan200/computercraft/shared/wired/WiredNode.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,151 @@ | |||||||
|  | package dan200.computercraft.shared.wired; | ||||||
|  |  | ||||||
|  | import com.google.common.base.Preconditions; | ||||||
|  | import dan200.computercraft.api.network.IPacketReceiver; | ||||||
|  | import dan200.computercraft.api.network.Packet; | ||||||
|  | import dan200.computercraft.api.network.wired.IWiredElement; | ||||||
|  | import dan200.computercraft.api.network.wired.IWiredNetwork; | ||||||
|  | import dan200.computercraft.api.network.wired.IWiredNode; | ||||||
|  | import dan200.computercraft.api.network.wired.IWiredSender; | ||||||
|  | import dan200.computercraft.api.peripheral.IPeripheral; | ||||||
|  |  | ||||||
|  | import javax.annotation.Nonnull; | ||||||
|  | import java.util.Collections; | ||||||
|  | import java.util.HashSet; | ||||||
|  | import java.util.Map; | ||||||
|  | import java.util.Set; | ||||||
|  | import java.util.concurrent.locks.Lock; | ||||||
|  |  | ||||||
|  | public final class WiredNode implements IWiredNode | ||||||
|  | { | ||||||
|  |     private Set<IPacketReceiver> receivers; | ||||||
|  |  | ||||||
|  |     final IWiredElement element; | ||||||
|  |     Map<String, IPeripheral> peripherals = Collections.emptyMap(); | ||||||
|  |  | ||||||
|  |     final HashSet<WiredNode> neighbours = new HashSet<>(); | ||||||
|  |     volatile WiredNetwork network; | ||||||
|  |  | ||||||
|  |     public WiredNode( IWiredElement element ) | ||||||
|  |     { | ||||||
|  |         this.element = element; | ||||||
|  |         this.network = new WiredNetwork( this ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public synchronized void addReceiver( @Nonnull IPacketReceiver receiver ) | ||||||
|  |     { | ||||||
|  |         if( receivers == null ) receivers = new HashSet<>(); | ||||||
|  |         receivers.add( receiver ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public synchronized void removeReceiver( @Nonnull IPacketReceiver receiver ) | ||||||
|  |     { | ||||||
|  |         if( receivers != null ) receivers.remove( receiver ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     synchronized void tryTransmit( Packet packet, double packetDistance, boolean packetInterdimensional, double range, boolean interdimensional ) | ||||||
|  |     { | ||||||
|  |         if( receivers == null ) return; | ||||||
|  |  | ||||||
|  |         for( IPacketReceiver receiver : receivers ) | ||||||
|  |         { | ||||||
|  |             if( !packetInterdimensional ) | ||||||
|  |             { | ||||||
|  |                 double receiveRange = Math.max( range, receiver.getRange() ); // Ensure range is symmetrical | ||||||
|  |                 if( interdimensional || receiver.isInterdimensional() || packetDistance < receiveRange ) | ||||||
|  |                 { | ||||||
|  |                     receiver.receiveSameDimension( packet, packetDistance + element.getPosition().distanceTo( receiver.getPosition() ) ); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 if( interdimensional || receiver.isInterdimensional() ) | ||||||
|  |                 { | ||||||
|  |                     receiver.receiveDifferentDimension( packet ); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public boolean isWireless() | ||||||
|  |     { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void transmitSameDimension( @Nonnull Packet packet, double range ) | ||||||
|  |     { | ||||||
|  |         Preconditions.checkNotNull( packet, "packet cannot be null" ); | ||||||
|  |         if( !(packet.getSender() instanceof IWiredSender) || ((IWiredSender) packet.getSender()).getNode() != this ) | ||||||
|  |         { | ||||||
|  |             throw new IllegalArgumentException( "Sender is not in the network" ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         acquireReadLock(); | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             network.transmitPacket( this, packet, range, false ); | ||||||
|  |         } | ||||||
|  |         finally | ||||||
|  |         { | ||||||
|  |             network.lock.readLock().unlock(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void transmitInterdimensional( @Nonnull Packet packet ) | ||||||
|  |     { | ||||||
|  |         Preconditions.checkNotNull( packet, "packet cannot be null" ); | ||||||
|  |         if( !(packet.getSender() instanceof IWiredSender) || ((IWiredSender) packet.getSender()).getNode() != this ) | ||||||
|  |         { | ||||||
|  |             throw new IllegalArgumentException( "Sender is not in the network" ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         acquireReadLock(); | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             network.transmitPacket( this, packet, 0, true ); | ||||||
|  |         } | ||||||
|  |         finally | ||||||
|  |         { | ||||||
|  |             network.lock.readLock().unlock(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Nonnull | ||||||
|  |     @Override | ||||||
|  |     public IWiredElement getElement() | ||||||
|  |     { | ||||||
|  |         return element; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Nonnull | ||||||
|  |     @Override | ||||||
|  |     public IWiredNetwork getNetwork() | ||||||
|  |     { | ||||||
|  |         return network; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String toString() | ||||||
|  |     { | ||||||
|  |         return "WiredNode{@" + element.getPosition() + " (" + element.getClass().getSimpleName() + ")}"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void acquireReadLock() | ||||||
|  |     { | ||||||
|  |         WiredNetwork currentNetwork = network; | ||||||
|  |         while( true ) | ||||||
|  |         { | ||||||
|  |             Lock lock = currentNetwork.lock.readLock(); | ||||||
|  |             lock.lock(); | ||||||
|  |             if( currentNetwork == network ) return; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             lock.unlock(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										506
									
								
								src/test/java/dan200/computercraft/shared/wired/NetworkTest.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										506
									
								
								src/test/java/dan200/computercraft/shared/wired/NetworkTest.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,506 @@ | |||||||
|  | package dan200.computercraft.shared.wired; | ||||||
|  |  | ||||||
|  | import com.google.common.collect.Maps; | ||||||
|  | import com.google.common.collect.Sets; | ||||||
|  | import dan200.computercraft.ComputerCraft; | ||||||
|  | import dan200.computercraft.api.ComputerCraftAPI; | ||||||
|  | import dan200.computercraft.api.lua.ILuaContext; | ||||||
|  | import dan200.computercraft.api.lua.LuaException; | ||||||
|  | import dan200.computercraft.api.network.wired.IWiredElement; | ||||||
|  | import dan200.computercraft.api.network.wired.IWiredNetwork; | ||||||
|  | import dan200.computercraft.api.network.wired.IWiredNetworkChange; | ||||||
|  | import dan200.computercraft.api.network.wired.IWiredNode; | ||||||
|  | import dan200.computercraft.api.peripheral.IComputerAccess; | ||||||
|  | import dan200.computercraft.api.peripheral.IPeripheral; | ||||||
|  | import net.minecraft.util.EnumFacing; | ||||||
|  | import net.minecraft.util.math.BlockPos; | ||||||
|  | import net.minecraft.util.math.Vec3d; | ||||||
|  | import net.minecraft.world.World; | ||||||
|  | import org.apache.logging.log4j.LogManager; | ||||||
|  | import org.junit.Before; | ||||||
|  | import org.junit.Ignore; | ||||||
|  | import org.junit.Test; | ||||||
|  |  | ||||||
|  | import javax.annotation.Nonnull; | ||||||
|  | import javax.annotation.Nullable; | ||||||
|  | import java.util.Collections; | ||||||
|  | import java.util.Map; | ||||||
|  | import java.util.Set; | ||||||
|  | import java.util.function.BiConsumer; | ||||||
|  | import java.util.function.BiFunction; | ||||||
|  |  | ||||||
|  | import static junit.framework.TestCase.assertEquals; | ||||||
|  | import static org.junit.Assert.*; | ||||||
|  |  | ||||||
|  | public class NetworkTest | ||||||
|  | { | ||||||
|  |     @Before | ||||||
|  |     public void setup() | ||||||
|  |     { | ||||||
|  |         ComputerCraft.log = LogManager.getLogger(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void testConnect() | ||||||
|  |     { | ||||||
|  |         NetworkElement | ||||||
|  |             aE = new NetworkElement( null, null, "a" ), | ||||||
|  |             bE = new NetworkElement( null, null, "b" ), | ||||||
|  |             cE = new NetworkElement( null, null, "c" ); | ||||||
|  |  | ||||||
|  |         IWiredNode | ||||||
|  |             aN = aE.getNode(), | ||||||
|  |             bN = bE.getNode(), | ||||||
|  |             cN = cE.getNode(); | ||||||
|  |  | ||||||
|  |         assertNotEquals( "A's and B's network must be different", aN.getNetwork(), bN.getNetwork() ); | ||||||
|  |         assertNotEquals( "A's and C's network must be different", aN.getNetwork(), cN.getNetwork() ); | ||||||
|  |         assertNotEquals( "B's and C's network must be different", bN.getNetwork(), cN.getNetwork() ); | ||||||
|  |  | ||||||
|  |         assertTrue( "Must be able to add connection", aN.getNetwork().connect( aN, bN ) ); | ||||||
|  |         assertFalse( "Cannot add connection twice", aN.getNetwork().connect( aN, bN ) ); | ||||||
|  |  | ||||||
|  |         assertEquals( "A's and B's network must be equal", aN.getNetwork(), bN.getNetwork() ); | ||||||
|  |         assertEquals( "A's network should be A and B", Sets.newHashSet( aN, bN ), nodes( aN.getNetwork() ) ); | ||||||
|  |  | ||||||
|  |         assertEquals( "A's peripheral set should be A, B", Sets.newHashSet( "a", "b" ), aE.allPeripherals().keySet() ); | ||||||
|  |         assertEquals( "B's peripheral set should be A, B", Sets.newHashSet( "a", "b" ), bE.allPeripherals().keySet() ); | ||||||
|  |  | ||||||
|  |         aN.getNetwork().connect( aN, cN ); | ||||||
|  |  | ||||||
|  |         assertEquals( "A's and B's network must be equal", aN.getNetwork(), bN.getNetwork() ); | ||||||
|  |         assertEquals( "A's and C's network must be equal", aN.getNetwork(), cN.getNetwork() ); | ||||||
|  |         assertEquals( "A's network should be A, B and C", Sets.newHashSet( aN, bN, cN ), nodes( aN.getNetwork() ) ); | ||||||
|  |  | ||||||
|  |         assertEquals( "A's neighbour set should be B, C", Sets.newHashSet( bN, cN ), neighbours( aN ) ); | ||||||
|  |         assertEquals( "B's neighbour set should be A", Sets.newHashSet( aN ), neighbours( bN ) ); | ||||||
|  |         assertEquals( "C's neighbour set should be A", Sets.newHashSet( aN ), neighbours( cN ) ); | ||||||
|  |  | ||||||
|  |         assertEquals( "A's peripheral set should be A, B, C", Sets.newHashSet( "a", "b", "c" ), aE.allPeripherals().keySet() ); | ||||||
|  |         assertEquals( "B's peripheral set should be A, B, C", Sets.newHashSet( "a", "b", "c" ), bE.allPeripherals().keySet() ); | ||||||
|  |         assertEquals( "C's peripheral set should be A, B, C", Sets.newHashSet( "a", "b", "c" ), cE.allPeripherals().keySet() ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void testDisconnectNoChange() | ||||||
|  |     { | ||||||
|  |         NetworkElement | ||||||
|  |             aE = new NetworkElement( null, null, "a" ), | ||||||
|  |             bE = new NetworkElement( null, null, "b" ), | ||||||
|  |             cE = new NetworkElement( null, null, "c" ); | ||||||
|  |  | ||||||
|  |         IWiredNode | ||||||
|  |             aN = aE.getNode(), | ||||||
|  |             bN = bE.getNode(), | ||||||
|  |             cN = cE.getNode(); | ||||||
|  |  | ||||||
|  |         aN.getNetwork().connect( aN, bN ); | ||||||
|  |         aN.getNetwork().connect( aN, cN ); | ||||||
|  |         aN.getNetwork().connect( bN, cN ); | ||||||
|  |  | ||||||
|  |         aN.getNetwork().disconnect( aN, bN ); | ||||||
|  |  | ||||||
|  |         assertEquals( "A's and B's network must be equal", aN.getNetwork(), bN.getNetwork() ); | ||||||
|  |         assertEquals( "A's and C's network must be equal", aN.getNetwork(), cN.getNetwork() ); | ||||||
|  |         assertEquals( "A's network should be A, B and C", Sets.newHashSet( aN, bN, cN ), nodes( aN.getNetwork() ) ); | ||||||
|  |  | ||||||
|  |         assertEquals( "A's peripheral set should be A, B, C", Sets.newHashSet( "a", "b", "c" ), aE.allPeripherals().keySet() ); | ||||||
|  |         assertEquals( "B's peripheral set should be A, B, C", Sets.newHashSet( "a", "b", "c" ), bE.allPeripherals().keySet() ); | ||||||
|  |         assertEquals( "C's peripheral set should be A, B, C", Sets.newHashSet( "a", "b", "c" ), cE.allPeripherals().keySet() ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void testDisconnectLeaf() | ||||||
|  |     { | ||||||
|  |         NetworkElement | ||||||
|  |             aE = new NetworkElement( null, null, "a" ), | ||||||
|  |             bE = new NetworkElement( null, null, "b" ), | ||||||
|  |             cE = new NetworkElement( null, null, "c" ); | ||||||
|  |  | ||||||
|  |         IWiredNode | ||||||
|  |             aN = aE.getNode(), | ||||||
|  |             bN = bE.getNode(), | ||||||
|  |             cN = cE.getNode(); | ||||||
|  |  | ||||||
|  |         aN.getNetwork().connect( aN, bN ); | ||||||
|  |         aN.getNetwork().connect( aN, cN ); | ||||||
|  |  | ||||||
|  |         aN.getNetwork().disconnect( aN, bN ); | ||||||
|  |  | ||||||
|  |         assertNotEquals( "A's and B's network must not be equal", aN.getNetwork(), bN.getNetwork() ); | ||||||
|  |         assertEquals( "A's and C's network must be equal", aN.getNetwork(), cN.getNetwork() ); | ||||||
|  |         assertEquals( "A's network should be A and C", Sets.newHashSet( aN, cN ), nodes( aN.getNetwork() ) ); | ||||||
|  |         assertEquals( "B's network should be B", Sets.newHashSet( bN ), nodes( bN.getNetwork() ) ); | ||||||
|  |  | ||||||
|  |         assertEquals( "A's peripheral set should be A, C", Sets.newHashSet( "a", "c" ), aE.allPeripherals().keySet() ); | ||||||
|  |         assertEquals( "B's peripheral set should be B", Sets.newHashSet( "b" ), bE.allPeripherals().keySet() ); | ||||||
|  |         assertEquals( "C's peripheral set should be A, C", Sets.newHashSet( "a", "c" ), cE.allPeripherals().keySet() ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void testDisconnectSplit() | ||||||
|  |     { | ||||||
|  |         NetworkElement | ||||||
|  |             aE = new NetworkElement( null, null, "a" ), | ||||||
|  |             aaE = new NetworkElement( null, null, "a_" ), | ||||||
|  |             bE = new NetworkElement( null, null, "b" ), | ||||||
|  |             bbE = new NetworkElement( null, null, "b_" ); | ||||||
|  |  | ||||||
|  |         IWiredNode | ||||||
|  |             aN = aE.getNode(), | ||||||
|  |             aaN = aaE.getNode(), | ||||||
|  |             bN = bE.getNode(), | ||||||
|  |             bbN = bbE.getNode(); | ||||||
|  |  | ||||||
|  |         aN.getNetwork().connect( aN, aaN ); | ||||||
|  |         bN.getNetwork().connect( bN, bbN ); | ||||||
|  |  | ||||||
|  |         aN.getNetwork().connect( aN, bN ); | ||||||
|  |  | ||||||
|  |         aN.getNetwork().disconnect( aN, bN ); | ||||||
|  |  | ||||||
|  |         assertNotEquals( "A's and B's network must not be equal", aN.getNetwork(), bN.getNetwork() ); | ||||||
|  |         assertEquals( "A's and A_'s network must be equal", aN.getNetwork(), aaN.getNetwork() ); | ||||||
|  |         assertEquals( "B's and B_'s network must be equal", bN.getNetwork(), bbN.getNetwork() ); | ||||||
|  |  | ||||||
|  |         assertEquals( "A's network should be A and A_", Sets.newHashSet( aN, aaN ), nodes( aN.getNetwork() ) ); | ||||||
|  |         assertEquals( "B's network should be B and B_", Sets.newHashSet( bN, bbN ), nodes( bN.getNetwork() ) ); | ||||||
|  |  | ||||||
|  |         assertEquals( "A's peripheral set should be A and A_", Sets.newHashSet( "a", "a_" ), aE.allPeripherals().keySet() ); | ||||||
|  |         assertEquals( "B's peripheral set should be B and B_", Sets.newHashSet( "b", "b_" ), bE.allPeripherals().keySet() ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void testRemoveSingle() | ||||||
|  |     { | ||||||
|  |         NetworkElement aE = new NetworkElement( null, null, "a" ); | ||||||
|  |         IWiredNode aN = aE.getNode(); | ||||||
|  |  | ||||||
|  |         IWiredNetwork network = aN.getNetwork(); | ||||||
|  |         assertFalse( "Cannot remove node from an empty network", aN.remove() ); | ||||||
|  |         assertEquals( "Networks are same before and after", network, aN.getNetwork() ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void testRemoveLeaf() | ||||||
|  |     { | ||||||
|  |         NetworkElement | ||||||
|  |             aE = new NetworkElement( null, null, "a" ), | ||||||
|  |             bE = new NetworkElement( null, null, "b" ), | ||||||
|  |             cE = new NetworkElement( null, null, "c" ); | ||||||
|  |  | ||||||
|  |         IWiredNode | ||||||
|  |             aN = aE.getNode(), | ||||||
|  |             bN = bE.getNode(), | ||||||
|  |             cN = cE.getNode(); | ||||||
|  |  | ||||||
|  |         aN.getNetwork().connect( aN, bN ); | ||||||
|  |         aN.getNetwork().connect( aN, cN ); | ||||||
|  |  | ||||||
|  |         assertTrue( "Must be able to remove node", aN.getNetwork().remove( bN ) ); | ||||||
|  |         assertFalse( "Cannot remove a second time", aN.getNetwork().remove( bN ) ); | ||||||
|  |  | ||||||
|  |         assertNotEquals( "A's and B's network must not be equal", aN.getNetwork(), bN.getNetwork() ); | ||||||
|  |         assertEquals( "A's and C's network must be equal", aN.getNetwork(), cN.getNetwork() ); | ||||||
|  |  | ||||||
|  |         assertEquals( "A's network should be A and C", Sets.newHashSet( aN, cN ), nodes( aN.getNetwork() ) ); | ||||||
|  |         assertEquals( "B's network should be B", Sets.newHashSet( bN ), nodes( bN.getNetwork() ) ); | ||||||
|  |  | ||||||
|  |         assertEquals( "A's peripheral set should be A, C", Sets.newHashSet( "a", "c" ), aE.allPeripherals().keySet() ); | ||||||
|  |         assertEquals( "B's peripheral set should be empty", Sets.newHashSet(), bE.allPeripherals().keySet() ); | ||||||
|  |         assertEquals( "C's peripheral set should be A, C", Sets.newHashSet( "a", "c" ), cE.allPeripherals().keySet() ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void testRemoveSplit() | ||||||
|  |     { | ||||||
|  |         NetworkElement | ||||||
|  |             aE = new NetworkElement( null, null, "a" ), | ||||||
|  |             aaE = new NetworkElement( null, null, "a_" ), | ||||||
|  |             bE = new NetworkElement( null, null, "b" ), | ||||||
|  |             bbE = new NetworkElement( null, null, "b_" ), | ||||||
|  |             cE = new NetworkElement( null, null, "c" ); | ||||||
|  |  | ||||||
|  |         IWiredNode | ||||||
|  |             aN = aE.getNode(), | ||||||
|  |             aaN = aaE.getNode(), | ||||||
|  |             bN = bE.getNode(), | ||||||
|  |             bbN = bbE.getNode(), | ||||||
|  |             cN = cE.getNode(); | ||||||
|  |  | ||||||
|  |         aN.getNetwork().connect( aN, aaN ); | ||||||
|  |         bN.getNetwork().connect( bN, bbN ); | ||||||
|  |  | ||||||
|  |         cN.getNetwork().connect( aN, cN ); | ||||||
|  |         cN.getNetwork().connect( bN, cN ); | ||||||
|  |  | ||||||
|  |         cN.getNetwork().remove( cN ); | ||||||
|  |  | ||||||
|  |         assertNotEquals( "A's and B's network must not be equal", aN.getNetwork(), bN.getNetwork() ); | ||||||
|  |         assertEquals( "A's and A_'s network must be equal", aN.getNetwork(), aaN.getNetwork() ); | ||||||
|  |         assertEquals( "B's and B_'s network must be equal", bN.getNetwork(), bbN.getNetwork() ); | ||||||
|  |  | ||||||
|  |         assertEquals( "A's network should be A and A_", Sets.newHashSet( aN, aaN ), nodes( aN.getNetwork() ) ); | ||||||
|  |         assertEquals( "B's network should be B and B_", Sets.newHashSet( bN, bbN ), nodes( bN.getNetwork() ) ); | ||||||
|  |         assertEquals( "C's network should be C", Sets.newHashSet( cN ), nodes( cN.getNetwork() ) ); | ||||||
|  |  | ||||||
|  |         assertEquals( "A's peripheral set should be A and A_", Sets.newHashSet( "a", "a_" ), aE.allPeripherals().keySet() ); | ||||||
|  |         assertEquals( "B's peripheral set should be B and B_", Sets.newHashSet( "b", "b_" ), bE.allPeripherals().keySet() ); | ||||||
|  |         assertEquals( "C's peripheral set should be empty", Sets.newHashSet(), cE.allPeripherals().keySet() ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @Ignore("Takes a long time to run, mostly for stress testing") | ||||||
|  |     public void testLarge() | ||||||
|  |     { | ||||||
|  |         final int BRUTE_SIZE = 16; | ||||||
|  |         final int TOGGLE_CONNECTION_TIMES = 5; | ||||||
|  |         final int TOGGLE_NODE_TIMES = 5; | ||||||
|  |  | ||||||
|  |         Grid<IWiredNode> grid = new Grid<>( BRUTE_SIZE ); | ||||||
|  |         grid.map( ( existing, pos ) -> new NetworkElement( null, null, "n_" + pos ).getNode() ); | ||||||
|  |  | ||||||
|  |         // Test connecting | ||||||
|  |         { | ||||||
|  |             long start = System.nanoTime(); | ||||||
|  |  | ||||||
|  |             grid.forEach( ( existing, pos ) -> { | ||||||
|  |                 for( EnumFacing facing : EnumFacing.VALUES ) | ||||||
|  |                 { | ||||||
|  |                     BlockPos offset = pos.offset( facing ); | ||||||
|  |                     if( (offset.getX() > BRUTE_SIZE / 2) == (pos.getX() > BRUTE_SIZE / 2) ) | ||||||
|  |                     { | ||||||
|  |                         IWiredNode other = grid.get( offset ); | ||||||
|  |                         if( other != null ) existing.getNetwork().connect( existing, other ); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } ); | ||||||
|  |  | ||||||
|  |             long end = System.nanoTime(); | ||||||
|  |  | ||||||
|  |             System.out.printf( "Connecting %s³ nodes took %s seconds\n", BRUTE_SIZE, (end - start) * 1e-9 ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Test toggling | ||||||
|  |         { | ||||||
|  |             IWiredNode left = grid.get( new BlockPos( BRUTE_SIZE / 2, 0, 0 ) ); | ||||||
|  |             IWiredNode right = grid.get( new BlockPos( BRUTE_SIZE / 2 + 1, 0, 0 ) ); | ||||||
|  |             assertNotEquals( left.getNetwork(), right.getNetwork() ); | ||||||
|  |  | ||||||
|  |             long start = System.nanoTime(); | ||||||
|  |             for( int i = 0; i < TOGGLE_CONNECTION_TIMES; i++ ) | ||||||
|  |             { | ||||||
|  |                 left.getNetwork().connect( left, right ); | ||||||
|  |                 left.getNetwork().disconnect( left, right ); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             long end = System.nanoTime(); | ||||||
|  |  | ||||||
|  |             System.out.printf( "Toggling connection %s times took %s seconds\n", TOGGLE_CONNECTION_TIMES, (end - start) * 1e-9 ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         { | ||||||
|  |             IWiredNode left = grid.get( new BlockPos( BRUTE_SIZE / 2, 0, 0 ) ); | ||||||
|  |             IWiredNode right = grid.get( new BlockPos( BRUTE_SIZE / 2 + 1, 0, 0 ) ); | ||||||
|  |             IWiredNode centre = new NetworkElement( null, null, "c" ).getNode(); | ||||||
|  |             assertNotEquals( left.getNetwork(), right.getNetwork() ); | ||||||
|  |  | ||||||
|  |             long start = System.nanoTime(); | ||||||
|  |             for( int i = 0; i < TOGGLE_NODE_TIMES; i++ ) | ||||||
|  |             { | ||||||
|  |                 left.getNetwork().connect( left, centre ); | ||||||
|  |                 right.getNetwork().connect( right, centre ); | ||||||
|  |  | ||||||
|  |                 left.getNetwork().remove( centre ); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             long end = System.nanoTime(); | ||||||
|  |  | ||||||
|  |             System.out.printf( "Toggling node %s times took %s seconds\n", TOGGLE_NODE_TIMES, (end - start) * 1e-9 ); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static class NetworkElement implements IWiredElement | ||||||
|  |     { | ||||||
|  |         private final World world; | ||||||
|  |         private final Vec3d position; | ||||||
|  |         private final String id; | ||||||
|  |         private final IWiredNode node; | ||||||
|  |         private final Map<String, IPeripheral> localPeripherals = Maps.newHashMap(); | ||||||
|  |         private final Map<String, IPeripheral> remotePeripherals = Maps.newHashMap(); | ||||||
|  |  | ||||||
|  |         private NetworkElement( World world, Vec3d position, String id ) | ||||||
|  |         { | ||||||
|  |             this.world = world; | ||||||
|  |             this.position = position; | ||||||
|  |             this.id = id; | ||||||
|  |             this.node = ComputerCraftAPI.createWiredNodeForElement( this ); | ||||||
|  |             this.addPeripheral( id ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Nonnull | ||||||
|  |         @Override | ||||||
|  |         public World getWorld() | ||||||
|  |         { | ||||||
|  |             return world; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Nonnull | ||||||
|  |         @Override | ||||||
|  |         public Vec3d getPosition() | ||||||
|  |         { | ||||||
|  |             return position; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Nonnull | ||||||
|  |         @Override | ||||||
|  |         public String getSenderID() | ||||||
|  |         { | ||||||
|  |             return id; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Override | ||||||
|  |         public String toString() | ||||||
|  |         { | ||||||
|  |             return "NetworkElement{" + id + "}"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Nonnull | ||||||
|  |         @Override | ||||||
|  |         public IWiredNode getNode() | ||||||
|  |         { | ||||||
|  |             return node; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Override | ||||||
|  |         public void networkChanged( @Nonnull IWiredNetworkChange change ) | ||||||
|  |         { | ||||||
|  |             remotePeripherals.keySet().removeAll( change.peripheralsRemoved().keySet() ); | ||||||
|  |             remotePeripherals.putAll( change.peripheralsAdded() ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Nonnull | ||||||
|  |         @Override | ||||||
|  |         public Map<String, IPeripheral> getPeripherals() | ||||||
|  |         { | ||||||
|  |             return Collections.unmodifiableMap( localPeripherals ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public NetworkElement addPeripheral( String name ) | ||||||
|  |         { | ||||||
|  |             localPeripherals.put( name, new NetworkPeripheral() ); | ||||||
|  |             getNode().invalidate(); | ||||||
|  |             return this; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Nonnull | ||||||
|  |         public Map<String, IPeripheral> allPeripherals() | ||||||
|  |         { | ||||||
|  |             return remotePeripherals; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static class NetworkPeripheral implements IPeripheral | ||||||
|  |     { | ||||||
|  |         @Nonnull | ||||||
|  |         @Override | ||||||
|  |         public String getType() | ||||||
|  |         { | ||||||
|  |             return "test"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Nonnull | ||||||
|  |         @Override | ||||||
|  |         public String[] getMethodNames() | ||||||
|  |         { | ||||||
|  |             return new String[0]; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Nullable | ||||||
|  |         @Override | ||||||
|  |         public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException | ||||||
|  |         { | ||||||
|  |             return new Object[0]; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Override | ||||||
|  |         public boolean equals( @Nullable IPeripheral other ) | ||||||
|  |         { | ||||||
|  |             return this == other; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static class Grid<T> | ||||||
|  |     { | ||||||
|  |         private final int size; | ||||||
|  |         private final T[] box; | ||||||
|  |  | ||||||
|  |         @SuppressWarnings("unchecked") | ||||||
|  |         public Grid( int size ) | ||||||
|  |         { | ||||||
|  |             this.size = size; | ||||||
|  |             this.box = (T[]) new Object[size * size * size]; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void set( BlockPos pos, T elem ) | ||||||
|  |         { | ||||||
|  |             int x = pos.getX(), y = pos.getY(), z = pos.getZ(); | ||||||
|  |  | ||||||
|  |             if( x >= 0 && x < size && y >= 0 && y < size && z >= 0 && z < size ) | ||||||
|  |             { | ||||||
|  |                 box[x * size * size + y * size + z] = elem; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 throw new IndexOutOfBoundsException( pos.toString() ); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public T get( BlockPos pos ) | ||||||
|  |         { | ||||||
|  |             int x = pos.getX(), y = pos.getY(), z = pos.getZ(); | ||||||
|  |  | ||||||
|  |             return x >= 0 && x < size && y >= 0 && y < size && z >= 0 && z < size | ||||||
|  |                 ? box[x * size * size + y * size + z] | ||||||
|  |                 : null; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void forEach( BiConsumer<T, BlockPos> transform ) | ||||||
|  |         { | ||||||
|  |             for( int x = 0; x < size; x++ ) | ||||||
|  |             { | ||||||
|  |                 for( int y = 0; y < size; y++ ) | ||||||
|  |                 { | ||||||
|  |                     for( int z = 0; z < size; z++ ) | ||||||
|  |                     { | ||||||
|  |                         transform.accept( box[x * size * size + y * size + z], new BlockPos( x, y, z ) ); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void map( BiFunction<T, BlockPos, T> transform ) | ||||||
|  |         { | ||||||
|  |             for( int x = 0; x < size; x++ ) | ||||||
|  |             { | ||||||
|  |                 for( int y = 0; y < size; y++ ) | ||||||
|  |                 { | ||||||
|  |                     for( int z = 0; z < size; z++ ) | ||||||
|  |                     { | ||||||
|  |                         box[x * size * size + y * size + z] = transform.apply( box[x * size * size + y * size + z], new BlockPos( x, y, z ) ); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static Set<WiredNode> nodes( IWiredNetwork network ) | ||||||
|  |     { | ||||||
|  |         return ((WiredNetwork) network).nodes; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static Set<WiredNode> neighbours( IWiredNode node ) | ||||||
|  |     { | ||||||
|  |         return ((WiredNode) node).neighbours; | ||||||
|  |     } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 SquidDev
					SquidDev