diff --git a/src/main/java/li/cil/oc/api/machine/MachineHost.java b/src/main/java/li/cil/oc/api/machine/MachineHost.java
index 67c7eae4a..d79124702 100644
--- a/src/main/java/li/cil/oc/api/machine/MachineHost.java
+++ b/src/main/java/li/cil/oc/api/machine/MachineHost.java
@@ -55,4 +55,13 @@ public interface MachineHost extends EnvironmentHost {
* @param node the node that was disconnected from the network.
*/
void onMachineDisconnect(Node node);
+
+ /** Helper method for printing the machine position in error messages and debug statements. */
+ default String machinePosition()
+ {
+ if (world() != null && world().provider != null)
+ return String.format("(%g, %g, %g, %d)", xPosition(), yPosition(), zPosition(), world().provider.getDimension());
+ else
+ return String.format("(%g, %g, %g)", xPosition(), yPosition(), zPosition());
+ }
}
diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/sh.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/sh.lua
index d4e4dd9db..de7a7b1c4 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/bin/sh.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/bin/sh.lua
@@ -41,5 +41,5 @@ if #args == 0 then
end
else
-- execute command.
- return sh.execute(...)
+ return sh.execute(_ENV, ...)
end
diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_shell.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_shell.lua
index 2576b07ee..7c6701715 100644
--- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_shell.lua
+++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_shell.lua
@@ -10,8 +10,8 @@ function shell.execute(command, env, ...)
if not sh then
return false, reason
end
- local proc = process.load(sh, nil, nil, command)
- local result = table.pack(process.internal.continue(proc, env, command, ...))
+ local proc = process.load(sh, env, nil, command)
+ local result = table.pack(process.internal.continue(proc, command, ...))
if result.n == 0 then return true end
return table.unpack(result, 1, result.n)
end
diff --git a/src/main/resources/assets/opencomputers/robot.names b/src/main/resources/assets/opencomputers/robot.names
index 3d862d926..d3e64e7bc 100644
--- a/src/main/resources/assets/opencomputers/robot.names
+++ b/src/main/resources/assets/opencomputers/robot.names
@@ -34,6 +34,7 @@ Claptrap # Borderlands
Codsworth # Fallout 4
Continuity # Mona Lisa Overdrive
Cortana # Halo
+Crow # Mystery Science Theater 3000
Crypto # Kodos
Curiosity # Mars Rover
Daedalus # Deus Ex
@@ -60,6 +61,7 @@ Ghost # I, Robot quote "There have always been ghosts in the machin
GLaDOS # Portal
Gipsy Danger # Pacific Rim
G.U.N.T.E.R. # Lost in Space
+Gypsy # Mystery Science Theater 3000
HAL 9000 # Space Odyssey
Harbinger # Mass Effect
Harkness # Fallout 3
@@ -117,6 +119,7 @@ T-1000 # Terminator
Tachikoma # Ghost in the Shell
TARA UH # Perry Rhodan
Terminator # Terminator
+Tom Servo # Mystery Science Theater 3000
Rinzler # Tron
Twiki # Buck Rodgers
Uniblab # The Jetsons
@@ -132,3 +135,4 @@ Wheatley # Portal
Wintermute # Neuromancer
Wobbo # Contributor
Yui-MHCP001 # AKA Yui (Mental Health Counseling Program 001) - Sword Art Online
+Zone of Endless # Ace Combat series
diff --git a/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala b/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala
index b131af852..9ab47f3db 100644
--- a/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala
+++ b/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala
@@ -131,7 +131,7 @@ trait NetworkControl[AETile >: Null <: TileEntity with IActionHost with IGridHos
case (key, value) => hash += key -> value
}
hash.update("isCraftable", Boolean.box(aeItem.isCraftable))
- hash.update("size", Int.box(aeItem.getStackSize.toInt))
+ hash.update("size", Long.box(aeItem.getStackSize))
hash
}
diff --git a/src/main/scala/li/cil/oc/server/component/DiskDriveMountable.scala b/src/main/scala/li/cil/oc/server/component/DiskDriveMountable.scala
index fd6edaa7a..930df534f 100644
--- a/src/main/scala/li/cil/oc/server/component/DiskDriveMountable.scala
+++ b/src/main/scala/li/cil/oc/server/component/DiskDriveMountable.scala
@@ -83,6 +83,14 @@ class DiskDriveMountable(val rack: api.internal.Rack, val slot: Int) extends Abs
else result(false)
}
+ @Callback(doc = "function(): string -- Return the internal floppy disk address")
+ def media(context: Context, args: Arguments): Array[AnyRef] = {
+ if (filesystemNode.isEmpty)
+ result(Unit, "drive is empty")
+ else
+ result(filesystemNode.head.address)
+ }
+
// ----------------------------------------------------------------------- //
// Analyzable
diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala
index c1c7f8efd..008e58aed 100644
--- a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala
+++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala
@@ -44,9 +44,9 @@ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRest
val stack = inventory.getStackInSlot(selectedSlot)
if (!stack.isEmpty && stack.getCount > 0) {
val blockPos = position.offset(facing)
- InventoryUtils.inventoryAt(blockPos, facing.getOpposite) match {
- case Some(inv) if mayInteract(blockPos, facing.getOpposite, inv) =>
- if (!InventoryUtils.insertIntoInventory(stack, inv, count)) {
+ InventoryUtils.inventorySourceAt(blockPos, facing.getOpposite) match {
+ case Some(inv) if mayInteract(inv) =>
+ if (!InventoryUtils.insertIntoInventory(stack, inv.inventory, count)) {
// Cannot drop into that inventory.
return result(false, "inventory full")
}
@@ -103,9 +103,9 @@ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRest
val count = args.optItemCount(1)
val blockPos = position.offset(facing)
- var extracted: Int = InventoryUtils.inventoryAt(blockPos, facing.getOpposite) match {
- case Some(inventory) => mayInteract(blockPos, facing.getOpposite)
- InventoryUtils.extractAnyFromInventory((is, sim) => InventoryUtils.insertIntoInventory(is, InventoryUtils.asItemHandler(this.inventory), slots = Option(insertionSlots), simulate = sim), inventory, count)
+ var extracted: Int = InventoryUtils.inventorySourceAt(blockPos, facing.getOpposite) match {
+ case Some(inventory) => mayInteract(inventory)
+ InventoryUtils.extractAnyFromInventory((is, sim) => InventoryUtils.insertIntoInventory(is, InventoryUtils.asItemHandler(this.inventory), slots = Option(insertionSlots), simulate = sim), inventory.inventory, count)
case _ => 0
}
if (extracted <= 0) {
diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala
index 5df2854a9..0e858c9f1 100644
--- a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala
+++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala
@@ -60,8 +60,8 @@ trait InventoryWorldControlMk2 extends InventoryAware with WorldAware with SideR
}
private def withInventory(blockPos: BlockPosition, fromSide: EnumFacing, f: IItemHandler => Array[AnyRef]) =
- InventoryUtils.inventoryAt(blockPos, fromSide) match {
- case Some(inventory) if mayInteract(blockPos, fromSide) => f(inventory)
+ InventoryUtils.inventorySourceAt(blockPos, fromSide) match {
+ case Some(inventory) if mayInteract(inventory) => f(inventory.inventory)
case _ => result(Unit, "no inventory")
}
}
diff --git a/src/main/scala/li/cil/oc/server/component/traits/WorldAware.scala b/src/main/scala/li/cil/oc/server/component/traits/WorldAware.scala
index eaf94a5f4..b63dc7b0f 100644
--- a/src/main/scala/li/cil/oc/server/component/traits/WorldAware.scala
+++ b/src/main/scala/li/cil/oc/server/component/traits/WorldAware.scala
@@ -2,15 +2,14 @@ package li.cil.oc.server.component.traits
import li.cil.oc.OpenComputers
import li.cil.oc.Settings
-import li.cil.oc.util.BlockPosition
+import li.cil.oc.util.{BlockInventorySource, BlockPosition, EntityInventorySource, InventorySource}
import li.cil.oc.util.ExtendedBlock._
import li.cil.oc.util.ExtendedWorld._
import net.minecraft.entity.Entity
import net.minecraft.entity.EntityLivingBase
import net.minecraft.entity.item.EntityMinecart
import net.minecraft.entity.player.EntityPlayer
-import net.minecraft.util.EnumFacing
-import net.minecraft.util.EnumHand
+import net.minecraft.util.{EnumActionResult, EnumFacing, EnumHand}
import net.minecraft.util.math.AxisAlignedBB
import net.minecraft.world.WorldServer
import net.minecraftforge.common.MinecraftForge
@@ -35,7 +34,7 @@ trait WorldAware {
player
}
- def mayInteract(blockPos: BlockPosition, face: EnumFacing): Boolean = {
+ private def mayInteract(blockPos: BlockPosition, face: EnumFacing): Boolean = {
try {
val event = new PlayerInteractEvent.RightClickBlock(fakePlayer, EnumHand.MAIN_HAND, blockPos.toBlockPos, face, null)
MinecraftForge.EVENT_BUS.post(event)
@@ -47,9 +46,25 @@ trait WorldAware {
}
}
- def mayInteract(blockPos: BlockPosition, side: EnumFacing, inventory: IItemHandler): Boolean = mayInteract(blockPos, side) && (inventory match {
+ private def mayInteract(entity: Entity): Boolean = {
+ try {
+ val event = new PlayerInteractEvent.EntityInteract(fakePlayer, EnumHand.MAIN_HAND, entity)
+ MinecraftForge.EVENT_BUS.post(event)
+ !event.isCanceled
+ } catch {
+ case t: Throwable =>
+ OpenComputers.log.warn("Some event handler threw up while checking for permission to access an entity.", t)
+ true
+ }
+ }
+
+ def mayInteract(inv: InventorySource): Boolean = (inv.inventory match {
case inv: InvWrapper if inv.getInv != null => inv.getInv.isUsableByPlayer(fakePlayer)
case _ => true
+ }) && (inv match {
+ case inv: BlockInventorySource => mayInteract(inv.position, inv.side)
+ case inv: EntityInventorySource => mayInteract(inv.entity)
+ case _ => true
})
def entitiesInBounds[Type <: Entity](clazz: Class[Type], bounds: AxisAlignedBB) = {
diff --git a/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala b/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala
index ee46da2f1..d896e8fe8 100644
--- a/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala
+++ b/src/main/scala/li/cil/oc/server/component/traits/WorldInventoryAnalytics.scala
@@ -6,13 +6,13 @@ import li.cil.oc.api.machine.Callback
import li.cil.oc.api.machine.Context
import li.cil.oc.api.prefab.ItemStackArrayValue
import li.cil.oc.server.component.result
-import li.cil.oc.util.{BlockPosition, DatabaseAccess, InventoryUtils}
+import li.cil.oc.util.{BlockInventorySource, BlockPosition, DatabaseAccess, EntityInventorySource, InventorySource, InventoryUtils, StackOption}
import li.cil.oc.util.ExtendedWorld._
import li.cil.oc.util.ExtendedArguments._
import net.minecraft.block.Block
-import li.cil.oc.util.StackOption
import net.minecraft.item.ItemStack
import net.minecraft.util.EnumFacing
+import net.minecraftforge.fml.common.registry.EntityRegistry
import net.minecraftforge.items.IItemHandler
import net.minecraftforge.oredict.OreDictionary
@@ -102,8 +102,12 @@ trait WorldInventoryAnalytics extends WorldAware with SideRestricted with Networ
}
case _ => None
}
- withInventory(facing, inventory => blockAt(position.offset(facing)) match {
- case Some(block) => result(block.getRegistryName)
+ withInventorySource(facing, {
+ case BlockInventorySource(position, _, _) => blockAt(position) match {
+ case Some(block) => result(block.getRegistryName)
+ case _ => result(Unit, "Unknown")
+ }
+ case EntityInventorySource(entity, _, _) => result(EntityRegistry.getEntry(entity.getClass).getRegistryName)
case _ => result(Unit, "Unknown")
})
}
@@ -122,9 +126,12 @@ trait WorldInventoryAnalytics extends WorldAware with SideRestricted with Networ
withInventory(facing, inventory => store(inventory.getStackInSlot(args.checkSlot(inventory, 1))))
}
- private def withInventory(side: EnumFacing, f: IItemHandler => Array[AnyRef]) =
- InventoryUtils.inventoryAt(position.offset(side), side.getOpposite) match {
- case Some(inventory) if mayInteract(position.offset(side), side.getOpposite, inventory) => f(inventory)
+ private def withInventorySource(side: EnumFacing, f: InventorySource => Array[AnyRef]) =
+ InventoryUtils.inventorySourceAt(position.offset(side), side.getOpposite) match {
+ case Some(inventory) if mayInteract(inventory) => f(inventory)
case _ => result(Unit, "no inventory")
}
+
+ private def withInventory(side: EnumFacing, f: IItemHandler => Array[AnyRef]) =
+ withInventorySource(side, is => f(is.inventory))
}
diff --git a/src/main/scala/li/cil/oc/server/machine/Machine.scala b/src/main/scala/li/cil/oc/server/machine/Machine.scala
index 40b707cc7..60cc85116 100644
--- a/src/main/scala/li/cil/oc/server/machine/Machine.scala
+++ b/src/main/scala/li/cil/oc/server/machine/Machine.scala
@@ -799,7 +799,7 @@ class Machine(val host: MachineHost) extends AbstractManagedEnvironment with mac
catch {
case t: Throwable =>
OpenComputers.log.error(
- s"""Unexpected error loading a state of computer at (${host.xPosition}, ${host.yPosition}, ${host.zPosition}). """ +
+ s"""Unexpected error loading a state of computer at ${host.machinePosition()}. """ +
s"""State: ${state.headOption.fold("no state")(_.toString)}. Unless you're upgrading/downgrading across a major version, please report this! Thank you.""", t)
close()
}
@@ -882,7 +882,7 @@ class Machine(val host: MachineHost) extends AbstractManagedEnvironment with mac
catch {
case t: Throwable =>
OpenComputers.log.error(
- s"""Unexpected error saving a state of computer at (${host.xPosition}, ${host.yPosition}, ${host.zPosition}). """ +
+ s"""Unexpected error saving a state of computer at ${host.machinePosition()}. """ +
s"""State: ${state.headOption.fold("no state")(_.toString)}. Unless you're upgrading/downgrading across a major version, please report this! Thank you.""", t)
}
})
diff --git a/src/main/scala/li/cil/oc/server/machine/luac/NativeLuaArchitecture.scala b/src/main/scala/li/cil/oc/server/machine/luac/NativeLuaArchitecture.scala
index f87d313d1..ed13eb4e3 100644
--- a/src/main/scala/li/cil/oc/server/machine/luac/NativeLuaArchitecture.scala
+++ b/src/main/scala/li/cil/oc/server/machine/luac/NativeLuaArchitecture.scala
@@ -376,7 +376,7 @@ abstract class NativeLuaArchitecture(val machine: api.machine.Machine) extends A
try lua.gc(LuaState.GcAction.COLLECT, 0) catch {
case t: Throwable =>
- OpenComputers.log.warn(s"Error cleaning up loaded computer @ (${machine.host.xPosition}, ${machine.host.yPosition}, ${machine.host.zPosition}). This either means the server is badly overloaded or a user created an evil __gc method, accidentally or not.")
+ OpenComputers.log.warn(s"Error cleaning up loaded computer @ ${machine.host().machinePosition()}. This either means the server is badly overloaded or a user created an evil __gc method, accidentally or not.")
machine.crash("error in garbage collector, most likely __gc method timed out")
}
} catch {
@@ -414,15 +414,15 @@ abstract class NativeLuaArchitecture(val machine: api.machine.Machine) extends A
try lua.gc(LuaState.GcAction.COLLECT, 0) catch {
case t: Throwable =>
- OpenComputers.log.warn(s"Error cleaning up loaded computer @ (${machine.host.xPosition}, ${machine.host.yPosition}, ${machine.host.zPosition}). This either means the server is badly overloaded or a user created an evil __gc method, accidentally or not.")
+ OpenComputers.log.warn(s"Error cleaning up loaded computer @ ${machine.host().machinePosition()}. This either means the server is badly overloaded or a user created an evil __gc method, accidentally or not.")
machine.crash("error in garbage collector, most likely __gc method timed out")
}
} catch {
case e: LuaRuntimeException =>
- OpenComputers.log.warn(s"Could not persist computer @ (${machine.host.xPosition}, ${machine.host.yPosition}, ${machine.host.zPosition}).\n${e.toString}" + (if (e.getLuaStackTrace.isEmpty) "" else "\tat " + e.getLuaStackTrace.mkString("\n\tat ")))
+ OpenComputers.log.warn(s"Could not persist computer @ ${machine.host().machinePosition()}.\n${e.toString}" + (if (e.getLuaStackTrace.isEmpty) "" else "\tat " + e.getLuaStackTrace.mkString("\n\tat ")))
nbt.removeTag("state")
case e: LuaGcMetamethodException =>
- OpenComputers.log.warn(s"Could not persist computer @ (${machine.host.xPosition}, ${machine.host.yPosition}, ${machine.host.zPosition}).\n${e.toString}")
+ OpenComputers.log.warn(s"Could not persist computer @ ${machine.host().machinePosition()}.\n${e.toString}")
nbt.removeTag("state")
}
diff --git a/src/main/scala/li/cil/oc/util/InventoryUtils.scala b/src/main/scala/li/cil/oc/util/InventoryUtils.scala
index d01c7714d..92fddbcd1 100644
--- a/src/main/scala/li/cil/oc/util/InventoryUtils.scala
+++ b/src/main/scala/li/cil/oc/util/InventoryUtils.scala
@@ -40,33 +40,40 @@ object InventoryUtils {
(!checkNBT || ItemStack.areItemStackTagsEqual(stackA, stackB))
/**
- * Retrieves an actual inventory implementation for a specified world coordinate.
- *
- * This performs special handling for (double-)chests and also checks for
- * mine carts with chests.
+ * Retrieves an actual inventory implementation for a specified world coordinate,
+ * complete with a reference to the source of said implementation.
*/
- def inventoryAt(position: BlockPosition, side: EnumFacing): Option[IItemHandler] = position.world match {
+ def inventorySourceAt(position: BlockPosition, side: EnumFacing): Option[InventorySource] = position.world match {
case Some(world) if world.blockExists(position) => world.getTileEntity(position) match {
- case tile: TileEntity if tile.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side) => Option(tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side))
- case tile: IInventory => Option(asItemHandler(tile, side))
+ case tile: TileEntity if tile.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side) => Option(BlockInventorySource(position, side, tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side)))
+ case tile: IInventory => Option(BlockInventorySource(position, side, asItemHandler(tile, side)))
case _ => world.getEntitiesWithinAABB(classOf[Entity], position.bounds)
.filter(e => !e.isDead && e.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side))
- .map(_.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side))
- .find(_ != null)
+ .map(a => EntityInventorySource(a, side, a.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side)))
+ .find(a => a != null && a.inventory != null)
}
case _ => None
}
- def anyInventoryAt(position: BlockPosition): Option[IItemHandler] = {
+ /**
+ * Retrieves an actual inventory implementation for a specified world coordinate.
+ */
+ def inventoryAt(position: BlockPosition, side: EnumFacing): Option[IItemHandler] = inventorySourceAt(position, side)
+ .map(a => a.inventory)
+
+ def anyInventorySourceAt(position: BlockPosition): Option[InventorySource] = {
for(side <- null :: EnumFacing.VALUES.toList) {
- inventoryAt(position, side) match {
- case inv: Some[IItemHandler] => return inv
+ inventorySourceAt(position, side) match {
+ case inv: Some[InventorySource] => return inv
case _ =>
}
}
None
}
+ def anyInventoryAt(position: BlockPosition): Option[IItemHandler] = anyInventorySourceAt(position)
+ .map(a => a.inventory)
+
/**
* Inserts a stack into an inventory.
*
@@ -397,3 +404,10 @@ object InventoryUtils {
case _ => null
}
}
+
+sealed trait InventorySource {
+ def side: EnumFacing
+ def inventory: IItemHandler
+}
+final case class BlockInventorySource(position: BlockPosition, side: EnumFacing, inventory: IItemHandler) extends InventorySource
+final case class EntityInventorySource(entity: Entity, side: EnumFacing, inventory: IItemHandler) extends InventorySource
\ No newline at end of file