Merge branch 'master-MC1.7.10' of github.com:MightyPirates/OpenComputers into OC1.5-MC1.7.10

Conflicts:
	build.properties
This commit is contained in:
Florian Nücke 2015-02-25 12:57:04 +01:00
commit 30f23adf35
30 changed files with 278 additions and 88 deletions

View File

@ -1,7 +1,7 @@
minecraft.version=1.7.10 minecraft.version=1.7.10
forge.version=10.13.2.1236 forge.version=10.13.2.1236
oc.version=1.5.0 oc.version=1.5.1
oc.subversion= oc.subversion=
ae2.version=rv1-stable-1 ae2.version=rv1-stable-1

View File

@ -195,7 +195,7 @@ oc:tooltip.Cable=Ein billiger Weg, verschiedene Blöcke miteinander zu verbinden
oc:tooltip.Capacitor=Speichert Energie für spätere Verwendung. Kann extrem schnell befüllt und entleert werden. oc:tooltip.Capacitor=Speichert Energie für spätere Verwendung. Kann extrem schnell befüllt und entleert werden.
oc:tooltip.CardBase=Wie der Name schon sagt, werden alle Erweiterungskarten hieraus hergestellt. oc:tooltip.CardBase=Wie der Name schon sagt, werden alle Erweiterungskarten hieraus hergestellt.
oc:tooltip.Case=Das Computergehäuse ist der essentielle Grundbaustein für einen Computer. §fErweiterungskarten§7, §fRAM§7 und §fFestplatten§7 können in einem Gehäuse installiert werden.[nl] Slots: §f%s§7 oc:tooltip.Case=Das Computergehäuse ist der essentielle Grundbaustein für einen Computer. §fErweiterungskarten§7, §fRAM§7 und §fFestplatten§7 können in einem Gehäuse installiert werden.[nl] Slots: §f%s§7
oc:tooltip.Charger=Lädt Roboter mit Energie aus Kondensatoren auf. Die Ladegeschwindigkeit hängt vom eingehenden §fRedstonesignal§7 ab, wobei kein Signal "nicht laden" und ein Signal mit maximaler Stärke "schnellstmöglich laden" heißt. oc:tooltip.Charger=Lädt Roboter mit Energie aus Kondensatoren auf. Die Ladegeschwindigkeit hängt vom eingehenden §fRedstonesignal§7 ab, wobei kein Signal "nicht laden" und ein Signal mit maximaler Stärke "schnellstmöglich laden" heißt. Erlaubt es auch Tablets zu laden, und ermöglicht Zugriff auf Festplatten in Tablets.
oc:tooltip.CircuitBoard=Mühsam ernährt sich das Eichhörnchen. Wenn es groß wird, wird es mal eine gedruckte Leiterplatte. oc:tooltip.CircuitBoard=Mühsam ernährt sich das Eichhörnchen. Wenn es groß wird, wird es mal eine gedruckte Leiterplatte.
oc:tooltip.ControlUnit=Klingt wichtig, ist es auch. Man baut daraus immerhin CPUs. Wie könnte es da nicht wichtig sein. oc:tooltip.ControlUnit=Klingt wichtig, ist es auch. Man baut daraus immerhin CPUs. Wie könnte es da nicht wichtig sein.
oc:tooltip.ComponentBus=Diese Erweiterung erlaubt es es Servern, mit noch mehr Komponenten gleichzeitig zu kommunizieren, ähnlich wie CPUs.[nl] Supported components: §f%s§7 oc:tooltip.ComponentBus=Diese Erweiterung erlaubt es es Servern, mit noch mehr Komponenten gleichzeitig zu kommunizieren, ähnlich wie CPUs.[nl] Supported components: §f%s§7

View File

@ -219,7 +219,7 @@ oc:tooltip.Cable=A cheap way of connecting blocks.
oc:tooltip.Capacitor=Stores energy for later use. Can be filled and emptied very quickly. oc:tooltip.Capacitor=Stores energy for later use. Can be filled and emptied very quickly.
oc:tooltip.CardBase=As the name indicates, this is the basic building block for all expansion cards. oc:tooltip.CardBase=As the name indicates, this is the basic building block for all expansion cards.
oc:tooltip.Case=The Computer Case is the basic building block for computers and houses the computer's §fextension cards§7, §fRAM§7 and §fhard disks§7.[nl] Slots: §f%s§7 oc:tooltip.Case=The Computer Case is the basic building block for computers and houses the computer's §fextension cards§7, §fRAM§7 and §fhard disks§7.[nl] Slots: §f%s§7
oc:tooltip.Charger=Transfers energy from capacitors into adjacent robots and drones. The transfer rate depends on the incoming §fredstone signal§7, where no signal means don't charge devices, and maximum strength means charge at full speed. oc:tooltip.Charger=Transfers energy from capacitors into adjacent robots and drones. The transfer rate depends on the incoming §fredstone signal§7, where no signal means don't charge devices, and maximum strength means charge at full speed. Can also be used to charge tablets and access hard drives in tablets.
oc:tooltip.CircuitBoard=Now we're getting somewhere. Can be etched to obtain a printed circuit board. oc:tooltip.CircuitBoard=Now we're getting somewhere. Can be etched to obtain a printed circuit board.
oc:tooltip.ControlUnit=This is the unit that... controls... stuff. You need it to build a CPU. So yeah, totally important. oc:tooltip.ControlUnit=This is the unit that... controls... stuff. You need it to build a CPU. So yeah, totally important.
oc:tooltip.ComponentBus=This expansion allows servers to communicate with more components at the same time, similar to how CPUs do.[nl] Supported components: §f%s§7 oc:tooltip.ComponentBus=This expansion allows servers to communicate with more components at the same time, similar to how CPUs do.[nl] Supported components: §f%s§7

View File

@ -1,8 +1,8 @@
local rc = require('rc') local rc = require('rc')
local args = table.pack(...) local args = table.pack(...)
if args.n < 2 then if args.n < 1 then
io.write("Usage: rc <service> <command> [args...]") io.write("Usage: rc <service> [command] [args...]")
return return
end end
@ -11,3 +11,4 @@ local result, reason = rc.runCommand(table.unpack(args))
if not result then if not result then
io.stderr:write(reason .. "\n") io.stderr:write(reason .. "\n")
end end

View File

@ -1,4 +1,5 @@
local fs = require('filesystem') local fs = require('filesystem')
local serialization = require('serialization')
-- Keeps track of loaded scripts to retain local values between invocation -- Keeps track of loaded scripts to retain local values between invocation
-- of their command callbacks. -- of their command callbacks.
@ -18,6 +19,19 @@ local function loadConfig()
return nil, reason return nil, reason
end end
local function saveConfig(conf)
local file, reason = io.open('/etc/rc.cfg', 'w')
if not file then
return nil, reason
end
for key, value in pairs(conf) do
file:write(tostring(key) .. " = " .. serialization.serialize(value) .. "\n")
end
file:close()
return true
end
function rc.load(name, args) function rc.load(name, args)
if loaded[name] then if loaded[name] then
return loaded[name] return loaded[name]
@ -39,14 +53,47 @@ function rc.unload(name)
loaded[name] = nil loaded[name] = nil
end end
local function rawRunCommand(name, cmd, args, ...) local function rawRunCommand(conf, name, cmd, args, ...)
local result, what = rc.load(name, args) local result, what = rc.load(name, args)
if result then if result then
if type(result[cmd]) == "function" then if not cmd then
result, what = xpcall(result[cmd], debug.traceback, ...) io.output():write("Commands for service " .. name .. "\n")
if result then for command, val in pairs(result) do
if type(val) == "function" then
io.output():write(tostring(command) .. " ")
end
end
return true
elseif type(result[cmd]) == "function" then
res, what = xpcall(result[cmd], debug.traceback, ...)
if res then
return true return true
end end
elseif cmd == "restart" and type(result["stop"]) == "function" and type(result["start"]) == "function" then
res, what = xpcall(result["stop"], debug.traceback, ...)
if res then
res, what = xpcall(result["start"], debug.traceback, ...)
if res then
return true
end
end
elseif cmd == "enable" then
conf.enabled = conf.enabled or {}
for _, _name in ipairs(conf.enabled) do
if name == _name then
return nil, "Service already enabled"
end
end
conf.enabled[#conf.enabled + 1] = name
return saveConfig(conf)
elseif cmd == "disable" then
conf.enabled = conf.enabled or {}
for n, _name in ipairs(conf.enabled) do
if name == _name then
table.remove(conf.enabled, n)
end
end
return saveConfig(conf)
else else
what = "Command '" .. cmd .. "' not found in daemon '" .. name .. "'" what = "Command '" .. cmd .. "' not found in daemon '" .. name .. "'"
end end
@ -59,7 +106,7 @@ function rc.runCommand(name, cmd, ...)
if not conf then if not conf then
return nil, reason return nil, reason
end end
return rawRunCommand(name, cmd, conf[name], ...) return rawRunCommand(conf, name, cmd, conf[name], ...)
end end
function rc.allRunCommand(cmd, ...) function rc.allRunCommand(cmd, ...)
@ -69,9 +116,10 @@ function rc.allRunCommand(cmd, ...)
end end
local results = {} local results = {}
for _, name in ipairs(conf.enabled or {}) do for _, name in ipairs(conf.enabled or {}) do
results[name] = table.pack(rawRunCommand(name, cmd, conf[name], ...)) results[name] = table.pack(rawRunCommand(conf, name, cmd, conf[name], ...))
end end
return results return results
end end
return rc return rc

View File

@ -5,7 +5,7 @@ SYNOPSIS
df [FILE]... df [FILE]...
DESCRIPTION DESCRIPTION
`cp` allows copying single files on a filesystem and across filesystems. `df` outputs disk space information for the file systems containing the specified files. If no file names are given it returns the information for all currently mounted file systems.
EXAMPLES EXAMPLES
df df

View File

@ -6,7 +6,7 @@ SYNOPSIS
primary TYPE ADDRESS primary TYPE ADDRESS
DESCRIPTION DESCRIPTION
This program allows reading the address of the current primary component of the specified type. It also allows chaning the current primary component for a specified type by providing the (abbreviated) address of the new primary component. This program allows reading the address of the current primary component of the specified type. It also allows changing the current primary component for a specified type by providing the (abbreviated) address of the new primary component.
EXAMPLES EXAMPLES
primary gpu primary gpu

View File

@ -0,0 +1,36 @@
NAME
rc - Manage services
SYNOPSIS
rc SERVICE COMMAND [ARGS...]
DESCRIPTION
Controls services in /etc/rc.d/
Common commands are start/stop/restart, there are also special commands enable/disable. A command is global function in executable file that is stored in /etc/rc.d/ directory. Service cen define own commands.
COMMANDS
start
This command starts specified service, executed automatically for all enbled services when system boots.
stop
This command stops specified service.
restart
This command restarts specified service. This command doesn't have to be implemented by services when start and stop commands are present.
enable
This command enables specified service. Executing this command won't start the service. It's implemented by the rc library, but can be overridden by service.
disable
This command disables specified service. Executing this command won't stop the service. It's implemented by the rc library, but can be overridden by service.
EXAMPLES
rc example
Lists commands of example service.
rc example start
Starts example setvice.
rc example enable
Makes example start on system boot.

View File

@ -15,6 +15,7 @@ import li.cil.oc.api.detail.ItemInfo
import li.cil.oc.client.renderer.PetRenderer import li.cil.oc.client.renderer.PetRenderer
import li.cil.oc.client.{PacketSender => ClientPacketSender} import li.cil.oc.client.{PacketSender => ClientPacketSender}
import li.cil.oc.common.item.data.MicrocontrollerData import li.cil.oc.common.item.data.MicrocontrollerData
import li.cil.oc.common.tileentity.Robot
import li.cil.oc.common.tileentity.traits.power import li.cil.oc.common.tileentity.traits.power
import li.cil.oc.integration.Mods import li.cil.oc.integration.Mods
import li.cil.oc.integration.util import li.cil.oc.integration.util
@ -40,6 +41,12 @@ object EventHandler {
var totalWorldTicks = 0L var totalWorldTicks = 0L
private val runningRobots = mutable.Set.empty[Robot]
def onRobotStart(robot: Robot): Unit = runningRobots += robot
def onRobotStopped(robot: Robot): Unit = runningRobots -= robot
def schedule(tileEntity: TileEntity) { def schedule(tileEntity: TileEntity) {
if (SideTracker.isServer) pending.synchronized { if (SideTracker.isServer) pending.synchronized {
pending += (() => Network.joinOrCreateNetwork(tileEntity)) pending += (() => Network.joinOrCreateNetwork(tileEntity))
@ -108,6 +115,13 @@ object EventHandler {
case t: Throwable => OpenComputers.log.warn("Error in scheduled tick action.", t) case t: Throwable => OpenComputers.log.warn("Error in scheduled tick action.", t)
} }
}) })
val invalid = mutable.ArrayBuffer.empty[Robot]
runningRobots.foreach(robot => {
if (robot.isInvalid) invalid += robot
else robot.machine.update()
})
runningRobots --= invalid
} }
@SubscribeEvent @SubscribeEvent
@ -207,15 +221,7 @@ object EventHandler {
// Presents! // Presents!
val present = api.Items.get("present").createItemStack(1) val present = api.Items.get("present").createItemStack(1)
e.player.worldObj.playSoundAtEntity(e.player, "note.pling", 0.2f, 1f) e.player.worldObj.playSoundAtEntity(e.player, "note.pling", 0.2f, 1f)
if (e.player.inventory.addItemStackToInventory(present)) { InventoryUtils.addToPlayerInventory(present, e.player)
e.player.inventory.markDirty()
if (e.player.openContainer != null) {
e.player.openContainer.detectAndSendChanges()
}
}
else {
e.player.dropPlayerItemWithRandomChoice(present, false)
}
} }
case _ => // Nope. case _ => // Nope.
} }
@ -236,9 +242,8 @@ object EventHandler {
for (slot <- 0 until e.craftMatrix.getSizeInventory) { for (slot <- 0 until e.craftMatrix.getSizeInventory) {
val stack = e.craftMatrix.getStackInSlot(slot) val stack = e.craftMatrix.getStackInSlot(slot)
if (api.Items.get(stack) == item) { if (api.Items.get(stack) == item) {
callback(stack).foreach(extra => if (!e.player.inventory.addItemStackToInventory(extra)) { callback(stack).foreach(extra =>
e.player.dropPlayerItemWithRandomChoice(extra, false) InventoryUtils.addToPlayerInventory(extra, e.player))
})
} }
} }
true true

View File

@ -83,11 +83,16 @@ object SaveHandler {
def loadNBT(nbt: NBTTagCompound, name: String): NBTTagCompound = { def loadNBT(nbt: NBTTagCompound, name: String): NBTTagCompound = {
val data = load(nbt, name) val data = load(nbt, name)
if (data.length > 0) { if (data.length > 0) try {
val bais = new ByteArrayInputStream(data) val bais = new ByteArrayInputStream(data)
val dis = new DataInputStream(bais) val dis = new DataInputStream(bais)
CompressedStreamTools.read(dis) CompressedStreamTools.read(dis)
} }
catch {
case t: Throwable =>
OpenComputers.log.warn("There was an error trying to restore a block's state from external data. This indicates that data was somehow corrupted.", t)
new NBTTagCompound()
}
else new NBTTagCompound() else new NBTTagCompound()
} }

View File

@ -3,6 +3,7 @@ package li.cil.oc.common.block
import java.util import java.util
import li.cil.oc.Settings import li.cil.oc.Settings
import li.cil.oc.api
import li.cil.oc.client.KeyBindings import li.cil.oc.client.KeyBindings
import li.cil.oc.common.Tier import li.cil.oc.common.Tier
import li.cil.oc.common.item.data.MicrocontrollerData import li.cil.oc.common.item.data.MicrocontrollerData
@ -10,6 +11,7 @@ import li.cil.oc.common.tileentity
import li.cil.oc.integration.util.NEI import li.cil.oc.integration.util.NEI
import li.cil.oc.integration.util.Wrench import li.cil.oc.integration.util.Wrench
import li.cil.oc.util.BlockPosition import li.cil.oc.util.BlockPosition
import li.cil.oc.util.InventoryUtils
import li.cil.oc.util.Rarity import li.cil.oc.util.Rarity
import net.minecraft.entity.EntityLivingBase import net.minecraft.entity.EntityLivingBase
import net.minecraft.entity.player.EntityPlayer import net.minecraft.entity.player.EntityPlayer
@ -68,16 +70,32 @@ class Microcontroller(protected implicit val tileTag: ClassTag[tileentity.Microc
override def onBlockActivated(world: World, x: Int, y: Int, z: Int, player: EntityPlayer, override def onBlockActivated(world: World, x: Int, y: Int, z: Int, player: EntityPlayer,
side: ForgeDirection, hitX: Float, hitY: Float, hitZ: Float) = { side: ForgeDirection, hitX: Float, hitY: Float, hitZ: Float) = {
if (!player.isSneaking && !Wrench.holdsApplicableWrench(player, BlockPosition(x, y, z))) { if (!Wrench.holdsApplicableWrench(player, BlockPosition(x, y, z))) {
if (!world.isRemote) { if (!player.isSneaking) {
world.getTileEntity(x, y, z) match { if (!world.isRemote) {
case mcu: tileentity.Microcontroller => world.getTileEntity(x, y, z) match {
if (mcu.machine.isRunning) mcu.machine.stop() case mcu: tileentity.Microcontroller =>
else mcu.machine.start() if (mcu.machine.isRunning) mcu.machine.stop()
case _ => else mcu.machine.start()
case _ =>
}
} }
true
} }
true else if (api.Items.get(player.getHeldItem) == api.Items.get("eeprom")) {
if (!world.isRemote) {
world.getTileEntity(x, y, z) match {
case mcu: tileentity.Microcontroller =>
val newEeprom = player.inventory.decrStackSize(player.inventory.currentItem, 1)
mcu.changeEEPROM(newEeprom) match {
case Some(oldEeprom) => InventoryUtils.addToPlayerInventory(oldEeprom, player)
case _ =>
}
}
}
true
}
else false
} }
else false else false
} }

View File

@ -3,6 +3,7 @@ package li.cil.oc.common.container
import li.cil.oc.client.gui.Icons import li.cil.oc.client.gui.Icons
import li.cil.oc.common import li.cil.oc.common
import li.cil.oc.common.InventorySlots.InventorySlot import li.cil.oc.common.InventorySlots.InventorySlot
import li.cil.oc.util.InventoryUtils
import li.cil.oc.util.SideTracker import li.cil.oc.util.SideTracker
import net.minecraft.entity.player.EntityPlayer import net.minecraft.entity.player.EntityPlayer
import net.minecraft.inventory.IInventory import net.minecraft.inventory.IInventory
@ -36,8 +37,7 @@ class DynamicComponentSlot(val container: Player, inventory: IInventory, index:
if (SideTracker.isServer && getHasStack && !isItemValid(getStack)) { if (SideTracker.isServer && getHasStack && !isItemValid(getStack)) {
val stack = getStack val stack = getStack
putStack(null) putStack(null)
player.inventory.addItemStackToInventory(stack) InventoryUtils.addToPlayerInventory(stack, player)
player.dropPlayerItemWithRandomChoice(stack, false)
} }
} }
} }

View File

@ -31,7 +31,7 @@ object RobotCommonHandler {
val maxFlyingHeight = Settings.get.limitFlightHeight val maxFlyingHeight = Settings.get.limitFlightHeight
def isMovingDown = e.direction == ForgeDirection.DOWN def isMovingDown = e.direction == ForgeDirection.DOWN
def hasAdjacentBlock(pos: BlockPosition) = ForgeDirection.VALID_DIRECTIONS.exists(side => world.isSideSolid(pos.offset(side), side.getOpposite)) def hasAdjacentBlock(pos: BlockPosition) = ForgeDirection.VALID_DIRECTIONS.exists(side => world.isSideSolid(pos.offset(side), side.getOpposite))
def isWithinFlyingHeight(pos: BlockPosition) = (1 to maxFlyingHeight).exists(n => !world.isAirBlock(pos.offset(e.direction.getOpposite, n))) def isWithinFlyingHeight(pos: BlockPosition) = (1 to maxFlyingHeight).exists(n => !world.isAirBlock(pos.offset(ForgeDirection.DOWN, n)))
val startPos = BlockPosition(robot) val startPos = BlockPosition(robot)
val targetPos = startPos.offset(e.direction) val targetPos = startPos.offset(e.direction)
// New movement rules as of 1.5: // New movement rules as of 1.5:

View File

@ -280,7 +280,7 @@ object Items extends ItemAPI {
def init() { def init() {
multi = new item.Delegator() { multi = new item.Delegator() {
lazy val configuredItems = Array( def configuredItems = Array(
createOpenOS(), createOpenOS(),
createLuaBios(), createLuaBios(),
createConfiguredDrone(), createConfiguredDrone(),

View File

@ -11,6 +11,8 @@ import net.minecraftforge.common.util.Constants.NBT
trait Inventory extends IInventory { trait Inventory extends IInventory {
def items: Array[Option[ItemStack]] def items: Array[Option[ItemStack]]
def updateItems(slot: Int, stack: ItemStack) = items(slot) = Option(stack)
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
override def getStackInSlot(slot: Int) = override def getStackInSlot(slot: Int) =
@ -45,7 +47,7 @@ trait Inventory extends IInventory {
} }
val oldStack = items(slot) val oldStack = items(slot)
items(slot) = None updateItems(slot, null)
if (oldStack.isDefined) { if (oldStack.isDefined) {
onItemRemoved(slot, oldStack.get) onItemRemoved(slot, oldStack.get)
} }
@ -53,7 +55,7 @@ trait Inventory extends IInventory {
if (stack.stackSize > getInventoryStackLimit) { if (stack.stackSize > getInventoryStackLimit) {
stack.stackSize = getInventoryStackLimit stack.stackSize = getInventoryStackLimit
} }
items(slot) = Some(stack) updateItems(slot, stack)
} }
if (items(slot).isDefined) { if (items(slot).isDefined) {
@ -84,7 +86,7 @@ trait Inventory extends IInventory {
nbt.getTagList(Settings.namespace + "items", NBT.TAG_COMPOUND).foreach((slotNbt: NBTTagCompound) => { nbt.getTagList(Settings.namespace + "items", NBT.TAG_COMPOUND).foreach((slotNbt: NBTTagCompound) => {
val slot = slotNbt.getByte("slot") val slot = slotNbt.getByte("slot")
if (slot >= 0 && slot < items.length) { if (slot >= 0 && slot < items.length) {
items(slot) = Option(ItemUtils.loadStack(slotNbt.getCompoundTag("item"))) updateItems(slot, ItemUtils.loadStack(slotNbt.getCompoundTag("item")))
} }
}) })
} }

View File

@ -11,7 +11,9 @@ trait ItemStackInventory extends Inventory {
// The item stack that provides the inventory. // The item stack that provides the inventory.
def container: ItemStack def container: ItemStack
lazy val items = Array.fill[Option[ItemStack]](getSizeInventory)(None) private lazy val inventory = Array.fill[Option[ItemStack]](getSizeInventory)(None)
override def items = inventory
// Initialize the list automatically if we have a container. // Initialize the list automatically if we have a container.
if (container != null) { if (container != null) {
@ -24,14 +26,14 @@ trait ItemStackInventory extends Inventory {
container.setTagCompound(new NBTTagCompound()) container.setTagCompound(new NBTTagCompound())
} }
for (i <- 0 until items.length) { for (i <- 0 until items.length) {
items(i) = None updateItems(i, null)
} }
if (container.getTagCompound.hasKey(Settings.namespace + "items")) { if (container.getTagCompound.hasKey(Settings.namespace + "items")) {
val list = container.getTagCompound.getTagList(Settings.namespace + "items", NBT.TAG_COMPOUND) val list = container.getTagCompound.getTagList(Settings.namespace + "items", NBT.TAG_COMPOUND)
for (i <- 0 until (list.tagCount min items.length)) { for (i <- 0 until (list.tagCount min items.length)) {
val tag = list.getCompoundTagAt(i) val tag = list.getCompoundTagAt(i)
if (!tag.hasNoTags) { if (!tag.hasNoTags) {
items(i) = Option(ItemUtils.loadStack(tag)) updateItems(i, ItemUtils.loadStack(tag))
} }
} }
} }

View File

@ -1,9 +1,13 @@
package li.cil.oc.common.item package li.cil.oc.common.item
import li.cil.oc.Settings import li.cil.oc.Settings
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.world.World
class EEPROM extends SimpleItem { class EEPROM extends SimpleItem {
override def doesSneakBypassUse(world: World, x: Int, y: Int, z: Int, player: EntityPlayer): Boolean = true
override def getItemStackDisplayName(stack: ItemStack): String = { override def getItemStackDisplayName(stack: ItemStack): String = {
if (stack.hasTagCompound) { if (stack.hasTagCompound) {
val tag = stack.getTagCompound val tag = stack.getTagCompound

View File

@ -4,6 +4,7 @@ import java.util.Random
import li.cil.oc.OpenComputers import li.cil.oc.OpenComputers
import li.cil.oc.api import li.cil.oc.api
import li.cil.oc.util.InventoryUtils
import li.cil.oc.util.ItemUtils import li.cil.oc.util.ItemUtils
import net.minecraft.entity.player.EntityPlayer import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
@ -20,15 +21,7 @@ class Present(val parent: Delegator) extends Delegate {
if (!world.isRemote) { if (!world.isRemote) {
world.playSoundAtEntity(player, "random.levelup", 0.2f, 1f) world.playSoundAtEntity(player, "random.levelup", 0.2f, 1f)
val present = Present.nextPresent() val present = Present.nextPresent()
if (player.inventory.addItemStackToInventory(present)) { InventoryUtils.addToPlayerInventory(present, player)
player.inventory.markDirty()
if (player.openContainer != null) {
player.openContainer.detectAndSendChanges()
}
}
else {
player.dropPlayerItemWithRandomChoice(present, false)
}
} }
} }
stack stack

View File

@ -17,20 +17,26 @@ class MicrocontrollerData extends ItemData {
var tier = Tier.One var tier = Tier.One
var components = Array.empty[ItemStack] var components = Array[ItemStack](null)
var storedEnergy = 0 var storedEnergy = 0
override def load(nbt: NBTTagCompound) { override def load(nbt: NBTTagCompound) {
tier = nbt.getByte(Settings.namespace + "tier") tier = nbt.getByte(Settings.namespace + "tier")
components = nbt.getTagList(Settings.namespace + "components", NBT.TAG_COMPOUND). components = nbt.getTagList(Settings.namespace + "components", NBT.TAG_COMPOUND).
toArray[NBTTagCompound].map(ItemUtils.loadStack) toArray[NBTTagCompound].map(ItemUtils.loadStack).filter(_ != null)
storedEnergy = nbt.getInteger(Settings.namespace + "storedEnergy") storedEnergy = nbt.getInteger(Settings.namespace + "storedEnergy")
// Reserve slot for EEPROM if necessary, avoids having to resize the
// components array in the MCU tile entity, which isn't possible currently.
if (!components.exists(stack => api.Items.get(stack) == api.Items.get("eeprom"))) {
components :+= null
}
} }
override def save(nbt: NBTTagCompound) { override def save(nbt: NBTTagCompound) {
nbt.setByte(Settings.namespace + "tier", tier.toByte) nbt.setByte(Settings.namespace + "tier", tier.toByte)
nbt.setNewTagList(Settings.namespace + "components", components.toIterable) nbt.setNewTagList(Settings.namespace + "components", components.filter(_ != null).toIterable)
nbt.setInteger(Settings.namespace + "storedEnergy", storedEnergy) nbt.setInteger(Settings.namespace + "storedEnergy", storedEnergy)
} }

View File

@ -83,7 +83,7 @@ class Assembler extends traits.Environment with traits.PowerAcceptor with traits
requiredEnergy = totalRequiredEnergy requiredEnergy = totalRequiredEnergy
ServerPacketSender.sendRobotAssembling(this, assembling = true) ServerPacketSender.sendRobotAssembling(this, assembling = true)
for (slot <- 0 until getSizeInventory) items(slot) = None for (slot <- 0 until getSizeInventory) updateItems(slot, null)
markDirty() markDirty()
true true

View File

@ -221,7 +221,9 @@ class Microcontroller extends traits.PowerAcceptor with traits.Hub with traits.C
nbt.setNewCompoundTag("info", info.save) nbt.setNewCompoundTag("info", info.save)
} }
override lazy val items = info.components.map(Option(_)) override def items = info.components.map(Option(_))
override def updateItems(slot: Int, stack: ItemStack): Unit = info.components(slot) = stack
override def getSizeInventory = info.components.length override def getSizeInventory = info.components.length
@ -232,4 +234,19 @@ class Microcontroller extends traits.PowerAcceptor with traits.Hub with traits.C
// Nope. // Nope.
override def decrStackSize(slot: Int, amount: Int) = null override def decrStackSize(slot: Int, amount: Int) = null
// For hotswapping EEPROMs.
def changeEEPROM(newEeprom: ItemStack) = {
val oldEepromIndex = info.components.indexWhere(api.Items.get(_) == api.Items.get("eeprom"))
if (oldEepromIndex >= 0) {
val oldEeprom = info.components(oldEepromIndex)
super.setInventorySlotContents(oldEepromIndex, newEeprom)
Some(oldEeprom)
}
else {
assert(info.components(getSizeInventory - 1) == null)
super.setInventorySlotContents(getSizeInventory - 1, newEeprom)
None
}
}
} }

View File

@ -13,6 +13,7 @@ import li.cil.oc.api.event.RobotMoveEvent
import li.cil.oc.api.internal import li.cil.oc.api.internal
import li.cil.oc.api.network._ import li.cil.oc.api.network._
import li.cil.oc.client.gui import li.cil.oc.client.gui
import li.cil.oc.common.EventHandler
import li.cil.oc.common.Slot import li.cil.oc.common.Slot
import li.cil.oc.common.Tier import li.cil.oc.common.Tier
import li.cil.oc.common.inventory.InventorySelection import li.cil.oc.common.inventory.InventorySelection
@ -402,6 +403,16 @@ class Robot extends traits.Computer with traits.PowerInformation with IFluidHand
} }
} }
// The robot's machine is updated in a tick handler, to avoid delayed tile
// entity creation when moving, which would screw over all the things...
override protected def updateComputer(): Unit = {}
override protected def onRunningChanged(): Unit = {
super.onRunningChanged()
if (isRunning) EventHandler.onRobotStart(this)
else EventHandler.onRobotStopped(this)
}
override protected def initialize() { override protected def initialize() {
if (isServer) { if (isServer) {
// Ensure we have a node address, because the proxy needs this to initialize // Ensure we have a node address, because the proxy needs this to initialize
@ -419,6 +430,7 @@ class Robot extends traits.Computer with traits.PowerInformation with IFluidHand
case _ => case _ =>
} }
} }
else EventHandler.onRobotStopped(this)
} }
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
@ -452,6 +464,7 @@ class Robot extends traits.Computer with traits.PowerInformation with IFluidHand
// robot's proxy instance. // robot's proxy instance.
_isOutputEnabled = hasRedstoneCard _isOutputEnabled = hasRedstoneCard
_isAbstractBusAvailable = hasAbstractBusCard _isAbstractBusAvailable = hasAbstractBusCard
if (isRunning) EventHandler.onRobotStart(this)
} }
// Side check for Waila (and other mods that may call this client side). // Side check for Waila (and other mods that may call this client side).

View File

@ -98,19 +98,18 @@ trait Computer extends Environment with ComponentInventory with Rotatable with B
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
override def updateEntity() { override def updateEntity(): Unit = {
// If we're not yet in a network we might have just been loaded from disk,
// meaning there may be other tile entities that also have not re-joined
// the network. We skip the update this round to allow other tile entities
// to join the network, too, avoiding issues of missing nodes (e.g. in the
// GPU which would otherwise loose track of its screen).
if (isServer && isConnected) { if (isServer && isConnected) {
// If we're not yet in a network we might have just been loaded from disk, updateComputer()
// meaning there may be other tile entities that also have not re-joined
// the network. We skip the update this round to allow other tile entities
// to join the network, too, avoiding issues of missing nodes (e.g. in the
// GPU which would otherwise loose track of its screen).
machine.update()
if (_isRunning != machine.isRunning) { if (_isRunning != machine.isRunning) {
_isRunning = machine.isRunning _isRunning = machine.isRunning
markDirty() onRunningChanged()
ServerPacketSender.sendComputerState(this)
} }
updateComponents() updateComponents()
@ -119,6 +118,15 @@ trait Computer extends Environment with ComponentInventory with Rotatable with B
super.updateEntity() super.updateEntity()
} }
protected def updateComputer(): Unit = {
machine.update()
}
protected def onRunningChanged(): Unit = {
markDirty()
ServerPacketSender.sendComputerState(this)
}
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
override def readFromNBTForServer(nbt: NBTTagCompound) { override def readFromNBTForServer(nbt: NBTTagCompound) {

View File

@ -9,7 +9,9 @@ import net.minecraft.nbt.NBTTagCompound
import net.minecraftforge.common.util.ForgeDirection import net.minecraftforge.common.util.ForgeDirection
trait Inventory extends TileEntity with inventory.Inventory { trait Inventory extends TileEntity with inventory.Inventory {
lazy val items = Array.fill[Option[ItemStack]](getSizeInventory)(None) private lazy val inventory = Array.fill[Option[ItemStack]](getSizeInventory)(None)
def items = inventory
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //

View File

@ -2,12 +2,15 @@ package li.cil.oc.common.tileentity.traits
import cpw.mods.fml.relauncher.Side import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly import cpw.mods.fml.relauncher.SideOnly
import li.cil.oc.Settings
import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.server.{PacketSender => ServerPacketSender}
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound
trait PowerInformation extends TileEntity { trait PowerInformation extends TileEntity {
private var lastSentRatio = -1.0 private var lastSentRatio = -1.0
private var ticksUntilSync = 0
def globalBuffer: Double def globalBuffer: Double
def globalBuffer_=(value: Double): Unit def globalBuffer_=(value: Double): Unit
@ -18,12 +21,23 @@ trait PowerInformation extends TileEntity {
protected def updatePowerInformation() { protected def updatePowerInformation() {
val ratio = if (globalBufferSize > 0) globalBuffer / globalBufferSize else 0 val ratio = if (globalBufferSize > 0) globalBuffer / globalBufferSize else 0
if (lastSentRatio < 0 || math.abs(lastSentRatio - ratio) > (5.0 / 100.0)) { if (shouldSync(ratio) || hasChangedSignificantly(ratio)) {
lastSentRatio = ratio lastSentRatio = ratio
ServerPacketSender.sendPowerState(this) ServerPacketSender.sendPowerState(this)
} }
} }
private def hasChangedSignificantly(ratio: Double) = lastSentRatio < 0 || math.abs(lastSentRatio - ratio) > (5.0 / 100.0)
private def shouldSync(ratio: Double) = {
ticksUntilSync -= 1
if (ticksUntilSync <= 0) {
ticksUntilSync = (100 / Settings.get.tickFrequency).toInt max 1
lastSentRatio != ratio
}
else false
}
@SideOnly(Side.CLIENT) @SideOnly(Side.CLIENT)
override def readFromNBTForClient(nbt: NBTTagCompound) { override def readFromNBTForClient(nbt: NBTTagCompound) {
super.readFromNBTForClient(nbt) super.readFromNBTForClient(nbt)

View File

@ -220,7 +220,7 @@ object PacketSender {
val pb = new SimplePacketBuilder(PacketType.PowerState) val pb = new SimplePacketBuilder(PacketType.PowerState)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.writeDouble(t.globalBuffer) pb.writeDouble(math.round(t.globalBuffer))
pb.writeDouble(t.globalBufferSize) pb.writeDouble(t.globalBufferSize)
pb.sendToPlayersNearTileEntity(t) pb.sendToPlayersNearTileEntity(t)

View File

@ -153,7 +153,7 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
override def attackTargetEntityWithCurrentItem(entity: Entity) { override def attackTargetEntityWithCurrentItem(entity: Entity) {
callUsingItemInSlot(0, stack => entity match { callUsingItemInSlot(agent.equipmentInventory, 0, stack => entity match {
case player: EntityPlayer if !canAttackPlayer(player) => // Avoid player damage. case player: EntityPlayer if !canAttackPlayer(player) => // Avoid player damage.
case _ => case _ =>
val event = new RobotAttackEntityEvent.Pre(agent, entity) val event = new RobotAttackEntityEvent.Pre(agent, entity)
@ -173,7 +173,7 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc
} }
false false
} }
!cancel && callUsingItemInSlot(0, stack => { !cancel && callUsingItemInSlot(agent.equipmentInventory, 0, stack => {
val result = isItemUseAllowed(stack) && (entity.interactFirst(this) || (entity match { val result = isItemUseAllowed(stack) && (entity.interactFirst(this) || (entity match {
case living: EntityLivingBase if getCurrentEquippedItem != null => getCurrentEquippedItem.interactWithEntity(this, living) case living: EntityLivingBase if getCurrentEquippedItem != null => getCurrentEquippedItem.interactWithEntity(this, living)
case _ => false case _ => false
@ -186,7 +186,7 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc
} }
def activateBlockOrUseItem(x: Int, y: Int, z: Int, side: Int, hitX: Float, hitY: Float, hitZ: Float, duration: Double): ActivationType.Value = { def activateBlockOrUseItem(x: Int, y: Int, z: Int, side: Int, hitX: Float, hitY: Float, hitZ: Float, duration: Double): ActivationType.Value = {
callUsingItemInSlot(0, stack => { callUsingItemInSlot(agent.equipmentInventory, 0, stack => {
if (shouldCancel(() => ForgeEventFactory.onPlayerInteract(this, Action.RIGHT_CLICK_BLOCK, x, y, z, side, world))) { if (shouldCancel(() => ForgeEventFactory.onPlayerInteract(this, Action.RIGHT_CLICK_BLOCK, x, y, z, side, world))) {
return ActivationType.None return ActivationType.None
} }
@ -216,7 +216,7 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc
} }
def useEquippedItem(duration: Double) = { def useEquippedItem(duration: Double) = {
callUsingItemInSlot(0, stack => { callUsingItemInSlot(agent.equipmentInventory, 0, stack => {
if (!shouldCancel(() => ForgeEventFactory.onPlayerInteract(this, Action.RIGHT_CLICK_AIR, 0, 0, 0, 0, world))) { if (!shouldCancel(() => ForgeEventFactory.onPlayerInteract(this, Action.RIGHT_CLICK_AIR, 0, 0, 0, 0, world))) {
tryUseItem(stack, duration) tryUseItem(stack, duration)
} }
@ -260,7 +260,7 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc
} }
def placeBlock(slot: Int, x: Int, y: Int, z: Int, side: Int, hitX: Float, hitY: Float, hitZ: Float): Boolean = { def placeBlock(slot: Int, x: Int, y: Int, z: Int, side: Int, hitX: Float, hitY: Float, hitZ: Float): Boolean = {
callUsingItemInSlot(slot, stack => { callUsingItemInSlot(agent.mainInventory, slot, stack => {
if (shouldCancel(() => ForgeEventFactory.onPlayerInteract(this, Action.RIGHT_CLICK_BLOCK, x, y, z, side, world))) { if (shouldCancel(() => ForgeEventFactory.onPlayerInteract(this, Action.RIGHT_CLICK_BLOCK, x, y, z, side, world))) {
return false return false
} }
@ -270,7 +270,7 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc
} }
def clickBlock(x: Int, y: Int, z: Int, side: Int): Double = { def clickBlock(x: Int, y: Int, z: Int, side: Int): Double = {
callUsingItemInSlot(0, stack => { callUsingItemInSlot(agent.equipmentInventory, 0, stack => {
if (shouldCancel(() => ForgeEventFactory.onPlayerInteract(this, Action.LEFT_CLICK_BLOCK, x, y, z, side, world))) { if (shouldCancel(() => ForgeEventFactory.onPlayerInteract(this, Action.LEFT_CLICK_BLOCK, x, y, z, side, world))) {
return 0 return 0
} }
@ -392,7 +392,7 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc
} }
} }
private def callUsingItemInSlot[T](slot: Int, f: (ItemStack) => T, repair: Boolean = true) = { private def callUsingItemInSlot[T](inventory: IInventory, slot: Int, f: (ItemStack) => T, repair: Boolean = true) = {
val itemsBefore = adjacentItems val itemsBefore = adjacentItems
val stack = inventory.getStackInSlot(slot) val stack = inventory.getStackInSlot(slot)
val oldStack = if (stack != null) stack.copy() else null val oldStack = if (stack != null) stack.copy() else null

View File

@ -11,6 +11,7 @@ import li.cil.oc.server.agent.Player
import li.cil.oc.util.BlockPosition import li.cil.oc.util.BlockPosition
import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.ExtendedWorld._
import li.cil.oc.util.InventoryUtils
import net.minecraft.entity.Entity import net.minecraft.entity.Entity
import net.minecraft.entity.EntityLivingBase import net.minecraft.entity.EntityLivingBase
import net.minecraft.entity.item.EntityMinecart import net.minecraft.entity.item.EntityMinecart
@ -280,10 +281,7 @@ trait Agent extends traits.WorldControl with traits.InventoryControl with traits
entity.captureDrops = false entity.captureDrops = false
for (drop <- entity.capturedDrops) { for (drop <- entity.capturedDrops) {
val stack = drop.getEntityItem val stack = drop.getEntityItem
player.inventory.addItemStackToInventory(stack) InventoryUtils.addToPlayerInventory(stack, player)
if (stack.stackSize > 0) {
player.dropPlayerItemWithRandomChoice(stack, inPlace = false)
}
} }
entity.capturedDrops.clear() entity.capturedDrops.clear()
} }

View File

@ -9,6 +9,7 @@ import li.cil.oc.api.machine.Callback
import li.cil.oc.api.machine.Context import li.cil.oc.api.machine.Context
import li.cil.oc.api.network._ import li.cil.oc.api.network._
import li.cil.oc.api.prefab import li.cil.oc.api.prefab
import li.cil.oc.util.InventoryUtils
import net.minecraft.entity.player.EntityPlayer import net.minecraft.entity.player.EntityPlayer
import net.minecraft.inventory import net.minecraft.inventory
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
@ -67,10 +68,9 @@ class UpgradeCrafting(val host: EnvironmentHost with internal.Robot) extends pre
} }
} }
save() save()
val inventory = host.player.inventory InventoryUtils.addToPlayerInventory(result, host.player)
inventory.addItemStackToInventory(result)
for (stack <- surplus) { for (stack <- surplus) {
inventory.addItemStackToInventory(stack) InventoryUtils.addToPlayerInventory(stack, host.player)
} }
load() load()
} }
@ -79,7 +79,7 @@ class UpgradeCrafting(val host: EnvironmentHost with internal.Robot) extends pre
} }
def load() { def load() {
val inventory = host.player.inventory val inventory = host.mainInventory()
amountPossible = Int.MaxValue amountPossible = Int.MaxValue
for (slot <- 0 until getSizeInventory) { for (slot <- 0 until getSizeInventory) {
val stack = inventory.getStackInSlot(toParentSlot(slot)) val stack = inventory.getStackInSlot(toParentSlot(slot))
@ -91,7 +91,7 @@ class UpgradeCrafting(val host: EnvironmentHost with internal.Robot) extends pre
} }
def save() { def save() {
val inventory = host.player.inventory val inventory = host.mainInventory()
for (slot <- 0 until getSizeInventory) { for (slot <- 0 until getSizeInventory) {
inventory.setInventorySlotContents(toParentSlot(slot), getStackInSlot(slot)) inventory.setInventorySlotContents(toParentSlot(slot), getStackInSlot(slot))
} }
@ -100,7 +100,7 @@ class UpgradeCrafting(val host: EnvironmentHost with internal.Robot) extends pre
private def toParentSlot(slot: Int) = { private def toParentSlot(slot: Int) = {
val col = slot % 3 val col = slot % 3
val row = slot / 3 val row = slot / 3
row * 4 + col + 4 // first four are always: tool, card, disk, upgrade row * 4 + col
} }
} }

View File

@ -3,6 +3,7 @@ package li.cil.oc.util
import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.ExtendedWorld._
import net.minecraft.entity.item.EntityItem import net.minecraft.entity.item.EntityItem
import net.minecraft.entity.item.EntityMinecartContainer import net.minecraft.entity.item.EntityMinecartContainer
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.inventory.IInventory import net.minecraft.inventory.IInventory
import net.minecraft.inventory.ISidedInventory import net.minecraft.inventory.ISidedInventory
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
@ -233,7 +234,7 @@ object InventoryUtils {
/** /**
* Utility method for dumping all inventory contents into the world. * Utility method for dumping all inventory contents into the world.
*/ */
def dropAllSlots(position: BlockPosition, inventory: IInventory) { def dropAllSlots(position: BlockPosition, inventory: IInventory): Unit = {
for (slot <- 0 until inventory.getSizeInventory) { for (slot <- 0 until inventory.getSizeInventory) {
Option(inventory.getStackInSlot(slot)) match { Option(inventory.getStackInSlot(slot)) match {
case Some(stack) if stack.stackSize > 0 => case Some(stack) if stack.stackSize > 0 =>
@ -244,6 +245,23 @@ object InventoryUtils {
} }
} }
/**
* Try inserting an item stack into a player inventory. If that fails, drop it into the world.
*/
def addToPlayerInventory(stack: ItemStack, player: EntityPlayer): Unit = {
if (stack != null) {
if (player.inventory.addItemStackToInventory(stack)) {
player.inventory.markDirty()
if (player.openContainer != null) {
player.openContainer.detectAndSendChanges()
}
}
if (stack.stackSize > 0) {
player.dropPlayerItemWithRandomChoice(stack, false)
}
}
}
/** /**
* Utility method for spawning an item stack in the world. * Utility method for spawning an item stack in the world.
*/ */