From e9d74f21a73aac938341d797a7fa4ed88f59f3e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Wed, 28 Jan 2015 21:17:31 +0100 Subject: [PATCH] Hallelujah! Robot movement works again. Updating robots from tick handler now, to ensure tile entities are set immediately. Also making rotatable trait for tile entities "hybrid" so it can use either metadata *or* its own data. --- .../scala/li/cil/oc/common/EventHandler.scala | 9 +++ .../li/cil/oc/common/block/RobotProxy.scala | 2 +- .../li/cil/oc/common/tileentity/Robot.scala | 64 +++++++++++++------ .../common/tileentity/traits/Computer.scala | 18 ++++-- .../common/tileentity/traits/Rotatable.scala | 49 ++++++++++---- 5 files changed, 101 insertions(+), 41 deletions(-) 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]) =