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.
This commit is contained in:
Florian Nücke 2015-01-28 21:17:31 +01:00
parent 19f614a3eb
commit e9d74f21a7
5 changed files with 101 additions and 41 deletions

View File

@ -8,6 +8,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.integration.Mods import li.cil.oc.integration.Mods
import li.cil.oc.integration.util import li.cil.oc.integration.util
import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.server.{PacketSender => ServerPacketSender}
@ -32,6 +33,12 @@ import scala.concurrent.Future
object EventHandler { object EventHandler {
private val pending = mutable.Buffer.empty[() => Unit] 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) { def schedule(tileEntity: TileEntity) {
if (SideTracker.isServer) pending.synchronized { if (SideTracker.isServer) pending.synchronized {
pending += (() => Network.joinOrCreateNetwork(tileEntity)) pending += (() => Network.joinOrCreateNetwork(tileEntity))
@ -64,6 +71,8 @@ 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)
} }
}) })
runningRobots.foreach(_.machine.update())
} }
@SubscribeEvent @SubscribeEvent

View File

@ -106,7 +106,7 @@ class RobotProxy extends RedstoneAware with traits.StateAware {
override def createNewTileEntity(world: World, metadata: Int) = { override def createNewTileEntity(world: World, metadata: Int) = {
moving.get match { moving.get match {
case Some(robot) => null // new tileentity.RobotProxy(robot) case Some(robot) => new tileentity.RobotProxy(robot)
case _ => new tileentity.RobotProxy() case _ => new tileentity.RobotProxy()
} }
} }

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
@ -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 // 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 // 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. // 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 // In some cases (though I couldn't quite figure out which one) setBlock
// will return true, even though the block was not created / adjusted. // will return true, even though the block was not created / adjusted.
val created = world.setBlockState(newPosition, blockRobotProxy.getDefaultState, 1) val created = world.setBlockState(newPosition, world.getBlockState(oldPosition), 1) &&
val newProxy = world.getTileEntity(newPosition) world.getTileEntity(newPosition) == proxy
if (created && newProxy != null) { if (created) {
assert(newProxy.getPos == newPosition) assert(getPos == newPosition)
proxy = newProxy.asInstanceOf[RobotProxy]
setPos(newPosition)
world.setBlockState(oldPosition, net.minecraft.init.Blocks.air.getDefaultState, 1) world.setBlockState(oldPosition, net.minecraft.init.Blocks.air.getDefaultState, 1)
world.setBlockState(oldPosition, blockRobotAfterImage.getDefaultState, 1) world.setBlockState(oldPosition, blockRobotAfterImage.getDefaultState, 1)
assert(world.getBlockState(oldPosition).getBlock == blockRobotAfterImage) assert(world.getBlockState(oldPosition).getBlock == blockRobotAfterImage)
@ -383,6 +382,16 @@ class Robot extends traits.Computer with traits.PowerInformation with IFluidHand
inventory.decrementAnimations() 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() { 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
@ -400,6 +409,7 @@ class Robot extends traits.Computer with traits.PowerInformation with IFluidHand
case _ => 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") animationTicksTotal = nbt.getInteger(Settings.namespace + "animationTicksTotal")
animationTicksLeft = nbt.getInteger(Settings.namespace + "animationTicksLeft") animationTicksLeft = nbt.getInteger(Settings.namespace + "animationTicksLeft")
if (animationTicksLeft > 0) { if (animationTicksLeft > 0) {
val moveFromX = nbt.getInteger(Settings.namespace + "moveFromX") if (nbt.hasKey(Settings.namespace + "moveFromX")) {
val moveFromY = nbt.getInteger(Settings.namespace + "moveFromY") val moveFromX = nbt.getInteger(Settings.namespace + "moveFromX")
val moveFromZ = nbt.getInteger(Settings.namespace + "moveFromZ") val moveFromY = nbt.getInteger(Settings.namespace + "moveFromY")
moveFrom = Some(new BlockPos(moveFromX, moveFromY, moveFromZ)) val moveFromZ = nbt.getInteger(Settings.namespace + "moveFromZ")
moveFrom = Some(new BlockPos(moveFromX, moveFromY, moveFromZ))
}
swingingTool = nbt.getBoolean(Settings.namespace + "swingingTool") swingingTool = nbt.getBoolean(Settings.namespace + "swingingTool")
turnAxis = nbt.getByte(Settings.namespace + "turnAxis") turnAxis = nbt.getByte(Settings.namespace + "turnAxis")
} }
@ -445,9 +457,13 @@ class Robot extends traits.Computer with traits.PowerInformation with IFluidHand
if (isAnimatingMove || isAnimatingSwing || isAnimatingTurn) { if (isAnimatingMove || isAnimatingSwing || isAnimatingTurn) {
nbt.setInteger(Settings.namespace + "animationTicksTotal", animationTicksTotal) nbt.setInteger(Settings.namespace + "animationTicksTotal", animationTicksTotal)
nbt.setInteger(Settings.namespace + "animationTicksLeft", animationTicksLeft) nbt.setInteger(Settings.namespace + "animationTicksLeft", animationTicksLeft)
nbt.setInteger(Settings.namespace + "moveFromX", moveFrom.get.getX) moveFrom match {
nbt.setInteger(Settings.namespace + "moveFromY", moveFrom.get.getY) case Some(blockPos) =>
nbt.setInteger(Settings.namespace + "moveFromZ", moveFrom.get.getZ) 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.setBoolean(Settings.namespace + "swingingTool", swingingTool)
nbt.setByte(Settings.namespace + "turnAxis", turnAxis.toByte) 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") animationTicksTotal = nbt.getInteger("animationTicksTotal")
animationTicksLeft = nbt.getInteger("animationTicksLeft") animationTicksLeft = nbt.getInteger("animationTicksLeft")
if (animationTicksLeft > 0) { if (animationTicksLeft > 0) {
val moveFromX = nbt.getInteger("moveFromX") if (nbt.hasKey("moveFromX")) {
val moveFromY = nbt.getInteger("moveFromY") val moveFromX = nbt.getInteger("moveFromX")
val moveFromZ = nbt.getInteger("moveFromZ") val moveFromY = nbt.getInteger("moveFromY")
moveFrom = Some(new BlockPos(moveFromX, moveFromY, moveFromZ)) val moveFromZ = nbt.getInteger("moveFromZ")
moveFrom = Some(new BlockPos(moveFromX, moveFromY, moveFromZ))
}
swingingTool = nbt.getBoolean("swingingTool") swingingTool = nbt.getBoolean("swingingTool")
turnAxis = nbt.getByte("turnAxis") turnAxis = nbt.getByte("turnAxis")
} }
@ -484,9 +502,13 @@ class Robot extends traits.Computer with traits.PowerInformation with IFluidHand
if (isAnimatingMove || isAnimatingSwing || isAnimatingTurn) { if (isAnimatingMove || isAnimatingSwing || isAnimatingTurn) {
nbt.setInteger("animationTicksTotal", animationTicksTotal) nbt.setInteger("animationTicksTotal", animationTicksTotal)
nbt.setInteger("animationTicksLeft", animationTicksLeft) nbt.setInteger("animationTicksLeft", animationTicksLeft)
nbt.setInteger("moveFromX", moveFrom.get.getX) moveFrom match {
nbt.setInteger("moveFromY", moveFrom.get.getY) case Some(blockPos) =>
nbt.setInteger("moveFromZ", moveFrom.get.getZ) nbt.setInteger("moveFromX", blockPos.getX)
nbt.setInteger("moveFromY", blockPos.getY)
nbt.setInteger("moveFromZ", blockPos.getZ)
case _ =>
}
nbt.setBoolean("swingingTool", swingingTool) nbt.setBoolean("swingingTool", swingingTool)
nbt.setByte("turnAxis", turnAxis.toByte) nbt.setByte("turnAxis", turnAxis.toByte)
} }

View File

@ -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 (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
@ -115,6 +115,10 @@ trait Computer extends Environment with ComponentInventory with Rotatable with B
super.updateEntity() super.updateEntity()
} }
protected def updateComputer(): Unit = {
machine.update()
}
protected def onRunningChanged(): Unit = { protected def onRunningChanged(): Unit = {
markDirty() markDirty()
ServerPacketSender.sendComputerState(this) ServerPacketSender.sendComputerState(this)

View File

@ -4,6 +4,7 @@ import li.cil.oc.api.internal
import li.cil.oc.common.block import li.cil.oc.common.block
import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.server.{PacketSender => ServerPacketSender}
import li.cil.oc.util.ExtendedWorld._ import li.cil.oc.util.ExtendedWorld._
import net.minecraft.block.state.IBlockState
import net.minecraft.entity.Entity import net.minecraft.entity.Entity
import net.minecraft.util.EnumFacing 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. */ /** Translation for facings based on current pitch and yaw. */
private var cachedTranslation: Array[EnumFacing] = null private var cachedTranslation: Array[EnumFacing] = null
@ -83,7 +90,7 @@ trait Rotatable extends RotationAware with internal.Rotatable {
def pitch = getBlockType match { def pitch = getBlockType match {
case rotatable: block.traits.OmniRotatable => rotatable.getPitch(world.getBlockState(getPos)) case rotatable: block.traits.OmniRotatable => rotatable.getPitch(world.getBlockState(getPos))
case _ => EnumFacing.NORTH case _ => _pitch
} }
def pitch_=(value: EnumFacing): Unit = def pitch_=(value: EnumFacing): Unit =
@ -95,7 +102,7 @@ trait Rotatable extends RotationAware with internal.Rotatable {
def yaw = getBlockType match { def yaw = getBlockType match {
case rotatable: block.traits.OmniRotatable => rotatable.getYaw(world.getBlockState(getPos)) case rotatable: block.traits.OmniRotatable => rotatable.getYaw(world.getBlockState(getPos))
case rotatable: block.traits.Rotatable => rotatable.getFacing(world.getBlockState(getPos)) case rotatable: block.traits.Rotatable => rotatable.getFacing(world.getBlockState(getPos))
case _ => EnumFacing.SOUTH case _ => _yaw
} }
def yaw_=(value: EnumFacing): Unit = 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. */ /** Validates new values against the allowed rotations as set in our block. */
private def trySetPitchYaw(pitch: EnumFacing, yaw: EnumFacing) = { private def trySetPitchYaw(pitch: EnumFacing, yaw: EnumFacing) = {
val oldState = world.getBlockState(getPos) val oldState = world.getBlockState(getPos)
val newState = getBlockType match { def setState(newState: IBlockState): Boolean = {
case rotatable: block.traits.OmniRotatable => rotatable.withPitchAndYaw(oldState, pitch, yaw) if (oldState.hashCode() != newState.hashCode()) {
case rotatable: block.traits.Rotatable => rotatable.withFacing(oldState, yaw) world.setBlockState(getPos, newState)
case _ => oldState cacheDirty = true
true
}
else false
} }
if (oldState.hashCode() != newState.hashCode()) { getBlockType match {
world.setBlockState(getPos, newState) case rotatable: block.traits.OmniRotatable =>
cacheDirty = true setState(rotatable.withPitchAndYaw(oldState, pitch, yaw))
true 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]) = private def invert(t: Array[EnumFacing]) =