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)
This commit is contained in:
Florian Nücke 2014-03-24 17:17:39 +01:00
parent 4c564d72d9
commit 75f661696f
11 changed files with 94 additions and 38 deletions

View File

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

View File

@ -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.
// ----------------------------------------------------------------------- //

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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