From edbbefe5849a5fde1194dcb2f251732fc82f80d4 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Wed, 10 Jul 2019 16:20:45 +0900 Subject: [PATCH] fix wrong assert usage; addOneArgFun for Lua globals works --- .../mods/dwarventech/virtualcomputer/bfvm.kts | 2 +- src/net/torvald/UnsafePtr.kt | 2 +- .../spriteanimation/SpriteAnimation.kt | 4 +- src/net/torvald/terrarum/gameactors/Hitbox.kt | 6 +- .../virtualcomputer/computer/LuaComputerVM.kt | 114 ++++++++++++++- .../virtualcomputer/computer/MDA.kt | 134 ++++++++++++++++-- .../standalone/StandaloneApp.kt | 16 ++- 7 files changed, 252 insertions(+), 26 deletions(-) diff --git a/assets/mods/dwarventech/virtualcomputer/bfvm.kts b/assets/mods/dwarventech/virtualcomputer/bfvm.kts index 8289634dc..2a40c4a83 100644 --- a/assets/mods/dwarventech/virtualcomputer/bfvm.kts +++ b/assets/mods/dwarventech/virtualcomputer/bfvm.kts @@ -365,7 +365,7 @@ class BFVM( // commenting out because of the suspected (or minor?) performance impact. // You may break the glass and use this tool when some fucking incomprehensible bugs ("vittujen vitun bugit") // appear (e.g. getting garbage values when it fucking shouldn't) - assert(destroyed) { throw NullPointerException("The pointer is already destroyed ($this)") } + assert(!destroyed) { throw NullPointerException("The pointer is already destroyed ($this)") } // OOB Check: debugging purposes only -- comment out for the production //if (index !in 0 until size) throw IndexOutOfBoundsException("Index: $index; alloc size: $size") diff --git a/src/net/torvald/UnsafePtr.kt b/src/net/torvald/UnsafePtr.kt index 06bae25b7..e1317ced5 100644 --- a/src/net/torvald/UnsafePtr.kt +++ b/src/net/torvald/UnsafePtr.kt @@ -70,7 +70,7 @@ class UnsafePtr(pointer: Long, allocSize: Long) { // commenting out because of the suspected (or minor?) performance impact. // You may break the glass and use this tool when some fucking incomprehensible bugs ("vittujen vitun bugit") // appear (e.g. getting garbage values when it fucking shouldn't) - assert(destroyed) { throw NullPointerException("The pointer is already destroyed ($this)") } + assert(!destroyed) { throw NullPointerException("The pointer is already destroyed ($this)") } // OOB Check: debugging purposes only -- comment out for the production //if (index !in 0 until size) throw IndexOutOfBoundsException("Index: $index; alloc size: $size") diff --git a/src/net/torvald/spriteanimation/SpriteAnimation.kt b/src/net/torvald/spriteanimation/SpriteAnimation.kt index 0c8d3900b..4894b714f 100644 --- a/src/net/torvald/spriteanimation/SpriteAnimation.kt +++ b/src/net/torvald/spriteanimation/SpriteAnimation.kt @@ -122,8 +122,8 @@ class SpriteAnimation(@Transient val parentActor: ActorWBMovable) { * @param scale */ fun render(batch: SpriteBatch, posX: Float, posY: Float, scale: Float = 1f) { - assert(cellWidth == 0 || cellHeight == 0) { - throw Error("Sprite width or height is set to zero! ($cellWidth, $cellHeight); master: $parentActor") + assert(cellWidth > 0 || cellHeight > 0) { + "Sprite width or height is set to zero! ($cellWidth, $cellHeight); master: $parentActor" } if (visible) { diff --git a/src/net/torvald/terrarum/gameactors/Hitbox.kt b/src/net/torvald/terrarum/gameactors/Hitbox.kt index 7c74e7261..aac4fd0e8 100644 --- a/src/net/torvald/terrarum/gameactors/Hitbox.kt +++ b/src/net/torvald/terrarum/gameactors/Hitbox.kt @@ -27,7 +27,7 @@ class Hitbox (x1: Double, y1: Double, width: Double, height: Double, var suppres this.width = width this.height = height - assert(!suppressWarning && (width == 0.0 || height == 0.0)) { + if (!suppressWarning && (width == 0.0 || height == 0.0)) { println("[Hitbox] width or height is zero ($this), perhaps you want to check it out?") printStackTrace(this) } @@ -66,7 +66,7 @@ class Hitbox (x1: Double, y1: Double, width: Double, height: Double, var suppres this.width = width this.height = height - assert(!suppressWarning && (width == 0.0 || height == 0.0)) { + if (!suppressWarning && (width == 0.0 || height == 0.0)) { println("[Hitbox] width or height is zero ($this), perhaps you want to check it out?") printStackTrace(this) } @@ -84,7 +84,7 @@ class Hitbox (x1: Double, y1: Double, width: Double, height: Double, var suppres fun setPosition(x1: Double, y1: Double): Hitbox { hitboxStart = Point2d(x1, y1) - assert(!suppressWarning && (width == 0.0 || height == 0.0)) { + if (!suppressWarning && (width == 0.0 || height == 0.0)) { println("[Hitbox] width or height is zero ($this), perhaps you want to check it out?") printStackTrace(this) } diff --git a/src/net/torvald/terrarum/modulecomputers/virtualcomputer/computer/LuaComputerVM.kt b/src/net/torvald/terrarum/modulecomputers/virtualcomputer/computer/LuaComputerVM.kt index e316d4d4e..2eac36095 100644 --- a/src/net/torvald/terrarum/modulecomputers/virtualcomputer/computer/LuaComputerVM.kt +++ b/src/net/torvald/terrarum/modulecomputers/virtualcomputer/computer/LuaComputerVM.kt @@ -1,9 +1,119 @@ package net.torvald.terrarum.modulecomputers.virtualcomputer.computer +import org.luaj.vm2.Globals +import org.luaj.vm2.LuaValue +import org.luaj.vm2.lib.OneArgFunction +import org.luaj.vm2.lib.jse.JsePlatform +import java.io.InputStream + /** * New plan: screw teletype and gui; only the simple 80*24 (size may mary) dumb terminal * * Created by minjaesong on 2019-07-09. */ -class LuaComputerVM { -} \ No newline at end of file +class LuaComputerVM(val display: MDA) { + + val luaInstance: Globals = JsePlatform.standardGlobals() + + val stdout = MDAPrintStream(display) + val stderr = MDAPrintStream(display) + val stdin = LuaComputerInputStream(this) + + + init { + // bit-bit32 alias + luaInstance["bit"] = luaInstance["bit32"] + + // set input/outputstreams + luaInstance.STDOUT = stdout + luaInstance.STDERR = stderr + luaInstance.STDIN = stdin + + luaInstance.addOneArgFun("upgoer") { p0 -> + display.println("Up-goer ${p0.toint()} goes up!") + LuaValue.NIL + } + + luaInstance.addOneArgFun("perkele.upgoer") { p0 -> + display.println("Up-goer ${p0.toint()} goes up!") + LuaValue.NIL + } + + luaInstance.addOneArgFun("perkele.saatana.jumalauta.vittu.upgoer") { p0 -> + display.println("Up-goer ${p0.toint()} goes up!") + LuaValue.NIL + } + + luaInstance.load("""print('Hello, world!') print('Ready.')""").invoke() + luaInstance.load("""print(upgoer)""").invoke() + luaInstance.load("""upgoer(1)""").invoke() + luaInstance.load("""perkele.upgoer(2)""").invoke() + luaInstance.load("""perkele.saatana.jumalauta.vittu.upgoer(5)""").invoke() + } + +} + +class LuaComputerInputStream(val host: LuaComputerVM) : InputStream() { + override fun read(): Int { + TODO("not implemented") + } +} + +/** + * Install a function into the lua. + * @param identifier How you might call this lua function. E.g. "term.println" + */ +fun Globals.addOneArgFun(identifier: String, function: (p0: LuaValue) -> LuaValue) { + val theActualFun = object : OneArgFunction() { + override fun call(p0: LuaValue): LuaValue { + return function(p0) + } + } + + val tableNames = identifier.split('.') + + if (tableNames.isEmpty()) throw IllegalArgumentException("Identifier is empty") + + //println(tableNames) + + if (this[tableNames[0]].isnil()) { + this[tableNames[0]] = LuaValue.tableOf() + } + else if (!this[tableNames[0]].istable()) { + throw IllegalStateException("Redefinition: '${tableNames[0]}' (${this[tableNames[0]]})") + } + + var currentTable = this[tableNames[0]] + + // turn nils into tables + if (tableNames.size > 1) { + tableNames.slice(1..tableNames.lastIndex).forEachIndexed { index, it -> + if (currentTable[it].isnil()) { + currentTable[it] = LuaValue.tableOf() + } + else if (!currentTable[it].istable()) { + throw IllegalStateException("Redefinition: '${tableNames.slice(0..(index + 1)).joinToString(".")}' (${currentTable[it]})") + } + + currentTable = currentTable[it] + } + + // actually put the function onto the target + // for some reason, memoisation doesn't work here so we use recursion to reach the target table as generated above + fun putIntoTheTableRec(luaTable: LuaValue, recursionCount: Int) { + if (recursionCount == tableNames.lastIndex - 1) { + luaTable[tableNames[tableNames.lastIndex]] = theActualFun + } + else { + putIntoTheTableRec(luaTable[tableNames[recursionCount + 1]], recursionCount + 1) + } + } + + putIntoTheTableRec(this[tableNames[0]], 0) + } + else { + this[tableNames[0]] = theActualFun + } +} + +// don't add ZeroArgFun, TwoArgFun, ThreeArgFun until you make sure addOneArgFun to work! \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulecomputers/virtualcomputer/computer/MDA.kt b/src/net/torvald/terrarum/modulecomputers/virtualcomputer/computer/MDA.kt index 3a926a9f5..4eae960db 100644 --- a/src/net/torvald/terrarum/modulecomputers/virtualcomputer/computer/MDA.kt +++ b/src/net/torvald/terrarum/modulecomputers/virtualcomputer/computer/MDA.kt @@ -1,7 +1,11 @@ package net.torvald.terrarum.modulecomputers.virtualcomputer.computer import net.torvald.UnsafeHelper +import net.torvald.terrarum.AppLoader import net.torvald.terrarum.gameworld.fmod +import java.io.OutputStream +import java.io.PrintStream +import java.nio.charset.Charset /** * Only one kind of display adapter should exist in the game: they add nothing to the game and the game @@ -13,10 +17,16 @@ import net.torvald.terrarum.gameworld.fmod */ class MDA(val width: Int, val height: Int) { + companion object { + val charset: Charset = Charset.forName("cp437") + } + init { if (width % 2 == 1) throw IllegalArgumentException("Display width must be an even number (width = $width)") } + private val arrayElemOffset = 8L * if (AppLoader.is32BitJVM) 1 else 2 // 8 for 32-bit, 16 for 64-bit + private val glyphs = UnsafeHelper.allocate(width.toLong() * height) private val attributes = UnsafeHelper.allocate(width.toLong() * height) @@ -36,7 +46,7 @@ class MDA(val width: Int, val height: Int) { for every byte: - (msb) 0000 bbff (lsb) + (msb) 00bb 00ff (lsb) where: @@ -52,6 +62,8 @@ class MDA(val width: Int, val height: Int) { */ + fun toAttribute(back: Int, fore: Int) = (back.shl(4) or fore).toByte() + private fun wrapAround(x: Int, y: Int) = (x fmod width) to (y fmod height) private fun toAddress(x: Int, y: Int) = (y * width + x).toLong() inline private fun Pair.toAddress() = toAddress(this.first, this.second) @@ -69,32 +81,134 @@ class MDA(val width: Int, val height: Int) { attributes[a] = attribute } + private fun set(offset: Int, glyph: Byte, attribute: Byte) { + glyphs[offset.toLong()] = glyph + attributes[offset.toLong()] = attribute + } + fun setCursor(x: Int, y: Int) { cursor = wrapAround(x, y).toAddress().toInt() } + // SETTEXT methods should not deal with the scrolling, it must be handled by the PRINT methods. - fun setText(x: Int, y: Int, text: ByteArray, attirbute: ByteArray) { - UnsafeHelper.memcpyRaw(text, 0, null, glyphs.ptr + wrapAround(x, y).toAddress(), text.size.toLong()) - UnsafeHelper.memcpyRaw(attirbute, 0, null, attributes.ptr + wrapAround(x, y).toAddress(), text.size.toLong()) + /** Bulk write method. Any control characers will be represented as a glyph, rather than an actual control sequence. + * E.g. '\n' will print a symbol. */ + fun setText(x: Int, y: Int, text: ByteArray, attribute: ByteArray) { + UnsafeHelper.memcpyRaw(text, arrayElemOffset, null, glyphs.ptr + wrapAround(x, y).toAddress(), text.size.toLong()) + UnsafeHelper.memcpyRaw(attribute, arrayElemOffset, null, attributes.ptr + wrapAround(x, y).toAddress(), text.size.toLong()) } + private fun setText(offset: Int, text: ByteArray, attribute: ByteArray) { + UnsafeHelper.memcpyRaw(text, arrayElemOffset, null, glyphs.ptr + offset, text.size.toLong()) + UnsafeHelper.memcpyRaw(attribute, arrayElemOffset, null, attributes.ptr + offset, text.size.toLong()) + } + + /** Bulk write method. Any control characers will be represented as a glyph, rather than an actual control sequence. + * E.g. '\n' will print a symbol. */ fun setText(x: Int, y: Int, text: ByteArray, attribute: Byte) { - setText(x, y, text) - val a = wrapAround(x, y).toAddress() - for (i in 0 until text.size) { - attributes[a + i] = attribute - } + setText(x, y, text, ByteArray(text.size) { attribute }) } + private fun setText(offset: Int, text: ByteArray, attribute: Byte = toAttribute(background, foreground)) { + setText(offset, text, ByteArray(text.size) { attribute }) + } + + /** Bulk write method. Any control characers will be represented as a glyph, rather than an actual control sequence. + * E.g. '\n' will print a symbol. */ fun setText(x: Int, y: Int, text: ByteArray) { - setText(x, y, text, (background.shl(0b11) or foreground).toByte()) + setText(x, y, text, toAttribute(background, foreground)) + } + + private fun setOneText(offset: Int, text: Byte, attribute: Byte = toAttribute(background, foreground)) { + glyphs[offset.toLong()] = text + attributes[offset.toLong()] = attribute } + fun println(text: String) { + print(text) + write(0x0A) + } + fun print(text: String) { + print(text.toByteArray(charset)) + } + + fun println(text: ByteArray) { + print(text) + write(0x0A) + } + fun print(text: ByteArray) { + text.forEach { write(it) } + } + + fun write(text: Byte) { + + when (text) { + // CR + 0x0D.toByte() -> { /* do nothing */ } + // LF + 0x0A.toByte() -> newline() + + else -> { + setOneText(cursor, text) + cursor += 1 + + if (cursor > width * height) { + scroll(1) + } + } + } + + } + + /** + * moves text and the current cursor position + */ + fun scroll(amount: Int) { + val offset = (width * amount).toLong() + if (amount < 0) throw IllegalArgumentException("amount = $amount") + + UnsafeHelper.memcpy(glyphs, offset, glyphs, 0L, glyphs.size - offset) + UnsafeHelper.memcpy(attributes, offset, attributes, 0L, attributes.size - offset) + + cursor -= offset.toInt() + if (cursor < 0) cursor = 0 + } + + /** + * Advance one line, scroll the screen if necessary + */ + fun newline() { + cursor += width + + if (cursor >= width * height) { + scroll(1) + } + + cursor = (cursor / width) * width // set cursorX to 0 + } + fun dispose() { glyphs.destroy() attributes.destroy() } +} + +private class MDAOutputStream(val mda: MDA) : OutputStream() { + override fun write(b: Int) { + mda.write(b.toByte()) + } +} + +class MDAPrintStream(val mda: MDA) : PrintStream(MDAOutputStream(mda)) { + override fun print(s: String?) { + mda.print((s ?: "").toByteArray(MDA.charset)) + } + + override fun println(s: String?) { + print(s) + mda.newline() + } } \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulecomputers/virtualcomputer/standalone/StandaloneApp.kt b/src/net/torvald/terrarum/modulecomputers/virtualcomputer/standalone/StandaloneApp.kt index cbbdb9f7f..17d358437 100644 --- a/src/net/torvald/terrarum/modulecomputers/virtualcomputer/standalone/StandaloneApp.kt +++ b/src/net/torvald/terrarum/modulecomputers/virtualcomputer/standalone/StandaloneApp.kt @@ -7,6 +7,7 @@ import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Texture import com.badlogic.gdx.graphics.g2d.SpriteBatch +import net.torvald.terrarum.modulecomputers.virtualcomputer.computer.LuaComputerVM import net.torvald.terrarum.modulecomputers.virtualcomputer.computer.MDA import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack @@ -26,6 +27,7 @@ class StandaloneApp : Game() { lateinit var vmThread: Thread val display = MDA(80, 25) + val vm = LuaComputerVM(display) override fun create() { font = TextureRegionPack(Gdx.files.internal("assets/mods/dwarventech/gui/lcd.tga"), 12, 16) @@ -50,10 +52,10 @@ class StandaloneApp : Game() { private val lcdOffY = 56f private val lcdCol = arrayOf( - Color(0x14141400), - Color(0x141414AA), - Color(0x14141455), - Color(0x141414FF) + Color(0x10101000), + Color(0x101010AA), + Color(0x10101055), + Color(0x101010FF) ) private var textCursorDrawTimer = 0f // 0f..0.5f: not draw @@ -61,6 +63,8 @@ class StandaloneApp : Game() { override fun render() { Gdx.graphics.setTitle("Terrarum Lua Computer Standalone — F: ${Gdx.graphics.framesPerSecond}") + //display.print(ByteArray(1){ (Math.random() * 255).toByte() }) + batch.inUse { batch.color = Color.WHITE batch.draw(background, 0f, 0f) @@ -69,8 +73,6 @@ class StandaloneApp : Game() { // draw the screen textCursorDrawTimer += Gdx.graphics.rawDeltaTime if (textCursorDrawTimer > 1f) textCursorDrawTimer -= 1f - val cursorX = display.cursor % display.width - val cursorY = display.cursor / display.height for (i in 0 until display.width * display.height) { val drawX = ((i % display.width) * font.tileW).toFloat() @@ -78,7 +80,7 @@ class StandaloneApp : Game() { val (g, a) = display.rawGet(i) val glyph = g.toUint() val glyphBack = glyph + 256 - val back = (a.toUint() ushr 0x3) % lcdCol.size + val back = (a.toUint() ushr 4) % lcdCol.size val fore = a.toUint() % lcdCol.size if (display.blink && i == display.cursor && textCursorDrawTimer >= 0.5f) {