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.{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

View File

@ -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()
}
}

View File

@ -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)
}

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 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)

View File

@ -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]) =