From 75f661696f52d2a8054f1613f6bb1f6ae549cbc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Mon, 24 Mar 2014 17:17:39 +0100 Subject: [PATCH] overscaling chars on screens a little (amount configurable) to (hopefully) suppress/reduce render glitches on less accurate graphics cards fixed potential NPE in analyzer fixed minor derp in adapter triggering ic2 events in tick handler to avoid recreating tile entity in invalidate in certain cases (RiM) saving chunk coords in computer nbt for reliably loading them again even when moved (RiM or similar) --- src/main/resources/reference.conf | 4 ++ src/main/scala/li/cil/oc/Settings.scala | 1 + .../renderer/MonospaceFontRenderer.scala | 11 +++-- .../scala/li/cil/oc/common/SaveHandler.scala | 8 +++- .../li/cil/oc/common/item/Analyzer.scala | 3 +- .../li/cil/oc/common/tileentity/Adapter.scala | 9 +++- .../tileentity/traits/IC2PowerGridAware.scala | 9 ++++ .../tileentity/traits/PowerAcceptor.scala | 20 +++------ .../scala/li/cil/oc/server/TickHandler.scala | 41 +++++++++++++++---- .../oc/server/component/machine/Machine.scala | 4 +- .../machine/NativeLuaArchitecture.scala | 22 ++++++++-- 11 files changed, 94 insertions(+), 38 deletions(-) create mode 100644 src/main/scala/li/cil/oc/common/tileentity/traits/IC2PowerGridAware.scala diff --git a/src/main/resources/reference.conf b/src/main/resources/reference.conf index dc7d017d5..293e43d97 100644 --- a/src/main/resources/reference.conf +++ b/src/main/resources/reference.conf @@ -61,6 +61,10 @@ opencomputers { # The volume multiplier applied to sounds from this mod like the computer # running noise. Disable sounds by setting this to zero. soundVolume: 1.0 + + # This is the scaling of the individual chars rendered on screens. This + # is set to slightly overscale per default, to avoid + fontCharScale: 1.01 } # Computer related settings, concerns server performance and security. diff --git a/src/main/scala/li/cil/oc/Settings.scala b/src/main/scala/li/cil/oc/Settings.scala index 3760982af..4bd33e7e6 100644 --- a/src/main/scala/li/cil/oc/Settings.scala +++ b/src/main/scala/li/cil/oc/Settings.scala @@ -29,6 +29,7 @@ class Settings(config: Config) { val pasteShortcut = config.getStringList("client.pasteShortcut").toSet val robotLabels = config.getBoolean("client.robotLabels") val soundVolume = config.getDouble("client.soundVolume").toFloat max 0 min 2 + val fontCharScale = config.getDouble("client.fontCharScale") max 0.5 min 2 val rTreeDebugRenderer = false // *Not* to be configurable via config file. // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/client/renderer/MonospaceFontRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/MonospaceFontRenderer.scala index 397e2b062..c2ca64c47 100644 --- a/src/main/scala/li/cil/oc/client/renderer/MonospaceFontRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/MonospaceFontRenderer.scala @@ -43,6 +43,9 @@ object MonospaceFontRenderer { // Set up the display lists. { + val s = Settings.get.fontCharScale + val dw = charWidth * s - charWidth + val dh = charHeight * s - charHeight val t = Tessellator.instance // Now create lists for all printable chars. for (index <- 1 until 0xFF) { @@ -52,10 +55,10 @@ object MonospaceFontRenderer { val v = y * vStep GL11.glNewList(charLists + index, GL11.GL_COMPILE) t.startDrawingQuads() - t.addVertexWithUV(0, charHeight, 0, u, v + vSize) - t.addVertexWithUV(charWidth, charHeight, 0, u + uSize, v + vSize) - t.addVertexWithUV(charWidth, 0, 0, u + uSize, v) - t.addVertexWithUV(0, 0, 0, u, v) + t.addVertexWithUV(-dw, charHeight * s, 0, u, v + vSize) + t.addVertexWithUV(charWidth * s, charHeight * s, 0, u + uSize, v + vSize) + t.addVertexWithUV(charWidth * s, -dh, 0, u + uSize, v) + t.addVertexWithUV(-dw, -dh, 0, u, v) t.draw() GL11.glTranslatef(charWidth, 0, 0) GL11.glEndList() diff --git a/src/main/scala/li/cil/oc/common/SaveHandler.scala b/src/main/scala/li/cil/oc/common/SaveHandler.scala index b0e36feae..5a1410ea7 100644 --- a/src/main/scala/li/cil/oc/common/SaveHandler.scala +++ b/src/main/scala/li/cil/oc/common/SaveHandler.scala @@ -19,7 +19,13 @@ object SaveHandler { def scheduleSave(dimension: Int, chunk: ChunkCoordIntPair, name: String, data: Array[Byte]) = saveData.synchronized { if (chunk == null) throw new IllegalArgumentException("chunk is null") - else saveData.getOrElseUpdate(dimension, mutable.Map.empty).getOrElseUpdate(chunk, mutable.Map.empty) += name -> data + else { + val chunks = saveData.getOrElseUpdate(dimension, mutable.Map.empty) + // Make sure we get rid of old versions (e.g. left over by other mods + // triggering a save - this is mostly used for RiM compatibility). + chunks.values.foreach(_ -= name) + chunks.getOrElseUpdate(chunk, mutable.Map.empty) += name -> data + } } def load(dimension: Int, chunk: ChunkCoordIntPair, name: String): Array[Byte] = { diff --git a/src/main/scala/li/cil/oc/common/item/Analyzer.scala b/src/main/scala/li/cil/oc/common/item/Analyzer.scala index c14ffa137..7613a0186 100644 --- a/src/main/scala/li/cil/oc/common/item/Analyzer.scala +++ b/src/main/scala/li/cil/oc/common/item/Analyzer.scala @@ -1,6 +1,5 @@ package li.cil.oc.common.item -import cpw.mods.fml.common.network.Player import java.util import li.cil.oc.Settings import li.cil.oc.api.network._ @@ -70,8 +69,8 @@ class Analyzer(val parent: Delegator) extends Delegate { player.sendChatToPlayer(ChatMessageComponent.createFromTranslationWithSubstitutions( Settings.namespace + "gui.Analyzer.Address", address)) + PacketSender.sendAnalyze(address, player) } - PacketSender.sendAnalyze(address, player) } override def registerIcons(iconRegister: IconRegister) { diff --git a/src/main/scala/li/cil/oc/common/tileentity/Adapter.scala b/src/main/scala/li/cil/oc/common/tileentity/Adapter.scala index 71dbaa8f1..ffca01838 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Adapter.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Adapter.scala @@ -27,7 +27,7 @@ class Adapter extends traits.Environment with Analyzable { override def updateEntity() { super.updateEntity() - if (blocks.nonEmpty) { + if (updatingBlocks.nonEmpty) { for (block <- updatingBlocks) { block.update() } @@ -100,6 +100,13 @@ class Adapter extends traits.Environment with Analyzable { // ----------------------------------------------------------------------- // + override def onDisconnect(node: Node) { + super.onDisconnect(node) + if (node == this.node) { + updatingBlocks.clear() + } + } + override def readFromNBT(nbt: NBTTagCompound) { super.readFromNBT(nbt) diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/IC2PowerGridAware.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/IC2PowerGridAware.scala new file mode 100644 index 000000000..23e6333d4 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/IC2PowerGridAware.scala @@ -0,0 +1,9 @@ +package li.cil.oc.common.tileentity.traits + +import ic2.api.energy.tile.IEnergySink +import cpw.mods.fml.common.Optional + +@Optional.Interface(iface = "ic2.api.energy.tile.IEnergySink", modid = "IC2") +trait IC2PowerGridAware extends IEnergySink { + var addedToPowerGrid = false +} diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/PowerAcceptor.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/PowerAcceptor.scala index 4b50b5d63..83d160722 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/PowerAcceptor.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/PowerAcceptor.scala @@ -4,21 +4,19 @@ import buildcraft.api.power.{PowerHandler, IPowerReceptor} import cofh.api.energy.IEnergyHandler import cpw.mods.fml.common.{ModAPIManager, Loader, Optional} import cpw.mods.fml.relauncher.{Side, SideOnly} -import ic2.api.energy.event.{EnergyTileUnloadEvent, EnergyTileLoadEvent} -import ic2.api.energy.tile.IEnergySink import li.cil.oc.api.network.Connector +import li.cil.oc.server.TickHandler import li.cil.oc.Settings -import net.minecraftforge.common.{MinecraftForge, ForgeDirection} +import net.minecraftforge.common.ForgeDirection import universalelectricity.api.energy.{IEnergyContainer, IEnergyInterface} @Optional.InterfaceList(Array( new Optional.Interface(iface = "buildcraft.api.power.IPowerReceptor", modid = "BuildCraftAPI|power"), - new Optional.Interface(iface = "ic2.api.energy.tile.IEnergySink", modid = "IC2"), new Optional.Interface(iface = "cofh.api.energy.IEnergyHandler", modid = "ThermalExpansion"), new Optional.Interface(iface = "universalelectricity.api.energy.IEnergyInterface", modid = "UniversalElectricity"), new Optional.Interface(iface = "universalelectricity.api.energy.IEnergyContainer", modid = "UniversalElectricity") )) -trait PowerAcceptor extends TileEntity with IPowerReceptor with IEnergySink with IEnergyHandler with IEnergyInterface with IEnergyContainer { +trait PowerAcceptor extends TileEntity with IPowerReceptor with IC2PowerGridAware with IEnergyHandler with IEnergyInterface with IEnergyContainer { @SideOnly(Side.CLIENT) protected def hasConnector(side: ForgeDirection) = false @@ -99,22 +97,14 @@ trait PowerAcceptor extends TileEntity with IPowerReceptor with IEnergySink with // ----------------------------------------------------------------------- // // IndustrialCraft2 - private var addedToPowerGrid = false - @Optional.Method(modid = "IC2") def loadIC2() { - if (!addedToPowerGrid) { - MinecraftForge.EVENT_BUS.post(new EnergyTileLoadEvent(this)) - addedToPowerGrid = true - } + if (!addedToPowerGrid) TickHandler.scheduleIC2Add(this) } @Optional.Method(modid = "IC2") def unloadIC2() { - if (addedToPowerGrid) { - MinecraftForge.EVENT_BUS.post(new EnergyTileUnloadEvent(this)) - addedToPowerGrid = false - } + if (addedToPowerGrid) TickHandler.scheduleIC2Remove(this) } @Optional.Method(modid = "IC2") diff --git a/src/main/scala/li/cil/oc/server/TickHandler.scala b/src/main/scala/li/cil/oc/server/TickHandler.scala index 492c94634..4ebd52afa 100644 --- a/src/main/scala/li/cil/oc/server/TickHandler.scala +++ b/src/main/scala/li/cil/oc/server/TickHandler.scala @@ -2,35 +2,58 @@ package li.cil.oc.server import codechicken.multipart.TMultiPart import cpw.mods.fml.common.{FMLCommonHandler, Optional, TickType, ITickHandler} +import ic2.api.energy.event.{EnergyTileUnloadEvent, EnergyTileLoadEvent} import java.util import li.cil.oc.api.Network +import li.cil.oc.common.tileentity.traits.IC2PowerGridAware import net.minecraft.tileentity.TileEntity +import net.minecraftforge.common.MinecraftForge import scala.collection.mutable +import li.cil.oc.OpenComputers +import java.util.logging.Level object TickHandler extends ITickHandler { - val pendingAdds = mutable.Buffer.empty[() => Unit] + val pending = mutable.Buffer.empty[() => Unit] def schedule(tileEntity: TileEntity) = - if (FMLCommonHandler.instance.getEffectiveSide.isServer) pendingAdds.synchronized { - pendingAdds += (() => Network.joinOrCreateNetwork(tileEntity)) + if (FMLCommonHandler.instance.getEffectiveSide.isServer) pending.synchronized { + pending += (() => Network.joinOrCreateNetwork(tileEntity)) } @Optional.Method(modid = "ForgeMultipart") def schedule(part: TMultiPart) = - if (FMLCommonHandler.instance.getEffectiveSide.isServer) pendingAdds.synchronized { - pendingAdds += (() => Network.joinOrCreateNetwork(part.tile)) + if (FMLCommonHandler.instance.getEffectiveSide.isServer) pending.synchronized { + pending += (() => Network.joinOrCreateNetwork(part.tile)) } + @Optional.Method(modid = "IC2") + def scheduleIC2Add(tileEntity: TileEntity with IC2PowerGridAware) = pending.synchronized { + pending += (() => if (!tileEntity.addedToPowerGrid && !tileEntity.isInvalid) { + MinecraftForge.EVENT_BUS.post(new EnergyTileLoadEvent(tileEntity)) + tileEntity.addedToPowerGrid = true + }) + } + + @Optional.Method(modid = "IC2") + def scheduleIC2Remove(tileEntity: TileEntity with IC2PowerGridAware) = pending.synchronized { + pending += (() => if (tileEntity.addedToPowerGrid) { + MinecraftForge.EVENT_BUS.post(new EnergyTileUnloadEvent(tileEntity)) + tileEntity.addedToPowerGrid = false + }) + } + override def getLabel = "OpenComputers Network Initialization Ticker" override def ticks() = util.EnumSet.of(TickType.SERVER) override def tickStart(`type`: util.EnumSet[TickType], tickData: AnyRef*) {} - override def tickEnd(`type`: util.EnumSet[TickType], tickData: AnyRef*) = pendingAdds.synchronized { - for (callback <- pendingAdds) { - callback() + override def tickEnd(`type`: util.EnumSet[TickType], tickData: AnyRef*) = pending.synchronized { + for (callback <- pending) { + try callback() catch { + case t: Throwable => OpenComputers.log.log(Level.WARNING, "Error in scheduled tick action.", t) + } } - pendingAdds.clear() + pending.clear() } } diff --git a/src/main/scala/li/cil/oc/server/component/machine/Machine.scala b/src/main/scala/li/cil/oc/server/component/machine/Machine.scala index 56025691f..67a268821 100644 --- a/src/main/scala/li/cil/oc/server/component/machine/Machine.scala +++ b/src/main/scala/li/cil/oc/server/component/machine/Machine.scala @@ -505,10 +505,10 @@ class Machine(val owner: Owner, val rom: Option[ManagedEnvironment], constructor for ((address, name) <- _components) { if (node.network.node(address) == null) { if (name == "filesystem") { - OpenComputers.log.fine("A component of type '%s' disappeared! This usually means that it didn't save its node.".format(name)) + OpenComputers.log.fine(s"A component of type '$name' disappeared ($address)! This usually means that it didn't save its node.") OpenComputers.log.fine("If this was a file system provided by a ComputerCraft peripheral, this is normal.") } - else OpenComputers.log.warning("A component of type '%s' disappeared! This usually means that it didn't save its node.".format(name)) + else OpenComputers.log.warning(s"A component of type '$name' disappeared ($address)! This usually means that it didn't save its node.") signal("component_removed", address, name) invalid += address } diff --git a/src/main/scala/li/cil/oc/server/component/machine/NativeLuaArchitecture.scala b/src/main/scala/li/cil/oc/server/component/machine/NativeLuaArchitecture.scala index c4c3d38ea..02bcd82d6 100644 --- a/src/main/scala/li/cil/oc/server/component/machine/NativeLuaArchitecture.scala +++ b/src/main/scala/li/cil/oc/server/component/machine/NativeLuaArchitecture.scala @@ -596,10 +596,18 @@ class NativeLuaArchitecture(val machine: api.machine.Machine) extends Architectu // on. First, clear the stack, meaning the current kernel. lua.setTop(0) + // Since we have no world yet, we rely on the dimension we were saved in. + // Same goes for the chunk. This also works around issues with computers + // being moved (e.g. Redstone in Motion). val dimension = nbt.getInteger("dimension") + val chunk = + if (nbt.hasKey("chunkX") && nbt.hasKey("chunkZ")) + new ChunkCoordIntPair(nbt.getInteger("chunkX"), nbt.getInteger("chunkZ")) + else + new ChunkCoordIntPair(machine.owner.x >> 4, machine.owner.z >> 4) val kernel = if (nbt.hasKey("kernel")) nbt.getByteArray("kernel") - else SaveHandler.load(dimension, new ChunkCoordIntPair(machine.owner.x >> 4, machine.owner.z >> 4), node.address + "_kernel") + else SaveHandler.load(dimension, chunk, node.address + "_kernel") unpersist(kernel) if (!lua.isThread(1)) { // This shouldn't really happen, but there's a chance it does if @@ -609,7 +617,7 @@ class NativeLuaArchitecture(val machine: api.machine.Machine) extends Architectu if (state.contains(Machine.State.SynchronizedCall) || state.contains(Machine.State.SynchronizedReturn)) { val stack = if (nbt.hasKey("stack")) nbt.getByteArray("stack") - else SaveHandler.load(dimension, new ChunkCoordIntPair(machine.owner.x >> 4, machine.owner.z >> 4), node.address + "_stack") + else SaveHandler.load(dimension, chunk, node.address + "_stack") unpersist(stack) if (!(if (state.contains(Machine.State.SynchronizedCall)) lua.isFunction(2) else lua.isTable(2))) { // Same as with the above, should not really happen normally, but @@ -637,14 +645,20 @@ class NativeLuaArchitecture(val machine: api.machine.Machine) extends Architectu // Try persisting Lua, because that's what all of the rest depends on. // Save the kernel state (which is always at stack index one). assert(lua.isThread(1)) + + // We have to save the dimension and chunk coordinates, because they are + // not available on load / may have changed if the computer was moved. val dimension = machine.owner.world.provider.dimensionId + val chunk = new ChunkCoordIntPair(machine.owner.x >> 4, machine.owner.z >> 4) nbt.setInteger("dimension", dimension) - SaveHandler.scheduleSave(dimension, new ChunkCoordIntPair(machine.owner.x >> 4, machine.owner.z >> 4), node.address + "_kernel", persist(1)) + nbt.setInteger("chunkX", chunk.chunkXPos) + nbt.setInteger("chunkZ", chunk.chunkZPos) + SaveHandler.scheduleSave(dimension, chunk, node.address + "_kernel", persist(1)) // While in a driver call we have one object on the global stack: either // the function to call the driver with, or the result of the call. if (state.contains(Machine.State.SynchronizedCall) || state.contains(Machine.State.SynchronizedReturn)) { assert(if (state.contains(Machine.State.SynchronizedCall)) lua.isFunction(2) else lua.isTable(2)) - SaveHandler.scheduleSave(dimension, new ChunkCoordIntPair(machine.owner.x >> 4, machine.owner.z >> 4), node.address + "_stack", persist(2)) + SaveHandler.scheduleSave(dimension, chunk, node.address + "_stack", persist(2)) } nbt.setInteger("kernelMemory", math.ceil(kernelMemory / ramScale).toInt)