diff --git a/src/main/scala/li/cil/oc/common/EventHandler.scala b/src/main/scala/li/cil/oc/common/EventHandler.scala index e8a88959e..b1419570f 100644 --- a/src/main/scala/li/cil/oc/common/EventHandler.scala +++ b/src/main/scala/li/cil/oc/common/EventHandler.scala @@ -8,6 +8,7 @@ import li.cil.oc.api.detail.ItemInfo import li.cil.oc.client.renderer.PetRenderer import li.cil.oc.client.{PacketSender => ClientPacketSender} import li.cil.oc.common.item.data.MicrocontrollerData +import li.cil.oc.common.tileentity.Robot import li.cil.oc.integration.Mods import li.cil.oc.integration.util import li.cil.oc.server.{PacketSender => ServerPacketSender} @@ -32,6 +33,12 @@ import scala.concurrent.Future object EventHandler { private val pending = mutable.Buffer.empty[() => Unit] + 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) { if (SideTracker.isServer) pending.synchronized { pending += (() => Network.joinOrCreateNetwork(tileEntity)) @@ -64,6 +71,8 @@ object EventHandler { case t: Throwable => OpenComputers.log.warn("Error in scheduled tick action.", t) } }) + + runningRobots.foreach(_.machine.update()) } @SubscribeEvent diff --git a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala index a15d4264d..bd10b19f2 100644 --- a/src/main/scala/li/cil/oc/common/block/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/block/RobotProxy.scala @@ -106,7 +106,7 @@ class RobotProxy extends RedstoneAware with traits.StateAware { override def createNewTileEntity(world: World, metadata: Int) = { moving.get match { - case Some(robot) => null // new tileentity.RobotProxy(robot) + case Some(robot) => new tileentity.RobotProxy(robot) case _ => new tileentity.RobotProxy() } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala index ef5807c20..91d31a6fe 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala @@ -13,6 +13,7 @@ import li.cil.oc.api.event.RobotMoveEvent import li.cil.oc.api.internal import li.cil.oc.api.network._ import li.cil.oc.client.gui +import li.cil.oc.common.EventHandler import li.cil.oc.common.Slot import li.cil.oc.common.Tier import li.cil.oc.common.inventory.InventorySelection @@ -243,15 +244,13 @@ class Robot extends traits.Computer with traits.PowerInformation with IFluidHand // worked before the client is notified so that we can use the same trick on // the client by sending a corresponding packet. This also saves us from // having to send the complete state again (e.g. screen buffer) each move. - // world.setBlockToAir(newPosition) + world.setBlockToAir(newPosition) // In some cases (though I couldn't quite figure out which one) setBlock // will return true, even though the block was not created / adjusted. - val created = world.setBlockState(newPosition, blockRobotProxy.getDefaultState, 1) - val newProxy = world.getTileEntity(newPosition) - if (created && newProxy != null) { - assert(newProxy.getPos == newPosition) - proxy = newProxy.asInstanceOf[RobotProxy] - setPos(newPosition) + val created = world.setBlockState(newPosition, world.getBlockState(oldPosition), 1) && + world.getTileEntity(newPosition) == proxy + if (created) { + assert(getPos == newPosition) world.setBlockState(oldPosition, net.minecraft.init.Blocks.air.getDefaultState, 1) world.setBlockState(oldPosition, blockRobotAfterImage.getDefaultState, 1) assert(world.getBlockState(oldPosition).getBlock == blockRobotAfterImage) @@ -383,6 +382,16 @@ class Robot extends traits.Computer with traits.PowerInformation with IFluidHand inventory.decrementAnimations() } + // 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() { if (isServer) { // Ensure we have a node address, because the proxy needs this to initialize @@ -400,6 +409,7 @@ class Robot extends traits.Computer with traits.PowerInformation with IFluidHand case _ => } } + else EventHandler.onRobotStopped(this) } // ----------------------------------------------------------------------- // @@ -422,10 +432,12 @@ class Robot extends traits.Computer with traits.PowerInformation with IFluidHand animationTicksTotal = nbt.getInteger(Settings.namespace + "animationTicksTotal") animationTicksLeft = nbt.getInteger(Settings.namespace + "animationTicksLeft") if (animationTicksLeft > 0) { - val moveFromX = nbt.getInteger(Settings.namespace + "moveFromX") - val moveFromY = nbt.getInteger(Settings.namespace + "moveFromY") - val moveFromZ = nbt.getInteger(Settings.namespace + "moveFromZ") - moveFrom = Some(new BlockPos(moveFromX, moveFromY, moveFromZ)) + if (nbt.hasKey(Settings.namespace + "moveFromX")) { + val moveFromX = nbt.getInteger(Settings.namespace + "moveFromX") + val moveFromY = nbt.getInteger(Settings.namespace + "moveFromY") + val moveFromZ = nbt.getInteger(Settings.namespace + "moveFromZ") + moveFrom = Some(new BlockPos(moveFromX, moveFromY, moveFromZ)) + } swingingTool = nbt.getBoolean(Settings.namespace + "swingingTool") turnAxis = nbt.getByte(Settings.namespace + "turnAxis") } @@ -445,9 +457,13 @@ class Robot extends traits.Computer with traits.PowerInformation with IFluidHand if (isAnimatingMove || isAnimatingSwing || isAnimatingTurn) { nbt.setInteger(Settings.namespace + "animationTicksTotal", animationTicksTotal) nbt.setInteger(Settings.namespace + "animationTicksLeft", animationTicksLeft) - nbt.setInteger(Settings.namespace + "moveFromX", moveFrom.get.getX) - nbt.setInteger(Settings.namespace + "moveFromY", moveFrom.get.getY) - nbt.setInteger(Settings.namespace + "moveFromZ", moveFrom.get.getZ) + moveFrom match { + case Some(blockPos) => + nbt.setInteger(Settings.namespace + "moveFromX", blockPos.getX) + nbt.setInteger(Settings.namespace + "moveFromY", blockPos.getY) + nbt.setInteger(Settings.namespace + "moveFromZ", blockPos.getZ) + case _ => + } nbt.setBoolean(Settings.namespace + "swingingTool", swingingTool) nbt.setByte(Settings.namespace + "turnAxis", turnAxis.toByte) } @@ -465,10 +481,12 @@ class Robot extends traits.Computer with traits.PowerInformation with IFluidHand animationTicksTotal = nbt.getInteger("animationTicksTotal") animationTicksLeft = nbt.getInteger("animationTicksLeft") if (animationTicksLeft > 0) { - val moveFromX = nbt.getInteger("moveFromX") - val moveFromY = nbt.getInteger("moveFromY") - val moveFromZ = nbt.getInteger("moveFromZ") - moveFrom = Some(new BlockPos(moveFromX, moveFromY, moveFromZ)) + if (nbt.hasKey("moveFromX")) { + val moveFromX = nbt.getInteger("moveFromX") + val moveFromY = nbt.getInteger("moveFromY") + val moveFromZ = nbt.getInteger("moveFromZ") + moveFrom = Some(new BlockPos(moveFromX, moveFromY, moveFromZ)) + } swingingTool = nbt.getBoolean("swingingTool") turnAxis = nbt.getByte("turnAxis") } @@ -484,9 +502,13 @@ class Robot extends traits.Computer with traits.PowerInformation with IFluidHand if (isAnimatingMove || isAnimatingSwing || isAnimatingTurn) { nbt.setInteger("animationTicksTotal", animationTicksTotal) nbt.setInteger("animationTicksLeft", animationTicksLeft) - nbt.setInteger("moveFromX", moveFrom.get.getX) - nbt.setInteger("moveFromY", moveFrom.get.getY) - nbt.setInteger("moveFromZ", moveFrom.get.getZ) + moveFrom match { + case Some(blockPos) => + nbt.setInteger("moveFromX", blockPos.getX) + nbt.setInteger("moveFromY", blockPos.getY) + nbt.setInteger("moveFromZ", blockPos.getZ) + case _ => + } nbt.setBoolean("swingingTool", swingingTool) nbt.setByte("turnAxis", turnAxis.toByte) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala index 7fe0892de..6a422b3ee 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala @@ -95,14 +95,14 @@ 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 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). - machine.update() + updateComputer() if (_isRunning != machine.isRunning) { _isRunning = machine.isRunning @@ -115,6 +115,10 @@ trait Computer extends Environment with ComponentInventory with Rotatable with B super.updateEntity() } + protected def updateComputer(): Unit = { + machine.update() + } + protected def onRunningChanged(): Unit = { markDirty() ServerPacketSender.sendComputerState(this) diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/Rotatable.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/Rotatable.scala index 79ffae0ca..ba6662413 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/Rotatable.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/Rotatable.scala @@ -4,6 +4,7 @@ import li.cil.oc.api.internal import li.cil.oc.common.block import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.ExtendedWorld._ +import net.minecraft.block.state.IBlockState import net.minecraft.entity.Entity import net.minecraft.util.EnumFacing @@ -66,9 +67,15 @@ trait Rotatable extends RotationAware with internal.Rotatable { } // ----------------------------------------------------------------------- // - // State + // State - stored either in metadata or locally // ----------------------------------------------------------------------- // + /** One of Up, Down and North (where north means forward/no pitch). */ + private var _pitch = EnumFacing.NORTH + + /** One of the four cardinal directions. */ + private var _yaw = EnumFacing.SOUTH + /** Translation for facings based on current pitch and yaw. */ private var cachedTranslation: Array[EnumFacing] = null @@ -83,7 +90,7 @@ trait Rotatable extends RotationAware with internal.Rotatable { def pitch = getBlockType match { case rotatable: block.traits.OmniRotatable => rotatable.getPitch(world.getBlockState(getPos)) - case _ => EnumFacing.NORTH + case _ => _pitch } def pitch_=(value: EnumFacing): Unit = @@ -95,7 +102,7 @@ trait Rotatable extends RotationAware with internal.Rotatable { def yaw = getBlockType match { case rotatable: block.traits.OmniRotatable => rotatable.getYaw(world.getBlockState(getPos)) case rotatable: block.traits.Rotatable => rotatable.getFacing(world.getBlockState(getPos)) - case _ => EnumFacing.SOUTH + case _ => _yaw } def yaw_=(value: EnumFacing): Unit = @@ -196,17 +203,35 @@ trait Rotatable extends RotationAware with internal.Rotatable { /** Validates new values against the allowed rotations as set in our block. */ private def trySetPitchYaw(pitch: EnumFacing, yaw: EnumFacing) = { val oldState = world.getBlockState(getPos) - val newState = getBlockType match { - case rotatable: block.traits.OmniRotatable => rotatable.withPitchAndYaw(oldState, pitch, yaw) - case rotatable: block.traits.Rotatable => rotatable.withFacing(oldState, yaw) - case _ => oldState + def setState(newState: IBlockState): Boolean = { + if (oldState.hashCode() != newState.hashCode()) { + world.setBlockState(getPos, newState) + cacheDirty = true + true + } + else false } - if (oldState.hashCode() != newState.hashCode()) { - world.setBlockState(getPos, newState) - cacheDirty = true - true + getBlockType match { + case rotatable: block.traits.OmniRotatable => + setState(rotatable.withPitchAndYaw(oldState, pitch, yaw)) + case rotatable: block.traits.Rotatable => + setState(rotatable.withFacing(oldState, yaw)) + case _ => + var changed = false + if (pitch != _pitch) { + changed = true + _pitch = pitch + } + if (yaw != _yaw) { + changed = true + _yaw = yaw + } + if (changed) { + cacheDirty = true + updateTranslation() + } + changed } - else false } private def invert(t: Array[EnumFacing]) =