Make it not outright crash with 0xC0000005 or 0xC00000FD
This commit is contained in:
parent
9a958ecccb
commit
49d6cb0d89
@ -52,14 +52,14 @@ fun LuaThread.getLine2d(stackIndex: Int = -1): Line2d? {
|
||||
push(1)
|
||||
loadTableValue(abs)
|
||||
|
||||
val x = getVector2d(abs + 1)
|
||||
val x = getVector2d()
|
||||
pop()
|
||||
x ?: return null
|
||||
|
||||
push(2)
|
||||
loadTableValue(abs)
|
||||
|
||||
val y = getVector2d(abs + 1)
|
||||
val y = getVector2d()
|
||||
pop()
|
||||
y ?: return null
|
||||
|
||||
@ -90,14 +90,14 @@ fun LuaThread.getVector2d(stackIndex: Int = -1): Vector2d? {
|
||||
push(1)
|
||||
loadTableValue(abs)
|
||||
|
||||
val x = getDouble(abs + 1)
|
||||
val x = getDouble()
|
||||
pop()
|
||||
x ?: return null
|
||||
|
||||
push(2)
|
||||
loadTableValue(abs)
|
||||
|
||||
val y = getDouble(abs + 1)
|
||||
val y = getDouble()
|
||||
pop()
|
||||
y ?: return null
|
||||
|
||||
@ -128,14 +128,14 @@ fun LuaThread.getVector2f(stackIndex: Int = -1): Vector2f? {
|
||||
push(1)
|
||||
loadTableValue(abs)
|
||||
|
||||
val x = getFloat(abs + 1)
|
||||
val x = getFloat()
|
||||
pop()
|
||||
x ?: return null
|
||||
|
||||
push(2)
|
||||
loadTableValue(abs)
|
||||
|
||||
val y = getFloat(abs + 1)
|
||||
val y = getFloat()
|
||||
pop()
|
||||
y ?: return null
|
||||
|
||||
@ -168,9 +168,9 @@ fun LuaThread.getVector2iOrAABB(stackIndex: Int = -1): Either<Vector2i, AABB>? {
|
||||
pop()
|
||||
|
||||
if (type == LuaType.NUMBER) {
|
||||
return Either.right(getAABB(stackIndex) ?: return null)
|
||||
return Either.right(getAABB() ?: return null)
|
||||
} else {
|
||||
return Either.left(getVector2i(stackIndex) ?: return null)
|
||||
return Either.left(getVector2i() ?: return null)
|
||||
}
|
||||
}
|
||||
|
||||
@ -223,14 +223,14 @@ fun LuaThread.getVector2i(stackIndex: Int = -1): Vector2i? {
|
||||
push(1)
|
||||
loadTableValue(abs)
|
||||
|
||||
val x = getLong(abs + 1)
|
||||
val x = getLong()
|
||||
pop()
|
||||
x ?: return null
|
||||
|
||||
push(2)
|
||||
loadTableValue(abs)
|
||||
|
||||
val y = getLong(abs + 1)
|
||||
val y = getLong()
|
||||
pop()
|
||||
y ?: return null
|
||||
|
||||
@ -262,28 +262,28 @@ fun LuaThread.getColor(stackIndex: Int = -1): RGBAColor? {
|
||||
push(1)
|
||||
loadTableValue(abs)
|
||||
|
||||
val x = getLong(abs + 1)
|
||||
val x = getLong()
|
||||
pop()
|
||||
x ?: return null
|
||||
|
||||
push(2)
|
||||
loadTableValue(abs)
|
||||
|
||||
val y = getLong(abs + 1)
|
||||
val y = getLong()
|
||||
pop()
|
||||
y ?: return null
|
||||
|
||||
push(3)
|
||||
loadTableValue(abs)
|
||||
|
||||
val z = getLong(abs + 1)
|
||||
val z = getLong()
|
||||
pop()
|
||||
z ?: return null
|
||||
|
||||
push(4)
|
||||
loadTableValue(abs)
|
||||
|
||||
val w = getLong(abs + 1) ?: 255L
|
||||
val w = getLong() ?: 255L
|
||||
pop()
|
||||
|
||||
return RGBAColor(x.toInt(), y.toInt(), z.toInt(), w.toInt())
|
||||
@ -313,28 +313,28 @@ fun LuaThread.getAABB(stackIndex: Int = -1): AABB? {
|
||||
push(1)
|
||||
loadTableValue(abs)
|
||||
|
||||
val x = getDouble(abs + 1)
|
||||
val x = getDouble()
|
||||
pop()
|
||||
x ?: return null
|
||||
|
||||
push(2)
|
||||
loadTableValue(abs)
|
||||
|
||||
val y = getDouble(abs + 1)
|
||||
val y = getDouble()
|
||||
pop()
|
||||
y ?: return null
|
||||
|
||||
push(3)
|
||||
loadTableValue(abs)
|
||||
|
||||
val z = getDouble(abs + 1)
|
||||
val z = getDouble()
|
||||
pop()
|
||||
z ?: return null
|
||||
|
||||
push(4)
|
||||
loadTableValue(abs)
|
||||
|
||||
val w = getDouble(abs + 1)
|
||||
val w = getDouble()
|
||||
pop()
|
||||
w ?: return null
|
||||
|
||||
@ -343,10 +343,10 @@ fun LuaThread.getAABB(stackIndex: Int = -1): AABB? {
|
||||
|
||||
fun LuaThread.ArgStack.nextAABB(position: Int = this.position++): AABB {
|
||||
if (position !in 1 ..this.top)
|
||||
throw IllegalArgumentException("bad argument #$position: RGBAColor expected, got nil")
|
||||
throw IllegalArgumentException("bad argument #$position: AABB expected, got nil")
|
||||
|
||||
return lua.getAABB(position)
|
||||
?: throw IllegalArgumentException("bad argument #$position: RGBAColor expected, got ${lua.typeAt(position)}")
|
||||
?: throw IllegalArgumentException("bad argument #$position: AABB expected, got ${lua.typeAt(position)}")
|
||||
}
|
||||
|
||||
fun LuaThread.ArgStack.nextOptionalAABB(position: Int = this.position++): AABB? {
|
||||
@ -365,28 +365,28 @@ fun LuaThread.getAABBi(stackIndex: Int = -1): AABBi? {
|
||||
push(1)
|
||||
loadTableValue(abs)
|
||||
|
||||
val x = getLong(abs + 1)
|
||||
val x = getLong()
|
||||
pop()
|
||||
x ?: return null
|
||||
|
||||
push(2)
|
||||
loadTableValue(abs)
|
||||
|
||||
val y = getLong(abs + 1)
|
||||
val y = getLong()
|
||||
pop()
|
||||
y ?: return null
|
||||
|
||||
push(3)
|
||||
loadTableValue(abs)
|
||||
|
||||
val z = getLong(abs + 1)
|
||||
val z = getLong()
|
||||
pop()
|
||||
z ?: return null
|
||||
|
||||
push(4)
|
||||
loadTableValue(abs)
|
||||
|
||||
val w = getLong(abs + 1)
|
||||
val w = getLong()
|
||||
pop()
|
||||
w ?: return null
|
||||
|
||||
|
@ -5,11 +5,12 @@ import ru.dbotthepony.kstarbound.lua.userdata.LuaFuture
|
||||
import ru.dbotthepony.kstarbound.lua.userdata.LuaPathFinder
|
||||
import ru.dbotthepony.kstarbound.util.random.random
|
||||
import java.io.Closeable
|
||||
import java.lang.ref.Cleaner.Cleanable
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
import java.util.random.RandomGenerator
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
class LuaSharedState(val handlesThread: LuaThread) : Closeable {
|
||||
class LuaSharedState(val handlesThread: LuaThread, private val cleanable: Cleanable) : Closeable {
|
||||
private val pendingFree = ConcurrentLinkedQueue<Int>()
|
||||
private val freeHandles = IntAVLTreeSet()
|
||||
private var nextHandle = 0
|
||||
@ -25,10 +26,15 @@ class LuaSharedState(val handlesThread: LuaThread) : Closeable {
|
||||
var isValid = true
|
||||
private set
|
||||
|
||||
fun ensureValid() {
|
||||
check(isValid) { "Tried to use NULL LuaState!" }
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
if (!isValid) return
|
||||
isValid = false
|
||||
namedHandles.clear()
|
||||
cleanable.clean()
|
||||
}
|
||||
|
||||
fun initializeHandles(mainThread: LuaThread) {
|
||||
@ -51,7 +57,7 @@ class LuaSharedState(val handlesThread: LuaThread) : Closeable {
|
||||
}
|
||||
|
||||
fun cleanup() {
|
||||
check(isValid) { "Shared state is no longer valid" }
|
||||
ensureValid()
|
||||
if (handlesInUse == 0) return
|
||||
var handle = pendingFree.poll()
|
||||
|
||||
@ -67,7 +73,7 @@ class LuaSharedState(val handlesThread: LuaThread) : Closeable {
|
||||
}
|
||||
|
||||
fun allocateHandle(name: Any?): LuaHandle {
|
||||
check(isValid) { "Shared state is no longer valid" }
|
||||
ensureValid()
|
||||
require(name == null || name !in namedHandles) { "Named handle '$name' already exists" }
|
||||
handlesInUse++
|
||||
|
||||
@ -93,12 +99,12 @@ class LuaSharedState(val handlesThread: LuaThread) : Closeable {
|
||||
}
|
||||
|
||||
fun getNamedHandle(key: Any): LuaHandle {
|
||||
check(isValid) { "Shared state is no longer valid" }
|
||||
ensureValid()
|
||||
return namedHandles[key] ?: throw NoSuchElementException("No such handle: $key")
|
||||
}
|
||||
|
||||
fun findNamedHandle(key: Any): LuaHandle? {
|
||||
check(isValid) { "Shared state is no longer valid" }
|
||||
ensureValid()
|
||||
return namedHandles[key]
|
||||
}
|
||||
}
|
||||
|
@ -56,18 +56,18 @@ class LuaThread private constructor(
|
||||
CallContext.getCallContext(Type.SINT, arrayOf(Type.POINTER), CallingConvention.DEFAULT, false)
|
||||
)
|
||||
|
||||
this.cleanable = Cleaner.Cleanable {
|
||||
LuaJNR.INSTANCE.lua_close(pointer)
|
||||
panic.dispose()
|
||||
}
|
||||
|
||||
panic.setAutoRelease(false)
|
||||
LuaJNR.INSTANCE.lua_atpanic(pointer, panic.address)
|
||||
|
||||
val handles = LuaJNR.INSTANCE.lua_newthread(pointer)
|
||||
storeRef(LUA_REGISTRYINDEX)
|
||||
LuaJNR.INSTANCE.luaL_ref(pointer, LUA_REGISTRYINDEX)
|
||||
val handlesThread = LuaThread(handles, stringInterner)
|
||||
sharedState = LuaSharedState(handlesThread)
|
||||
|
||||
sharedState = LuaSharedState(handlesThread, {
|
||||
LuaJNR.INSTANCE.lua_close(pointer)
|
||||
panic.dispose()
|
||||
})
|
||||
|
||||
sharedState.handlesThread.sharedState = sharedState
|
||||
|
||||
push("__nils")
|
||||
@ -140,13 +140,11 @@ class LuaThread private constructor(
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
if (cleanable != null) {
|
||||
cleanable!!.clean()
|
||||
sharedState.close()
|
||||
}
|
||||
sharedState.close()
|
||||
}
|
||||
|
||||
val stackTop: Int get() {
|
||||
sharedState.ensureValid()
|
||||
val value = LuaJNR.INSTANCE.lua_gettop(this.pointer)
|
||||
check(value >= 0) { "Invalid stack top $value" }
|
||||
return value
|
||||
@ -156,6 +154,7 @@ class LuaThread private constructor(
|
||||
* Converts the acceptable index idx into an equivalent absolute index (that is, one that does not depend on the stack size).
|
||||
*/
|
||||
fun absStackIndex(index: Int): Int {
|
||||
sharedState.ensureValid()
|
||||
if (index >= 0)
|
||||
return index
|
||||
|
||||
@ -173,6 +172,7 @@ class LuaThread private constructor(
|
||||
}
|
||||
|
||||
fun load(code: String, chunkName: String = "@main chunk") {
|
||||
sharedState.ensureValid()
|
||||
val bytes = code.toByteArray(charset = Charsets.UTF_8)
|
||||
val buf = ByteBuffer.allocateDirect(bytes.size)
|
||||
buf.order(ByteOrder.nativeOrder())
|
||||
@ -319,6 +319,7 @@ class LuaThread private constructor(
|
||||
private var initCalled = false
|
||||
|
||||
fun initScripts(callInit: Boolean = true): Boolean {
|
||||
sharedState.ensureValid()
|
||||
check(!initCalled) { "Already initialized scripts!" }
|
||||
initCalled = true
|
||||
|
||||
@ -362,10 +363,12 @@ class LuaThread private constructor(
|
||||
}
|
||||
|
||||
fun attach(script: AssetPath) {
|
||||
sharedState.ensureValid()
|
||||
attach(script.fullPath)
|
||||
}
|
||||
|
||||
fun attach(script: String) {
|
||||
sharedState.ensureValid()
|
||||
if (initCalled) {
|
||||
// minor hiccups during unpopulated script cache should be tolerable
|
||||
load(Starbound.readLuaScript(script).join(), "@$script")
|
||||
@ -392,6 +395,7 @@ class LuaThread private constructor(
|
||||
}
|
||||
|
||||
private fun getStringRaw(stackIndex: Int = -1, limit: Long = DEFAULT_STRING_LIMIT): String? {
|
||||
sharedState.ensureValid()
|
||||
require(limit <= Int.MAX_VALUE) { "Can't allocate string bigger than ${Int.MAX_VALUE} characters" }
|
||||
val stack = MemoryStack.stackPush()
|
||||
val status = stack.mallocLong(1)
|
||||
@ -410,19 +414,19 @@ class LuaThread private constructor(
|
||||
return readBytes.toString(charset = Charsets.UTF_8)
|
||||
}
|
||||
|
||||
fun isCFunction(stackIndex: Int = -1): Boolean = LuaJNR.INSTANCE.lua_iscfunction(this.pointer, this.absStackIndex(stackIndex)) > 0
|
||||
fun isFunction(stackIndex: Int = -1): Boolean = this.typeAt(stackIndex) == LuaType.FUNCTION
|
||||
fun isInteger(stackIndex: Int = -1): Boolean = LuaJNR.INSTANCE.lua_isinteger(this.pointer, this.absStackIndex(stackIndex)) > 0
|
||||
fun isLightUserdata(stackIndex: Int = -1): Boolean = this.typeAt(stackIndex) == LuaType.LIGHTUSERDATA
|
||||
fun isNil(stackIndex: Int = -1): Boolean = this.typeAt(stackIndex) == LuaType.NIL
|
||||
fun isNone(stackIndex: Int = -1): Boolean = this.typeAt(stackIndex) == LuaType.NONE
|
||||
fun isNoneOrNil(stackIndex: Int = -1): Boolean = this.typeAt(stackIndex).let { it == LuaType.NIL || it == LuaType.NONE }
|
||||
fun isNumber(stackIndex: Int = -1): Boolean = LuaJNR.INSTANCE.lua_isnumber(this.pointer, this.absStackIndex(stackIndex)) > 0
|
||||
fun isString(stackIndex: Int = -1): Boolean = LuaJNR.INSTANCE.lua_isstring(this.pointer, this.absStackIndex(stackIndex)) > 0
|
||||
fun isTable(stackIndex: Int = -1): Boolean = this.typeAt(stackIndex) == LuaType.TABLE
|
||||
fun isThread(stackIndex: Int = -1): Boolean = this.typeAt(stackIndex) == LuaType.THREAD
|
||||
fun isUserdata(stackIndex: Int = -1): Boolean = LuaJNR.INSTANCE.lua_isuserdata(this.pointer, this.absStackIndex(stackIndex)) > 0
|
||||
fun isBoolean(stackIndex: Int = -1): Boolean = this.typeAt(stackIndex) == LuaType.BOOLEAN
|
||||
fun isCFunction(stackIndex: Int = -1): Boolean { sharedState.ensureValid(); return LuaJNR.INSTANCE.lua_iscfunction(this.pointer, this.absStackIndex(stackIndex)) > 0 }
|
||||
fun isFunction(stackIndex: Int = -1): Boolean { sharedState.ensureValid(); return this.typeAt(stackIndex) == LuaType.FUNCTION }
|
||||
fun isInteger(stackIndex: Int = -1): Boolean { sharedState.ensureValid(); return LuaJNR.INSTANCE.lua_isinteger(this.pointer, this.absStackIndex(stackIndex)) > 0 }
|
||||
fun isLightUserdata(stackIndex: Int = -1): Boolean { sharedState.ensureValid(); return this.typeAt(stackIndex) == LuaType.LIGHTUSERDATA }
|
||||
fun isNil(stackIndex: Int = -1): Boolean { sharedState.ensureValid(); return this.typeAt(stackIndex) == LuaType.NIL }
|
||||
fun isNone(stackIndex: Int = -1): Boolean { sharedState.ensureValid(); return this.typeAt(stackIndex) == LuaType.NONE }
|
||||
fun isNoneOrNil(stackIndex: Int = -1): Boolean { sharedState.ensureValid(); return this.typeAt(stackIndex).let { it == LuaType.NIL || it == LuaType.NONE } }
|
||||
fun isNumber(stackIndex: Int = -1): Boolean { sharedState.ensureValid(); return LuaJNR.INSTANCE.lua_isnumber(this.pointer, this.absStackIndex(stackIndex)) > 0 }
|
||||
fun isString(stackIndex: Int = -1): Boolean { sharedState.ensureValid(); return LuaJNR.INSTANCE.lua_isstring(this.pointer, this.absStackIndex(stackIndex)) > 0 }
|
||||
fun isTable(stackIndex: Int = -1): Boolean { sharedState.ensureValid(); return this.typeAt(stackIndex) == LuaType.TABLE }
|
||||
fun isThread(stackIndex: Int = -1): Boolean { sharedState.ensureValid(); return this.typeAt(stackIndex) == LuaType.THREAD }
|
||||
fun isUserdata(stackIndex: Int = -1): Boolean { sharedState.ensureValid(); return LuaJNR.INSTANCE.lua_isuserdata(this.pointer, this.absStackIndex(stackIndex)) > 0 }
|
||||
fun isBoolean(stackIndex: Int = -1): Boolean { sharedState.ensureValid(); return this.typeAt(stackIndex) == LuaType.BOOLEAN }
|
||||
|
||||
fun getBoolean(stackIndex: Int = -1): Boolean? {
|
||||
if (!this.isBoolean(stackIndex))
|
||||
@ -514,6 +518,7 @@ class LuaThread private constructor(
|
||||
}
|
||||
|
||||
fun typeAt(stackIndex: Int = -1): LuaType {
|
||||
sharedState.ensureValid()
|
||||
return LuaType.valueOf(LuaJNR.INSTANCE.lua_type(this.pointer, stackIndex))
|
||||
}
|
||||
|
||||
@ -597,7 +602,7 @@ class LuaThread private constructor(
|
||||
push()
|
||||
val top = this.stackTop
|
||||
|
||||
while (LuaJNR.INSTANCE.lua_next(this.pointer, top - 1) != 0) {
|
||||
while (LuaJNR.INSTANCE.lua_next(this.pointer, abs) != 0) {
|
||||
val key = this.getJson(top, limit = limit)
|
||||
val value = this.getJson(top + 1, limit = limit)
|
||||
|
||||
@ -690,6 +695,7 @@ class LuaThread private constructor(
|
||||
}
|
||||
|
||||
fun getObject(stackIndex: Int = -1): Any? {
|
||||
sharedState.ensureValid()
|
||||
return LuaJNI.lua_tojobject(pointer.address(), stackIndex)
|
||||
}
|
||||
|
||||
@ -785,6 +791,7 @@ class LuaThread private constructor(
|
||||
}
|
||||
|
||||
fun loadTableValue(stackIndex: Int = -2): LuaType {
|
||||
sharedState.ensureValid()
|
||||
return LuaType.valueOf(LuaJNR.INSTANCE.lua_gettable(this.pointer, stackIndex))
|
||||
}
|
||||
|
||||
@ -850,16 +857,19 @@ class LuaThread private constructor(
|
||||
}
|
||||
|
||||
fun pop(amount: Int = 1) {
|
||||
sharedState.ensureValid()
|
||||
require(amount >= 0) { "Invalid amount of values to pop: $amount" }
|
||||
if (amount == 0) return
|
||||
LuaJNR.INSTANCE.lua_settop(this.pointer, -amount - 1)
|
||||
}
|
||||
|
||||
fun storeGlobal(name: String) {
|
||||
sharedState.ensureValid()
|
||||
LuaJNR.INSTANCE.lua_setglobal(this.pointer, name)
|
||||
}
|
||||
|
||||
fun loadGlobal(name: String): LuaType {
|
||||
sharedState.ensureValid()
|
||||
return LuaType.valueOf(LuaJNR.INSTANCE.lua_getglobal(this.pointer, name))
|
||||
}
|
||||
|
||||
@ -1163,6 +1173,7 @@ class LuaThread private constructor(
|
||||
}
|
||||
|
||||
fun push(function: Fn, performanceCritical: Boolean) {
|
||||
sharedState.ensureValid()
|
||||
LuaJNI.lua_pushcclosure(pointer.address()) {
|
||||
closure(it, function, performanceCritical)
|
||||
}
|
||||
@ -1213,6 +1224,7 @@ class LuaThread private constructor(
|
||||
}
|
||||
|
||||
fun ensureExtraCapacity(maxSize: Int): Boolean {
|
||||
sharedState.ensureValid()
|
||||
return LuaJNR.INSTANCE.lua_checkstack(pointer, maxSize)
|
||||
}
|
||||
|
||||
@ -1227,18 +1239,22 @@ class LuaThread private constructor(
|
||||
}
|
||||
|
||||
fun moveStackValuesOnto(other: LuaThread, amount: Int = 1) {
|
||||
sharedState.ensureValid()
|
||||
LuaJNR.INSTANCE.lua_xmove(pointer, other.pointer, amount)
|
||||
}
|
||||
|
||||
fun push() {
|
||||
sharedState.ensureValid()
|
||||
LuaJNR.INSTANCE.lua_pushnil(this.pointer)
|
||||
}
|
||||
|
||||
fun push(value: Long) {
|
||||
sharedState.ensureValid()
|
||||
LuaJNR.INSTANCE.lua_pushinteger(this.pointer, value)
|
||||
}
|
||||
|
||||
fun push(value: Long?) {
|
||||
sharedState.ensureValid()
|
||||
if (value == null) {
|
||||
LuaJNR.INSTANCE.lua_pushnil(pointer)
|
||||
} else {
|
||||
@ -1247,18 +1263,22 @@ class LuaThread private constructor(
|
||||
}
|
||||
|
||||
fun push(value: Double) {
|
||||
sharedState.ensureValid()
|
||||
LuaJNR.INSTANCE.lua_pushnumber(this.pointer, value)
|
||||
}
|
||||
|
||||
fun push(value: Float) {
|
||||
sharedState.ensureValid()
|
||||
LuaJNR.INSTANCE.lua_pushnumber(this.pointer, value.toDouble())
|
||||
}
|
||||
|
||||
fun push(value: Boolean) {
|
||||
sharedState.ensureValid()
|
||||
LuaJNR.INSTANCE.lua_pushboolean(this.pointer, if (value) 1 else 0)
|
||||
}
|
||||
|
||||
fun push(value: Double?) {
|
||||
sharedState.ensureValid()
|
||||
if (value == null) {
|
||||
LuaJNR.INSTANCE.lua_pushnil(pointer)
|
||||
} else {
|
||||
@ -1267,6 +1287,7 @@ class LuaThread private constructor(
|
||||
}
|
||||
|
||||
fun push(value: Float?) {
|
||||
sharedState.ensureValid()
|
||||
if (value == null) {
|
||||
LuaJNR.INSTANCE.lua_pushnil(pointer)
|
||||
} else {
|
||||
@ -1275,6 +1296,7 @@ class LuaThread private constructor(
|
||||
}
|
||||
|
||||
fun push(value: Boolean?) {
|
||||
sharedState.ensureValid()
|
||||
if (value == null) {
|
||||
LuaJNR.INSTANCE.lua_pushnil(pointer)
|
||||
} else {
|
||||
@ -1283,6 +1305,7 @@ class LuaThread private constructor(
|
||||
}
|
||||
|
||||
fun push(value: String?) {
|
||||
sharedState.ensureValid()
|
||||
if (value == null) {
|
||||
push()
|
||||
} else {
|
||||
@ -1291,10 +1314,12 @@ class LuaThread private constructor(
|
||||
}
|
||||
|
||||
fun push(value: LuaHandle) {
|
||||
sharedState.ensureValid()
|
||||
value.push(this)
|
||||
}
|
||||
|
||||
fun pushObject(value: Any) {
|
||||
sharedState.ensureValid()
|
||||
LuaJNI.lua_pushjobject(pointer.address(), value)
|
||||
}
|
||||
|
||||
@ -1334,6 +1359,7 @@ class LuaThread private constructor(
|
||||
}
|
||||
|
||||
fun copy(fromIndex: Int, toIndex: Int) {
|
||||
sharedState.ensureValid()
|
||||
LuaJNR.INSTANCE.lua_copy(pointer, fromIndex, toIndex)
|
||||
}
|
||||
|
||||
@ -1352,18 +1378,22 @@ class LuaThread private constructor(
|
||||
}
|
||||
|
||||
fun setTop(topIndex: Int) {
|
||||
sharedState.ensureValid()
|
||||
LuaJNR.INSTANCE.lua_settop(pointer, topIndex)
|
||||
}
|
||||
|
||||
fun storeRef(tableIndex: Int) {
|
||||
sharedState.ensureValid()
|
||||
LuaJNR.INSTANCE.luaL_ref(pointer, tableIndex)
|
||||
}
|
||||
|
||||
fun pushTable(arraySize: Int = 0, hashSize: Int = 0) {
|
||||
sharedState.ensureValid()
|
||||
LuaJNR.INSTANCE.lua_createtable(pointer, arraySize, hashSize)
|
||||
}
|
||||
|
||||
fun setTableValue(stackIndex: Int = -3) {
|
||||
sharedState.ensureValid()
|
||||
LuaJNR.INSTANCE.lua_settable(this.pointer, stackIndex)
|
||||
}
|
||||
|
||||
|
@ -225,6 +225,26 @@ sealed class LegacyWorldStorage() : WorldStorage() {
|
||||
}
|
||||
|
||||
override fun saveEntities(pos: ChunkPos, entities: Collection<AbstractEntity>) {
|
||||
// since entities access their Lua state on save, we must collect everything before "unloading" them from memory
|
||||
val uniques = HashMap<String, Vector2d>()
|
||||
val entityData = ArrayList<FastByteArrayOutputStream>()
|
||||
|
||||
for (entity in entities) {
|
||||
Starbound.legacyStoreJson {
|
||||
val stream = FastByteArrayOutputStream()
|
||||
val data = JsonObject()
|
||||
entity.serialize(data)
|
||||
VersionRegistry.make(entity.type.storeName, data).write(DataOutputStream(stream))
|
||||
entityData.add(stream)
|
||||
}
|
||||
|
||||
val uniqueID = entity.uniqueID.get()
|
||||
|
||||
if (uniqueID != null) {
|
||||
uniques[uniqueID] = entity.position
|
||||
}
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
val chunkX = pos.x
|
||||
val chunkY = pos.y
|
||||
@ -236,23 +256,11 @@ sealed class LegacyWorldStorage() : WorldStorage() {
|
||||
val streamEntities = DataOutputStream(BufferedOutputStream(DeflaterOutputStream(buffEntities)))
|
||||
val streamUniques = DataOutputStream(BufferedOutputStream(DeflaterOutputStream(buffUniques)))
|
||||
|
||||
val uniques = HashMap<String, Vector2d>()
|
||||
|
||||
try {
|
||||
streamEntities.writeVarInt(entities.size)
|
||||
streamEntities.writeVarInt(entityData.size)
|
||||
|
||||
for (entity in entities) {
|
||||
Starbound.legacyStoreJson {
|
||||
val data = JsonObject()
|
||||
entity.serialize(data)
|
||||
VersionRegistry.make(entity.type.storeName, data).write(streamEntities)
|
||||
}
|
||||
|
||||
val uniqueID = entity.uniqueID.get()
|
||||
|
||||
if (uniqueID != null) {
|
||||
uniques[uniqueID] = entity.position
|
||||
}
|
||||
for (data in entityData) {
|
||||
streamEntities.write(data.array, 0, data.length)
|
||||
}
|
||||
|
||||
streamEntities.close()
|
||||
|
@ -264,31 +264,37 @@ class NativeLocalWorldStorage(file: File?) : WorldStorage() {
|
||||
""".trimIndent())
|
||||
|
||||
override fun saveEntities(pos: ChunkPos, entities: Collection<AbstractEntity>) {
|
||||
// since entities access their Lua state on save, we must collect everything before "unloading" them from memory
|
||||
val storeData = JsonArray()
|
||||
val uniques = ArrayList<Runnable>()
|
||||
|
||||
for (entity in entities) {
|
||||
Starbound.storeJson {
|
||||
val data = JsonObject()
|
||||
entity.serialize(data)
|
||||
storeData.add(VersionRegistry.make(entity.type.storeName, data).toJson())
|
||||
|
||||
if (entity.uniqueID.get() != null) {
|
||||
uniques.add {
|
||||
writeUniqueEntity.setString(1, entity.uniqueID.get())
|
||||
writeUniqueEntity.setInt(2, pos.x)
|
||||
writeUniqueEntity.setInt(3, pos.y)
|
||||
writeUniqueEntity.setDouble(4, entity.position.x)
|
||||
writeUniqueEntity.setDouble(5, entity.position.y)
|
||||
|
||||
writeUniqueEntity.execute()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
executor.execute {
|
||||
entitiesSavepoint.execute {
|
||||
clearUniqueEntities.setInt(1, pos.x)
|
||||
clearUniqueEntities.setInt(2, pos.y)
|
||||
clearUniqueEntities.execute()
|
||||
|
||||
val storeData = JsonArray()
|
||||
|
||||
for (entity in entities) {
|
||||
Starbound.storeJson {
|
||||
val data = JsonObject()
|
||||
entity.serialize(data)
|
||||
storeData.add(VersionRegistry.make(entity.type.storeName, data).toJson())
|
||||
|
||||
if (entity.uniqueID.get() != null) {
|
||||
writeUniqueEntity.setString(1, entity.uniqueID.get())
|
||||
writeUniqueEntity.setInt(2, pos.x)
|
||||
writeUniqueEntity.setInt(3, pos.y)
|
||||
writeUniqueEntity.setDouble(4, entity.position.x)
|
||||
writeUniqueEntity.setDouble(5, entity.position.y)
|
||||
|
||||
writeUniqueEntity.execute()
|
||||
}
|
||||
}
|
||||
}
|
||||
uniques.forEach { it.run() }
|
||||
|
||||
writeEntities.setInt(1, pos.x)
|
||||
writeEntities.setInt(2, pos.y)
|
||||
|
@ -231,7 +231,11 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, Server
|
||||
// bumpState(State.CAVE_LIQUID)
|
||||
|
||||
for (obj in world.storage.loadEntities(pos).await()) {
|
||||
obj.joinWorld(world)
|
||||
try {
|
||||
obj.joinWorld(world)
|
||||
} catch (err: Exception) {
|
||||
LOGGER.error("Exception while spawning entity $obj in world", err)
|
||||
}
|
||||
}
|
||||
|
||||
bumpState(state)
|
||||
|
@ -215,14 +215,13 @@ class ServerWorld private constructor(
|
||||
it.client.enqueueWarp(WarpAlias.Return)
|
||||
}
|
||||
|
||||
callUninitOnEntities()
|
||||
|
||||
if (!uncleanShutdown) {
|
||||
saveMetadata()
|
||||
storage.commit()
|
||||
}
|
||||
|
||||
storage.close()
|
||||
callUninitOnEntities()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -253,15 +253,50 @@ abstract class AbstractEntity : Comparable<AbstractEntity> {
|
||||
check(!world.entities.containsKey(entityID)) { "Duplicate entity ID: $entityID" }
|
||||
innerWorld = world
|
||||
|
||||
uniqueID.get()?.let {
|
||||
check(it !in world.uniqueEntities) { "Duplicate unique entity ID: $it" }
|
||||
world.uniqueEntities[it] = this
|
||||
try {
|
||||
uniqueID.get()?.let {
|
||||
check(it !in world.uniqueEntities) { "Duplicate unique entity ID: $it" }
|
||||
world.uniqueEntities[it] = this
|
||||
}
|
||||
} catch (err: Throwable) {
|
||||
if (world is ClientWorld) {
|
||||
world.client.activeConnection?.freeEntityID(entityID)
|
||||
}
|
||||
|
||||
innerWorld = null
|
||||
throw err
|
||||
}
|
||||
|
||||
world.entities[entityID] = this
|
||||
world.entityList.add(this)
|
||||
spatialEntry = world.entityIndex.Entry(this)
|
||||
onJoinWorld(world)
|
||||
|
||||
try {
|
||||
onJoinWorld(world)
|
||||
} catch (err: Throwable) {
|
||||
try {
|
||||
uninit(world)
|
||||
} catch (err2: Exception) {
|
||||
// should be harmless
|
||||
err.addSuppressed(err2)
|
||||
}
|
||||
|
||||
world.entities.remove(entityID)
|
||||
world.entityList.remove(this)
|
||||
spatialEntry!!.remove()
|
||||
spatialEntry = null
|
||||
|
||||
uniqueID.get()?.let {
|
||||
world.uniqueEntities.remove(it)
|
||||
}
|
||||
|
||||
if (world is ClientWorld) {
|
||||
world.client.activeConnection?.freeEntityID(entityID)
|
||||
}
|
||||
|
||||
innerWorld = null
|
||||
throw err
|
||||
}
|
||||
|
||||
if (visibleToRemotes) {
|
||||
if (world is ClientWorld && !isRemote) {
|
||||
|
@ -8,6 +8,7 @@ import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonNull
|
||||
import com.google.gson.JsonObject
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import ru.dbotthepony.kommons.gson.get
|
||||
import ru.dbotthepony.kommons.gson.set
|
||||
import ru.dbotthepony.kommons.util.Either
|
||||
@ -404,9 +405,13 @@ class MonsterEntity(val variant: MonsterVariant, level: Double? = null) : ActorE
|
||||
return
|
||||
}
|
||||
} else {
|
||||
luaUpdate.update(delta) {
|
||||
luaMovement.clearControlsIfNeeded()
|
||||
forceRegions.clear()
|
||||
try {
|
||||
luaUpdate.update(delta) {
|
||||
luaMovement.clearControlsIfNeeded()
|
||||
forceRegions.clear()
|
||||
}
|
||||
} catch (err: Exception) {
|
||||
LOGGER.error("Exception while ticking $this", err)
|
||||
}
|
||||
|
||||
if (shouldDie) {
|
||||
@ -429,4 +434,8 @@ class MonsterEntity(val variant: MonsterVariant, level: Double? = null) : ActorE
|
||||
override fun handleMessage(connection: Int, message: String, arguments: JsonArray): JsonElement? {
|
||||
return luaMessages.handle(message, connection == connectionID, arguments) ?: statusController.handleMessage(message, connection == connectionID, arguments)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
}
|
||||
}
|
||||
|
@ -151,6 +151,12 @@ class StatusController(val entity: ActorEntity, val config: StatusControllerConf
|
||||
// provideStatusControllerBindings(this, lua) // provided through provideEntityBindings
|
||||
provideEntityBindings(entity, lua)
|
||||
|
||||
if (animator != null)
|
||||
// FIXME: effect animator's animator is not set in stone because of legacy protocol
|
||||
// god damn it
|
||||
// But at least it shouldn't change in this context
|
||||
provideAnimatorBindings(animator.animator, lua)
|
||||
|
||||
// TODO: Once we have brand new object-oriented Lua API, expose proper entity bindings here
|
||||
// TODO: Expose world bindings
|
||||
lua.initScripts()
|
||||
@ -261,13 +267,6 @@ class StatusController(val entity: ActorEntity, val config: StatusControllerConf
|
||||
} else {
|
||||
animator = EffectAnimator(KOptional(config.primaryAnimationConfig.fullPath))
|
||||
animatorID = effectAnimators.add(animator)
|
||||
|
||||
// FIXME: effect animator's animator is not set in stone because of legacy protocol
|
||||
// god damn it
|
||||
// But at least it shouldn't change in this context
|
||||
if (!entity.isRemote) {
|
||||
provideAnimatorBindings(animator.animator, lua)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -411,7 +411,7 @@ function seqNode:run(delta, blackboard)
|
||||
self.calls = self.calls + 1
|
||||
local size = self.size
|
||||
local isSelector = self.isSelector
|
||||
`
|
||||
|
||||
while self.index <= size do
|
||||
local child = self.children[self.index]
|
||||
local status = runAndReset(child, delta, blackboard)
|
||||
|
@ -200,13 +200,13 @@ do
|
||||
error('require: script path must be absolute: ' .. path)
|
||||
end
|
||||
|
||||
if loadedScripts[path] then return unpack(loadedScripts[path]) end
|
||||
if loadedScripts[path] then return table.unpack(loadedScripts[path]) end
|
||||
local fn = __require(path)
|
||||
|
||||
if fn then
|
||||
local result = {fn(...)}
|
||||
loadedScripts[path] = result
|
||||
return unpack(result)
|
||||
return table.unpack(result)
|
||||
else
|
||||
print('Failed to require Lua script ' .. path)
|
||||
loadedScripts[path] = {}
|
||||
@ -235,7 +235,7 @@ do
|
||||
end
|
||||
|
||||
low = 1
|
||||
high = tA
|
||||
high = a
|
||||
|
||||
if high % 1.0 ~= 0.0 then
|
||||
error('bad argument #1 to math.random: integer expected, got double', 2)
|
||||
@ -249,8 +249,8 @@ do
|
||||
error('bad argument #2 to math.random: number expected, got ' .. tB, 2)
|
||||
end
|
||||
|
||||
low = tA
|
||||
high = tB
|
||||
low = a
|
||||
high = b
|
||||
|
||||
if low % 1.0 ~= 0.0 then
|
||||
error('bad argument #1 to math.random: integer expected, got double', 2)
|
||||
@ -385,20 +385,4 @@ function mergeJson(base, with)
|
||||
end
|
||||
end
|
||||
|
||||
string.__index = string
|
||||
|
||||
do
|
||||
local sub = string.sub
|
||||
|
||||
function string:__index(key)
|
||||
if type(key) == 'number' then
|
||||
return sub(self, key, key)
|
||||
else
|
||||
return string[key]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
setmetatable('', string)
|
||||
|
||||
|
||||
|
@ -175,7 +175,7 @@ for fnName, implName in pairs(regularQueryFunctions) do
|
||||
options.callScript,
|
||||
options.callScriptArgs or {},
|
||||
options.callScriptResult,
|
||||
order(options.order, fullName),
|
||||
order(options.order, fullName)
|
||||
)
|
||||
else
|
||||
-- point + radius
|
||||
@ -190,7 +190,7 @@ for fnName, implName in pairs(regularQueryFunctions) do
|
||||
options.callScript,
|
||||
options.callScriptArgs or {},
|
||||
options.callScriptResult,
|
||||
order(options.order, fullName),
|
||||
order(options.order, fullName)
|
||||
)
|
||||
end
|
||||
end
|
||||
@ -218,7 +218,7 @@ for fnName, implName in pairs(regularQueryFunctions) do
|
||||
options.callScript,
|
||||
options.callScriptArgs or {},
|
||||
options.callScriptResult,
|
||||
order(options.order, fullName),
|
||||
order(options.order, fullName)
|
||||
)
|
||||
end
|
||||
end
|
||||
@ -244,7 +244,7 @@ for fnName, implName in pairs(regularQueryFunctions) do
|
||||
options.callScript,
|
||||
options.callScriptArgs or {},
|
||||
options.callScriptResult,
|
||||
order(options.order, fullName),
|
||||
order(options.order, fullName)
|
||||
)
|
||||
end
|
||||
end
|
||||
@ -284,7 +284,7 @@ do
|
||||
options.callScript,
|
||||
options.callScriptArgs or {},
|
||||
options.callScriptResult,
|
||||
order(options.order, fullName),
|
||||
order(options.order, fullName)
|
||||
)
|
||||
else
|
||||
-- point + radius
|
||||
@ -300,7 +300,7 @@ do
|
||||
options.callScript,
|
||||
options.callScriptArgs or {},
|
||||
options.callScriptResult,
|
||||
order(options.order, fullName),
|
||||
order(options.order, fullName)
|
||||
)
|
||||
end
|
||||
end
|
||||
@ -333,7 +333,7 @@ do
|
||||
options.callScript,
|
||||
options.callScriptArgs or {},
|
||||
options.callScriptResult,
|
||||
order(options.order, fullName),
|
||||
order(options.order, fullName)
|
||||
)
|
||||
end
|
||||
end
|
||||
@ -364,7 +364,7 @@ do
|
||||
options.callScript,
|
||||
options.callScriptArgs or {},
|
||||
options.callScriptResult,
|
||||
order(options.order, fullName),
|
||||
order(options.order, fullName)
|
||||
)
|
||||
end
|
||||
end
|
||||
@ -423,7 +423,7 @@ do
|
||||
options.callScript,
|
||||
options.callScriptArgs or {},
|
||||
options.callScriptResult,
|
||||
order(options.order, fullName),
|
||||
order(options.order, fullName)
|
||||
)
|
||||
else
|
||||
-- point + radius
|
||||
@ -439,7 +439,7 @@ do
|
||||
options.callScript,
|
||||
options.callScriptArgs or {},
|
||||
options.callScriptResult,
|
||||
order(options.order, fullName),
|
||||
order(options.order, fullName)
|
||||
)
|
||||
end
|
||||
end
|
||||
@ -468,7 +468,7 @@ do
|
||||
options.callScript,
|
||||
options.callScriptArgs or {},
|
||||
options.callScriptResult,
|
||||
order(options.order, fullName),
|
||||
order(options.order, fullName)
|
||||
)
|
||||
end
|
||||
end
|
||||
@ -495,7 +495,7 @@ do
|
||||
options.callScript,
|
||||
options.callScriptArgs or {},
|
||||
options.callScriptResult,
|
||||
order(options.order, fullName),
|
||||
order(options.order, fullName)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user