From cbfbc2f152878284934cb3c5dd40091730042ec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Mon, 14 Oct 2013 16:43:22 +0200 Subject: [PATCH] refactored screen env into screen's companion object (same as already done with computer); keeping track of pressed keys client-side to send char on key-up (lwjgl doesn't seem to properly provide that, code and char always seem to be 0 on key up) and to send key up events for all still pressed keys when closing the gui. server will have to keep track of all key presses (and the players that did them) at some point, too, since clients may just disconnect, for example (or be malicious) --- .../opencomputers/lua/rom/lib/component.lua | 2 +- li/cil/oc/client/gui/Screen.scala | 17 +++-- li/cil/oc/common/component/Screen.scala | 67 ++++++++++++++++++- .../common/component/ScreenEnvironment.scala | 62 ----------------- li/cil/oc/common/tileentity/Screen.scala | 15 +++-- li/cil/oc/server/component/GraphicsCard.scala | 4 +- 6 files changed, 91 insertions(+), 76 deletions(-) delete mode 100644 li/cil/oc/common/component/ScreenEnvironment.scala diff --git a/assets/opencomputers/lua/rom/lib/component.lua b/assets/opencomputers/lua/rom/lib/component.lua index 43c151db8..60b60f724 100644 --- a/assets/opencomputers/lua/rom/lib/component.lua +++ b/assets/opencomputers/lua/rom/lib/component.lua @@ -33,7 +33,7 @@ function component.primary(componentType, ...) end else assert(component.isAvailable(componentType), - "no primary " .. componentType .. " available") + "no primary '" .. componentType .. "' available") return primaries[componentType] end end diff --git a/li/cil/oc/client/gui/Screen.scala b/li/cil/oc/client/gui/Screen.scala index d13cbc63c..7656b3188 100644 --- a/li/cil/oc/client/gui/Screen.scala +++ b/li/cil/oc/client/gui/Screen.scala @@ -10,6 +10,7 @@ import net.minecraft.client.renderer.texture.TextureManager import net.minecraft.util.ResourceLocation import org.lwjgl.input.Keyboard import org.lwjgl.opengl.GL11 +import scala.collection.mutable /** * This GUI shows the buffer of a single screen. @@ -25,7 +26,9 @@ import org.lwjgl.opengl.GL11 class Screen(val tileEntity: tileentity.Screen) extends MCGuiScreen { tileEntity.guiScreen = Some(this) - var (x, y, innerWidth, innerHeight, scale) = (0, 0, 0, 0, 0.0) + private var (x, y, innerWidth, innerHeight, scale) = (0, 0, 0, 0, 0.0) + + private val pressedKeys = mutable.Map.empty[Int, Char] /** Must be called when the size of the underlying screen changes */ def setSize(w: Double, h: Double) = { @@ -53,18 +56,19 @@ class Screen(val tileEntity: tileentity.Screen) extends MCGuiScreen { super.handleKeyboardInput() val code = Keyboard.getEventKey - val char = Keyboard.getEventCharacter - if (code != Keyboard.KEY_ESCAPE && code != Keyboard.KEY_F11) if (code == Keyboard.KEY_INSERT && MCGuiScreen.isShiftKeyDown) { if (Keyboard.getEventKeyState) PacketSender.sendClipboard(tileEntity, MCGuiScreen.getClipboardString) } else if (Keyboard.getEventKeyState) { + val char = Keyboard.getEventCharacter PacketSender.sendKeyDown(tileEntity, char, code) + pressedKeys += code -> char } - else { - PacketSender.sendKeyUp(tileEntity, char, code) + else pressedKeys.remove(code) match { + case Some(char) => PacketSender.sendKeyUp(tileEntity, char, code) + case _ => // Wasn't pressed while viewing the screen. } } @@ -85,6 +89,9 @@ class Screen(val tileEntity: tileentity.Screen) extends MCGuiScreen { override def onGuiClosed() = { super.onGuiClosed() tileEntity.guiScreen = None + for ((code, char) <- pressedKeys) { + PacketSender.sendKeyUp(tileEntity, char, code) + } } override def drawScreen(mouseX: Int, mouseY: Int, dt: Float): Unit = { diff --git a/li/cil/oc/common/component/Screen.scala b/li/cil/oc/common/component/Screen.scala index 3f1108885..bf3745132 100644 --- a/li/cil/oc/common/component/Screen.scala +++ b/li/cil/oc/common/component/Screen.scala @@ -1,13 +1,16 @@ package li.cil.oc.common.component +import li.cil.oc.api.network.{Message, Visibility, Node} import li.cil.oc.util.TextBuffer import net.minecraft.nbt.NBTTagCompound -class Screen(val owner: ScreenEnvironment) { +class Screen(val owner: Screen.Environment) { val supportedResolutions = List((40, 24), (80, 24)) private val buffer = new TextBuffer(80, 24) + // ----------------------------------------------------------------------- // + def text = buffer.toString def lines = buffer.buffer @@ -40,6 +43,8 @@ class Screen(val owner: ScreenEnvironment) { if (buffer.copy(col, row, w, h, tx, ty)) owner.onScreenCopy(col, row, w, h, tx, ty) + // ----------------------------------------------------------------------- // + def load(nbt: NBTTagCompound) = { buffer.readFromNBT(nbt.getCompoundTag("buffer")) } @@ -49,4 +54,64 @@ class Screen(val owner: ScreenEnvironment) { buffer.writeToNBT(nbtBuffer) nbt.setCompoundTag("buffer", nbtBuffer) } +} + +object Screen { + + trait Environment extends Node { + final val screen = new Screen(this) + + // ----------------------------------------------------------------------- // + + override val name = "screen" + + override val visibility = Visibility.Network + + override def receive(message: Message): Option[Array[Any]] = super.receive(message).orElse { + message.data match { + case Array(w: Int, h: Int) if message.name == "screen.resolution=" => + result(screen.resolution = (w, h)) + case Array() if message.name == "screen.resolution" => { + val (w, h) = screen.resolution + result(w, h) + } + case Array() if message.name == "screen.resolutions" => + result(screen.supportedResolutions: _*) + case Array(x: Int, y: Int, value: String) if message.name == "screen.set" => + screen.set(x, y, value); None + case Array(x: Int, y: Int, w: Int, h: Int, value: Char) if message.name == "screen.fill" => + screen.fill(x, y, w, h, value); None + case Array(x: Int, y: Int, w: Int, h: Int, tx: Int, ty: Int) if message.name == "screen.copy" => + screen.copy(x, y, w, h, tx, ty); None + case _ => None + } + } + + // ----------------------------------------------------------------------- // + + override def load(nbt: NBTTagCompound) = { + super.load(nbt) + screen.load(nbt.getCompoundTag("screen")) + } + + override def save(nbt: NBTTagCompound) = { + super.save(nbt) + + val screenNbt = new NBTTagCompound + screen.save(screenNbt) + nbt.setCompoundTag("screen", screenNbt) + } + + // ----------------------------------------------------------------------- // + + def onScreenResolutionChange(w: Int, h: Int) = + network.foreach(_.sendToVisible(this, "computer.signal", "screen_resized", w, h)) + + def onScreenSet(col: Int, row: Int, s: String) {} + + def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) {} + + def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) {} + } + } \ No newline at end of file diff --git a/li/cil/oc/common/component/ScreenEnvironment.scala b/li/cil/oc/common/component/ScreenEnvironment.scala deleted file mode 100644 index 5d5669b83..000000000 --- a/li/cil/oc/common/component/ScreenEnvironment.scala +++ /dev/null @@ -1,62 +0,0 @@ -package li.cil.oc.common.component - -import li.cil.oc.api.network.{Node, Visibility, Message} -import net.minecraft.nbt.NBTTagCompound - -/** - * Environment for screen components. - * - * The environment of a screen is responsible for synchronizing the component - * between server and client. These callbacks are only called on the server - * side to trigger changes being sent to clients and saving the current state. - */ -trait ScreenEnvironment extends Node { - val screen = new Screen(this) - - override val name = "screen" - - override val visibility = Visibility.Network - - override def receive(message: Message): Option[Array[Any]] = super.receive(message).orElse { - message.data match { - case Array(w: Int, h: Int) if message.name == "screen.resolution=" => - result(screen.resolution = (w, h)) - case Array() if message.name == "screen.resolution" => { - val (w, h) = screen.resolution - result(w, h) - } - case Array() if message.name == "screen.resolutions" => - result(screen.supportedResolutions: _*) - case Array(x: Int, y: Int, value: String) if message.name == "screen.set" => - screen.set(x, y, value); None - case Array(x: Int, y: Int, w: Int, h: Int, value: Char) if message.name == "screen.fill" => - screen.fill(x, y, w, h, value); None - case Array(x: Int, y: Int, w: Int, h: Int, tx: Int, ty: Int) if message.name == "screen.copy" => - screen.copy(x, y, w, h, tx, ty); None - case _ => None - } - } - - override def load(nbt: NBTTagCompound) = { - super.load(nbt) - screen.load(nbt.getCompoundTag("screen")) - } - - override def save(nbt: NBTTagCompound) = { - super.save(nbt) - - val screenNbt = new NBTTagCompound - screen.save(screenNbt) - nbt.setCompoundTag("screen", screenNbt) - - } - - def onScreenResolutionChange(w: Int, h: Int) = - network.foreach(_.sendToVisible(this, "computer.signal", "screen_resized", w, h)) - - def onScreenSet(col: Int, row: Int, s: String) {} - - def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) {} - - def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) {} -} \ No newline at end of file diff --git a/li/cil/oc/common/tileentity/Screen.scala b/li/cil/oc/common/tileentity/Screen.scala index 0528de26a..9854dacf9 100644 --- a/li/cil/oc/common/tileentity/Screen.scala +++ b/li/cil/oc/common/tileentity/Screen.scala @@ -3,16 +3,23 @@ package li.cil.oc.common.tileentity import li.cil.oc.api.network.PoweredNode import li.cil.oc.client.gui import li.cil.oc.client.{PacketSender => ClientPacketSender} -import li.cil.oc.common.component.ScreenEnvironment +import li.cil.oc.common.component import li.cil.oc.server.{PacketSender => ServerPacketSender} import net.minecraft.nbt.NBTTagCompound -class Screen extends Rotatable with ScreenEnvironment with PoweredNode { +class Screen extends Rotatable with component.Screen.Environment with PoweredNode { var guiScreen: Option[gui.Screen] = None - /** Read and reset to false from the tile entity renderer. */ + /** + * Read and reset to false from the tile entity renderer. This is used to + * keep rendering a little more efficient by compiling the displayed text + * into an OpenGL display list, and only re-compiling that list when the + * text/display has actually changed. + */ var hasChanged = false + // ----------------------------------------------------------------------- // + override def readFromNBT(nbt: NBTTagCompound) = { super.readFromNBT(nbt) load(nbt.getCompoundTag("node")) @@ -32,8 +39,6 @@ class Screen extends Rotatable with ScreenEnvironment with PoweredNode { ClientPacketSender.sendScreenBufferRequest(this) } - // ----------------------------------------------------------------------- // - // IScreenEnvironment // ----------------------------------------------------------------------- // override def onScreenResolutionChange(w: Int, h: Int) = { diff --git a/li/cil/oc/server/component/GraphicsCard.scala b/li/cil/oc/server/component/GraphicsCard.scala index 60ce7c9e2..2a3458d45 100644 --- a/li/cil/oc/server/component/GraphicsCard.scala +++ b/li/cil/oc/server/component/GraphicsCard.scala @@ -1,7 +1,7 @@ package li.cil.oc.server.component import li.cil.oc.api.network.{Node, Visibility, Message} -import li.cil.oc.common.component.ScreenEnvironment +import li.cil.oc.common.component import net.minecraft.nbt.NBTTagCompound class GraphicsCard extends Node { @@ -21,7 +21,7 @@ class GraphicsCard extends Node { network.fold(None: Option[Array[Any]])(network => { network.node(new String(address, "UTF-8")) match { case None => result(Unit, "invalid address") - case Some(node: ScreenEnvironment) => + case Some(node: component.Screen.Environment) => screen = node.address result(true) case _ => result(Unit, "not a screen")