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

Conflicts:
	src/main/scala/li/cil/oc/client/PacketHandler.scala
	src/main/scala/li/cil/oc/common/PacketBuilder.scala
This commit is contained in:
Florian Nücke 2014-08-23 23:35:28 +02:00
commit dbc7ad14bc
10 changed files with 184 additions and 134 deletions

View File

@ -1,9 +1,12 @@
package li.cil.oc.client package li.cil.oc.client
import java.io.EOFException
import cpw.mods.fml.common.eventhandler.SubscribeEvent import cpw.mods.fml.common.eventhandler.SubscribeEvent
import cpw.mods.fml.common.network.FMLNetworkEvent.ClientCustomPacketEvent import cpw.mods.fml.common.network.FMLNetworkEvent.ClientCustomPacketEvent
import li.cil.oc.Localization import li.cil.oc.Localization
import li.cil.oc.api.component import li.cil.oc.api.component
import li.cil.oc.api.network.ManagedEnvironment
import li.cil.oc.client.renderer.PetRenderer import li.cil.oc.client.renderer.PetRenderer
import li.cil.oc.common.tileentity._ import li.cil.oc.common.tileentity._
import li.cil.oc.common.tileentity.traits._ import li.cil.oc.common.tileentity.traits._
@ -59,6 +62,7 @@ object PacketHandler extends CommonPacketHandler {
case PacketType.TextBufferInit => onTextBufferInit(p) case PacketType.TextBufferInit => onTextBufferInit(p)
case PacketType.TextBufferPaletteChange => onTextBufferPaletteChange(p) case PacketType.TextBufferPaletteChange => onTextBufferPaletteChange(p)
case PacketType.TextBufferPowerChange => onTextBufferPowerChange(p) case PacketType.TextBufferPowerChange => onTextBufferPowerChange(p)
case PacketType.TextBufferMulti => onTextBufferMulti(p)
case PacketType.TextBufferResolutionChange => onTextBufferResolutionChange(p) case PacketType.TextBufferResolutionChange => onTextBufferResolutionChange(p)
case PacketType.TextBufferSet => onTextBufferSet(p) case PacketType.TextBufferSet => onTextBufferSet(p)
case PacketType.ScreenTouchMode => onScreenTouchMode(p) case PacketType.ScreenTouchMode => onScreenTouchMode(p)
@ -286,8 +290,8 @@ object PacketHandler extends CommonPacketHandler {
case _ => // Invalid packet. case _ => // Invalid packet.
} }
def onTextBufferColorChange(p: PacketParser) { def onTextBufferColorChange(p: PacketParser, env: Option[ManagedEnvironment] = None) {
ComponentTracker.get(p.readUTF()) match { env.orElse(ComponentTracker.get(p.readUTF())) match {
case Some(buffer: component.TextBuffer) => case Some(buffer: component.TextBuffer) =>
val foreground = p.readInt() val foreground = p.readInt()
val foregroundIsPalette = p.readBoolean() val foregroundIsPalette = p.readBoolean()
@ -299,8 +303,8 @@ object PacketHandler extends CommonPacketHandler {
} }
} }
def onTextBufferCopy(p: PacketParser) { def onTextBufferCopy(p: PacketParser, env: Option[ManagedEnvironment] = None) {
ComponentTracker.get(p.readUTF()) match { env.orElse(ComponentTracker.get(p.readUTF())) match {
case Some(buffer: component.TextBuffer) => case Some(buffer: component.TextBuffer) =>
val col = p.readInt() val col = p.readInt()
val row = p.readInt() val row = p.readInt()
@ -313,16 +317,16 @@ object PacketHandler extends CommonPacketHandler {
} }
} }
def onTextBufferDepthChange(p: PacketParser) { def onTextBufferDepthChange(p: PacketParser, env: Option[ManagedEnvironment] = None) {
ComponentTracker.get(p.readUTF()) match { env.orElse(ComponentTracker.get(p.readUTF())) match {
case Some(buffer: component.TextBuffer) => case Some(buffer: component.TextBuffer) =>
buffer.setColorDepth(component.TextBuffer.ColorDepth.values.apply(p.readInt())) buffer.setColorDepth(component.TextBuffer.ColorDepth.values.apply(p.readInt()))
case _ => // Invalid packet. case _ => // Invalid packet.
} }
} }
def onTextBufferFill(p: PacketParser) { def onTextBufferFill(p: PacketParser, env: Option[ManagedEnvironment] = None) {
ComponentTracker.get(p.readUTF()) match { env.orElse(ComponentTracker.get(p.readUTF())) match {
case Some(buffer: component.TextBuffer) => case Some(buffer: component.TextBuffer) =>
val col = p.readInt() val col = p.readInt()
val row = p.readInt() val row = p.readInt()
@ -341,8 +345,8 @@ object PacketHandler extends CommonPacketHandler {
} }
} }
def onTextBufferPaletteChange(p: PacketParser) { def onTextBufferPaletteChange(p: PacketParser, env: Option[ManagedEnvironment] = None) {
ComponentTracker.get(p.readUTF()) match { env.orElse(ComponentTracker.get(p.readUTF())) match {
case Some(buffer: component.TextBuffer) => case Some(buffer: component.TextBuffer) =>
val index = p.readInt() val index = p.readInt()
val color = p.readInt() val color = p.readInt()
@ -358,8 +362,29 @@ object PacketHandler extends CommonPacketHandler {
case _ => // Invalid packet. case _ => // Invalid packet.
} }
def onTextBufferResolutionChange(p: PacketParser) { def onTextBufferMulti(p: PacketParser) =
ComponentTracker.get(p.readUTF()) match { ComponentTracker.get(p.readUTF()) match {
case Some(buffer: component.TextBuffer) =>
try while (true) {
p.readPacketType() match {
case PacketType.TextBufferColorChange => onTextBufferColorChange(p, Some(buffer))
case PacketType.TextBufferCopy => onTextBufferCopy(p, Some(buffer))
case PacketType.TextBufferDepthChange => onTextBufferDepthChange(p, Some(buffer))
case PacketType.TextBufferFill => onTextBufferFill(p, Some(buffer))
case PacketType.TextBufferPaletteChange => onTextBufferPaletteChange(p, Some(buffer))
case PacketType.TextBufferResolutionChange => onTextBufferResolutionChange(p, Some(buffer))
case PacketType.TextBufferSet => onTextBufferSet(p, Some(buffer))
case _ => // Invalid packet.
}
}
catch {
case ignored: EOFException => // No more commands.
}
case _ => // Invalid packet.
}
def onTextBufferResolutionChange(p: PacketParser, env: Option[ManagedEnvironment] = None) {
env.orElse(ComponentTracker.get(p.readUTF())) match {
case Some(buffer: component.TextBuffer) => case Some(buffer: component.TextBuffer) =>
val w = p.readInt() val w = p.readInt()
val h = p.readInt() val h = p.readInt()
@ -368,8 +393,8 @@ object PacketHandler extends CommonPacketHandler {
} }
} }
def onTextBufferSet(p: PacketParser) { def onTextBufferSet(p: PacketParser, env: Option[ManagedEnvironment] = None) {
ComponentTracker.get(p.readUTF()) match { env.orElse(ComponentTracker.get(p.readUTF())) match {
case Some(buffer: component.TextBuffer) => case Some(buffer: component.TextBuffer) =>
val col = p.readInt() val col = p.readInt()
val row = p.readInt() val row = p.readInt()

View File

@ -3,7 +3,7 @@ package li.cil.oc.client
import li.cil.oc.Settings import li.cil.oc.Settings
import li.cil.oc.common.tileentity._ import li.cil.oc.common.tileentity._
import li.cil.oc.common.tileentity.traits.Computer import li.cil.oc.common.tileentity.traits.Computer
import li.cil.oc.common.{CompressedPacketBuilder, PacketBuilder, PacketType} import li.cil.oc.common.{CompressedPacketBuilder, PacketType, SimplePacketBuilder}
import net.minecraft.client.Minecraft import net.minecraft.client.Minecraft
import net.minecraft.client.audio.PositionedSoundRecord import net.minecraft.client.audio.PositionedSoundRecord
import net.minecraft.util.ResourceLocation import net.minecraft.util.ResourceLocation
@ -15,7 +15,7 @@ object PacketSender {
protected var clipboardCooldown = 0L protected var clipboardCooldown = 0L
def sendComputerPower(t: Computer, power: Boolean) { def sendComputerPower(t: Computer, power: Boolean) {
val pb = new PacketBuilder(PacketType.ComputerPower) val pb = new SimplePacketBuilder(PacketType.ComputerPower)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.writeBoolean(power) pb.writeBoolean(power)
@ -24,7 +24,7 @@ object PacketSender {
} }
def sendKeyDown(address: String, char: Char, code: Int) { def sendKeyDown(address: String, char: Char, code: Int) {
val pb = new PacketBuilder(PacketType.KeyDown) val pb = new SimplePacketBuilder(PacketType.KeyDown)
pb.writeUTF(address) pb.writeUTF(address)
pb.writeChar(char) pb.writeChar(char)
@ -34,7 +34,7 @@ object PacketSender {
} }
def sendKeyUp(address: String, char: Char, code: Int) { def sendKeyUp(address: String, char: Char, code: Int) {
val pb = new PacketBuilder(PacketType.KeyUp) val pb = new SimplePacketBuilder(PacketType.KeyUp)
pb.writeUTF(address) pb.writeUTF(address)
pb.writeChar(char) pb.writeChar(char)
@ -65,7 +65,7 @@ object PacketSender {
} }
def sendMouseClick(address: String, x: Int, y: Int, drag: Boolean, button: Int) { def sendMouseClick(address: String, x: Int, y: Int, drag: Boolean, button: Int) {
val pb = new PacketBuilder(PacketType.MouseClickOrDrag) val pb = new SimplePacketBuilder(PacketType.MouseClickOrDrag)
pb.writeUTF(address) pb.writeUTF(address)
pb.writeShort(x) pb.writeShort(x)
@ -77,7 +77,7 @@ object PacketSender {
} }
def sendMouseScroll(address: String, x: Int, y: Int, scroll: Int) { def sendMouseScroll(address: String, x: Int, y: Int, scroll: Int) {
val pb = new PacketBuilder(PacketType.MouseScroll) val pb = new SimplePacketBuilder(PacketType.MouseScroll)
pb.writeUTF(address) pb.writeUTF(address)
pb.writeShort(x) pb.writeShort(x)
@ -88,7 +88,7 @@ object PacketSender {
} }
def sendMouseUp(address: String, x: Int, y: Int, button: Int) { def sendMouseUp(address: String, x: Int, y: Int, button: Int) {
val pb = new PacketBuilder(PacketType.MouseUp) val pb = new SimplePacketBuilder(PacketType.MouseUp)
pb.writeUTF(address) pb.writeUTF(address)
pb.writeShort(x) pb.writeShort(x)
@ -99,12 +99,12 @@ object PacketSender {
} }
def sendMultiPlace() { def sendMultiPlace() {
val pb = new PacketBuilder(PacketType.MultiPartPlace) val pb = new SimplePacketBuilder(PacketType.MultiPartPlace)
pb.sendToServer() pb.sendToServer()
} }
def sendPetVisibility() { def sendPetVisibility() {
val pb = new PacketBuilder(PacketType.PetVisibility) val pb = new SimplePacketBuilder(PacketType.PetVisibility)
pb.writeBoolean(!Settings.get.hideOwnPet) pb.writeBoolean(!Settings.get.hideOwnPet)
@ -112,7 +112,7 @@ object PacketSender {
} }
def sendRobotAssemblerStart(t: RobotAssembler) { def sendRobotAssemblerStart(t: RobotAssembler) {
val pb = new PacketBuilder(PacketType.RobotAssemblerStart) val pb = new SimplePacketBuilder(PacketType.RobotAssemblerStart)
pb.writeTileEntity(t) pb.writeTileEntity(t)
@ -120,7 +120,7 @@ object PacketSender {
} }
def sendRobotStateRequest(dimension: Int, x: Int, y: Int, z: Int) { def sendRobotStateRequest(dimension: Int, x: Int, y: Int, z: Int) {
val pb = new PacketBuilder(PacketType.RobotStateRequest) val pb = new SimplePacketBuilder(PacketType.RobotStateRequest)
pb.writeInt(dimension) pb.writeInt(dimension)
pb.writeInt(x) pb.writeInt(x)
@ -131,7 +131,7 @@ object PacketSender {
} }
def sendServerPower(t: ServerRack, number: Int, power: Boolean) { def sendServerPower(t: ServerRack, number: Int, power: Boolean) {
val pb = new PacketBuilder(PacketType.ComputerPower) val pb = new SimplePacketBuilder(PacketType.ComputerPower)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.writeInt(number) pb.writeInt(number)
@ -141,7 +141,7 @@ object PacketSender {
} }
def sendServerRange(t: ServerRack, range: Int) { def sendServerRange(t: ServerRack, range: Int) {
val pb = new PacketBuilder(PacketType.ServerRange) val pb = new SimplePacketBuilder(PacketType.ServerRange)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.writeInt(range) pb.writeInt(range)
@ -150,7 +150,7 @@ object PacketSender {
} }
def sendServerSide(t: ServerRack, number: Int, side: ForgeDirection) { def sendServerSide(t: ServerRack, number: Int, side: ForgeDirection) {
val pb = new PacketBuilder(PacketType.ServerSide) val pb = new SimplePacketBuilder(PacketType.ServerSide)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.writeInt(number) pb.writeInt(number)
@ -160,7 +160,7 @@ object PacketSender {
} }
def sendServerSwitchMode(t: ServerRack, internal: Boolean) { def sendServerSwitchMode(t: ServerRack, internal: Boolean) {
val pb = new PacketBuilder(PacketType.ServerSwitchMode) val pb = new SimplePacketBuilder(PacketType.ServerSwitchMode)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.writeBoolean(internal) pb.writeBoolean(internal)
@ -169,7 +169,7 @@ object PacketSender {
} }
def sendTextBufferInit(address: String) { def sendTextBufferInit(address: String) {
val pb = new PacketBuilder(PacketType.TextBufferInit) val pb = new SimplePacketBuilder(PacketType.TextBufferInit)
pb.writeUTF(address) pb.writeUTF(address)

View File

@ -17,8 +17,7 @@ import net.minecraftforge.common.util.ForgeDirection
import scala.collection.convert.WrapAsScala._ import scala.collection.convert.WrapAsScala._
// Necessary to keep track of the GZIP stream. abstract class PacketBuilder(stream: OutputStream) extends DataOutputStream(stream) {
abstract class PacketBuilderBase[T <: OutputStream](protected val stream: T) extends DataOutputStream(stream) {
def writeTileEntity(t: TileEntity) = { def writeTileEntity(t: TileEntity) = {
writeInt(t.getWorldObj.provider.dimensionId) writeInt(t.getWorldObj.provider.dimensionId)
writeInt(t.xCoord) writeInt(t.xCoord)
@ -38,6 +37,8 @@ abstract class PacketBuilderBase[T <: OutputStream](protected val stream: T) ext
def writeNBT(nbt: NBTTagCompound) = CompressedStreamTools.writeCompressed(nbt, this) def writeNBT(nbt: NBTTagCompound) = CompressedStreamTools.writeCompressed(nbt, this)
def writePacketType(pt: PacketType.Value) = writeByte(pt.id)
def sendToAllPlayers() = OpenComputers.channel.sendToAll(packet) def sendToAllPlayers() = OpenComputers.channel.sendToAll(packet)
def sendToNearbyPlayers(t: TileEntity, range: Double = 1024): Unit = sendToNearbyPlayers(t.getWorldObj, t.xCoord + 0.5, t.yCoord + 0.5, t.zCoord + 0.5, range) def sendToNearbyPlayers(t: TileEntity, range: Double = 1024): Unit = sendToNearbyPlayers(t.getWorldObj, t.xCoord + 0.5, t.yCoord + 0.5, t.zCoord + 0.5, range)
@ -64,7 +65,10 @@ abstract class PacketBuilderBase[T <: OutputStream](protected val stream: T) ext
protected def packet: FMLProxyPacket protected def packet: FMLProxyPacket
} }
class PacketBuilder(packetType: PacketType.Value) extends PacketBuilderBase(PacketBuilder.newData(compressed = false)) { // Necessary to keep track of the GZIP stream.
abstract class PacketBuilderBase[T <: OutputStream](protected val stream: T) extends PacketBuilder(stream)
class SimplePacketBuilder(packetType: PacketType.Value) extends PacketBuilderBase(PacketBuilder.newData(compressed = false)) {
writeByte(packetType.id) writeByte(packetType.id)
override protected def packet = { override protected def packet = {

View File

@ -82,6 +82,8 @@ abstract class PacketHandler {
} }
def readNBT() = CompressedStreamTools.readCompressed(this) def readNBT() = CompressedStreamTools.readCompressed(this)
def readPacketType() = PacketType(readByte())
} }
} }

View File

@ -32,6 +32,7 @@ object PacketType extends Enumeration {
TextBufferDepthChange, TextBufferDepthChange,
TextBufferFill, TextBufferFill,
TextBufferInit, // Goes both ways. TextBufferInit, // Goes both ways.
TextBufferMulti,
TextBufferPaletteChange, TextBufferPaletteChange,
TextBufferPowerChange, TextBufferPowerChange,
TextBufferResolutionChange, TextBufferResolutionChange,

View File

@ -30,13 +30,12 @@ object SaveHandler {
scheduleSave(owner.world, owner.x, owner.z, nbt, name, data) scheduleSave(owner.world, owner.x, owner.z, nbt, name, data)
} }
def scheduleSave(owner: Owner, nbt: NBTTagCompound, name: String, save: NBTTagCompound => Unit) {
scheduleSave(owner, nbt, name, writeNBT(save))
}
def scheduleSave(container: Container, nbt: NBTTagCompound, name: String, save: NBTTagCompound => Unit) { def scheduleSave(container: Container, nbt: NBTTagCompound, name: String, save: NBTTagCompound => Unit) {
val tmpNbt = new NBTTagCompound() scheduleSave(container.world, math.round(container.xPosition - 0.5).toInt, math.round(container.zPosition - 0.5).toInt, nbt, name, writeNBT(save))
save(tmpNbt)
val baos = new ByteArrayOutputStream()
val dos = new DataOutputStream(baos)
CompressedStreamTools.write(tmpNbt, dos)
scheduleSave(container.world, math.round(container.xPosition - 0.5).toInt, math.round(container.zPosition - 0.5).toInt, nbt, name, baos.toByteArray)
} }
def scheduleSave(world: World, x: Int, z: Int, nbt: NBTTagCompound, name: String, data: Array[Byte]) { def scheduleSave(world: World, x: Int, z: Int, nbt: NBTTagCompound, name: String, data: Array[Byte]) {
@ -52,6 +51,19 @@ object SaveHandler {
scheduleSave(dimension, chunk, name, data) scheduleSave(dimension, chunk, name, data)
} }
def scheduleSave(world: World, x: Int, z: Int, nbt: NBTTagCompound, name: String, save: NBTTagCompound => Unit) {
scheduleSave(world, x, z, nbt, name, writeNBT(save))
}
private def writeNBT(save: NBTTagCompound => Unit) = {
val tmpNbt = new NBTTagCompound()
save(tmpNbt)
val baos = new ByteArrayOutputStream()
val dos = new DataOutputStream(baos)
CompressedStreamTools.write(tmpNbt, dos)
baos.toByteArray
}
def loadNBT(nbt: NBTTagCompound, name: String): NBTTagCompound = { def loadNBT(nbt: NBTTagCompound, name: String): NBTTagCompound = {
val data = load(nbt, name) val data = load(nbt, name)
val bais = new ByteArrayInputStream(data) val bais = new ByteArrayInputStream(data)

View File

@ -8,7 +8,7 @@ import li.cil.oc.api.driver.Container
import li.cil.oc.api.network._ import li.cil.oc.api.network._
import li.cil.oc.client.renderer.TextBufferRenderCache import li.cil.oc.client.renderer.TextBufferRenderCache
import li.cil.oc.client.{ComponentTracker => ClientComponentTracker, PacketSender => ClientPacketSender} import li.cil.oc.client.{ComponentTracker => ClientComponentTracker, PacketSender => ClientPacketSender}
import li.cil.oc.common.{SaveHandler, tileentity} import li.cil.oc.common._
import li.cil.oc.server.component.Keyboard import li.cil.oc.server.component.Keyboard
import li.cil.oc.server.{ComponentTracker => ServerComponentTracker, PacketSender => ServerPacketSender} import li.cil.oc.server.{ComponentTracker => ServerComponentTracker, PacketSender => ServerPacketSender}
import li.cil.oc.util.{PackedColor, SideTracker} import li.cil.oc.util.{PackedColor, SideTracker}
@ -43,6 +43,15 @@ class TextBuffer(val owner: Container) extends ManagedComponent with api.compone
private var relativeLitArea = -1.0 private var relativeLitArea = -1.0
private var _pendingCommands: Option[PacketBuilder] = None
private def pendingCommands = _pendingCommands.getOrElse {
val pb = new CompressedPacketBuilder(PacketType.TextBufferMulti)
pb.writeUTF(node.address)
_pendingCommands = Some(pb)
pb
}
var fullyLitCost = computeFullyLitCost() var fullyLitCost = computeFullyLitCost()
// This computes the energy cost (per tick) to keep the screen running if // This computes the energy cost (per tick) to keep the screen running if
@ -96,6 +105,11 @@ class TextBuffer(val owner: Container) extends ManagedComponent with api.compone
} }
} }
} }
this.synchronized {
_pendingCommands.foreach(_.sendToNearbyPlayers(owner))
_pendingCommands = None
}
} }
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
@ -550,41 +564,41 @@ object TextBuffer {
class ServerProxy(val owner: TextBuffer) extends Proxy { class ServerProxy(val owner: TextBuffer) extends Proxy {
override def onScreenColorChange() { override def onScreenColorChange() {
owner.owner.markChanged() owner.owner.markChanged()
ServerPacketSender.sendTextBufferColorChange(owner.node.address, owner.data.foreground, owner.data.background, owner.owner) owner.synchronized(ServerPacketSender.appendTextBufferColorChange(owner.pendingCommands, owner.data.foreground, owner.data.background))
} }
override def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) { override def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) {
super.onScreenCopy(col, row, w, h, tx, ty) super.onScreenCopy(col, row, w, h, tx, ty)
owner.owner.markChanged() owner.owner.markChanged()
ServerPacketSender.sendTextBufferCopy(owner.node.address, col, row, w, h, tx, ty, owner.owner) owner.synchronized(ServerPacketSender.appendTextBufferCopy(owner.pendingCommands, col, row, w, h, tx, ty))
} }
override def onScreenDepthChange(depth: ColorDepth) { override def onScreenDepthChange(depth: ColorDepth) {
owner.owner.markChanged() owner.owner.markChanged()
ServerPacketSender.sendTextBufferDepthChange(owner.node.address, depth, owner.owner) owner.synchronized(ServerPacketSender.appendTextBufferDepthChange(owner.pendingCommands, depth))
} }
override def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) { override def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) {
super.onScreenFill(col, row, w, h, c) super.onScreenFill(col, row, w, h, c)
owner.owner.markChanged() owner.owner.markChanged()
ServerPacketSender.sendTextBufferFill(owner.node.address, col, row, w, h, c, owner.owner) owner.synchronized(ServerPacketSender.appendTextBufferFill(owner.pendingCommands, col, row, w, h, c))
} }
override def onScreenPaletteChange(index: Int) { override def onScreenPaletteChange(index: Int) {
owner.owner.markChanged() owner.owner.markChanged()
ServerPacketSender.sendTextBufferPaletteChange(owner.node.address, index, owner.getPaletteColor(index), owner.owner) owner.synchronized(ServerPacketSender.appendTextBufferPaletteChange(owner.pendingCommands, index, owner.getPaletteColor(index)))
} }
override def onScreenResolutionChange(w: Int, h: Int) { override def onScreenResolutionChange(w: Int, h: Int) {
super.onScreenResolutionChange(w, h) super.onScreenResolutionChange(w, h)
owner.owner.markChanged() owner.owner.markChanged()
ServerPacketSender.sendTextBufferResolutionChange(owner.node.address, w, h, owner.owner) owner.synchronized(ServerPacketSender.appendTextBufferResolutionChange(owner.pendingCommands, w, h))
} }
override def onScreenSet(col: Int, row: Int, s: String, vertical: Boolean) { override def onScreenSet(col: Int, row: Int, s: String, vertical: Boolean) {
super.onScreenSet(col, row, s, vertical) super.onScreenSet(col, row, s, vertical)
owner.owner.markChanged() owner.owner.markChanged()
ServerPacketSender.sendTextBufferSet(owner.node.address, col, row, s, vertical, owner.owner) owner.synchronized(ServerPacketSender.appendTextBufferSet(owner.pendingCommands, col, row, s, vertical))
} }
override def keyDown(character: Char, code: Int, player: EntityPlayer) { override def keyDown(character: Char, code: Int, player: EntityPlayer) {

View File

@ -2,6 +2,7 @@ package li.cil.oc.common.tileentity
import cpw.mods.fml.relauncher.{Side, SideOnly} import cpw.mods.fml.relauncher.{Side, SideOnly}
import li.cil.oc.api.network._ import li.cil.oc.api.network._
import li.cil.oc.common.SaveHandler
import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.server.{PacketSender => ServerPacketSender}
import li.cil.oc.{Settings, api} import li.cil.oc.{Settings, api}
import net.minecraft.entity.player.EntityPlayer import net.minecraft.entity.player.EntityPlayer
@ -308,16 +309,25 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w
override def readFromNBT(nbt: NBTTagCompound) { override def readFromNBT(nbt: NBTTagCompound) {
tier = nbt.getByte(Settings.namespace + "tier") max 0 min 1 tier = nbt.getByte(Settings.namespace + "tier") max 0 min 1
super.readFromNBT(nbt) super.readFromNBT(nbt)
if (nbt.hasKey(Settings.namespace + "volume") && nbt.hasKey(Settings.namespace + "colors")) {
nbt.getIntArray(Settings.namespace + "volume").copyToArray(volume) nbt.getIntArray(Settings.namespace + "volume").copyToArray(volume)
nbt.getIntArray(Settings.namespace + "colors").map(convertColor).copyToArray(colors) nbt.getIntArray(Settings.namespace + "colors").map(convertColor).copyToArray(colors)
}
else {
val tag = SaveHandler.loadNBT(nbt, node.address + "_data")
tag.getIntArray("volume").copyToArray(volume)
tag.getIntArray("colors").map(convertColor).copyToArray(colors)
}
scale = nbt.getDouble(Settings.namespace + "scale") scale = nbt.getDouble(Settings.namespace + "scale")
} }
override def writeToNBT(nbt: NBTTagCompound) = this.synchronized { override def writeToNBT(nbt: NBTTagCompound) = this.synchronized {
nbt.setByte(Settings.namespace + "tier", tier.toByte) nbt.setByte(Settings.namespace + "tier", tier.toByte)
super.writeToNBT(nbt) super.writeToNBT(nbt)
nbt.setIntArray(Settings.namespace + "volume", volume) SaveHandler.scheduleSave(world, x, z, nbt, node.address + "_data", tag => {
nbt.setIntArray(Settings.namespace + "colors", colors.map(convertColor)) tag.setIntArray("volume", volume)
tag.setIntArray("colors", colors.map(convertColor))
})
nbt.setDouble(Settings.namespace + "scale", scale) nbt.setDouble(Settings.namespace + "scale", scale)
} }

View File

@ -2,8 +2,8 @@ package li.cil.oc.server
import li.cil.oc.api.component.TextBuffer.ColorDepth import li.cil.oc.api.component.TextBuffer.ColorDepth
import li.cil.oc.api.driver.Container import li.cil.oc.api.driver.Container
import li.cil.oc.common._
import li.cil.oc.common.tileentity.traits._ import li.cil.oc.common.tileentity.traits._
import li.cil.oc.common.{CompressedPacketBuilder, PacketBuilder, PacketType, tileentity}
import li.cil.oc.util.PackedColor import li.cil.oc.util.PackedColor
import net.minecraft.entity.player.EntityPlayerMP import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
@ -13,7 +13,7 @@ import net.minecraftforge.common.util.ForgeDirection
object PacketSender { object PacketSender {
def sendAbstractBusState(t: AbstractBusAware) { def sendAbstractBusState(t: AbstractBusAware) {
val pb = new PacketBuilder(PacketType.AbstractBusState) val pb = new SimplePacketBuilder(PacketType.AbstractBusState)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.writeBoolean(t.isAbstractBusAvailable) pb.writeBoolean(t.isAbstractBusAvailable)
@ -22,7 +22,7 @@ object PacketSender {
} }
def sendAnalyze(address: String, player: EntityPlayerMP) { def sendAnalyze(address: String, player: EntityPlayerMP) {
val pb = new PacketBuilder(PacketType.Analyze) val pb = new SimplePacketBuilder(PacketType.Analyze)
pb.writeUTF(address) pb.writeUTF(address)
@ -30,7 +30,7 @@ object PacketSender {
} }
def sendChargerState(t: tileentity.Charger) { def sendChargerState(t: tileentity.Charger) {
val pb = new PacketBuilder(PacketType.ChargerState) val pb = new SimplePacketBuilder(PacketType.ChargerState)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.writeDouble(t.chargeSpeed) pb.writeDouble(t.chargeSpeed)
@ -40,7 +40,7 @@ object PacketSender {
} }
def sendColorChange(t: Colored) { def sendColorChange(t: Colored) {
val pb = new PacketBuilder(PacketType.ColorChange) val pb = new SimplePacketBuilder(PacketType.ColorChange)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.writeInt(t.color) pb.writeInt(t.color)
@ -49,7 +49,7 @@ object PacketSender {
} }
def sendComputerState(t: Computer) { def sendComputerState(t: Computer) {
val pb = new PacketBuilder(PacketType.ComputerState) val pb = new SimplePacketBuilder(PacketType.ComputerState)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.writeBoolean(t.isRunning) pb.writeBoolean(t.isRunning)
@ -58,7 +58,7 @@ object PacketSender {
} }
def sendComputerUserList(t: Computer, list: Array[String]) { def sendComputerUserList(t: Computer, list: Array[String]) {
val pb = new PacketBuilder(PacketType.ComputerUserList) val pb = new SimplePacketBuilder(PacketType.ComputerUserList)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.writeInt(list.length) pb.writeInt(list.length)
@ -68,7 +68,7 @@ object PacketSender {
} }
def sendDisassemblerActive(t: tileentity.Disassembler, active: Boolean) { def sendDisassemblerActive(t: tileentity.Disassembler, active: Boolean) {
val pb = new PacketBuilder(PacketType.DisassemblerActiveChange) val pb = new SimplePacketBuilder(PacketType.DisassemblerActiveChange)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.writeBoolean(active) pb.writeBoolean(active)
@ -77,7 +77,7 @@ object PacketSender {
} }
def sendFloppyChange(t: tileentity.DiskDrive, stack: ItemStack = null) { def sendFloppyChange(t: tileentity.DiskDrive, stack: ItemStack = null) {
val pb = new PacketBuilder(PacketType.FloppyChange) val pb = new SimplePacketBuilder(PacketType.FloppyChange)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.writeItemStack(stack) pb.writeItemStack(stack)
@ -86,7 +86,7 @@ object PacketSender {
} }
def sendHologramClear(t: tileentity.Hologram) { def sendHologramClear(t: tileentity.Hologram) {
val pb = new PacketBuilder(PacketType.HologramClear) val pb = new SimplePacketBuilder(PacketType.HologramClear)
pb.writeTileEntity(t) pb.writeTileEntity(t)
@ -94,7 +94,7 @@ object PacketSender {
} }
def sendHologramColor(t: tileentity.Hologram, index: Int, value: Int) { def sendHologramColor(t: tileentity.Hologram, index: Int, value: Int) {
val pb = new PacketBuilder(PacketType.HologramColor) val pb = new SimplePacketBuilder(PacketType.HologramColor)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.writeInt(index) pb.writeInt(index)
@ -104,7 +104,7 @@ object PacketSender {
} }
def sendHologramPowerChange(t: tileentity.Hologram) { def sendHologramPowerChange(t: tileentity.Hologram) {
val pb = new PacketBuilder(PacketType.HologramPowerChange) val pb = new SimplePacketBuilder(PacketType.HologramPowerChange)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.writeBoolean(t.hasPower) pb.writeBoolean(t.hasPower)
@ -113,7 +113,7 @@ object PacketSender {
} }
def sendHologramScale(t: tileentity.Hologram) { def sendHologramScale(t: tileentity.Hologram) {
val pb = new PacketBuilder(PacketType.HologramScale) val pb = new SimplePacketBuilder(PacketType.HologramScale)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.writeDouble(t.scale) pb.writeDouble(t.scale)
@ -140,7 +140,7 @@ object PacketSender {
} }
def sendPetVisibility(name: Option[String] = None, player: Option[EntityPlayerMP] = None) { def sendPetVisibility(name: Option[String] = None, player: Option[EntityPlayerMP] = None) {
val pb = new PacketBuilder(PacketType.PetVisibility) val pb = new SimplePacketBuilder(PacketType.PetVisibility)
name match { name match {
case Some(n) => case Some(n) =>
@ -162,7 +162,7 @@ object PacketSender {
} }
def sendPowerState(t: PowerInformation) { def sendPowerState(t: PowerInformation) {
val pb = new PacketBuilder(PacketType.PowerState) val pb = new SimplePacketBuilder(PacketType.PowerState)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.writeDouble(t.globalBuffer) pb.writeDouble(t.globalBuffer)
@ -172,7 +172,7 @@ object PacketSender {
} }
def sendRedstoneState(t: RedstoneAware) { def sendRedstoneState(t: RedstoneAware) {
val pb = new PacketBuilder(PacketType.RedstoneState) val pb = new SimplePacketBuilder(PacketType.RedstoneState)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.writeBoolean(t.isOutputEnabled) pb.writeBoolean(t.isOutputEnabled)
@ -184,7 +184,7 @@ object PacketSender {
} }
def sendRobotAssembling(t: tileentity.RobotAssembler, assembling: Boolean) { def sendRobotAssembling(t: tileentity.RobotAssembler, assembling: Boolean) {
val pb = new PacketBuilder(PacketType.RobotAssemblingState) val pb = new SimplePacketBuilder(PacketType.RobotAssemblingState)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.writeBoolean(assembling) pb.writeBoolean(assembling)
@ -193,7 +193,7 @@ object PacketSender {
} }
def sendRobotMove(t: tileentity.Robot, ox: Int, oy: Int, oz: Int, direction: ForgeDirection) { def sendRobotMove(t: tileentity.Robot, ox: Int, oy: Int, oz: Int, direction: ForgeDirection) {
val pb = new PacketBuilder(PacketType.RobotMove) val pb = new SimplePacketBuilder(PacketType.RobotMove)
// Custom pb.writeTileEntity() with fake coordinates (valid for the client). // Custom pb.writeTileEntity() with fake coordinates (valid for the client).
pb.writeInt(t.proxy.world.provider.dimensionId) pb.writeInt(t.proxy.world.provider.dimensionId)
@ -206,7 +206,7 @@ object PacketSender {
} }
def sendRobotAnimateSwing(t: tileentity.Robot) { def sendRobotAnimateSwing(t: tileentity.Robot) {
val pb = new PacketBuilder(PacketType.RobotAnimateSwing) val pb = new SimplePacketBuilder(PacketType.RobotAnimateSwing)
pb.writeTileEntity(t.proxy) pb.writeTileEntity(t.proxy)
pb.writeInt(t.animationTicksTotal) pb.writeInt(t.animationTicksTotal)
@ -215,7 +215,7 @@ object PacketSender {
} }
def sendRobotAnimateTurn(t: tileentity.Robot) { def sendRobotAnimateTurn(t: tileentity.Robot) {
val pb = new PacketBuilder(PacketType.RobotAnimateTurn) val pb = new SimplePacketBuilder(PacketType.RobotAnimateTurn)
pb.writeTileEntity(t.proxy) pb.writeTileEntity(t.proxy)
pb.writeByte(t.turnAxis) pb.writeByte(t.turnAxis)
@ -225,7 +225,7 @@ object PacketSender {
} }
def sendRobotInventory(t: tileentity.Robot, slot: Int, stack: ItemStack) { def sendRobotInventory(t: tileentity.Robot, slot: Int, stack: ItemStack) {
val pb = new PacketBuilder(PacketType.RobotInventoryChange) val pb = new SimplePacketBuilder(PacketType.RobotInventoryChange)
pb.writeTileEntity(t.proxy) pb.writeTileEntity(t.proxy)
pb.writeInt(slot) pb.writeInt(slot)
@ -235,7 +235,7 @@ object PacketSender {
} }
def sendRobotSelectedSlotChange(t: tileentity.Robot) { def sendRobotSelectedSlotChange(t: tileentity.Robot) {
val pb = new PacketBuilder(PacketType.RobotSelectedSlotChange) val pb = new SimplePacketBuilder(PacketType.RobotSelectedSlotChange)
pb.writeTileEntity(t.proxy) pb.writeTileEntity(t.proxy)
pb.writeInt(t.selectedSlot) pb.writeInt(t.selectedSlot)
@ -244,7 +244,7 @@ object PacketSender {
} }
def sendRotatableState(t: Rotatable) { def sendRotatableState(t: Rotatable) {
val pb = new PacketBuilder(PacketType.RotatableState) val pb = new SimplePacketBuilder(PacketType.RotatableState)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.writeDirection(t.pitch) pb.writeDirection(t.pitch)
@ -254,59 +254,70 @@ object PacketSender {
} }
def sendSwitchActivity(t: tileentity.Switch) { def sendSwitchActivity(t: tileentity.Switch) {
val pb = new PacketBuilder(PacketType.SwitchActivity) val pb = new SimplePacketBuilder(PacketType.SwitchActivity)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.sendToNearbyPlayers(t, 64) pb.sendToNearbyPlayers(t, 64)
} }
def sendTextBufferColorChange(address: String, foreground: PackedColor.Color, background: PackedColor.Color, container: Container) { def appendTextBufferColorChange(pb: PacketBuilder, foreground: PackedColor.Color, background: PackedColor.Color) {
val pb = new PacketBuilder(PacketType.TextBufferColorChange) pb.writePacketType(PacketType.TextBufferColorChange)
pb.writeUTF(address)
pb.writeInt(foreground.value) pb.writeInt(foreground.value)
pb.writeBoolean(foreground.isPalette) pb.writeBoolean(foreground.isPalette)
pb.writeInt(background.value) pb.writeInt(background.value)
pb.writeBoolean(background.isPalette) pb.writeBoolean(background.isPalette)
pb.sendToNearbyPlayers(container)
} }
def sendTextBufferCopy(address: String, col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int, container: Container) { def appendTextBufferCopy(pb: PacketBuilder, col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) {
val pb = new PacketBuilder(PacketType.TextBufferCopy) pb.writePacketType(PacketType.TextBufferCopy)
pb.writeUTF(address)
pb.writeInt(col) pb.writeInt(col)
pb.writeInt(row) pb.writeInt(row)
pb.writeInt(w) pb.writeInt(w)
pb.writeInt(h) pb.writeInt(h)
pb.writeInt(tx) pb.writeInt(tx)
pb.writeInt(ty) pb.writeInt(ty)
pb.sendToNearbyPlayers(container)
} }
def sendTextBufferDepthChange(address: String, value: ColorDepth, container: Container) { def appendTextBufferDepthChange(pb: PacketBuilder, value: ColorDepth) {
val pb = new PacketBuilder(PacketType.TextBufferDepthChange) pb.writePacketType(PacketType.TextBufferDepthChange)
pb.writeUTF(address)
pb.writeInt(value.ordinal) pb.writeInt(value.ordinal)
pb.sendToNearbyPlayers(container)
} }
def sendTextBufferFill(address: String, col: Int, row: Int, w: Int, h: Int, c: Char, container: Container) { def appendTextBufferFill(pb: PacketBuilder, col: Int, row: Int, w: Int, h: Int, c: Char) {
val pb = new PacketBuilder(PacketType.TextBufferFill) pb.writePacketType(PacketType.TextBufferFill)
pb.writeUTF(address)
pb.writeInt(col) pb.writeInt(col)
pb.writeInt(row) pb.writeInt(row)
pb.writeInt(w) pb.writeInt(w)
pb.writeInt(h) pb.writeInt(h)
pb.writeChar(c) pb.writeChar(c)
}
pb.sendToNearbyPlayers(container) def appendTextBufferPaletteChange(pb: PacketBuilder, index: Int, color: Int) {
pb.writePacketType(PacketType.TextBufferPaletteChange)
pb.writeInt(index)
pb.writeInt(color)
}
def appendTextBufferResolutionChange(pb: PacketBuilder, w: Int, h: Int) {
pb.writePacketType(PacketType.TextBufferResolutionChange)
pb.writeInt(w)
pb.writeInt(h)
}
def appendTextBufferSet(pb: PacketBuilder, col: Int, row: Int, s: String, vertical: Boolean) {
pb.writePacketType(PacketType.TextBufferSet)
pb.writeInt(col)
pb.writeInt(row)
pb.writeUTF(s)
pb.writeBoolean(vertical)
} }
def sendTextBufferInit(address: String, value: NBTTagCompound, player: EntityPlayerMP) { def sendTextBufferInit(address: String, value: NBTTagCompound, player: EntityPlayerMP) {
@ -318,18 +329,8 @@ object PacketSender {
pb.sendToPlayer(player) pb.sendToPlayer(player)
} }
def sendTextBufferPaletteChange(address: String, index: Int, color: Int, container: Container) {
val pb = new PacketBuilder(PacketType.TextBufferPaletteChange)
pb.writeUTF(address)
pb.writeInt(index)
pb.writeInt(color)
pb.sendToNearbyPlayers(container)
}
def sendTextBufferPowerChange(address: String, hasPower: Boolean, container: Container) { def sendTextBufferPowerChange(address: String, hasPower: Boolean, container: Container) {
val pb = new PacketBuilder(PacketType.TextBufferPowerChange) val pb = new SimplePacketBuilder(PacketType.TextBufferPowerChange)
pb.writeUTF(address) pb.writeUTF(address)
pb.writeBoolean(hasPower) pb.writeBoolean(hasPower)
@ -337,30 +338,8 @@ object PacketSender {
pb.sendToNearbyPlayers(container) pb.sendToNearbyPlayers(container)
} }
def sendTextBufferResolutionChange(address: String, w: Int, h: Int, container: Container) {
val pb = new PacketBuilder(PacketType.TextBufferResolutionChange)
pb.writeUTF(address)
pb.writeInt(w)
pb.writeInt(h)
pb.sendToNearbyPlayers(container)
}
def sendTextBufferSet(address: String, col: Int, row: Int, s: String, vertical: Boolean, container: Container) {
val pb = new PacketBuilder(PacketType.TextBufferSet)
pb.writeUTF(address)
pb.writeInt(col)
pb.writeInt(row)
pb.writeUTF(s)
pb.writeBoolean(vertical)
pb.sendToNearbyPlayers(container)
}
def sendScreenTouchMode(t: tileentity.Screen, value: Boolean) { def sendScreenTouchMode(t: tileentity.Screen, value: Boolean) {
val pb = new PacketBuilder(PacketType.ScreenTouchMode) val pb = new SimplePacketBuilder(PacketType.ScreenTouchMode)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.writeBoolean(value) pb.writeBoolean(value)
@ -369,7 +348,7 @@ object PacketSender {
} }
def sendServerPresence(t: tileentity.ServerRack) { def sendServerPresence(t: tileentity.ServerRack) {
val pb = new PacketBuilder(PacketType.ServerPresence) val pb = new SimplePacketBuilder(PacketType.ServerPresence)
pb.writeTileEntity(t) pb.writeTileEntity(t)
t.servers.foreach { t.servers.foreach {
@ -384,7 +363,7 @@ object PacketSender {
} }
def sendServerState(t: tileentity.ServerRack) { def sendServerState(t: tileentity.ServerRack) {
val pb = new PacketBuilder(PacketType.ComputerState) val pb = new SimplePacketBuilder(PacketType.ComputerState)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.writeInt(-1) pb.writeInt(-1)
@ -394,7 +373,7 @@ object PacketSender {
} }
def sendServerState(t: tileentity.ServerRack, number: Int, player: Option[EntityPlayerMP] = None) { def sendServerState(t: tileentity.ServerRack, number: Int, player: Option[EntityPlayerMP] = None) {
val pb = new PacketBuilder(PacketType.ComputerState) val pb = new SimplePacketBuilder(PacketType.ComputerState)
pb.writeTileEntity(t) pb.writeTileEntity(t)
pb.writeInt(number) pb.writeInt(number)
@ -413,7 +392,7 @@ object PacketSender {
} }
def sendSound(world: World, x: Int, y: Int, z: Int, frequency: Int, duration: Int) { def sendSound(world: World, x: Int, y: Int, z: Int, frequency: Int, duration: Int) {
val pb = new PacketBuilder(PacketType.Sound) val pb = new SimplePacketBuilder(PacketType.Sound)
pb.writeInt(world.provider.dimensionId) pb.writeInt(world.provider.dimensionId)
pb.writeInt(x) pb.writeInt(x)

View File

@ -8,7 +8,7 @@ import li.cil.oc.api.machine._
import li.cil.oc.api.network._ import li.cil.oc.api.network._
import li.cil.oc.api.{FileSystem, Network, machine} import li.cil.oc.api.{FileSystem, Network, machine}
import li.cil.oc.common.component.ManagedComponent import li.cil.oc.common.component.ManagedComponent
import li.cil.oc.common.tileentity import li.cil.oc.common.{SaveHandler, tileentity}
import li.cil.oc.server.PacketSender import li.cil.oc.server.PacketSender
import li.cil.oc.server.driver.Registry import li.cil.oc.server.driver.Registry
import li.cil.oc.server.network.{ArgumentsImpl, Callbacks} import li.cil.oc.server.network.{ArgumentsImpl, Callbacks}
@ -584,7 +584,10 @@ class Machine(val owner: Owner, constructor: Constructor[_ <: Architecture]) ext
c.getString("address") -> c.getString("name") c.getString("address") -> c.getString("name")
}) })
tmp.foreach(fs => fs.load(nbt.getCompoundTag("tmp"))) tmp.foreach(fs => {
if (nbt.hasKey("tmp")) fs.load(nbt.getCompoundTag("tmp"))
else fs.load(SaveHandler.loadNBT(nbt, node.address + "_tmp"))
})
if (state.size > 0 && state.top != Machine.State.Stopped && init()) try { if (state.size > 0 && state.top != Machine.State.Stopped && init()) try {
architecture.load(nbt) architecture.load(nbt)
@ -653,7 +656,7 @@ class Machine(val owner: Owner, constructor: Constructor[_ <: Architecture]) ext
} }
nbt.setTag("components", componentsNbt) nbt.setTag("components", componentsNbt)
tmp.foreach(fs => nbt.setNewCompoundTag("tmp", fs.save)) tmp.foreach(fs => SaveHandler.scheduleSave(owner, nbt, node.address + "_tmp", fs.save _))
if (state.top != Machine.State.Stopped) try { if (state.top != Machine.State.Stopped) try {
architecture.save(nbt) architecture.save(nbt)