1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-11-11 10:53:03 +00:00

Merge pull request #183 from SquidDev-CC/feature/thread-safe-inventories

This commit is contained in:
Jonathan Coates
2019-08-01 14:30:32 +01:00
committed by GitHub
9 changed files with 273 additions and 354 deletions

View File

@@ -145,7 +145,9 @@ public interface ITurtleAccess
GameProfile getOwningPlayer(); GameProfile getOwningPlayer();
/** /**
* Get the inventory of this turtle * Get the inventory of this turtle.
*
* Note: this inventory should only be accessed and modified on the server thread.
* *
* @return This turtle's inventory * @return This turtle's inventory
* @see #getItemHandler() * @see #getItemHandler()
@@ -156,6 +158,8 @@ public interface ITurtleAccess
/** /**
* Get the inventory of this turtle as an {@link IItemHandlerModifiable}. * Get the inventory of this turtle as an {@link IItemHandlerModifiable}.
* *
* Note: this inventory should only be accessed and modified on the server thread.
*
* @return This turtle's inventory * @return This turtle's inventory
* @see #getInventory() * @see #getInventory()
* @see IItemHandlerModifiable * @see IItemHandlerModifiable

View File

@@ -11,6 +11,7 @@ import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.media.IMedia; import dan200.computercraft.api.media.IMedia;
import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.shared.MediaProviders;
import dan200.computercraft.shared.media.items.ItemDiskLegacy; import dan200.computercraft.shared.media.items.ItemDiskLegacy;
import dan200.computercraft.shared.util.StringUtil; import dan200.computercraft.shared.util.StringUtil;
import net.minecraft.item.Item; import net.minecraft.item.Item;
@@ -20,11 +21,11 @@ import javax.annotation.Nonnull;
import static dan200.computercraft.core.apis.ArgumentHelper.optString; import static dan200.computercraft.core.apis.ArgumentHelper.optString;
public class DiskDrivePeripheral implements IPeripheral class DiskDrivePeripheral implements IPeripheral
{ {
private final TileDiskDrive m_diskDrive; private final TileDiskDrive m_diskDrive;
public DiskDrivePeripheral( TileDiskDrive diskDrive ) DiskDrivePeripheral( TileDiskDrive diskDrive )
{ {
m_diskDrive = diskDrive; m_diskDrive = diskDrive;
} }
@@ -56,7 +57,7 @@ public class DiskDrivePeripheral implements IPeripheral
} }
@Override @Override
public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException
{ {
switch( method ) switch( method )
{ {
@@ -64,21 +65,26 @@ public class DiskDrivePeripheral implements IPeripheral
return new Object[] { !m_diskDrive.getDiskStack().isEmpty() }; return new Object[] { !m_diskDrive.getDiskStack().isEmpty() };
case 1: // getDiskLabel case 1: // getDiskLabel
{ {
IMedia media = m_diskDrive.getDiskMedia(); ItemStack stack = m_diskDrive.getDiskStack();
return media == null ? null : new Object[] { media.getLabel( m_diskDrive.getDiskStack() ) }; IMedia media = MediaProviders.get( stack );
return media == null ? null : new Object[] { media.getLabel( stack ) };
} }
case 2: // setDiskLabel case 2: // setDiskLabel
{ {
String label = optString( arguments, 0, null ); String label = optString( arguments, 0, null );
IMedia media = m_diskDrive.getDiskMedia(); return context.executeMainThreadTask( () -> {
ItemStack stack = m_diskDrive.getDiskStack();
IMedia media = MediaProviders.get( stack );
if( media == null ) return null; if( media == null ) return null;
ItemStack disk = m_diskDrive.getDiskStack(); if( !media.setLabel( stack, StringUtil.normaliseLabel( label ) ) )
label = StringUtil.normaliseLabel( label ); {
if( !media.setLabel( disk, label ) ) throw new LuaException( "Disk label cannot be changed" ); throw new LuaException( "Disk label cannot be changed" );
m_diskDrive.setDiskStack( disk ); }
m_diskDrive.setDiskStack( stack );
return null; return null;
} );
} }
case 3: // hasData case 3: // hasData
return new Object[] { m_diskDrive.getDiskMountPath( computer ) != null }; return new Object[] { m_diskDrive.getDiskMountPath( computer ) != null };
@@ -87,14 +93,16 @@ public class DiskDrivePeripheral implements IPeripheral
case 5: case 5:
{ {
// hasAudio // hasAudio
IMedia media = m_diskDrive.getDiskMedia(); ItemStack stack = m_diskDrive.getDiskStack();
return new Object[] { media != null && media.getAudio( m_diskDrive.getDiskStack() ) != null }; IMedia media = MediaProviders.get( stack );
return new Object[] { media != null && media.getAudio( stack ) != null };
} }
case 6: case 6:
{ {
// getAudioTitle // getAudioTitle
IMedia media = m_diskDrive.getDiskMedia(); ItemStack stack = m_diskDrive.getDiskStack();
return new Object[] { media != null ? media.getAudioTitle( m_diskDrive.getDiskStack() ) : false }; IMedia media = MediaProviders.get( stack );
return new Object[] { media != null ? media.getAudioTitle( stack ) : false };
} }
case 7: // playAudio case 7: // playAudio
m_diskDrive.playDiskAudio(); m_diskDrive.playDiskAudio();
@@ -131,8 +139,7 @@ public class DiskDrivePeripheral implements IPeripheral
@Override @Override
public boolean equals( IPeripheral other ) public boolean equals( IPeripheral other )
{ {
if( this == other ) return true; return this == other || other instanceof DiskDrivePeripheral && ((DiskDrivePeripheral) other).m_diskDrive == m_diskDrive;
return other instanceof DiskDrivePeripheral && ((DiskDrivePeripheral) other).m_diskDrive == m_diskDrive;
} }
@Nonnull @Nonnull

View File

@@ -318,35 +318,31 @@ public class TileDiskDrive extends TilePeripheralBase implements DefaultInventor
} }
@Nonnull @Nonnull
public ItemStack getDiskStack() ItemStack getDiskStack()
{ {
return getStackInSlot( 0 ); return getStackInSlot( 0 );
} }
public void setDiskStack( @Nonnull ItemStack stack ) void setDiskStack( @Nonnull ItemStack stack )
{ {
setInventorySlotContents( 0, stack ); setInventorySlotContents( 0, stack );
} }
public IMedia getDiskMedia() private IMedia getDiskMedia()
{ {
return MediaProviders.get( getDiskStack() ); return MediaProviders.get( getDiskStack() );
} }
public String getDiskMountPath( IComputerAccess computer ) String getDiskMountPath( IComputerAccess computer )
{ {
synchronized( this ) synchronized( this )
{
if( m_computers.containsKey( computer ) )
{ {
MountInfo info = m_computers.get( computer ); MountInfo info = m_computers.get( computer );
return info.mountPath; return info != null ? info.mountPath : null;
} }
} }
return null;
}
public void mount( IComputerAccess computer ) void mount( IComputerAccess computer )
{ {
synchronized( this ) synchronized( this )
{ {
@@ -355,7 +351,7 @@ public class TileDiskDrive extends TilePeripheralBase implements DefaultInventor
} }
} }
public void unmount( IComputerAccess computer ) void unmount( IComputerAccess computer )
{ {
synchronized( this ) synchronized( this )
{ {
@@ -364,7 +360,7 @@ public class TileDiskDrive extends TilePeripheralBase implements DefaultInventor
} }
} }
public void playDiskAudio() void playDiskAudio()
{ {
synchronized( this ) synchronized( this )
{ {
@@ -377,7 +373,7 @@ public class TileDiskDrive extends TilePeripheralBase implements DefaultInventor
} }
} }
public void stopDiskAudio() void stopDiskAudio()
{ {
synchronized( this ) synchronized( this )
{ {
@@ -386,7 +382,7 @@ public class TileDiskDrive extends TilePeripheralBase implements DefaultInventor
} }
} }
public void ejectDisk() void ejectDisk()
{ {
synchronized( this ) synchronized( this )
{ {

View File

@@ -51,8 +51,13 @@ public class PrinterPeripheral implements IPeripheral
} }
@Override @Override
public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException public Object[] callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException, InterruptedException
{ {
// FIXME: There's a theoretical race condition here between getCurrentPage and then using the page. Ideally
// we'd lock on the page, consume it, and unlock.
// FIXME: None of our page modification functions actually mark the tile as dirty, so the page may not be
// persisted correctly.
switch( method ) switch( method )
{ {
case 0: // write case 0: // write
@@ -89,10 +94,13 @@ public class PrinterPeripheral implements IPeripheral
return new Object[] { width, height }; return new Object[] { width, height };
} }
case 4: // newPage case 4: // newPage
return new Object[] { m_printer.startNewPage() }; return context.executeMainThreadTask( () -> new Object[] { m_printer.startNewPage() } );
case 5: // endPage case 5: // endPage
getCurrentPage();
return context.executeMainThreadTask( () -> {
getCurrentPage(); getCurrentPage();
return new Object[] { m_printer.endCurrentPage() }; return new Object[] { m_printer.endCurrentPage() };
} );
case 6: // getInkLevel case 6: // getInkLevel
return new Object[] { m_printer.getInkLevel() }; return new Object[] { m_printer.getInkLevel() };
case 7: case 7:
@@ -123,13 +131,11 @@ public class PrinterPeripheral implements IPeripheral
return m_printer; return m_printer;
} }
@Nonnull
private Terminal getCurrentPage() throws LuaException private Terminal getCurrentPage() throws LuaException
{ {
Terminal currentPage = m_printer.getCurrentPage(); Terminal currentPage = m_printer.getCurrentPage();
if( currentPage == null ) if( currentPage == null ) throw new LuaException( "Page not started" );
{
throw new LuaException( "Page not started" );
}
return currentPage; return currentPage;
} }
} }

View File

@@ -45,9 +45,9 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
{ {
// Statics // Statics
private static final int[] bottomSlots = new int[] { 7, 8, 9, 10, 11, 12 }; private static final int[] BOTTOM_SLOTS = new int[] { 7, 8, 9, 10, 11, 12 };
private static final int[] topSlots = new int[] { 1, 2, 3, 4, 5, 6 }; private static final int[] TOP_SLOTS = new int[] { 1, 2, 3, 4, 5, 6 };
private static final int[] sideSlots = new int[] { 0 }; private static final int[] SIDE_SLOTS = new int[] { 0 };
// Members // Members
@@ -88,18 +88,12 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
} }
// Read inventory // Read inventory
synchronized( m_inventory ) NBTTagList itemList = nbt.getTagList( "Items", Constants.NBT.TAG_COMPOUND );
for( int i = 0; i < itemList.tagCount(); i++ )
{ {
NBTTagList nbttaglist = nbt.getTagList( "Items", Constants.NBT.TAG_COMPOUND ); NBTTagCompound itemTag = itemList.getCompoundTagAt( i );
for( int i = 0; i < nbttaglist.tagCount(); i++ ) int slot = itemTag.getByte( "Slot" ) & 0xff;
{ if( slot < m_inventory.size() ) m_inventory.set( slot, new ItemStack( itemTag ) );
NBTTagCompound itemTag = nbttaglist.getCompoundTagAt( i );
int j = itemTag.getByte( "Slot" ) & 0xff;
if( j < m_inventory.size() )
{
m_inventory.set( j, new ItemStack( itemTag ) );
}
}
} }
} }
@@ -116,21 +110,18 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
} }
// Write inventory // Write inventory
synchronized( m_inventory ) NBTTagList itemList = new NBTTagList();
{
NBTTagList nbttaglist = new NBTTagList();
for( int i = 0; i < m_inventory.size(); i++ ) for( int i = 0; i < m_inventory.size(); i++ )
{ {
if( !m_inventory.get( i ).isEmpty() ) ItemStack stack = m_inventory.get( i );
{ if( stack.isEmpty() ) continue;
NBTTagCompound tag = new NBTTagCompound(); NBTTagCompound tag = new NBTTagCompound();
tag.setByte( "Slot", (byte) i ); tag.setByte( "Slot", (byte) i );
m_inventory.get( i ).writeToNBT( tag ); stack.writeToNBT( tag );
nbttaglist.appendTag( tag ); itemList.appendTag( tag );
}
}
nbt.setTag( "Items", nbttaglist );
} }
nbt.setTag( "Items", itemList );
return super.writeToNBT( nbt ); return super.writeToNBT( nbt );
} }
@@ -148,7 +139,7 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
return super.shouldRefresh( world, pos, oldState, newState ) || BlockPeripheral.getPeripheralType( newState ) != PeripheralType.Printer; return super.shouldRefresh( world, pos, oldState, newState ) || BlockPeripheral.getPeripheralType( newState ) != PeripheralType.Printer;
} }
public boolean isPrinting() boolean isPrinting()
{ {
return m_printing; return m_printing;
} }
@@ -173,74 +164,60 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
@Nonnull @Nonnull
@Override @Override
public ItemStack getStackInSlot( int i ) public ItemStack getStackInSlot( int slot )
{ {
return m_inventory.get( i ); return m_inventory.get( slot );
} }
@Nonnull @Nonnull
@Override @Override
public ItemStack removeStackFromSlot( int i ) public ItemStack removeStackFromSlot( int slot )
{ {
synchronized( m_inventory ) ItemStack result = m_inventory.get( slot );
{ m_inventory.set( slot, ItemStack.EMPTY );
ItemStack result = m_inventory.get( i );
m_inventory.set( i, ItemStack.EMPTY );
markDirty(); markDirty();
updateAnim(); updateAnim();
return result; return result;
} }
}
@Nonnull @Nonnull
@Override @Override
public ItemStack decrStackSize( int i, int j ) public ItemStack decrStackSize( int slot, int count )
{ {
synchronized( m_inventory ) ItemStack stack = m_inventory.get( slot );
{ if( stack.isEmpty() ) return ItemStack.EMPTY;
if( m_inventory.get( i ).isEmpty() ) return ItemStack.EMPTY;
if( m_inventory.get( i ).getCount() <= j ) if( stack.getCount() <= count )
{ {
ItemStack itemstack = m_inventory.get( i ); setInventorySlotContents( slot, ItemStack.EMPTY );
m_inventory.set( i, ItemStack.EMPTY ); return stack;
markDirty();
updateAnim();
return itemstack;
} }
ItemStack part = m_inventory.get( i ).splitStack( j ); ItemStack part = stack.splitStack( count );
if( m_inventory.get( i ).isEmpty() ) if( m_inventory.get( slot ).isEmpty() )
{ {
m_inventory.set( i, ItemStack.EMPTY ); m_inventory.set( slot, ItemStack.EMPTY );
updateAnim(); updateAnim();
} }
markDirty(); markDirty();
return part; return part;
} }
}
@Override @Override
public void setInventorySlotContents( int i, @Nonnull ItemStack stack ) public void setInventorySlotContents( int slot, @Nonnull ItemStack stack )
{ {
synchronized( m_inventory ) m_inventory.set( slot, stack );
{
m_inventory.set( i, stack );
markDirty(); markDirty();
updateAnim(); updateAnim();
} }
}
@Override @Override
public void clear() public void clear()
{
synchronized( m_inventory )
{ {
for( int i = 0; i < m_inventory.size(); i++ ) m_inventory.set( i, ItemStack.EMPTY ); for( int i = 0; i < m_inventory.size(); i++ ) m_inventory.set( i, ItemStack.EMPTY );
markDirty(); markDirty();
updateAnim(); updateAnim();
} }
}
@Override @Override
public boolean isItemValidForSlot( int slot, @Nonnull ItemStack stack ) public boolean isItemValidForSlot( int slot, @Nonnull ItemStack stack )
@@ -249,7 +226,7 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
{ {
return isInk( stack ); return isInk( stack );
} }
else if( slot >= topSlots[0] && slot <= topSlots[topSlots.length - 1] ) else if( slot >= TOP_SLOTS[0] && slot <= TOP_SLOTS[TOP_SLOTS.length - 1] )
{ {
return isPaper( stack ); return isPaper( stack );
} }
@@ -295,11 +272,11 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
switch( side ) switch( side )
{ {
case DOWN: // Bottom (Out tray) case DOWN: // Bottom (Out tray)
return bottomSlots; return BOTTOM_SLOTS;
case UP: // Top (In tray) case UP: // Top (In tray)
return topSlots; return TOP_SLOTS;
default: // Sides (Ink) default: // Sides (Ink)
return sideSlots; return SIDE_SLOTS;
} }
} }
@@ -311,14 +288,18 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
return new PrinterPeripheral( this ); return new PrinterPeripheral( this );
} }
public Terminal getCurrentPage() @Nullable
Terminal getCurrentPage()
{
synchronized( m_page )
{ {
return m_printing ? m_page : null; return m_printing ? m_page : null;
} }
}
public boolean startNewPage() boolean startNewPage()
{ {
synchronized( m_inventory ) synchronized( m_page )
{ {
if( !canInputPage() ) return false; if( !canInputPage() ) return false;
if( m_printing && !outputPage() ) return false; if( m_printing && !outputPage() ) return false;
@@ -326,49 +307,36 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
} }
} }
public boolean endCurrentPage() boolean endCurrentPage()
{ {
synchronized( m_inventory ) synchronized( m_page )
{ {
if( m_printing && outputPage() ) return m_printing && outputPage();
{
return true;
} }
} }
return false;
}
public int getInkLevel() int getInkLevel()
{
synchronized( m_inventory )
{ {
ItemStack inkStack = m_inventory.get( 0 ); ItemStack inkStack = m_inventory.get( 0 );
return isInk( inkStack ) ? inkStack.getCount() : 0; return isInk( inkStack ) ? inkStack.getCount() : 0;
} }
}
public int getPaperLevel() int getPaperLevel()
{ {
int count = 0; int count = 0;
synchronized( m_inventory )
{
for( int i = 1; i < 7; i++ ) for( int i = 1; i < 7; i++ )
{ {
ItemStack paperStack = m_inventory.get( i ); ItemStack paperStack = m_inventory.get( i );
if( !paperStack.isEmpty() && isPaper( paperStack ) ) if( isPaper( paperStack ) ) count += paperStack.getCount();
{
count += paperStack.getCount();
}
}
} }
return count; return count;
} }
public void setPageTitle( String title ) void setPageTitle( String title )
{ {
if( m_printing ) synchronized( m_page )
{ {
m_pageTitle = title; if( m_printing ) m_pageTitle = title;
} }
} }
@@ -384,17 +352,12 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
} }
private boolean canInputPage() private boolean canInputPage()
{
synchronized( m_inventory )
{ {
ItemStack inkStack = m_inventory.get( 0 ); ItemStack inkStack = m_inventory.get( 0 );
return !inkStack.isEmpty() && isInk( inkStack ) && getPaperLevel() > 0; return !inkStack.isEmpty() && isInk( inkStack ) && getPaperLevel() > 0;
} }
}
private boolean inputPage() private boolean inputPage()
{
synchronized( m_inventory )
{ {
ItemStack inkStack = m_inventory.get( 0 ); ItemStack inkStack = m_inventory.get( 0 );
if( !isInk( inkStack ) ) return false; if( !isInk( inkStack ) ) return false;
@@ -402,8 +365,8 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
for( int i = 1; i < 7; i++ ) for( int i = 1; i < 7; i++ )
{ {
ItemStack paperStack = m_inventory.get( i ); ItemStack paperStack = m_inventory.get( i );
if( !paperStack.isEmpty() && isPaper( paperStack ) ) if( paperStack.isEmpty() || !isPaper( paperStack ) ) continue;
{
// Setup the new page // Setup the new page
int colour = inkStack.getItemDamage(); int colour = inkStack.getItemDamage();
m_page.setTextColour( colour >= 0 && colour < 16 ? 15 - colour : 15 ); m_page.setTextColour( colour >= 0 && colour < 16 ? 15 - colour : 15 );
@@ -441,14 +404,10 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
m_printing = true; m_printing = true;
return true; return true;
} }
}
return false; return false;
} }
}
private boolean outputPage() private boolean outputPage()
{
synchronized( m_page )
{ {
int height = m_page.getHeight(); int height = m_page.getHeight();
String[] lines = new String[height]; String[] lines = new String[height];
@@ -460,9 +419,7 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
} }
ItemStack stack = ItemPrintout.createSingleFromTitleAndText( m_pageTitle, lines, colours ); ItemStack stack = ItemPrintout.createSingleFromTitleAndText( m_pageTitle, lines, colours );
synchronized( m_inventory ) for( int slot : BOTTOM_SLOTS )
{
for( int slot : bottomSlots )
{ {
if( m_inventory.get( slot ).isEmpty() ) if( m_inventory.get( slot ).isEmpty() )
{ {
@@ -471,14 +428,10 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
return true; return true;
} }
} }
}
return false; return false;
} }
}
private void ejectContents() private void ejectContents()
{
synchronized( m_inventory )
{ {
for( int i = 0; i < 13; i++ ) for( int i = 0; i < 13; i++ )
{ {
@@ -497,11 +450,8 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
} }
} }
} }
}
private void updateAnim() private void updateAnim()
{
synchronized( m_inventory )
{ {
int anim = 0; int anim = 0;
for( int i = 1; i < 7; i++ ) for( int i = 1; i < 7; i++ )
@@ -524,7 +474,6 @@ public class TilePrinter extends TilePeripheralBase implements DefaultSidedInven
} }
setAnim( anim ); setAnim( anim );
} }
}
@Override @Override
public boolean hasCapability( @Nonnull Capability<?> capability, @Nullable EnumFacing facing ) public boolean hasCapability( @Nonnull Capability<?> capability, @Nullable EnumFacing facing )

View File

@@ -33,7 +33,7 @@ public final class FurnaceRefuelHandler implements TurtleRefuelEvent.Handler
int fuelSpaceLeft = turtle.getFuelLimit() - turtle.getFuelLevel(); int fuelSpaceLeft = turtle.getFuelLimit() - turtle.getFuelLevel();
int fuelPerItem = getFuelPerItem( turtle.getItemHandler().getStackInSlot( slot ) ); int fuelPerItem = getFuelPerItem( turtle.getItemHandler().getStackInSlot( slot ) );
int fuelItemLimit = (int) Math.ceil( fuelSpaceLeft / (double) fuelPerItem ); int fuelItemLimit = (int) Math.ceil( fuelSpaceLeft / (double) fuelPerItem );
if ( limit > fuelItemLimit ) limit = fuelItemLimit; if( limit > fuelItemLimit ) limit = fuelItemLimit;
ItemStack stack = turtle.getItemHandler().extractItem( slot, limit, false ); ItemStack stack = turtle.getItemHandler().extractItem( slot, limit, false );
int fuelToGive = fuelPerItem * stack.getCount(); int fuelToGive = fuelPerItem * stack.getCount();

View File

@@ -334,9 +334,11 @@ public class TurtleAPI implements ILuaAPI
return tryCommand( context, new TurtleInspectCommand( InteractDirection.Up ) ); return tryCommand( context, new TurtleInspectCommand( InteractDirection.Up ) );
case 40: // inspectDown case 40: // inspectDown
return tryCommand( context, new TurtleInspectCommand( InteractDirection.Down ) ); return tryCommand( context, new TurtleInspectCommand( InteractDirection.Down ) );
case 41: case 41: // getItemDetail
{ {
// getItemDetail // FIXME: There's a race condition here if the stack is being modified (mutating NBT, etc...)
// on another thread. The obvious solution is to move this into a command, but some programs rely
// on this having a 0-tick delay.
int slot = parseOptionalSlotNumber( args, 0, m_turtle.getSelectedSlot() ); int slot = parseOptionalSlotNumber( args, 0, m_turtle.getSelectedSlot() );
ItemStack stack = m_turtle.getInventory().getStackInSlot( slot ); ItemStack stack = m_turtle.getInventory().getStackInSlot( slot );
if( stack.isEmpty() ) return new Object[] { null }; if( stack.isEmpty() ) return new Object[] { null };

View File

@@ -43,19 +43,16 @@ import net.minecraftforge.items.wrapper.InvWrapper;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Collections;
import static net.minecraftforge.items.CapabilityItemHandler.ITEM_HANDLER_CAPABILITY; import static net.minecraftforge.items.CapabilityItemHandler.ITEM_HANDLER_CAPABILITY;
public class TileTurtle extends TileComputerBase implements ITurtleTile, DefaultInventory public class TileTurtle extends TileComputerBase implements ITurtleTile, DefaultInventory
{ {
// Statics
public static final int INVENTORY_SIZE = 16; public static final int INVENTORY_SIZE = 16;
public static final int INVENTORY_WIDTH = 4; public static final int INVENTORY_WIDTH = 4;
public static final int INVENTORY_HEIGHT = 4; public static final int INVENTORY_HEIGHT = 4;
// Members
enum MoveState enum MoveState
{ {
NOT_MOVED, NOT_MOVED,
@@ -63,12 +60,12 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
MOVED MOVED
} }
private NonNullList<ItemStack> m_inventory; private final NonNullList<ItemStack> m_inventory = NonNullList.withSize( INVENTORY_SIZE, ItemStack.EMPTY );
private NonNullList<ItemStack> m_previousInventory; private final NonNullList<ItemStack> m_previousInventory = NonNullList.withSize( INVENTORY_SIZE, ItemStack.EMPTY );
private final IItemHandlerModifiable m_itemHandler = new InvWrapper( this ); private final IItemHandlerModifiable m_itemHandler = new InvWrapper( this );
private boolean m_inventoryChanged; private boolean m_inventoryChanged = false;
private TurtleBrain m_brain; private TurtleBrain m_brain = new TurtleBrain( this );
private MoveState m_moveState; private MoveState m_moveState = MoveState.NOT_MOVED;
private ComputerFamily m_family; private ComputerFamily m_family;
public TileTurtle() public TileTurtle()
@@ -78,15 +75,10 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
public TileTurtle( ComputerFamily family ) public TileTurtle( ComputerFamily family )
{ {
m_inventory = NonNullList.withSize( INVENTORY_SIZE, ItemStack.EMPTY );
m_previousInventory = NonNullList.withSize( INVENTORY_SIZE, ItemStack.EMPTY );
m_inventoryChanged = false;
m_brain = new TurtleBrain( this );
m_moveState = MoveState.NOT_MOVED;
m_family = family; m_family = family;
} }
public boolean hasMoved() private boolean hasMoved()
{ {
return m_moveState == MoveState.MOVED; return m_moveState == MoveState.MOVED;
} }
@@ -224,8 +216,6 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
{ {
super.update(); super.update();
m_brain.update(); m_brain.update();
synchronized( m_inventory )
{
if( !getWorld().isRemote && m_inventoryChanged ) if( !getWorld().isRemote && m_inventoryChanged )
{ {
ServerComputer computer = getServerComputer(); ServerComputer computer = getServerComputer();
@@ -238,7 +228,6 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
} }
} }
} }
}
@Override @Override
public void onNeighbourChange( @Nonnull BlockPos neighbour ) public void onNeighbourChange( @Nonnull BlockPos neighbour )
@@ -270,8 +259,8 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
// Read inventory // Read inventory
NBTTagList nbttaglist = nbt.getTagList( "Items", Constants.NBT.TAG_COMPOUND ); NBTTagList nbttaglist = nbt.getTagList( "Items", Constants.NBT.TAG_COMPOUND );
m_inventory = NonNullList.withSize( INVENTORY_SIZE, ItemStack.EMPTY ); m_inventory.clear();
m_previousInventory = NonNullList.withSize( INVENTORY_SIZE, ItemStack.EMPTY ); m_previousInventory.clear();
for( int i = 0; i < nbttaglist.tagCount(); i++ ) for( int i = 0; i < nbttaglist.tagCount(); i++ )
{ {
NBTTagCompound tag = nbttaglist.getCompoundTagAt( i ); NBTTagCompound tag = nbttaglist.getCompoundTagAt( i );
@@ -382,7 +371,7 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
return m_family; return m_family;
} }
public void setOwningPlayer( GameProfile player ) void setOwningPlayer( GameProfile player )
{ {
m_brain.setOwningPlayer( player ); m_brain.setOwningPlayer( player );
markDirty(); markDirty();
@@ -410,44 +399,26 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
@Override @Override
public ItemStack getStackInSlot( int slot ) public ItemStack getStackInSlot( int slot )
{ {
if( slot >= 0 && slot < INVENTORY_SIZE ) return slot >= 0 && slot < INVENTORY_SIZE ? m_inventory.get( slot ) : ItemStack.EMPTY;
{
synchronized( m_inventory )
{
return m_inventory.get( slot );
}
}
return ItemStack.EMPTY;
} }
@Nonnull @Nonnull
@Override @Override
public ItemStack removeStackFromSlot( int slot ) public ItemStack removeStackFromSlot( int slot )
{
synchronized( m_inventory )
{ {
ItemStack result = getStackInSlot( slot ); ItemStack result = getStackInSlot( slot );
setInventorySlotContents( slot, ItemStack.EMPTY ); setInventorySlotContents( slot, ItemStack.EMPTY );
return result; return result;
} }
}
@Nonnull @Nonnull
@Override @Override
public ItemStack decrStackSize( int slot, int count ) public ItemStack decrStackSize( int slot, int count )
{ {
if( count == 0 ) if( count == 0 ) return ItemStack.EMPTY;
{
return ItemStack.EMPTY;
}
synchronized( m_inventory )
{
ItemStack stack = getStackInSlot( slot ); ItemStack stack = getStackInSlot( slot );
if( stack.isEmpty() ) if( stack.isEmpty() ) return ItemStack.EMPTY;
{
return ItemStack.EMPTY;
}
if( stack.getCount() <= count ) if( stack.getCount() <= count )
{ {
@@ -459,28 +430,19 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
onInventoryDefinitelyChanged(); onInventoryDefinitelyChanged();
return part; return part;
} }
}
@Override @Override
public void setInventorySlotContents( int i, @Nonnull ItemStack stack ) public void setInventorySlotContents( int i, @Nonnull ItemStack stack )
{ {
if( i >= 0 && i < INVENTORY_SIZE ) if( i >= 0 && i < INVENTORY_SIZE && !InventoryUtil.areItemsEqual( stack, m_inventory.get( i ) ) )
{
synchronized( m_inventory )
{
if( !InventoryUtil.areItemsEqual( stack, m_inventory.get( i ) ) )
{ {
m_inventory.set( i, stack ); m_inventory.set( i, stack );
onInventoryDefinitelyChanged(); onInventoryDefinitelyChanged();
} }
} }
}
}
@Override @Override
public void clear() public void clear()
{
synchronized( m_inventory )
{ {
boolean changed = false; boolean changed = false;
for( int i = 0; i < INVENTORY_SIZE; i++ ) for( int i = 0; i < INVENTORY_SIZE; i++ )
@@ -491,19 +453,14 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
changed = true; changed = true;
} }
} }
if( changed )
{ if( changed ) onInventoryDefinitelyChanged();
onInventoryDefinitelyChanged();
}
}
} }
@Override @Override
public void markDirty() public void markDirty()
{ {
super.markDirty(); super.markDirty();
synchronized( m_inventory )
{
if( !m_inventoryChanged ) if( !m_inventoryChanged )
{ {
for( int n = 0; n < getSizeInventory(); n++ ) for( int n = 0; n < getSizeInventory(); n++ )
@@ -516,7 +473,6 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
} }
} }
} }
}
@Override @Override
public boolean isUsableByPlayer( @Nonnull EntityPlayer player ) public boolean isUsableByPlayer( @Nonnull EntityPlayer player )
@@ -574,8 +530,8 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
public void transferStateFrom( TileTurtle copy ) public void transferStateFrom( TileTurtle copy )
{ {
super.transferStateFrom( copy ); super.transferStateFrom( copy );
m_inventory = copy.m_inventory; Collections.copy( m_inventory, copy.m_inventory );
m_previousInventory = copy.m_previousInventory; Collections.copy( m_previousInventory, copy.m_previousInventory );
m_inventoryChanged = copy.m_inventoryChanged; m_inventoryChanged = copy.m_inventoryChanged;
m_brain = copy.m_brain; m_brain = copy.m_brain;
m_brain.setOwner( this ); m_brain.setOwner( this );

View File

@@ -342,7 +342,6 @@ local tMenuFuncs = {
redrawMenu() redrawMenu()
term.redirect( printerTerminal ) term.redirect( printerTerminal )
local timer = os.startTimer(0.5)
sleep(0.5) sleep(0.5)
end end