From 6cf32f1f74f8769e35dcb47ca15a751cf5600172 Mon Sep 17 00:00:00 2001 From: SquidDev Date: Mon, 16 Apr 2018 18:22:28 +0100 Subject: [PATCH] Various improvements to peripheral invalidation - Abstract peripheral ID and type checking into separate class - Update peripherals directly rather than marking as invalid then fetching from the network. - Update peripherals when adjacent tiles change This does result in a slightly more ugly interface, but reduces the amount of work needed to perform partial updates of peripherals, such as those done by neighbouring tile updates. --- .../api/network/wired/IWiredElement.java | 18 -- .../api/network/wired/IWiredNetwork.java | 18 +- .../api/network/wired/IWiredNode.java | 15 +- .../shared/peripheral/modem/TileCable.java | 127 ++++++-------- .../peripheral/modem/TileWiredModemFull.java | 155 +++++++++--------- .../modem/WiredModemLocalPeripheral.java | 139 ++++++++++++++++ .../shared/wired/WiredNetwork.java | 8 +- .../shared/wired/NetworkTest.java | 10 +- 8 files changed, 289 insertions(+), 201 deletions(-) create mode 100644 src/main/java/dan200/computercraft/shared/peripheral/modem/WiredModemLocalPeripheral.java diff --git a/src/main/java/dan200/computercraft/api/network/wired/IWiredElement.java b/src/main/java/dan200/computercraft/api/network/wired/IWiredElement.java index 2796cd5d8..8fdfa7655 100644 --- a/src/main/java/dan200/computercraft/api/network/wired/IWiredElement.java +++ b/src/main/java/dan200/computercraft/api/network/wired/IWiredElement.java @@ -1,11 +1,8 @@ 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. @@ -19,21 +16,6 @@ import java.util.Map; */ 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. diff --git a/src/main/java/dan200/computercraft/api/network/wired/IWiredNetwork.java b/src/main/java/dan200/computercraft/api/network/wired/IWiredNetwork.java index 3ab387e6c..2e9fa1176 100644 --- a/src/main/java/dan200/computercraft/api/network/wired/IWiredNetwork.java +++ b/src/main/java/dan200/computercraft/api/network/wired/IWiredNetwork.java @@ -1,6 +1,9 @@ package dan200.computercraft.api.network.wired; +import dan200.computercraft.api.peripheral.IPeripheral; + import javax.annotation.Nonnull; +import java.util.Map; /** * A wired network is composed of one of more {@link IWiredNode}s, a set of connections between them, and a series @@ -51,7 +54,8 @@ public interface IWiredNetwork /** * Sever all connections this node has, removing it from this network. * - * This should only be used on the server thread. + * This should only be used on the server thread. You should only call this on nodes + * that your network element owns. * * @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 @@ -62,13 +66,15 @@ public interface IWiredNetwork boolean remove( @Nonnull IWiredNode node ); /** - * Mark this node's peripherals as having changed. + * Update the peripherals a node provides. * - * This should only be used on the server thread. + * This should only be used on the server thread. You should only call this on nodes + * that your network element owns. * - * @param node The node to mark as invalid. + * @param node The node to attach peripherals for. + * @param peripherals The new peripherals for this node. * @throws IllegalArgumentException If the node is not in the network. - * @see IWiredElement#getPeripherals() + * @see IWiredNode#updatePeripherals(Map) */ - void invalidate( @Nonnull IWiredNode node ); + void updatePeripherals( @Nonnull IWiredNode node, @Nonnull Map peripherals ); } diff --git a/src/main/java/dan200/computercraft/api/network/wired/IWiredNode.java b/src/main/java/dan200/computercraft/api/network/wired/IWiredNode.java index 076c7bc03..b8b0097c7 100644 --- a/src/main/java/dan200/computercraft/api/network/wired/IWiredNode.java +++ b/src/main/java/dan200/computercraft/api/network/wired/IWiredNode.java @@ -1,8 +1,10 @@ package dan200.computercraft.api.network.wired; import dan200.computercraft.api.network.IPacketNetwork; +import dan200.computercraft.api.peripheral.IPeripheral; import javax.annotation.Nonnull; +import java.util.Map; /** * Wired nodes act as a layer between {@link IWiredElement}s and {@link IWiredNetwork}s. @@ -72,7 +74,8 @@ public interface IWiredNode extends IPacketNetwork /** * Sever all connections this node has, removing it from this network. * - * This should only be used on the server thread. + * This should only be used on the server thread. You should only call this on nodes + * that your network element owns. * * @return Whether this node was removed from the network. One cannot remove a node from a network where it is the * only element. @@ -87,12 +90,14 @@ public interface IWiredNode extends IPacketNetwork /** * Mark this node's peripherals as having changed. * - * This should only be used on the server thread. + * This should only be used on the server thread. You should only call this on nodes + * that your network element owns. * - * @see IWiredElement#getPeripherals() + * @param peripherals The new peripherals for this node. + * @see IWiredNetwork#updatePeripherals(IWiredNode, Map) */ - default void invalidate() + default void updatePeripherals( @Nonnull Map peripherals ) { - getNetwork().invalidate( this ); + getNetwork().updatePeripherals( this, peripherals ); } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/TileCable.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/TileCable.java index 84b98b507..8860a33c6 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/TileCable.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/TileCable.java @@ -16,10 +16,7 @@ import dan200.computercraft.shared.peripheral.PeripheralType; import dan200.computercraft.shared.peripheral.common.BlockCable; import dan200.computercraft.shared.peripheral.common.BlockCableModemVariant; import dan200.computercraft.shared.peripheral.common.PeripheralItemFactory; -import dan200.computercraft.shared.util.IDAssigner; -import dan200.computercraft.shared.util.PeripheralUtil; import dan200.computercraft.shared.wired.CapabilityWiredElement; -import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; @@ -35,7 +32,6 @@ import net.minecraftforge.common.capabilities.Capability; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.io.File; import java.util.Collections; import java.util.List; import java.util.Map; @@ -79,16 +75,6 @@ public class TileCable extends TileModemBase return new Vec3d( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 ); } - @Nonnull - @Override - public Map getPeripherals() - { - IPeripheral peripheral = m_entity.getConnectedPeripheral(); - return peripheral != null - ? Collections.singletonMap( m_entity.getConnectedPeripheralName(), peripheral ) - : Collections.emptyMap(); - } - @Override protected void attachPeripheral( String name, IPeripheral peripheral ) { @@ -105,9 +91,9 @@ public class TileCable extends TileModemBase // Members private boolean m_peripheralAccessAllowed; - private int m_attachedPeripheralID; + private WiredModemLocalPeripheral m_peripheral = new WiredModemLocalPeripheral(); - private boolean m_destroyed; + private boolean m_destroyed = false; private boolean m_hasDirection = false; private boolean m_connectionsFormed = false; @@ -115,14 +101,6 @@ public class TileCable extends TileModemBase private WiredModemElement m_cable; private IWiredNode m_node; - public TileCable() - { - m_peripheralAccessAllowed = false; - m_attachedPeripheralID = -1; - - m_destroyed = false; - } - @Override protected ModemPeripheral createPeripheral() { @@ -133,7 +111,7 @@ public class TileCable extends TileModemBase @Override protected boolean canSeePeripheral( @Nonnull String peripheralName ) { - return !peripheralName.equals( getConnectedPeripheralName() ); + return !peripheralName.equals( m_peripheral.getConnectedName() ); } @Nonnull @@ -294,10 +272,29 @@ public class TileCable extends TileModemBase modemChanged(); connectionsChanged(); - break; + return; } } } + + if( !world.isRemote && m_peripheralAccessAllowed ) + { + if( m_peripheral.attach( world, getPos(), dir ) ) updateConnectedPeripherals(); + } + } + + @Override + public void onNeighbourTileEntityChange( @Nonnull BlockPos neighbour ) + { + super.onNeighbourTileEntityChange( neighbour ); + if( !world.isRemote && m_peripheralAccessAllowed ) + { + EnumFacing facing = getDirection(); + if( getPos().offset( facing ).equals( neighbour ) ) + { + if( m_peripheral.attach( world, getPos(), facing ) ) updateConnectedPeripherals(); + } + } } public AxisAlignedBB getModemBounds() @@ -400,9 +397,9 @@ public class TileCable extends TileModemBase if( !getWorld().isRemote ) { // On server, we interacted if a peripheral was found - String oldPeriphName = getConnectedPeripheralName(); + String oldPeriphName = m_peripheral.getConnectedName(); togglePeripheralAccess(); - String periphName = getConnectedPeripheralName(); + String periphName = m_peripheral.getConnectedName(); if( !Objects.equal( periphName, oldPeriphName ) ) { @@ -437,7 +434,7 @@ public class TileCable extends TileModemBase // Read properties super.readFromNBT( nbttagcompound ); m_peripheralAccessAllowed = nbttagcompound.getBoolean( "peripheralAccess" ); - m_attachedPeripheralID = nbttagcompound.getInteger( "peripheralID" ); + m_peripheral.readNBT( nbttagcompound, "" ); } @Nonnull @@ -447,7 +444,7 @@ public class TileCable extends TileModemBase // Write properties nbttagcompound = super.writeToNBT( nbttagcompound ); nbttagcompound.setBoolean( "peripheralAccess", m_peripheralAccessAllowed ); - nbttagcompound.setInteger( "peripheralID", m_attachedPeripheralID ); + m_peripheral.writeNBT( nbttagcompound, "" ); return nbttagcompound; } @@ -476,8 +473,13 @@ public class TileCable extends TileModemBase if( !m_connectionsFormed ) { m_connectionsFormed = true; + connectionsChanged(); - if( m_peripheralAccessAllowed ) m_node.invalidate(); + if( m_peripheralAccessAllowed ) + { + m_peripheral.attach( world, pos, m_dir ); + updateConnectedPeripherals(); + } } } } @@ -515,15 +517,11 @@ public class TileCable extends TileModemBase if( getWorld().isRemote ) return; PeripheralType type = getPeripheralType(); - if( type == PeripheralType.Cable ) - { - m_attachedPeripheralID = -1; - } - if( type != PeripheralType.WiredModemWithCable && m_peripheralAccessAllowed ) { m_peripheralAccessAllowed = false; - m_node.invalidate(); + m_peripheral.detach(); + m_node.updatePeripherals( Collections.emptyMap() ); markDirty(); updateAnim(); } @@ -534,61 +532,34 @@ public class TileCable extends TileModemBase { if( !m_peripheralAccessAllowed ) { + m_peripheral.attach( world, getPos(), getDirection() ); + if( !m_peripheral.hasPeripheral() ) return; + m_peripheralAccessAllowed = true; - if( getConnectedPeripheral() == null ) - { - m_peripheralAccessAllowed = false; - return; - } + m_node.updatePeripherals( m_peripheral.toMap() ); } else { + m_peripheral.detach(); + m_peripheralAccessAllowed = false; + m_node.updatePeripherals( Collections.emptyMap() ); } updateAnim(); - m_node.invalidate(); } - private String getConnectedPeripheralName() + private void updateConnectedPeripherals() { - IPeripheral periph = getConnectedPeripheral(); - if( periph != null ) + Map peripherals = m_peripheral.toMap(); + if( peripherals.isEmpty() ) { - String type = periph.getType(); - if( m_attachedPeripheralID < 0 ) - { - m_attachedPeripheralID = IDAssigner.getNextIDFromFile( new File( - ComputerCraft.getWorldDir( getWorld() ), - "computer/lastid_" + type + ".txt" - ) ); - } - return type + "_" + m_attachedPeripheralID; + // If there are no peripherals then disable access and update the display state. + m_peripheralAccessAllowed = false; + updateAnim(); } - return null; - } - private IPeripheral getConnectedPeripheral() - { - if( m_peripheralAccessAllowed ) - { - if( getPeripheralType() == PeripheralType.WiredModemWithCable ) - { - EnumFacing facing = getDirection(); - BlockPos neighbour = getPos().offset( facing ); - IPeripheral peripheral = getPeripheral( getWorld(), neighbour, facing.getOpposite() ); - return peripheral == null || peripheral instanceof WiredModemPeripheral ? null : peripheral; - } - } - return null; - } - - public static IPeripheral getPeripheral( World world, BlockPos pos, EnumFacing facing ) - { - Block block = world.getBlockState( pos ).getBlock(); - if( block == ComputerCraft.Blocks.wiredModemFull || block == ComputerCraft.Blocks.cable ) return null; - - return PeripheralUtil.getPeripheral( world, pos, facing ); + m_node.updatePeripherals( peripherals ); } @Override diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/TileWiredModemFull.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/TileWiredModemFull.java index eb03f0f9d..84d205fdc 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/TileWiredModemFull.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/TileWiredModemFull.java @@ -13,7 +13,6 @@ import dan200.computercraft.api.network.wired.IWiredNode; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.shared.peripheral.common.BlockCable; import dan200.computercraft.shared.peripheral.common.TilePeripheralBase; -import dan200.computercraft.shared.util.IDAssigner; import dan200.computercraft.shared.wired.CapabilityWiredElement; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; @@ -24,11 +23,9 @@ import net.minecraft.util.math.Vec3d; import net.minecraft.util.text.TextComponentTranslation; import net.minecraft.world.World; import net.minecraftforge.common.capabilities.Capability; -import net.minecraftforge.common.util.Constants; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.io.File; import java.util.*; public class TileWiredModemFull extends TilePeripheralBase @@ -76,20 +73,12 @@ public class TileWiredModemFull extends TilePeripheralBase BlockPos pos = m_entity.getPos(); return new Vec3d( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 ); } - - @Nonnull - @Override - public Map getPeripherals() - { - return m_entity.getPeripherals(); - } } private WiredModemPeripheral[] m_modems = new WiredModemPeripheral[6]; private boolean m_peripheralAccessAllowed = false; - private int[] m_attachedPeripheralIDs = new int[6]; - private String[] m_attachedPeripheralTypes = new String[6]; + private WiredModemLocalPeripheral[] m_peripherals = new WiredModemLocalPeripheral[6]; private boolean m_destroyed = false; private boolean m_connectionsFormed = false; @@ -99,7 +88,7 @@ public class TileWiredModemFull extends TilePeripheralBase public TileWiredModemFull() { - Arrays.fill( m_attachedPeripheralIDs, -1 ); + for( int i = 0; i < m_peripherals.length; i++ ) m_peripherals[i] = new WiredModemLocalPeripheral(); } private void remove() @@ -152,18 +141,29 @@ public class TileWiredModemFull extends TilePeripheralBase { if( !world.isRemote && m_peripheralAccessAllowed ) { - Map updated = getPeripherals(); - - if( updated.isEmpty() ) + boolean hasChanged = false; + for( EnumFacing facing : EnumFacing.VALUES ) { - // If there are no peripherals then disable access and update the display state. - m_peripheralAccessAllowed = false; - updateAnim(); + hasChanged |= m_peripherals[facing.ordinal()].attach( world, getPos(), facing ); } - // Always invalidate the node: it's more accurate than checking if the peripherals - // have changed - m_node.invalidate(); + if( hasChanged ) updateConnectedPeripherals(); + } + } + + @Override + public void onNeighbourTileEntityChange( @Nonnull BlockPos neighbour ) + { + if( !world.isRemote && m_peripheralAccessAllowed ) + { + for( EnumFacing facing : EnumFacing.VALUES ) + { + if( getPos().offset( facing ).equals( neighbour ) ) + { + WiredModemLocalPeripheral peripheral = m_peripherals[facing.ordinal()]; + if( peripheral.attach( world, getPos(), facing ) ) updateConnectedPeripherals(); + } + } } } @@ -180,9 +180,9 @@ public class TileWiredModemFull extends TilePeripheralBase if( !getWorld().isRemote ) { // On server, we interacted if a peripheral was found - Set oldPeriphName = getPeripherals().keySet(); + Set oldPeriphName = getConnectedPeripheralNames(); togglePeripheralAccess(); - Set periphName = getPeripherals().keySet(); + Set periphName = getConnectedPeripheralNames(); if( !Objects.equal( periphName, oldPeriphName ) ) { @@ -220,17 +220,7 @@ public class TileWiredModemFull extends TilePeripheralBase { super.readFromNBT( tag ); m_peripheralAccessAllowed = tag.getBoolean( "peripheralAccess" ); - for( int i = 0; i < m_attachedPeripheralIDs.length; i++ ) - { - if( tag.hasKey( "peripheralID_" + i, Constants.NBT.TAG_ANY_NUMERIC ) ) - { - m_attachedPeripheralIDs[i] = tag.getInteger( "peripheralID_" + i ); - } - if( tag.hasKey( "peripheralType_" + i, Constants.NBT.TAG_STRING ) ) - { - m_attachedPeripheralTypes[i] = tag.getString( "peripheralType_" + i ); - } - } + for( int i = 0; i < m_peripherals.length; i++ ) m_peripherals[i].readNBT( tag, "_" + i ); } @Nonnull @@ -239,17 +229,7 @@ public class TileWiredModemFull extends TilePeripheralBase { tag = super.writeToNBT( tag ); tag.setBoolean( "peripheralAccess", m_peripheralAccessAllowed ); - for( int i = 0; i < m_attachedPeripheralIDs.length; i++ ) - { - if( m_attachedPeripheralIDs[i] >= 0 ) - { - tag.setInteger( "peripheralID_" + i, m_attachedPeripheralIDs[i] ); - } - if( m_attachedPeripheralTypes[i] != null ) - { - tag.setString( "peripheralType_" + i, m_attachedPeripheralTypes[i] ); - } - } + for( int i = 0; i < m_peripherals.length; i++ ) m_peripherals[i].writeNBT( tag, "_" + i ); return tag; } @@ -294,8 +274,16 @@ public class TileWiredModemFull extends TilePeripheralBase if( !m_connectionsFormed ) { m_connectionsFormed = true; + connectionsChanged(); - if( m_peripheralAccessAllowed ) m_node.invalidate(); + if( m_peripheralAccessAllowed ) + { + for( EnumFacing facing : EnumFacing.VALUES ) + { + m_peripherals[facing.ordinal()].attach( world, getPos(), facing ); + } + updateConnectedPeripherals(); + } } } @@ -326,60 +314,63 @@ public class TileWiredModemFull extends TilePeripheralBase { if( !m_peripheralAccessAllowed ) { - m_peripheralAccessAllowed = true; - if( getPeripherals().isEmpty() ) + boolean hasAny = false; + for( EnumFacing facing : EnumFacing.VALUES ) { - m_peripheralAccessAllowed = false; - return; + WiredModemLocalPeripheral peripheral = m_peripherals[facing.ordinal()]; + peripheral.attach( world, getPos(), facing ); + hasAny |= peripheral.hasPeripheral(); } + + if( !hasAny ) return; + + m_peripheralAccessAllowed = true; + m_node.updatePeripherals( getConnectedPeripherals() ); } else { m_peripheralAccessAllowed = false; + + for( WiredModemLocalPeripheral peripheral : m_peripherals ) peripheral.detach(); + m_node.updatePeripherals( Collections.emptyMap() ); } updateAnim(); - m_node.invalidate(); } - @Nonnull - private Map getPeripherals() + private Set getConnectedPeripheralNames() + { + if( !m_peripheralAccessAllowed ) return Collections.emptySet(); + + Set peripherals = new HashSet<>( 6 ); + for( WiredModemLocalPeripheral m_peripheral : m_peripherals ) + { + String name = m_peripheral.getConnectedName(); + if( name != null ) peripherals.add( name ); + } + return peripherals; + } + + private Map getConnectedPeripherals() { if( !m_peripheralAccessAllowed ) return Collections.emptyMap(); Map peripherals = new HashMap<>( 6 ); - for( EnumFacing facing : EnumFacing.VALUES ) - { - BlockPos neighbour = getPos().offset( facing ); - IPeripheral peripheral = TileCable.getPeripheral( getWorld(), neighbour, facing.getOpposite() ); - if( peripheral != null && !(peripheral instanceof WiredModemPeripheral) ) - { - String type = peripheral.getType(); - int id = m_attachedPeripheralIDs[facing.ordinal()]; - String oldType = m_attachedPeripheralTypes[facing.ordinal()]; - if( id < 0 || !type.equals( oldType ) ) - { - m_attachedPeripheralTypes[facing.ordinal()] = type; - id = m_attachedPeripheralIDs[facing.ordinal()] = IDAssigner.getNextIDFromFile( new File( - ComputerCraft.getWorldDir( getWorld() ), - "computer/lastid_" + type + ".txt" - ) ); - } - - peripherals.put( type + "_" + id, peripheral ); - } - } - + for( WiredModemLocalPeripheral m_peripheral : m_peripherals ) m_peripheral.extendMap( peripherals ); return peripherals; } - private String getCachedPeripheralName( EnumFacing facing ) + private void updateConnectedPeripherals() { - if( !m_peripheralAccessAllowed ) return null; + Map peripherals = getConnectedPeripherals(); + if( peripherals.isEmpty() ) + { + // If there are no peripherals then disable access and update the display state. + m_peripheralAccessAllowed = false; + updateAnim(); + } - int id = m_attachedPeripheralIDs[facing.ordinal()]; - String type = m_attachedPeripheralTypes[facing.ordinal()]; - return id < 0 || type == null ? null : type + "_" + id; + m_node.updatePeripherals( peripherals ); } // IWiredElementTile @@ -415,7 +406,7 @@ public class TileWiredModemFull extends TilePeripheralBase @Override protected boolean canSeePeripheral( @Nonnull String peripheralName ) { - return !peripheralName.equals( getCachedPeripheralName( side ) ); + return !peripheralName.equals( m_peripherals[side.ordinal()].getConnectedName() ); } @Nonnull diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/WiredModemLocalPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/WiredModemLocalPeripheral.java new file mode 100644 index 000000000..60e1256cd --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/WiredModemLocalPeripheral.java @@ -0,0 +1,139 @@ +package dan200.computercraft.shared.peripheral.modem; + +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.shared.util.IDAssigner; +import dan200.computercraft.shared.util.PeripheralUtil; +import net.minecraft.block.Block; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.common.util.Constants; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.io.File; +import java.util.Collections; +import java.util.Map; + +/** + * Represents a local peripheral exposed on the wired network + * + * This is responsible for getting the peripheral in world, tracking id and type and determining whether + * it has changed. + */ +public final class WiredModemLocalPeripheral +{ + private int id; + private String type; + + private IPeripheral peripheral; + + /** + * Attach a new peripheral from the world + * + * @param world The world to search in + * @param origin The position to search from + * @param direction The direction so search in + * @return Whether the peripheral changed. + */ + public boolean attach( @Nonnull World world, @Nonnull BlockPos origin, @Nonnull EnumFacing direction ) + { + IPeripheral oldPeripheral = this.peripheral; + IPeripheral peripheral = this.peripheral = getPeripheralFrom( world, origin, direction ); + + if( peripheral == null ) + { + return oldPeripheral != null; + } + else + { + String type = peripheral.getType(); + int id = this.id; + + if( id > 0 && this.type == null ) + { + // If we had an ID but no type, then just set the type. + this.type = type; + } + else if( id < 0 || !type.equals( this.type ) ) + { + this.type = type; + this.id = IDAssigner.getNextIDFromFile( new File( + ComputerCraft.getWorldDir( world ), + "computer/lastid_" + type + ".txt" + ) ); + } + + return oldPeripheral == null || !oldPeripheral.equals( peripheral ); + } + } + + /** + * Detach the current peripheral + * + * @return Whether the peripheral changed + */ + public boolean detach() + { + if( peripheral == null ) return false; + peripheral = null; + return true; + } + + @Nullable + public String getConnectedName() + { + return peripheral != null ? type + "_" + id : null; + } + + @Nullable + public IPeripheral getPeripheral() + { + return peripheral; + } + + public boolean hasPeripheral() + { + return peripheral != null; + } + + public void extendMap( @Nonnull Map peripherals ) + { + if( peripheral != null ) peripherals.put( type + "_" + id, peripheral ); + } + + public Map toMap() + { + return peripheral == null + ? Collections.emptyMap() + : Collections.singletonMap( type + "_" + id, peripheral ); + } + + public void writeNBT( @Nonnull NBTTagCompound tag, @Nonnull String suffix ) + { + if( id >= 0 ) tag.setInteger( "peripheralID" + suffix, id ); + if( type != null ) tag.setString( "peripheralType" + suffix, type ); + } + + public void readNBT( @Nonnull NBTTagCompound tag, @Nonnull String suffix ) + { + id = tag.hasKey( "peripheralID" + suffix, Constants.NBT.TAG_ANY_NUMERIC ) + ? tag.getInteger( "peripheralID" + suffix ) : -1; + + type = tag.hasKey( "peripheralType" + suffix, Constants.NBT.TAG_STRING ) + ? tag.getString( "peripheralType" + suffix ) : null; + } + + private static IPeripheral getPeripheralFrom( World world, BlockPos pos, EnumFacing direction ) + { + BlockPos offset = pos.offset( direction ); + + Block block = world.getBlockState( offset ).getBlock(); + if( block == ComputerCraft.Blocks.wiredModemFull || block == ComputerCraft.Blocks.cable ) return null; + + IPeripheral peripheral = PeripheralUtil.getPeripheral( world, offset, direction.getOpposite() ); + return peripheral instanceof WiredModemPeripheral ? null : peripheral; + } +} diff --git a/src/main/java/dan200/computercraft/shared/wired/WiredNetwork.java b/src/main/java/dan200/computercraft/shared/wired/WiredNetwork.java index c09391364..3956154d7 100644 --- a/src/main/java/dan200/computercraft/shared/wired/WiredNetwork.java +++ b/src/main/java/dan200/computercraft/shared/wired/WiredNetwork.java @@ -1,5 +1,7 @@ package dan200.computercraft.shared.wired; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; import dan200.computercraft.api.network.Packet; import dan200.computercraft.api.network.wired.IWiredNetwork; import dan200.computercraft.api.network.wired.IWiredNode; @@ -283,9 +285,10 @@ public final class WiredNetwork implements IWiredNetwork } @Override - public void invalidate( @Nonnull IWiredNode node ) + public void updatePeripherals( @Nonnull IWiredNode node, @Nonnull Map newPeripherals ) { WiredNode wired = checkNode( node ); + Preconditions.checkNotNull( peripherals, "peripherals cannot be null" ); lock.writeLock().lock(); try @@ -293,11 +296,10 @@ public final class WiredNetwork implements IWiredNetwork if( wired.network != this ) throw new IllegalStateException( "Node is not on this network" ); Map oldPeripherals = wired.peripherals; - Map newPeripherals = wired.element.getPeripherals(); WiredNetworkChange change = WiredNetworkChange.changeOf( oldPeripherals, newPeripherals ); if( change.isEmpty() ) return; - wired.peripherals = newPeripherals; + wired.peripherals = ImmutableMap.copyOf( newPeripherals ); // Detach the old peripherals then remove them. peripherals.keySet().removeAll( change.peripheralsRemoved().keySet() ); diff --git a/src/test/java/dan200/computercraft/shared/wired/NetworkTest.java b/src/test/java/dan200/computercraft/shared/wired/NetworkTest.java index a2ff905aa..5984c77ee 100644 --- a/src/test/java/dan200/computercraft/shared/wired/NetworkTest.java +++ b/src/test/java/dan200/computercraft/shared/wired/NetworkTest.java @@ -23,7 +23,6 @@ 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; @@ -379,17 +378,10 @@ public class NetworkTest remotePeripherals.putAll( change.peripheralsAdded() ); } - @Nonnull - @Override - public Map getPeripherals() - { - return Collections.unmodifiableMap( localPeripherals ); - } - public NetworkElement addPeripheral( String name ) { localPeripherals.put( name, new NetworkPeripheral() ); - getNode().invalidate(); + getNode().updatePeripherals( localPeripherals ); return this; }