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)

This commit is contained in:
Florian Nücke 2013-10-14 16:43:22 +02:00
parent deb45f374f
commit cbfbc2f152
6 changed files with 91 additions and 76 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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