Make it compile against PUC Lua
This commit is contained in:
parent
148ceba239
commit
9a958ecccb
@ -17,9 +17,6 @@ import kotlinx.coroutines.future.await
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.classdump.luna.compiler.CompilerChunkLoader
|
||||
import org.classdump.luna.compiler.CompilerSettings
|
||||
import org.classdump.luna.load.ChunkFactory
|
||||
import ru.dbotthepony.kstarbound.math.AABBTypeAdapter
|
||||
import ru.dbotthepony.kommons.gson.EitherTypeAdapter
|
||||
import ru.dbotthepony.kommons.gson.KOptionalTypeAdapter
|
||||
@ -281,26 +278,6 @@ object Starbound : BlockableEventLoop("Multiverse Thread"), Scheduler, ISBFileLo
|
||||
}
|
||||
}
|
||||
|
||||
private val loader = CompilerChunkLoader.of(CompilerSettings.defaultNoAccountingSettings(), "sb_lua_")
|
||||
private val scriptCache = ConcurrentHashMap<String, ChunkFactory>()
|
||||
|
||||
private fun loadScript0(path: String): ChunkFactory {
|
||||
val find = locate(path)
|
||||
|
||||
if (!find.exists) {
|
||||
throw NoSuchElementException("Script $path does not exist")
|
||||
}
|
||||
|
||||
val time = System.nanoTime()
|
||||
val result = loader.compileTextChunk(path, find.readToString())
|
||||
LOGGER.debug("Compiled {} in {} ms", path, (System.nanoTime() - time) / 1_000_000L)
|
||||
return result
|
||||
}
|
||||
|
||||
fun loadScript(path: String): ChunkFactory {
|
||||
return scriptCache.computeIfAbsent(path, ::loadScript0)
|
||||
}
|
||||
|
||||
private val luaScriptCache = Caffeine.newBuilder()
|
||||
.maximumSize(LUA_SCRIPT_CACHE_SIZE)
|
||||
.expireAfterAccess(Duration.ofHours(1L))
|
||||
|
@ -10,11 +10,6 @@ import com.google.gson.TypeAdapter
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.classdump.luna.ByteString
|
||||
import org.classdump.luna.LuaRuntimeException
|
||||
import org.classdump.luna.Table
|
||||
import org.classdump.luna.TableFactory
|
||||
import org.classdump.luna.runtime.ExecutionContext
|
||||
import ru.dbotthepony.kommons.gson.contains
|
||||
import ru.dbotthepony.kommons.gson.get
|
||||
import ru.dbotthepony.kommons.gson.value
|
||||
@ -32,16 +27,8 @@ import ru.dbotthepony.kstarbound.item.ItemStack
|
||||
import ru.dbotthepony.kstarbound.json.mergeJson
|
||||
import ru.dbotthepony.kstarbound.json.readJsonElement
|
||||
import ru.dbotthepony.kstarbound.json.writeJsonElement
|
||||
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
||||
import ru.dbotthepony.kstarbound.lua.LuaThread
|
||||
import ru.dbotthepony.kstarbound.lua.LuaType
|
||||
import ru.dbotthepony.kstarbound.lua.StateMachine
|
||||
import ru.dbotthepony.kstarbound.lua.from
|
||||
import ru.dbotthepony.kstarbound.lua.get
|
||||
import ru.dbotthepony.kstarbound.lua.indexNoYield
|
||||
import ru.dbotthepony.kstarbound.lua.toByteString
|
||||
import ru.dbotthepony.kstarbound.lua.toJson
|
||||
import ru.dbotthepony.kstarbound.lua.toJsonFromLua
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
import java.util.function.Supplier
|
||||
@ -190,74 +177,6 @@ fun ItemDescriptor(args: LuaThread.ArgStack): ItemDescriptor {
|
||||
return ItemDescriptor(args.lua, args.position)
|
||||
}
|
||||
|
||||
fun ItemDescriptor(data: Table, stateMachine: StateMachine): Supplier<ItemDescriptor> {
|
||||
val name = stateMachine.index(data, 1L, "name", "item")
|
||||
val count = stateMachine.optionalIndex(data, 2L, "count")
|
||||
val parameters = stateMachine.optionalIndex(data, 3L, "parameters", "data")
|
||||
var result: KOptional<ItemDescriptor> = KOptional.empty()
|
||||
|
||||
stateMachine.add {
|
||||
val iname = name.get()
|
||||
val icount = count.get().orElse(1L)
|
||||
val iparameters = parameters.get().map { if (it is Table) it.toJson(true) else throw LuaRuntimeException("Invalid item descriptor parameters ($it)") }.orElse { JsonObject() }
|
||||
|
||||
if (iname !is ByteString) throw LuaRuntimeException("Invalid item descriptor name (${iname})")
|
||||
if (icount !is Number) throw LuaRuntimeException("Invalid item descriptor count (${icount})")
|
||||
|
||||
result = KOptional(ItemDescriptor(iname.decode(), icount.toLong(), iparameters as JsonObject))
|
||||
}
|
||||
|
||||
return Supplier { result.value }
|
||||
}
|
||||
|
||||
fun ExecutionContext.ItemDescriptor(data: Table): ItemDescriptor {
|
||||
if (data.metatable?.rawget("__nils") != null)
|
||||
return ItemDescriptor(toJsonFromLua(data)) // assume it is json
|
||||
|
||||
val name = indexNoYield(data, 1L) ?: indexNoYield(data, "name") ?: indexNoYield(data, "item")
|
||||
val count = indexNoYield(data, 2L) ?: indexNoYield(data, "count") ?: 1L
|
||||
val parameters = indexNoYield(data, 3L) ?: indexNoYield(data, "parameters") ?: indexNoYield(data, "data")
|
||||
|
||||
if (name !is ByteString) throw LuaRuntimeException("Invalid item descriptor name (${name})")
|
||||
if (count !is Number) throw LuaRuntimeException("Invalid item descriptor count (${count})")
|
||||
|
||||
if (parameters == null) {
|
||||
return ItemDescriptor(name.decode(), count.toLong())
|
||||
} else if (parameters is Table) {
|
||||
return ItemDescriptor(name.decode(), count.toLong(), parameters.toJson(true) as JsonObject)
|
||||
} else {
|
||||
throw LuaRuntimeException("Invalid item descriptor parameters ($parameters)")
|
||||
}
|
||||
}
|
||||
|
||||
fun ExecutionContext.ItemDescriptor(data: Any?): ItemDescriptor {
|
||||
if (data is ByteString) {
|
||||
return ItemDescriptor(data.decode(), 1L, JsonObject())
|
||||
} else if (data is Table) {
|
||||
return ItemDescriptor(data)
|
||||
} else {
|
||||
return ItemDescriptor.EMPTY
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("Does not obey meta methods, find replacement where possible")
|
||||
fun ItemDescriptor(data: Table): ItemDescriptor {
|
||||
val name = data[1L] ?: data["name"] ?: data["item"]
|
||||
val count = data[2L] ?: data["count"] ?: 1L
|
||||
val parameters = data[3L] ?: data["parameters"] ?: data["data"]
|
||||
|
||||
if (name !is ByteString) throw LuaRuntimeException("Invalid item descriptor name (${name})")
|
||||
if (count !is Number) throw LuaRuntimeException("Invalid item descriptor count (${count})")
|
||||
|
||||
if (parameters == null) {
|
||||
return ItemDescriptor(name.decode(), count.toLong())
|
||||
} else if (parameters is Table) {
|
||||
return ItemDescriptor(name.decode(), count.toLong(), parameters.toJson(true) as JsonObject)
|
||||
} else {
|
||||
throw LuaRuntimeException("Invalid item descriptor parameters ($parameters)")
|
||||
}
|
||||
}
|
||||
|
||||
fun ItemDescriptor(stream: DataInputStream): ItemDescriptor {
|
||||
val name = stream.readInternedString()
|
||||
val count = stream.readVarLong()
|
||||
@ -324,18 +243,6 @@ data class ItemDescriptor(
|
||||
return !isEmpty
|
||||
}
|
||||
|
||||
fun toTable(allocator: TableFactory): Table? {
|
||||
if (isEmpty) {
|
||||
return null
|
||||
}
|
||||
|
||||
return allocator.newTable(0, 3).also {
|
||||
it.rawset("name", name.toByteString())
|
||||
it.rawset("count", count)
|
||||
it.rawset("parameters", allocator.from(parameters))
|
||||
}
|
||||
}
|
||||
|
||||
fun build(level: Double? = null, seed: Long? = null, random: RandomGenerator? = null): ItemStack {
|
||||
try {
|
||||
val (jConfig, jParameters) = buildConfig(level, seed, random)
|
||||
|
@ -11,13 +11,10 @@ import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonPrimitive
|
||||
import com.google.gson.TypeAdapter
|
||||
import com.google.gson.annotations.JsonAdapter
|
||||
import com.google.gson.internal.bind.TypeAdapters
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
||||
import org.classdump.luna.Table
|
||||
import org.classdump.luna.TableFactory
|
||||
import ru.dbotthepony.kommons.gson.consumeNull
|
||||
import ru.dbotthepony.kommons.gson.contains
|
||||
import ru.dbotthepony.kommons.gson.get
|
||||
@ -26,9 +23,7 @@ import ru.dbotthepony.kommons.io.writeBinaryString
|
||||
import ru.dbotthepony.kommons.io.writeVarLong
|
||||
import ru.dbotthepony.kommons.math.RGBAColor
|
||||
import ru.dbotthepony.kommons.util.Either
|
||||
import ru.dbotthepony.kstarbound.math.vector.Vector2f
|
||||
import ru.dbotthepony.kstarbound.Globals
|
||||
import ru.dbotthepony.kstarbound.Registry
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.defs.Drawable
|
||||
import ru.dbotthepony.kstarbound.defs.actor.PersistentStatusEffect
|
||||
@ -38,9 +33,8 @@ import ru.dbotthepony.kstarbound.defs.item.ItemRarity
|
||||
import ru.dbotthepony.kstarbound.json.mergeJson
|
||||
import ru.dbotthepony.kstarbound.json.stream
|
||||
import ru.dbotthepony.kstarbound.json.writeJsonElement
|
||||
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
||||
import ru.dbotthepony.kstarbound.lua.LuaThread
|
||||
import ru.dbotthepony.kstarbound.lua.from
|
||||
import ru.dbotthepony.kstarbound.math.vector.Vector2f
|
||||
import ru.dbotthepony.kstarbound.network.syncher.NetworkedElement
|
||||
import ru.dbotthepony.kstarbound.util.AssetPathStack
|
||||
import ru.dbotthepony.kstarbound.util.ManualLazy
|
||||
@ -236,17 +230,17 @@ open class ItemStack(val entry: ItemRegistry.Entry, val config: JsonObject, para
|
||||
|
||||
data class AgingResult(val new: ItemStack?, val ageUpdated: Boolean)
|
||||
|
||||
private val agingScripts: LuaEnvironment? by lazy {
|
||||
//val config = config.value ?: return@lazy null
|
||||
//if (config.itemTags)
|
||||
null
|
||||
}
|
||||
|
||||
open fun advanceAge(by: Double): AgingResult {
|
||||
val agingScripts = agingScripts ?: return AgingResult(null, false)
|
||||
// TODO
|
||||
val agingScripts = null as LuaThread? ?: return AgingResult(null, false)
|
||||
|
||||
val descriptor = createDescriptor()
|
||||
val updated = ItemDescriptor(agingScripts.invokeGlobal("ageItem", descriptor.toTable(agingScripts), by)[0] as Table)
|
||||
|
||||
val updated = agingScripts.invokeGlobal(
|
||||
"ageItem", 1,
|
||||
{ descriptor.store(this); push(by); 2 },
|
||||
::ItemDescriptor
|
||||
).orThrow { IllegalStateException("Aging script did not return new item state") }
|
||||
|
||||
if (descriptor != updated) {
|
||||
if (descriptor.name == updated.name) {
|
||||
@ -374,14 +368,6 @@ open class ItemStack(val entry: ItemRegistry.Entry, val config: JsonObject, para
|
||||
return createDescriptor().toJson()
|
||||
}
|
||||
|
||||
fun toTable(allocator: TableFactory): Table? {
|
||||
if (isEmpty) {
|
||||
return null
|
||||
}
|
||||
|
||||
return createDescriptor().toTable(allocator)
|
||||
}
|
||||
|
||||
class Adapter(gson: Gson) : TypeAdapter<ItemStack>() {
|
||||
override fun write(out: JsonWriter, value: ItemStack?) {
|
||||
val json = value?.toJson()
|
||||
|
@ -1,22 +1,7 @@
|
||||
package ru.dbotthepony.kstarbound.lua
|
||||
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonNull
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonPrimitive
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectAVLTreeMap
|
||||
import org.classdump.luna.ByteString
|
||||
import org.classdump.luna.LuaRuntimeException
|
||||
import org.classdump.luna.Table
|
||||
import org.classdump.luna.TableFactory
|
||||
import org.classdump.luna.impl.NonsuspendableFunctionException
|
||||
import org.classdump.luna.runtime.AbstractFunction3
|
||||
import org.classdump.luna.runtime.ExecutionContext
|
||||
import ru.dbotthepony.kommons.gson.set
|
||||
import ru.dbotthepony.kommons.math.RGBAColor
|
||||
import ru.dbotthepony.kommons.util.Either
|
||||
import ru.dbotthepony.kstarbound.math.AABB
|
||||
import ru.dbotthepony.kommons.util.IStruct2d
|
||||
import ru.dbotthepony.kommons.util.IStruct2f
|
||||
import ru.dbotthepony.kommons.util.IStruct2i
|
||||
@ -27,438 +12,14 @@ import ru.dbotthepony.kommons.util.IStruct4d
|
||||
import ru.dbotthepony.kommons.util.IStruct4f
|
||||
import ru.dbotthepony.kommons.util.IStruct4i
|
||||
import ru.dbotthepony.kstarbound.Registry
|
||||
import ru.dbotthepony.kstarbound.math.AABB
|
||||
import ru.dbotthepony.kstarbound.math.AABBi
|
||||
import ru.dbotthepony.kstarbound.math.Line2d
|
||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.math.vector.Vector2f
|
||||
import ru.dbotthepony.kstarbound.math.vector.Vector2i
|
||||
import ru.dbotthepony.kstarbound.json.InternedJsonElementAdapter
|
||||
import ru.dbotthepony.kstarbound.math.AABBi
|
||||
import ru.dbotthepony.kstarbound.math.Line2d
|
||||
import ru.dbotthepony.kstarbound.util.sbIntern
|
||||
import ru.dbotthepony.kstarbound.world.physics.Poly
|
||||
|
||||
fun ExecutionContext.toVector2i(table: Any): Vector2i {
|
||||
val x = indexNoYield(table, 1L)
|
||||
val y = indexNoYield(table, 2L)
|
||||
returnBuffer.setTo()
|
||||
|
||||
if (x !is Number) throw ClassCastException("Expected table representing a vector, but value at [1] is not a number: $x")
|
||||
if (y !is Number) throw ClassCastException("Expected table representing a vector, but value at [2] is not a number: $y")
|
||||
|
||||
return Vector2i(x.toInt(), y.toInt())
|
||||
}
|
||||
|
||||
fun ExecutionContext.toVector2d(table: Any): Vector2d {
|
||||
val x = indexNoYield(table, 1L)
|
||||
val y = indexNoYield(table, 2L)
|
||||
returnBuffer.setTo()
|
||||
|
||||
if (x !is Number) throw ClassCastException("Expected table representing a vector, but value at [1] is not a number: $x")
|
||||
if (y !is Number) throw ClassCastException("Expected table representing a vector, but value at [2] is not a number: $y")
|
||||
|
||||
return Vector2d(x.toDouble(), y.toDouble())
|
||||
}
|
||||
|
||||
fun ExecutionContext.toLine2d(table: Any): Line2d {
|
||||
val p0 = toVector2d(indexNoYield(table, 1L) ?: throw LuaRuntimeException("Invalid line: $table"))
|
||||
val p1 = toVector2d(indexNoYield(table, 2L) ?: throw LuaRuntimeException("Invalid line: $table"))
|
||||
return Line2d(p0, p1)
|
||||
}
|
||||
|
||||
fun String?.toByteString(): ByteString? {
|
||||
return if (this == null) null else ByteString.of(this)
|
||||
}
|
||||
|
||||
fun ExecutionContext.toPoly(table: Table): Poly {
|
||||
val vertices = ArrayList<Vector2d>()
|
||||
|
||||
for ((_, v) in table) {
|
||||
vertices.add(toVector2d(v))
|
||||
}
|
||||
|
||||
return Poly(vertices)
|
||||
}
|
||||
|
||||
fun ExecutionContext.toVector2f(table: Any): Vector2f {
|
||||
val x = indexNoYield(table, 1L)
|
||||
val y = indexNoYield(table, 2L)
|
||||
returnBuffer.setTo()
|
||||
|
||||
if (x !is Number) throw ClassCastException("Expected table representing a vector, but value at [1] is not a number: $x")
|
||||
if (y !is Number) throw ClassCastException("Expected table representing a vector, but value at [2] is not a number: $y")
|
||||
|
||||
return Vector2f(x.toFloat(), y.toFloat())
|
||||
}
|
||||
|
||||
fun ExecutionContext.toColor(table: Any): RGBAColor {
|
||||
val x = indexNoYield(table, 1L)
|
||||
val y = indexNoYield(table, 2L)
|
||||
val z = indexNoYield(table, 3L)
|
||||
val w = indexNoYield(table, 4L) ?: 255
|
||||
returnBuffer.setTo()
|
||||
|
||||
if (x !is Number) throw ClassCastException("Expected table representing a Color, but value at [1] is not a number: $x")
|
||||
if (y !is Number) throw ClassCastException("Expected table representing a Color, but value at [2] is not a number: $y")
|
||||
if (z !is Number) throw ClassCastException("Expected table representing a Color, but value at [3] is not a number: $z")
|
||||
if (w !is Number) throw ClassCastException("Expected table representing a Color, but value at [4] is not a number: $w")
|
||||
|
||||
return RGBAColor(x.toInt(), y.toInt(), z.toInt(), w.toInt())
|
||||
}
|
||||
|
||||
fun ExecutionContext.toAABB(table: Any): AABB {
|
||||
val x = indexNoYield(table, 1L)
|
||||
val y = indexNoYield(table, 2L)
|
||||
val z = indexNoYield(table, 3L)
|
||||
val w = indexNoYield(table, 4L)
|
||||
returnBuffer.setTo()
|
||||
|
||||
if (x !is Number) throw ClassCastException("Expected table representing a AABB, but value at [1] is not a number: $x")
|
||||
if (y !is Number) throw ClassCastException("Expected table representing a AABB, but value at [2] is not a number: $y")
|
||||
if (z !is Number) throw ClassCastException("Expected table representing a AABB, but value at [3] is not a number: $z")
|
||||
if (w !is Number) throw ClassCastException("Expected table representing a AABB, but value at [4] is not a number: $w")
|
||||
|
||||
return AABB(Vector2d(x.toDouble(), y.toDouble()), Vector2d(z.toDouble(), w.toDouble()))
|
||||
}
|
||||
|
||||
fun ExecutionContext.toAABBi(table: Any): AABBi {
|
||||
val x = indexNoYield(table, 1L)
|
||||
val y = indexNoYield(table, 2L)
|
||||
val z = indexNoYield(table, 3L)
|
||||
val w = indexNoYield(table, 4L)
|
||||
returnBuffer.setTo()
|
||||
|
||||
if (x !is Number) throw ClassCastException("Expected table representing a AABBi, but value at [1] is not a number: $x")
|
||||
if (y !is Number) throw ClassCastException("Expected table representing a AABBi, but value at [2] is not a number: $y")
|
||||
if (z !is Number) throw ClassCastException("Expected table representing a AABBi, but value at [3] is not a number: $z")
|
||||
if (w !is Number) throw ClassCastException("Expected table representing a AABBi, but value at [4] is not a number: $w")
|
||||
|
||||
return AABBi(Vector2i(x.toInt(), y.toInt()), Vector2i(z.toInt(), w.toInt()))
|
||||
}
|
||||
|
||||
fun toJsonFromLua(value: Any?): JsonElement {
|
||||
return when (value) {
|
||||
null, is JsonNull -> JsonNull.INSTANCE
|
||||
is String -> JsonPrimitive(value.sbIntern())
|
||||
is ByteString -> JsonPrimitive(value.decode().sbIntern())
|
||||
is Number -> JsonPrimitive(value)
|
||||
is Boolean -> InternedJsonElementAdapter.of(value)
|
||||
is Table -> value.toJson()
|
||||
else -> throw IllegalArgumentException("Unable to translate $value into json!")
|
||||
}
|
||||
}
|
||||
|
||||
fun Table.toJson(forceObject: Boolean = false): JsonElement {
|
||||
val arrayValues = Long2ObjectAVLTreeMap<JsonElement>()
|
||||
val hashValues = HashMap<String, JsonElement>()
|
||||
|
||||
val meta = metatable
|
||||
var hint = LUA_HINT_NONE
|
||||
|
||||
if (meta != null) {
|
||||
val getHint = meta["__typehint"]
|
||||
|
||||
if (getHint is Number) {
|
||||
hint = getHint.toLong()
|
||||
}
|
||||
|
||||
val nils = meta["__nils"]
|
||||
|
||||
if (nils is Table) {
|
||||
// Nil entries just have a garbage integer as their value
|
||||
for ((k, v) in nils) {
|
||||
val ik = k.toLuaInteger()
|
||||
|
||||
if (ik != null) {
|
||||
arrayValues[ik] = JsonNull.INSTANCE
|
||||
} else {
|
||||
hashValues[k.toString()] = JsonNull.INSTANCE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ((k, v) in this) {
|
||||
val ik = k.toLuaInteger()
|
||||
|
||||
if (ik != null) {
|
||||
arrayValues[ik] = toJsonFromLua(v)
|
||||
} else {
|
||||
hashValues[k.toString()] = toJsonFromLua(v)
|
||||
}
|
||||
}
|
||||
|
||||
val interpretAsList = !forceObject && hashValues.isEmpty() && hint != LUA_HINT_OBJECT
|
||||
|
||||
if (interpretAsList) {
|
||||
val list = JsonArray()
|
||||
|
||||
for ((k, v) in arrayValues) {
|
||||
val ik = k.toInt() - 1
|
||||
|
||||
while (list.size() <= ik) {
|
||||
list.add(JsonNull.INSTANCE)
|
||||
}
|
||||
|
||||
list[ik] = v
|
||||
}
|
||||
|
||||
return list
|
||||
} else {
|
||||
for ((k, v) in arrayValues) {
|
||||
hashValues[(k - 1L).toString()] = v
|
||||
}
|
||||
|
||||
return JsonObject().apply {
|
||||
for ((k, v) in hashValues) {
|
||||
this[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun TableFactory.from(value: JsonElement?): Any? {
|
||||
when (value) {
|
||||
is JsonPrimitive -> {
|
||||
if (value.isNumber) {
|
||||
return when (val v = value.asNumber) {
|
||||
is Float, is Double -> value.asDouble
|
||||
is Int, is Long -> value.asLong
|
||||
|
||||
else -> {
|
||||
// hi com.google.gson.internal.LazilyParsedNumber
|
||||
val doubleBits by lazy { v.toDouble() }
|
||||
|
||||
if (v.toString().contains('.') || doubleBits % 1.0 != 0.0) {
|
||||
v.toDouble()
|
||||
} else {
|
||||
v.toLong()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (value.isString) {
|
||||
return ByteString.of(value.asString)
|
||||
} else if (value.isBoolean) {
|
||||
return value.asBoolean
|
||||
} else {
|
||||
throw RuntimeException("unreachable code")
|
||||
}
|
||||
}
|
||||
|
||||
is JsonArray -> return from(value)
|
||||
is JsonObject -> return from(value)
|
||||
null, is JsonNull -> return null
|
||||
else -> throw RuntimeException(value::class.qualifiedName)
|
||||
}
|
||||
}
|
||||
|
||||
private data class JsonTable(val metatable: Table, val nils: Table, val data: Table)
|
||||
|
||||
fun Any?.toLuaInteger(): Long? {
|
||||
if (this == null)
|
||||
return null
|
||||
else if (this is Long)
|
||||
return this
|
||||
else if (this is Double) {
|
||||
if (this % 1.0 == 0.0) {
|
||||
return this.toLong()
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
} else if (this is ByteString) {
|
||||
val decoded = decode()
|
||||
|
||||
if (decoded.contains('.'))
|
||||
return null
|
||||
|
||||
return decoded.toLongOrNull()
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const val LUA_HINT_NONE = 0L
|
||||
const val LUA_HINT_ARRAY = 1L
|
||||
const val LUA_HINT_OBJECT = 2L
|
||||
|
||||
private object JsonTableIndex : AbstractFunction3<Table, Any, Any?>() {
|
||||
override fun resume(context: ExecutionContext?, suspendedState: Any?) {
|
||||
throw NonsuspendableFunctionException(this::class.java)
|
||||
}
|
||||
|
||||
private val __nils: ByteString = ByteString.of("__nils")
|
||||
|
||||
override fun invoke(context: ExecutionContext, self: Table, key: Any, value: Any?) {
|
||||
val meta = self.metatable
|
||||
val nils = meta[__nils] as Table
|
||||
|
||||
// If we are setting an entry to nil, need to add a bogus integer entry
|
||||
// to the __nils table, otherwise need to set the entry *in* the __nils
|
||||
// table to nil and remove it.
|
||||
|
||||
// TODO: __newindex is called only when assigning non-existing keys to values,
|
||||
// TODO: as per Lua manual.
|
||||
// TODO: Chucklefish weren't aware of this?
|
||||
|
||||
if (value == null) {
|
||||
nils[key] = 0L
|
||||
} else {
|
||||
nils[key] = null as Any?
|
||||
}
|
||||
|
||||
self[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
private fun TableFactory.createJsonTable(typeHint: Long, size: Int, hash: Int): JsonTable {
|
||||
val metatable = newTable()
|
||||
val nils = newTable()
|
||||
val data = newTable(size, hash)
|
||||
|
||||
metatable["__newindex"] = JsonTableIndex
|
||||
metatable["__nils"] = nils
|
||||
metatable["__typehint"] = typeHint
|
||||
|
||||
data.metatable = metatable
|
||||
return JsonTable(metatable, nils, data)
|
||||
}
|
||||
|
||||
fun TableFactory.from(value: JsonObject): Table {
|
||||
val (_, nils, data) = createJsonTable(LUA_HINT_OBJECT, 0, value.size())
|
||||
|
||||
for ((k, v) in value.entrySet()) {
|
||||
if (v.isJsonNull) {
|
||||
nils[k] = 0L
|
||||
} else {
|
||||
data[k] = from(v)
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
fun TableFactory.from(value: Int?): Long? {
|
||||
return value?.toLong()
|
||||
}
|
||||
|
||||
fun TableFactory.from(value: Long?): Long? {
|
||||
return value
|
||||
}
|
||||
|
||||
fun TableFactory.from(value: String?): ByteString? {
|
||||
return value.toByteString()
|
||||
}
|
||||
|
||||
fun TableFactory.from(value: ByteString?): ByteString? {
|
||||
return value
|
||||
}
|
||||
|
||||
fun TableFactory.from(value: JsonArray): Table {
|
||||
val (_, nils, data) = createJsonTable(LUA_HINT_ARRAY, 0, value.size())
|
||||
|
||||
for ((i, v) in value.withIndex()) {
|
||||
if (v.isJsonNull) {
|
||||
nils[i + 1L] = 0L
|
||||
} else {
|
||||
data[i + 1L] = from(v)
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
fun TableFactory.createJsonObject(): Table {
|
||||
return createJsonTable(LUA_HINT_OBJECT, 0, 0).data
|
||||
}
|
||||
|
||||
fun TableFactory.createJsonArray(): Table {
|
||||
return createJsonTable(LUA_HINT_ARRAY, 0, 0).data
|
||||
}
|
||||
|
||||
fun TableFactory.from(value: IStruct2d?): Table? {
|
||||
value ?: return null
|
||||
|
||||
return newTable(2, 0).apply {
|
||||
this[1L] = value.component1()
|
||||
this[2L] = value.component2()
|
||||
}
|
||||
}
|
||||
|
||||
fun TableFactory.from(value: Poly?): Table? {
|
||||
value ?: return null
|
||||
|
||||
return newTable(value.vertices.size, 0).apply {
|
||||
value.vertices.withIndex().forEach { (i, v) -> this[i + 1L] = from(v) }
|
||||
}
|
||||
}
|
||||
|
||||
fun TableFactory.from(value: IStruct2i?): Table? {
|
||||
value ?: return null
|
||||
|
||||
return newTable(2, 0).also {
|
||||
it.rawset(1L, value.component1())
|
||||
it.rawset(2L, value.component2())
|
||||
}
|
||||
}
|
||||
|
||||
fun TableFactory.from(value: IStruct3i?): Table? {
|
||||
value ?: return null
|
||||
|
||||
return newTable(3, 0).also {
|
||||
it.rawset(1L, value.component1())
|
||||
it.rawset(2L, value.component2())
|
||||
it.rawset(3L, value.component3())
|
||||
}
|
||||
}
|
||||
|
||||
fun TableFactory.from(value: IStruct4i?): Table? {
|
||||
value ?: return null
|
||||
|
||||
return newTable(3, 0).also {
|
||||
it.rawset(1L, value.component1())
|
||||
it.rawset(2L, value.component2())
|
||||
it.rawset(3L, value.component3())
|
||||
it.rawset(4L, value.component4())
|
||||
}
|
||||
}
|
||||
|
||||
fun TableFactory.from(value: RGBAColor?): Table? {
|
||||
value ?: return null
|
||||
|
||||
return newTable(3, 0).also {
|
||||
it.rawset(1L, value.redInt.toLong())
|
||||
it.rawset(2L, value.greenInt.toLong())
|
||||
it.rawset(3L, value.blueInt.toLong())
|
||||
it.rawset(4L, value.alphaInt.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
fun TableFactory.from(value: Collection<Any>): Table {
|
||||
return newTable(value.size, 0).also {
|
||||
for ((i, v) in value.withIndex()) {
|
||||
it.rawset(i + 1L, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun TableFactory.from(value: AABB?): Table? {
|
||||
value ?: return null
|
||||
|
||||
return newTable(3, 0).also {
|
||||
it.rawset(1L, value.mins.x)
|
||||
it.rawset(2L, value.mins.y)
|
||||
it.rawset(3L, value.maxs.x)
|
||||
it.rawset(4L, value.maxs.y)
|
||||
}
|
||||
}
|
||||
|
||||
fun TableFactory.tableFrom(collection: Collection<Any?>): Table {
|
||||
val alloc = newTable(collection.size, 0)
|
||||
|
||||
for ((i, v) in collection.withIndex())
|
||||
alloc[i + 1L] = v
|
||||
|
||||
return alloc
|
||||
}
|
||||
|
||||
// TODO: error reporting when argument was provided, but it is malformed
|
||||
// currently, invalid data gets silently discarded, and treated as "no value" aka null
|
||||
fun LuaThread.getPoly(stackIndex: Int = -1): Poly? {
|
||||
|
@ -1,400 +0,0 @@
|
||||
package ru.dbotthepony.kstarbound.lua
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import it.unimi.dsi.fastutil.objects.ObjectIterators
|
||||
import org.classdump.luna.LuaRuntimeException
|
||||
import org.classdump.luna.Table
|
||||
import org.classdump.luna.TableFactory
|
||||
import org.classdump.luna.impl.NonsuspendableFunctionException
|
||||
import org.classdump.luna.lib.ArgumentIterator
|
||||
import org.classdump.luna.lib.TableLib
|
||||
import org.classdump.luna.runtime.AbstractFunction0
|
||||
import org.classdump.luna.runtime.AbstractFunction1
|
||||
import org.classdump.luna.runtime.AbstractFunction2
|
||||
import org.classdump.luna.runtime.AbstractFunction3
|
||||
import org.classdump.luna.runtime.AbstractFunction4
|
||||
import org.classdump.luna.runtime.AbstractFunctionAnyArg
|
||||
import org.classdump.luna.runtime.Dispatch
|
||||
import org.classdump.luna.runtime.ExecutionContext
|
||||
import org.classdump.luna.runtime.LuaFunction
|
||||
import org.classdump.luna.runtime.UnresolvedControlThrowable
|
||||
import java.util.Spliterator
|
||||
import java.util.Spliterators
|
||||
import java.util.stream.Stream
|
||||
import java.util.stream.StreamSupport
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
fun ExecutionContext.indexNoYield(table: Any, key: Any): Any? {
|
||||
return try {
|
||||
Dispatch.index(this, table, key)
|
||||
returnBuffer.get0()
|
||||
} catch (err: UnresolvedControlThrowable) {
|
||||
throw RuntimeException("attempt to yield across C boundary", err)
|
||||
}
|
||||
}
|
||||
|
||||
fun ExecutionContext.indexSetNoYield(table: Any, key: Any, value: Any?) {
|
||||
try {
|
||||
Dispatch.setindex(this, table, key, value)
|
||||
} catch (err: UnresolvedControlThrowable) {
|
||||
throw RuntimeException("attempt to yield across C boundary", err)
|
||||
}
|
||||
}
|
||||
|
||||
fun ArgumentIterator.nextOptionalFloat(): Double? {
|
||||
if (hasNext()) {
|
||||
return nextFloat()
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
fun ArgumentIterator.nextOptionalInteger(): Long? {
|
||||
if (hasNext()) {
|
||||
return nextInteger()
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
operator fun Table.set(index: Any, value: Any?) {
|
||||
rawset(index, value)
|
||||
}
|
||||
|
||||
@Deprecated("Trying to set JSON to Lua table", level = DeprecationLevel.ERROR)
|
||||
operator fun Table.set(index: Any, value: JsonElement?) {
|
||||
rawset(index, value)
|
||||
}
|
||||
|
||||
operator fun Table.set(index: Long, value: Any?) {
|
||||
rawset(index, value)
|
||||
}
|
||||
|
||||
@Deprecated("Trying to set JSON to Lua table", level = DeprecationLevel.ERROR)
|
||||
operator fun Table.set(index: Long, value: JsonElement?) {
|
||||
rawset(index, value)
|
||||
}
|
||||
|
||||
operator fun Table.set(index: Int, value: Any?) {
|
||||
rawset(index.toLong(), value)
|
||||
}
|
||||
|
||||
@Deprecated("Trying to set JSON to Lua table", level = DeprecationLevel.ERROR)
|
||||
operator fun Table.set(index: Int, value: JsonElement?) {
|
||||
rawset(index.toLong(), value)
|
||||
}
|
||||
|
||||
operator fun Table.get(index: Any): Any? = rawget(index)
|
||||
operator fun Table.get(index: Long): Any? = rawget(index)
|
||||
operator fun Table.get(index: Int): Any? = rawget(index.toLong())
|
||||
|
||||
operator fun Table.contains(index: Any): Boolean {
|
||||
return rawget(index) != null
|
||||
}
|
||||
|
||||
operator fun Table.contains(index: Long): Boolean {
|
||||
return rawget(index) != null
|
||||
}
|
||||
|
||||
operator fun Table.iterator(): Iterator<Map.Entry<Any, Any>> {
|
||||
var key: Any? = initialKey() ?: return ObjectIterators.emptyIterator()
|
||||
data class Pair(override val key: Any, override val value: Any) : Map.Entry<Any, Any>
|
||||
|
||||
return object : Iterator<Map.Entry<Any, Any>> {
|
||||
override fun hasNext(): Boolean {
|
||||
return key != null
|
||||
}
|
||||
|
||||
override fun next(): Map.Entry<Any, Any> {
|
||||
val ikey = key ?: throw NoSuchElementException()
|
||||
val value = get(ikey)!!
|
||||
key = successorKeyOf(ikey)
|
||||
return Pair(ikey, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Table.spliterator(): Spliterator<Map.Entry<Any, Any>> {
|
||||
return Spliterators.spliteratorUnknownSize(iterator(), Spliterator.ORDERED)
|
||||
}
|
||||
|
||||
fun Table.stream(): Stream<Map.Entry<Any, Any>> {
|
||||
return StreamSupport.stream(spliterator(), false)
|
||||
}
|
||||
|
||||
/**
|
||||
* to be used in places where we need to "unpack" table, like this:
|
||||
*
|
||||
* ```lua
|
||||
* local array = unpack(tab)
|
||||
* ```
|
||||
*
|
||||
* except this function unpacks using rawget
|
||||
*/
|
||||
fun Table.unpackAsArray(): Array<Any?> {
|
||||
var min = Long.MAX_VALUE
|
||||
var max = 0L
|
||||
|
||||
for ((k, v) in this) {
|
||||
if (k is Long) {
|
||||
max = max(k, max)
|
||||
min = min(k, min)
|
||||
}
|
||||
}
|
||||
|
||||
val length = max - min
|
||||
|
||||
if (length <= 0L)
|
||||
return arrayOf()
|
||||
|
||||
val array = arrayOfNulls<Any>(length.toInt())
|
||||
var i2 = 0
|
||||
|
||||
for (i in min .. max) {
|
||||
array[i2++] = this[i]
|
||||
}
|
||||
|
||||
return array
|
||||
}
|
||||
|
||||
fun TableFactory.tableOf(vararg values: Any?): Table {
|
||||
val table = newTable(values.size, 0)
|
||||
|
||||
for ((i, v) in values.withIndex()) {
|
||||
table[i + 1L] = v
|
||||
}
|
||||
|
||||
return table
|
||||
}
|
||||
|
||||
fun TableFactory.tableOf(vararg values: Int): Table {
|
||||
val table = newTable(values.size, 0)
|
||||
|
||||
for ((i, v) in values.withIndex()) {
|
||||
table[i + 1L] = v.toLong()
|
||||
}
|
||||
|
||||
return table
|
||||
}
|
||||
|
||||
fun TableFactory.tableOf(vararg values: Long): Table {
|
||||
val table = newTable(values.size, 0)
|
||||
|
||||
for ((i, v) in values.withIndex()) {
|
||||
table[i + 1L] = v
|
||||
}
|
||||
|
||||
return table
|
||||
}
|
||||
|
||||
fun TableFactory.tableMapOf(vararg values: Pair<Any, Any?>): Table {
|
||||
val table = newTable(0, values.size)
|
||||
|
||||
for ((k, v) in values) {
|
||||
table[k] = v
|
||||
}
|
||||
|
||||
return table
|
||||
}
|
||||
|
||||
fun TableFactory.tableOf(): Table {
|
||||
return newTable()
|
||||
}
|
||||
|
||||
@Deprecated("Function is a stub")
|
||||
fun luaStub(message: String = "not yet implemented"): LuaFunction<Any?, Any?, Any?, Any?, Any?> {
|
||||
return object : LuaFunction<Any?, Any?, Any?, Any?, Any?>() {
|
||||
override fun resume(context: ExecutionContext?, suspendedState: Any?) {
|
||||
throw NonsuspendableFunctionException(this::class.java)
|
||||
}
|
||||
|
||||
override fun invoke(context: ExecutionContext?) {
|
||||
throw LuaRuntimeException("NYI: $message")
|
||||
}
|
||||
|
||||
override fun invoke(context: ExecutionContext?, arg1: Any?) {
|
||||
throw LuaRuntimeException("NYI: $message")
|
||||
}
|
||||
|
||||
override fun invoke(context: ExecutionContext?, arg1: Any?, arg2: Any?) {
|
||||
throw LuaRuntimeException("NYI: $message")
|
||||
}
|
||||
|
||||
override fun invoke(context: ExecutionContext?, arg1: Any?, arg2: Any?, arg3: Any?) {
|
||||
throw LuaRuntimeException("NYI: $message")
|
||||
}
|
||||
|
||||
override fun invoke(context: ExecutionContext?, arg1: Any?, arg2: Any?, arg3: Any?, arg4: Any?) {
|
||||
throw LuaRuntimeException("NYI: $message")
|
||||
}
|
||||
|
||||
override fun invoke(context: ExecutionContext?, arg1: Any?, arg2: Any?, arg3: Any?, arg4: Any?, arg5: Any?) {
|
||||
throw LuaRuntimeException("NYI: $message")
|
||||
}
|
||||
|
||||
override fun invoke(context: ExecutionContext?, args: Array<out Any>?) {
|
||||
throw LuaRuntimeException("NYI: $message")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun luaFunctionN(name: String, callable: ExecutionContext.(ArgumentIterator) -> Unit): LuaFunction<Any, Any, Any, Any, Any> {
|
||||
return object : AbstractFunctionAnyArg() {
|
||||
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
||||
throw NonsuspendableFunctionException(this::class.java)
|
||||
}
|
||||
|
||||
override fun invoke(context: ExecutionContext, args: Array<out Any>) {
|
||||
context.returnBuffer.setTo()
|
||||
|
||||
try {
|
||||
callable.invoke(context, ArgumentIterator.of(context, name, args))
|
||||
} catch (err: ClassCastException) {
|
||||
throw LuaRuntimeException(err)
|
||||
} catch (err: NullPointerException) {
|
||||
throw LuaRuntimeException(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun luaFunctionNS(name: String, callable: ExecutionContext.(ArgumentIterator) -> StateMachine): LuaFunction<Any, Any, Any, Any, Any> {
|
||||
return object : AbstractFunctionAnyArg() {
|
||||
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
||||
(suspendedState as StateMachine).run(context)
|
||||
}
|
||||
|
||||
override fun invoke(context: ExecutionContext, args: Array<out Any>) {
|
||||
context.returnBuffer.setTo()
|
||||
|
||||
try {
|
||||
callable.invoke(context, ArgumentIterator.of(context, name, args)).run(context)
|
||||
} catch (err: ClassCastException) {
|
||||
throw LuaRuntimeException(err)
|
||||
} catch (err: NullPointerException) {
|
||||
throw LuaRuntimeException(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun luaFunctionArray(callable: ExecutionContext.(Array<out Any?>) -> Unit): LuaFunction<Any, Any, Any, Any, Any> {
|
||||
return object : AbstractFunctionAnyArg() {
|
||||
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
||||
throw NonsuspendableFunctionException(this::class.java)
|
||||
}
|
||||
|
||||
override fun invoke(context: ExecutionContext, args: Array<out Any?>) {
|
||||
context.returnBuffer.setTo()
|
||||
|
||||
try {
|
||||
callable.invoke(context, args)
|
||||
} catch (err: ClassCastException) {
|
||||
throw LuaRuntimeException(err)
|
||||
} catch (err: NullPointerException) {
|
||||
throw LuaRuntimeException(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun luaFunction(callable: ExecutionContext.() -> Unit): LuaFunction<*, *, *, *, *> {
|
||||
return object : AbstractFunction0() {
|
||||
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
||||
throw NonsuspendableFunctionException(this::class.java)
|
||||
}
|
||||
|
||||
override fun invoke(context: ExecutionContext) {
|
||||
context.returnBuffer.setTo()
|
||||
|
||||
try {
|
||||
callable.invoke(context)
|
||||
} catch (err: ClassCastException) {
|
||||
throw LuaRuntimeException(err)
|
||||
} catch (err: NullPointerException) {
|
||||
throw LuaRuntimeException(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> luaFunction(callable: ExecutionContext.(T) -> Unit): LuaFunction<T, *, *, *, *> {
|
||||
return object : AbstractFunction1<T>() {
|
||||
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
||||
throw NonsuspendableFunctionException(this::class.java)
|
||||
}
|
||||
|
||||
override fun invoke(context: ExecutionContext, arg1: T) {
|
||||
context.returnBuffer.setTo()
|
||||
|
||||
try {
|
||||
callable.invoke(context, arg1)
|
||||
} catch (err: ClassCastException) {
|
||||
throw LuaRuntimeException(err)
|
||||
} catch (err: NullPointerException) {
|
||||
throw LuaRuntimeException(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T, T2> luaFunction(callable: ExecutionContext.(T, T2) -> Unit): LuaFunction<T, T2, *, *, *> {
|
||||
return object : AbstractFunction2<T, T2>() {
|
||||
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
||||
throw NonsuspendableFunctionException(this::class.java)
|
||||
}
|
||||
|
||||
override fun invoke(context: ExecutionContext, arg1: T, arg2: T2) {
|
||||
context.returnBuffer.setTo()
|
||||
|
||||
try {
|
||||
callable.invoke(context, arg1, arg2)
|
||||
} catch (err: ClassCastException) {
|
||||
throw LuaRuntimeException(err)
|
||||
} catch (err: NullPointerException) {
|
||||
throw LuaRuntimeException(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T, T2, T3> luaFunction(callable: ExecutionContext.(T, T2, T3) -> Unit): LuaFunction<T, T2, T3, *, *> {
|
||||
return object : AbstractFunction3<T, T2, T3>() {
|
||||
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
||||
throw NonsuspendableFunctionException(this::class.java)
|
||||
}
|
||||
|
||||
override fun invoke(context: ExecutionContext, arg1: T, arg2: T2, arg3: T3) {
|
||||
context.returnBuffer.setTo()
|
||||
|
||||
try {
|
||||
callable.invoke(context, arg1, arg2, arg3)
|
||||
} catch (err: ClassCastException) {
|
||||
throw LuaRuntimeException(err)
|
||||
} catch (err: NullPointerException) {
|
||||
throw LuaRuntimeException(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T, T2, T3, T4> luaFunction(callable: ExecutionContext.(T, T2, T3, T4) -> Unit): LuaFunction<T, T2, T3, T4, *> {
|
||||
return object : AbstractFunction4<T, T2, T3, T4>() {
|
||||
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
||||
throw NonsuspendableFunctionException(this::class.java)
|
||||
}
|
||||
|
||||
override fun invoke(context: ExecutionContext, arg1: T, arg2: T2, arg3: T3, arg4: T4) {
|
||||
context.returnBuffer.setTo()
|
||||
|
||||
try {
|
||||
callable.invoke(context, arg1, arg2, arg3, arg4)
|
||||
} catch (err: ClassCastException) {
|
||||
throw LuaRuntimeException(err)
|
||||
} catch (err: NullPointerException) {
|
||||
throw LuaRuntimeException(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,339 +0,0 @@
|
||||
package ru.dbotthepony.kstarbound.lua
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.classdump.luna.ByteString
|
||||
import org.classdump.luna.LuaObject
|
||||
import org.classdump.luna.LuaRuntimeException
|
||||
import org.classdump.luna.LuaType
|
||||
import org.classdump.luna.StateContext
|
||||
import org.classdump.luna.Table
|
||||
import org.classdump.luna.Variable
|
||||
import org.classdump.luna.compiler.CompilerChunkLoader
|
||||
import org.classdump.luna.compiler.CompilerSettings
|
||||
import org.classdump.luna.env.RuntimeEnvironments
|
||||
import org.classdump.luna.exec.CallPausedException
|
||||
import org.classdump.luna.exec.Continuation
|
||||
import org.classdump.luna.exec.DirectCallExecutor
|
||||
import org.classdump.luna.impl.DefaultTable
|
||||
import org.classdump.luna.impl.NonsuspendableFunctionException
|
||||
import org.classdump.luna.lib.BasicLib
|
||||
import org.classdump.luna.lib.CoroutineLib
|
||||
import org.classdump.luna.lib.MathLib
|
||||
import org.classdump.luna.lib.OsLib
|
||||
import org.classdump.luna.lib.StringLib
|
||||
import org.classdump.luna.lib.TableLib
|
||||
import org.classdump.luna.lib.Utf8Lib
|
||||
import org.classdump.luna.load.ChunkFactory
|
||||
import org.classdump.luna.runtime.AbstractFunction1
|
||||
import org.classdump.luna.runtime.Coroutine
|
||||
import org.classdump.luna.runtime.ExecutionContext
|
||||
import org.classdump.luna.runtime.LuaFunction
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.defs.AssetPath
|
||||
import ru.dbotthepony.kstarbound.lua.bindings.provideRootBindings
|
||||
import ru.dbotthepony.kstarbound.lua.bindings.provideUtilityBindings
|
||||
import ru.dbotthepony.kstarbound.util.random.random
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
|
||||
class LuaEnvironment : StateContext {
|
||||
private var nilMeta: Table? = null
|
||||
private var booleanMeta: Table? = null
|
||||
private var numberMeta: Table? = null
|
||||
private var stringMeta: Table? = null
|
||||
private var functionMeta: Table? = null
|
||||
private var threadMeta: Table? = null
|
||||
private var lightUserdataMeta: Table? = null
|
||||
|
||||
override fun getNilMetatable(): Table? {
|
||||
return nilMeta
|
||||
}
|
||||
|
||||
override fun getBooleanMetatable(): Table? {
|
||||
return booleanMeta
|
||||
}
|
||||
|
||||
override fun getNumberMetatable(): Table? {
|
||||
return numberMeta
|
||||
}
|
||||
|
||||
override fun getStringMetatable(): Table? {
|
||||
return stringMeta
|
||||
}
|
||||
|
||||
override fun getFunctionMetatable(): Table? {
|
||||
return functionMeta
|
||||
}
|
||||
|
||||
override fun getThreadMetatable(): Table? {
|
||||
return threadMeta
|
||||
}
|
||||
|
||||
override fun getLightUserdataMetatable(): Table? {
|
||||
return lightUserdataMeta
|
||||
}
|
||||
|
||||
override fun getMetatable(instance: Any?): Table? {
|
||||
if (instance is LuaObject)
|
||||
return instance.metatable
|
||||
|
||||
return when (val type = LuaType.typeOf(instance)!!) {
|
||||
LuaType.NIL -> nilMeta
|
||||
LuaType.BOOLEAN -> booleanMeta
|
||||
LuaType.NUMBER -> numberMeta
|
||||
LuaType.STRING -> stringMeta
|
||||
LuaType.FUNCTION -> functionMeta
|
||||
LuaType.USERDATA -> lightUserdataMeta
|
||||
LuaType.THREAD -> threadMeta
|
||||
else -> throw IllegalArgumentException("Illegal type: $type")
|
||||
}
|
||||
}
|
||||
|
||||
override fun setNilMetatable(table: Table?): Table? {
|
||||
val old = nilMeta; nilMeta = table; return old
|
||||
}
|
||||
|
||||
override fun setBooleanMetatable(table: Table?): Table? {
|
||||
val old = booleanMeta; booleanMeta = table; return old
|
||||
}
|
||||
|
||||
override fun setNumberMetatable(table: Table?): Table? {
|
||||
val old = numberMeta; numberMeta = table; return old
|
||||
}
|
||||
|
||||
override fun setStringMetatable(table: Table?): Table? {
|
||||
val old = stringMeta; stringMeta = table; return old
|
||||
}
|
||||
|
||||
override fun setFunctionMetatable(table: Table?): Table? {
|
||||
val old = functionMeta; functionMeta = table; return old
|
||||
}
|
||||
|
||||
override fun setThreadMetatable(table: Table?): Table? {
|
||||
val old = threadMeta; threadMeta = table; return old
|
||||
}
|
||||
|
||||
override fun setLightUserdataMetatable(table: Table?): Table? {
|
||||
val old = lightUserdataMeta; lightUserdataMeta = table; return old
|
||||
}
|
||||
|
||||
override fun setMetatable(instance: Any?, table: Table?): Table? {
|
||||
if (instance is LuaObject)
|
||||
return instance.setMetatable(table)
|
||||
else
|
||||
throw IllegalArgumentException("Can not set metatable of ${LuaType.typeOf(instance)}")
|
||||
}
|
||||
|
||||
override fun newTable(): Table {
|
||||
return DefaultTable()
|
||||
}
|
||||
|
||||
override fun newTable(array: Int, hash: Int): Table {
|
||||
return DefaultTable()
|
||||
}
|
||||
|
||||
val globals: Table = newTable()
|
||||
val executor: DirectCallExecutor = DirectCallExecutor.newExecutor()
|
||||
var random = random()
|
||||
|
||||
init {
|
||||
globals["_G"] = globals
|
||||
|
||||
globals["assert"] = BasicLib.assertFn()
|
||||
globals["error"] = BasicLib.error()
|
||||
globals["getmetatable"] = BasicLib.getmetatable()
|
||||
globals["ipairs"] = BasicLib.ipairs()
|
||||
globals["next"] = BasicLib.next()
|
||||
globals["pairs"] = BasicLib.pairs()
|
||||
globals["pcall"] = BasicLib.pcall()
|
||||
|
||||
globals["rawequal"] = BasicLib.rawequal()
|
||||
globals["rawget"] = BasicLib.rawget()
|
||||
globals["rawlen"] = BasicLib.rawlen()
|
||||
globals["rawset"] = BasicLib.rawset()
|
||||
globals["select"] = BasicLib.select()
|
||||
globals["setmetatable"] = BasicLib.setmetatable()
|
||||
globals["tostring"] = BasicLib.tostring()
|
||||
globals["tonumber"] = BasicLib.tonumber()
|
||||
globals["type"] = BasicLib.type()
|
||||
globals["_VERSION"] = BasicLib._VERSION
|
||||
globals["xpcall"] = BasicLib.xpcall()
|
||||
|
||||
globals["print"] = PrintFunction(globals)
|
||||
|
||||
// why not use _ENV anyway lol
|
||||
globals["self"] = newTable()
|
||||
|
||||
CoroutineLib.installInto(this, globals)
|
||||
TableLib.installInto(this, globals)
|
||||
MathLib.installInto(this, globals)
|
||||
StringLib.installInto(this, globals)
|
||||
OsLib.installInto(this, globals, RuntimeEnvironments.system())
|
||||
|
||||
val math = globals["math"] as Table
|
||||
|
||||
math["random"] = luaFunction { origin: Number?, bound: Number? ->
|
||||
if (origin == null && bound == null) {
|
||||
returnBuffer.setTo(random.nextDouble())
|
||||
} else if (bound == null) {
|
||||
val origin = origin!!.toLong()
|
||||
|
||||
if (origin > 1L) {
|
||||
returnBuffer.setTo(random.nextLong(1L, origin))
|
||||
} else if (origin == 1L) {
|
||||
returnBuffer.setTo(1L)
|
||||
} else {
|
||||
throw LuaRuntimeException("bad argument #1 to 'random' (interval is empty)")
|
||||
}
|
||||
} else {
|
||||
val origin = origin!!.toLong()
|
||||
val bound = bound.toLong()
|
||||
|
||||
if (bound > origin) {
|
||||
returnBuffer.setTo(random.nextLong(origin, bound))
|
||||
} else if (bound == origin) {
|
||||
returnBuffer.setTo(origin)
|
||||
} else {
|
||||
throw LuaRuntimeException("bad argument #1 to 'random' (interval is empty)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
math["randomseed"] = luaFunction { seed: Number ->
|
||||
random = random(seed.toLong())
|
||||
}
|
||||
|
||||
// TODO: NYI, maybe polyfill?
|
||||
Utf8Lib.installInto(this, globals)
|
||||
|
||||
// provideRootBindings(this)
|
||||
// provideUtilityBindings(this)
|
||||
}
|
||||
|
||||
private val scripts = ObjectArraySet<ChunkFactory>()
|
||||
private var initCalled = false
|
||||
private val loadedScripts = ObjectArraySet<String>()
|
||||
val require = LuaRequire()
|
||||
|
||||
inner class LuaRequire : AbstractFunction1<ByteString>() {
|
||||
override fun resume(context: ExecutionContext?, suspendedState: Any?) {
|
||||
throw NonsuspendableFunctionException(this::class.java)
|
||||
}
|
||||
|
||||
override fun invoke(context: ExecutionContext, arg1: ByteString) {
|
||||
val name = arg1.decode()
|
||||
|
||||
if (loadedScripts.add(name)) {
|
||||
val script = Starbound.loadScript(name)
|
||||
executor.call(this@LuaEnvironment, script.newInstance(Variable(globals)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
globals["require"] = require
|
||||
}
|
||||
|
||||
fun attach(script: ChunkFactory) {
|
||||
if (initCalled) {
|
||||
executor.call(this@LuaEnvironment, script.newInstance(Variable(globals)))
|
||||
} else {
|
||||
scripts.add(script)
|
||||
}
|
||||
}
|
||||
|
||||
fun attach(scripts: Collection<AssetPath>) {
|
||||
if (initCalled) {
|
||||
for (name in scripts) {
|
||||
if (loadedScripts.add(name.fullPath)) {
|
||||
val script = Starbound.loadScript(name.fullPath)
|
||||
executor.call(this@LuaEnvironment, script.newInstance(Variable(globals)))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (script in scripts) {
|
||||
if (loadedScripts.add(script.fullPath)) {
|
||||
this.scripts.add(Starbound.loadScript(script.fullPath))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun run(chunk: ChunkFactory): Array<out Any?> {
|
||||
return executor.call(this, chunk.newInstance(Variable(globals)))
|
||||
}
|
||||
|
||||
var errorState = false
|
||||
private set
|
||||
|
||||
fun markErrored() {
|
||||
errorState = true
|
||||
}
|
||||
|
||||
fun call(fn: Any, vararg args: Any?) = executor.call(this, fn, *args)
|
||||
|
||||
fun init(callInit: Boolean = true): Boolean {
|
||||
check(!initCalled) { "Already called init()" }
|
||||
initCalled = true
|
||||
|
||||
for (script in scripts) {
|
||||
try {
|
||||
executor.call(this, script.newInstance(Variable(globals)))
|
||||
} catch (err: Throwable) {
|
||||
// errorState = true
|
||||
LOGGER.error("Failed to attach script to environment", err)
|
||||
scripts.clear()
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
scripts.clear()
|
||||
|
||||
if (callInit) {
|
||||
val init = globals["init"]
|
||||
|
||||
if (init is LuaFunction<*, *, *, *, *>) {
|
||||
try {
|
||||
executor.call(this, init)
|
||||
} catch (err: Throwable) {
|
||||
// errorState = true
|
||||
LOGGER.error("Exception on init()", err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun invokeGlobal(name: String, vararg arguments: Any?): Array<Any?> {
|
||||
if (errorState || !initCalled)
|
||||
return arrayOf()
|
||||
|
||||
val load = globals[name]
|
||||
|
||||
if (load is LuaFunction<*, *, *, *, *>) {
|
||||
return try {
|
||||
executor.call(this, load, *arguments)
|
||||
} catch (err: Throwable) {
|
||||
// errorState = true
|
||||
LOGGER.error("Exception while calling global $name", err)
|
||||
arrayOf()
|
||||
}
|
||||
}
|
||||
|
||||
return arrayOf()
|
||||
}
|
||||
|
||||
private val loader by lazy { CompilerChunkLoader.of(CompilerSettings.defaultNoAccountingSettings(), "sb_lua${COUNTER.getAndIncrement()}_") }
|
||||
|
||||
// leaks memory until LuaEnvironment goes out of scope. Too bad!
|
||||
fun eval(chunk: String, name: String = "eval"): Array<Any?> {
|
||||
return executor.call(this, loader.compileTextChunk(chunk, name).newInstance(Variable(globals)))
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
private val COUNTER = AtomicLong()
|
||||
}
|
||||
}
|
@ -1,11 +1,12 @@
|
||||
package ru.dbotthepony.kstarbound.lua
|
||||
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import ru.dbotthepony.kstarbound.util.ActionPacer
|
||||
import ru.dbotthepony.kstarbound.util.sbIntern
|
||||
|
||||
class LuaMessageHandlerComponent(lua: LuaThread, val nameProvider: () -> String) {
|
||||
class LuaMessageHandlerComponent(val lua: LuaThread, val nameProvider: () -> String) {
|
||||
private val handlers = HashMap<String, LuaHandle>()
|
||||
|
||||
private fun setHandler(args: LuaThread.ArgStack): Int {
|
||||
@ -37,7 +38,7 @@ class LuaMessageHandlerComponent(lua: LuaThread, val nameProvider: () -> String)
|
||||
return handlers[name]
|
||||
}
|
||||
|
||||
inline fun handle(lua: LuaThread, message: String, isLocal: Boolean, arguments: LuaThread.() -> Int): JsonElement? {
|
||||
inline fun handle(message: String, isLocal: Boolean, arguments: LuaThread.() -> Int): JsonElement? {
|
||||
val handler = lookupHandler(message) ?: return null
|
||||
val top = lua.stackTop
|
||||
|
||||
@ -60,6 +61,18 @@ class LuaMessageHandlerComponent(lua: LuaThread, val nameProvider: () -> String)
|
||||
}
|
||||
}
|
||||
|
||||
fun handle(message: String, isLocal: Boolean, arguments: JsonArray): JsonElement? {
|
||||
return handle(message, isLocal) {
|
||||
ensureExtraCapacity(arguments.size() + 4)
|
||||
|
||||
for (argument in arguments) {
|
||||
push(argument)
|
||||
}
|
||||
|
||||
arguments.size()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val LOGGER = LogManager.getLogger()
|
||||
}
|
||||
|
@ -4,11 +4,12 @@ import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
|
||||
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.util.concurrent.ConcurrentLinkedQueue
|
||||
import java.util.random.RandomGenerator
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
class LuaSharedState(val handlesThread: LuaThread) {
|
||||
class LuaSharedState(val handlesThread: LuaThread) : Closeable {
|
||||
private val pendingFree = ConcurrentLinkedQueue<Int>()
|
||||
private val freeHandles = IntAVLTreeSet()
|
||||
private var nextHandle = 0
|
||||
@ -21,6 +22,15 @@ class LuaSharedState(val handlesThread: LuaThread) {
|
||||
var commonHandles by Delegates.notNull<CommonHandleRegistry>()
|
||||
private set
|
||||
|
||||
var isValid = true
|
||||
private set
|
||||
|
||||
override fun close() {
|
||||
if (!isValid) return
|
||||
isValid = false
|
||||
namedHandles.clear()
|
||||
}
|
||||
|
||||
fun initializeHandles(mainThread: LuaThread) {
|
||||
val future = LuaFuture.initializeHandle(mainThread)
|
||||
val pathFinder = LuaPathFinder.initializeHandle(mainThread)
|
||||
@ -32,6 +42,7 @@ class LuaSharedState(val handlesThread: LuaThread) {
|
||||
}
|
||||
|
||||
fun freeHandle(handle: Int, key: Any?) {
|
||||
if (!isValid) return
|
||||
pendingFree.add(handle)
|
||||
|
||||
if (key != null) {
|
||||
@ -40,6 +51,7 @@ class LuaSharedState(val handlesThread: LuaThread) {
|
||||
}
|
||||
|
||||
fun cleanup() {
|
||||
check(isValid) { "Shared state is no longer valid" }
|
||||
if (handlesInUse == 0) return
|
||||
var handle = pendingFree.poll()
|
||||
|
||||
@ -55,6 +67,7 @@ class LuaSharedState(val handlesThread: LuaThread) {
|
||||
}
|
||||
|
||||
fun allocateHandle(name: Any?): LuaHandle {
|
||||
check(isValid) { "Shared state is no longer valid" }
|
||||
require(name == null || name !in namedHandles) { "Named handle '$name' already exists" }
|
||||
handlesInUse++
|
||||
|
||||
@ -80,10 +93,12 @@ class LuaSharedState(val handlesThread: LuaThread) {
|
||||
}
|
||||
|
||||
fun getNamedHandle(key: Any): LuaHandle {
|
||||
check(isValid) { "Shared state is no longer valid" }
|
||||
return namedHandles[key] ?: throw NoSuchElementException("No such handle: $key")
|
||||
}
|
||||
|
||||
fun findNamedHandle(key: Any): LuaHandle? {
|
||||
check(isValid) { "Shared state is no longer valid" }
|
||||
return namedHandles[key]
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +140,10 @@ class LuaThread private constructor(
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
this.cleanable?.clean()
|
||||
if (cleanable != null) {
|
||||
cleanable!!.clean()
|
||||
sharedState.close()
|
||||
}
|
||||
}
|
||||
|
||||
val stackTop: Int get() {
|
||||
@ -232,6 +235,27 @@ class LuaThread private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns boolean indicating whenever function exists
|
||||
*/
|
||||
fun invokeGlobal(name: String, numArguments: Int): Boolean {
|
||||
val top = stackTop
|
||||
|
||||
try {
|
||||
val type = loadGlobal(name)
|
||||
|
||||
if (type != LuaType.FUNCTION) {
|
||||
pop(numArguments)
|
||||
return false
|
||||
}
|
||||
|
||||
call(numArguments)
|
||||
return true
|
||||
} finally {
|
||||
setTop(top)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns empty [KOptional] if function does not exist
|
||||
*/
|
||||
@ -279,12 +303,13 @@ class LuaThread private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun eval(chunk: String, name: String = "eval") {
|
||||
fun eval(chunk: String, name: String = "eval"): JsonElement {
|
||||
val top = stackTop
|
||||
|
||||
try {
|
||||
load(chunk, name)
|
||||
call()
|
||||
call(numResults = 1)
|
||||
return getJson() ?: JsonNull.INSTANCE
|
||||
} finally {
|
||||
setTop(top)
|
||||
}
|
||||
|
@ -1,63 +0,0 @@
|
||||
package ru.dbotthepony.kstarbound.lua
|
||||
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.classdump.luna.ByteString
|
||||
import org.classdump.luna.Conversions
|
||||
import org.classdump.luna.LuaRuntimeException
|
||||
import org.classdump.luna.runtime.AbstractFunctionAnyArg
|
||||
import org.classdump.luna.runtime.Dispatch
|
||||
import org.classdump.luna.runtime.ExecutionContext
|
||||
|
||||
class PrintFunction(val env: Any) : AbstractFunctionAnyArg() {
|
||||
private inner class State(arguments: Array<out Any>) {
|
||||
private val resolved = ArrayList<ByteString>(arguments.size)
|
||||
private var tostring: Any? = null
|
||||
|
||||
private val machine = StateMachine()
|
||||
|
||||
init {
|
||||
machine.add { Dispatch.index(it, env, "tostring") }
|
||||
machine.add {
|
||||
tostring = it.returnBuffer.get0()
|
||||
|
||||
if (tostring == null) {
|
||||
throw LuaRuntimeException("global 'tostring' is nil (required by 'print')")
|
||||
}
|
||||
}
|
||||
|
||||
for (argument in arguments) {
|
||||
machine.add { Dispatch.call(it, tostring, argument) }
|
||||
machine.add {
|
||||
val canonical = Conversions.canonicalRepresentationOf(it.returnBuffer.get0())
|
||||
|
||||
if (canonical is ByteString) {
|
||||
resolved.add(canonical)
|
||||
} else {
|
||||
throw LuaRuntimeException("Illegal value returned by 'tostring' (required by 'print')")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
machine.add {
|
||||
LOGGER.info(resolved.joinToString("\t") { it.decode() })
|
||||
it.returnBuffer.setTo()
|
||||
}
|
||||
}
|
||||
|
||||
fun run(context: ExecutionContext) {
|
||||
machine.run(context, this@PrintFunction, this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
||||
(suspendedState as State).run(context)
|
||||
}
|
||||
|
||||
override fun invoke(context: ExecutionContext, args: Array<out Any>) {
|
||||
State(args).run(context)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
}
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
package ru.dbotthepony.kstarbound.lua
|
||||
|
||||
import org.classdump.luna.LuaRuntimeException
|
||||
import org.classdump.luna.Table
|
||||
import org.classdump.luna.runtime.Dispatch
|
||||
import org.classdump.luna.runtime.ExecutionContext
|
||||
import org.classdump.luna.runtime.Resumable
|
||||
import org.classdump.luna.runtime.UnresolvedControlThrowable
|
||||
import ru.dbotthepony.kommons.util.KOptional
|
||||
import ru.dbotthepony.kstarbound.math.vector.Vector2i
|
||||
import java.util.function.Supplier
|
||||
|
||||
class StateMachine : Resumable {
|
||||
private val states = ArrayDeque<(ExecutionContext) -> Unit>()
|
||||
|
||||
fun add(state: (ExecutionContext) -> Unit): StateMachine {
|
||||
states.add(state)
|
||||
return this
|
||||
}
|
||||
|
||||
fun mergeInto(other: StateMachine): StateMachine {
|
||||
states.addAll(other.states)
|
||||
return other
|
||||
}
|
||||
|
||||
fun mergeFrom(other: StateMachine): StateMachine {
|
||||
other.mergeInto(this)
|
||||
return this
|
||||
}
|
||||
|
||||
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
||||
run(context, this, this)
|
||||
}
|
||||
|
||||
fun run(context: ExecutionContext): Boolean = run(context, this, this)
|
||||
|
||||
fun run(context: ExecutionContext, resumable: Resumable, suspendState: Any): Boolean {
|
||||
if (states.isEmpty())
|
||||
return false
|
||||
|
||||
while (states.isNotEmpty()) {
|
||||
val next = states.removeFirst()
|
||||
|
||||
try {
|
||||
next.invoke(context)
|
||||
} catch (err: UnresolvedControlThrowable) {
|
||||
throw err.resolve(resumable, suspendState)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun index(table: Table, vararg indexes: Any): Supplier<Any> {
|
||||
var value: KOptional<Any> = KOptional.empty()
|
||||
|
||||
for (index in indexes) {
|
||||
add {
|
||||
if (!value.isPresent) {
|
||||
value = KOptional.ofNullable(Dispatch.index(it, table, index))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Supplier {
|
||||
if (!value.isPresent) {
|
||||
throw LuaRuntimeException("Unable to locate value in table using following keys: ${indexes.joinToString()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun optionalIndex(table: Table, vararg indexes: Any): Supplier<KOptional<Any>> {
|
||||
var value: KOptional<Any> = KOptional.empty()
|
||||
|
||||
for (index in indexes) {
|
||||
add {
|
||||
if (!value.isPresent) {
|
||||
value = KOptional.ofNullable(Dispatch.index(it, table, index))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Supplier { value }
|
||||
}
|
||||
|
||||
fun loadVector2i(table: Table): Supplier<Vector2i> {
|
||||
val x = index(table, 1, "x")
|
||||
val y = index(table, 2, "y")
|
||||
var value = KOptional.empty<Vector2i>()
|
||||
|
||||
add {
|
||||
val ix = x.get()
|
||||
val iy = y.get()
|
||||
|
||||
if (ix !is Number) throw LuaRuntimeException("Invalid 'x' value for vector: ${x.get()}")
|
||||
if (iy !is Number) throw LuaRuntimeException("Invalid 'y' value for vector: ${x.get()}")
|
||||
|
||||
value = KOptional(Vector2i(ix.toInt(), iy.toInt()))
|
||||
}
|
||||
|
||||
return Supplier { value.value }
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ import ru.dbotthepony.kstarbound.world.entities.AnchorNetworkState
|
||||
import ru.dbotthepony.kstarbound.world.physics.Poly
|
||||
import kotlin.math.PI
|
||||
|
||||
class MovementControllerBindings(val self: ActorMovementController) {
|
||||
class MovementControllerBindings(val self: ActorMovementController, lua: LuaThread) {
|
||||
private fun mass(args: LuaThread.ArgStack): Int {
|
||||
args.lua.push(self.mass)
|
||||
return 1
|
||||
@ -303,7 +303,7 @@ class MovementControllerBindings(val self: ActorMovementController) {
|
||||
return 0
|
||||
}
|
||||
|
||||
fun init(lua: LuaThread) {
|
||||
init {
|
||||
lua.pushTable()
|
||||
lua.dup()
|
||||
lua.storeGlobal("mcontroller")
|
||||
|
@ -4,7 +4,6 @@ import com.google.gson.JsonNull
|
||||
import com.google.gson.JsonObject
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.classdump.luna.LuaRuntimeException
|
||||
import ru.dbotthepony.kstarbound.Globals
|
||||
import ru.dbotthepony.kstarbound.Registries
|
||||
import ru.dbotthepony.kstarbound.Registry
|
||||
@ -36,13 +35,11 @@ private fun <T : Any> lookup(registry: Registry<T>, args: LuaThread.ArgStack): R
|
||||
private fun <T : Any> lookupStrict(registry: Registry<T>, args: LuaThread.ArgStack): Registry.Entry<T> {
|
||||
return when (val type = args.peek()) {
|
||||
LuaType.NUMBER -> {
|
||||
val key = args.nextInt()
|
||||
registry[key] ?: throw LuaRuntimeException("No such ${registry.name}: $key")
|
||||
registry.getOrThrow(args.nextInt())
|
||||
}
|
||||
|
||||
LuaType.STRING -> {
|
||||
val key = args.nextString()
|
||||
registry[key] ?: throw LuaRuntimeException("No such ${registry.name}: $key")
|
||||
registry.getOrThrow(args.nextString())
|
||||
}
|
||||
|
||||
else -> throw IllegalArgumentException("Invalid registry key type: $type")
|
||||
@ -51,8 +48,7 @@ private fun <T : Any> lookupStrict(registry: Registry<T>, args: LuaThread.ArgSta
|
||||
|
||||
private fun registryDef(registry: Registry<*>): LuaThread.Fn {
|
||||
return LuaThread.Fn { args ->
|
||||
val name = args.nextString()
|
||||
val value = registry[name] ?: throw LuaRuntimeException("No such ${registry.name}: $name")
|
||||
val value = registry.getOrThrow(args.nextString())
|
||||
args.lua.push(value.json)
|
||||
return@Fn 1
|
||||
}
|
||||
@ -252,7 +248,7 @@ private fun createItem(args: LuaThread.ArgStack): Int {
|
||||
val seed = args.nextOptionalLong()
|
||||
|
||||
if (desc.name !in ItemRegistry) {
|
||||
throw LuaRuntimeException("No such item ${desc.name}")
|
||||
throw NoSuchElementException("No such item ${desc.name}")
|
||||
} else {
|
||||
val (_, params) = desc.buildConfig(level, seed)
|
||||
|
||||
@ -348,7 +344,7 @@ private fun loadVersionedJson(args: LuaThread.ArgStack): Int {
|
||||
private fun evalFunction(args: LuaThread.ArgStack): Int {
|
||||
val name = args.nextString()
|
||||
val value = args.nextDouble()
|
||||
val fn = Registries.jsonFunctions[name] ?: throw LuaRuntimeException("No such function $name")
|
||||
val fn = Registries.jsonFunctions[name] ?: throw NoSuchElementException("No such function $name")
|
||||
args.lua.push(fn.value.evaluate(value))
|
||||
return 1
|
||||
}
|
||||
@ -357,7 +353,7 @@ private fun evalFunction2(args: LuaThread.ArgStack): Int {
|
||||
val name = args.nextString()
|
||||
val value = args.nextDouble()
|
||||
val value2 = args.nextDouble()
|
||||
val fn = Registries.json2Functions[name] ?: throw LuaRuntimeException("No such function $name")
|
||||
val fn = Registries.json2Functions[name] ?: throw NoSuchElementException("No such function $name")
|
||||
args.lua.push(fn.value.evaluate(value, value2))
|
||||
return 1
|
||||
}
|
||||
@ -365,7 +361,7 @@ private fun evalFunction2(args: LuaThread.ArgStack): Int {
|
||||
private fun imageSize(args: LuaThread.ArgStack): Int {
|
||||
val name = args.nextString()
|
||||
val ref = SpriteReference.create(name)
|
||||
val sprite = ref.sprite ?: throw LuaRuntimeException("No such image or sprite $ref")
|
||||
val sprite = ref.sprite ?: throw NoSuchElementException("No such image or sprite $ref")
|
||||
args.lua.pushTable(2)
|
||||
args.lua.setTableValue(1, sprite.width)
|
||||
args.lua.setTableValue(2, sprite.height)
|
||||
@ -374,7 +370,7 @@ private fun imageSize(args: LuaThread.ArgStack): Int {
|
||||
|
||||
private fun imageSpaces(args: LuaThread.ArgStack): Int {
|
||||
val name = args.nextString()
|
||||
val image = Image.get(name) ?: throw LuaRuntimeException("No such image $name")
|
||||
val image = Image.get(name) ?: throw NoSuchElementException("No such image $name")
|
||||
|
||||
val pixelOffset = args.nextVector2i()
|
||||
val fillFactor = args.nextDouble()
|
||||
@ -394,7 +390,7 @@ private fun imageSpaces(args: LuaThread.ArgStack): Int {
|
||||
|
||||
private fun nonEmptyRegion(args: LuaThread.ArgStack): Int {
|
||||
val name = args.nextString()
|
||||
val image = Image.get(name) ?: throw LuaRuntimeException("No such image $name")
|
||||
val image = Image.get(name) ?: throw NoSuchElementException("No such image $name")
|
||||
args.lua.push(image.nonEmptyRegion)
|
||||
return 1
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package ru.dbotthepony.kstarbound.lua.bindings
|
||||
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.classdump.luna.LuaRuntimeException
|
||||
import ru.dbotthepony.kommons.util.KOptional
|
||||
import ru.dbotthepony.kstarbound.Registries
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
@ -18,7 +17,6 @@ import ru.dbotthepony.kstarbound.lua.nextAABBi
|
||||
import ru.dbotthepony.kstarbound.lua.nextVector2d
|
||||
import ru.dbotthepony.kstarbound.lua.nextVector2i
|
||||
import ru.dbotthepony.kstarbound.lua.push
|
||||
import ru.dbotthepony.kstarbound.lua.toJsonFromLua
|
||||
import ru.dbotthepony.kstarbound.lua.userdata.LuaFuture
|
||||
import ru.dbotthepony.kstarbound.lua.userdata.push
|
||||
import ru.dbotthepony.kstarbound.server.world.ServerWorld
|
||||
@ -239,10 +237,7 @@ private fun players(self: ServerWorld, args: LuaThread.ArgStack): Int {
|
||||
|
||||
private fun setSkyTime(self: ServerWorld, args: LuaThread.ArgStack): Int {
|
||||
val time = args.nextDouble()
|
||||
|
||||
if (time < 0.0)
|
||||
throw LuaRuntimeException("Negative time? $time")
|
||||
|
||||
require(time >= 0.0) { "Negative time? $time" }
|
||||
self.sky.time = time
|
||||
return 0
|
||||
}
|
||||
@ -301,7 +296,7 @@ private fun setDungeonGravity(self: ServerWorld, args: LuaThread.ArgStack): Int
|
||||
} else if (peek == LuaType.TABLE) {
|
||||
self.setDungeonGravity(id, args.nextVector2d())
|
||||
} else {
|
||||
throw LuaRuntimeException("Illegal gravity argument to setDungeonGravity: $peek")
|
||||
throw IllegalArgumentException("Illegal gravity argument to setDungeonGravity: $peek")
|
||||
}
|
||||
|
||||
return 0
|
||||
@ -336,7 +331,7 @@ private fun enqueuePlacement(self: ServerWorld, args: LuaThread.ArgStack): Int {
|
||||
|
||||
for (v in distributions) {
|
||||
// original engine treats distributions table as if it was originating from biome json files
|
||||
val unprepared = Starbound.gson.fromJson(toJsonFromLua(v), BiomePlaceablesDefinition.DistributionItem::class.java)
|
||||
val unprepared = Starbound.gson.fromJson(v, BiomePlaceablesDefinition.DistributionItem::class.java)
|
||||
val prepared = unprepared.create(BiomeDefinition.CreationParams(hueShift = 0.0, random = args.lua.random))
|
||||
items.add(prepared)
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ import ru.dbotthepony.kstarbound.json.mergeJson
|
||||
import ru.dbotthepony.kstarbound.lua.LuaHandle
|
||||
import ru.dbotthepony.kstarbound.lua.LuaThread
|
||||
import ru.dbotthepony.kstarbound.lua.LuaType
|
||||
import ru.dbotthepony.kstarbound.lua.toJsonFromLua
|
||||
import ru.dbotthepony.kstarbound.util.valueOf
|
||||
|
||||
private fun replaceBehaviorTag(parameter: NodeParameterValue, treeParameters: Map<String, NodeParameterValue>): NodeParameterValue {
|
||||
@ -246,7 +245,7 @@ private fun createBehaviorTree(args: LuaThread.ArgStack): Int {
|
||||
} else {
|
||||
mergedParams = Starbound.gson.fromJsonFast(base.json.deepCopy().also {
|
||||
it as JsonObject
|
||||
it["parameters"] = mergeJson(it["parameters"] ?: JsonNull.INSTANCE, toJsonFromLua(parameters))
|
||||
it["parameters"] = mergeJson(it["parameters"] ?: JsonNull.INSTANCE, parameters)
|
||||
}, BehaviorDefinition::class.java)
|
||||
}
|
||||
} else {
|
||||
|
@ -5,8 +5,6 @@ import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.future.await
|
||||
import kotlinx.coroutines.launch
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.classdump.luna.ByteString
|
||||
import ru.dbotthepony.kstarbound.lua.tableMapOf
|
||||
import ru.dbotthepony.kstarbound.math.vector.Vector2i
|
||||
import ru.dbotthepony.kstarbound.world.entities.tile.WorldObject
|
||||
|
||||
@ -99,7 +97,10 @@ class LegacyWireProcessor(val world: ServerWorld) {
|
||||
if (newState != node.state) {
|
||||
try {
|
||||
node.state = newState
|
||||
entity.lua.invokeGlobal("onInputNodeChange", entity.lua.tableMapOf(NODE_KEY to i.toLong(), LEVEL_KEY to newState))
|
||||
entity.lua.pushTable(hashSize = 2)
|
||||
entity.lua.setTableValue("node", i)
|
||||
entity.lua.setTableValue("level", newState)
|
||||
entity.lua.invokeGlobal("onInputNodeChange", 1)
|
||||
} catch (err: Throwable) {
|
||||
LOGGER.error("Exception while updating wire state of $entity at ${entity.tilePosition} (input node index $i)", err)
|
||||
}
|
||||
@ -159,8 +160,6 @@ class LegacyWireProcessor(val world: ServerWorld) {
|
||||
}
|
||||
|
||||
companion object {
|
||||
val NODE_KEY: ByteString = ByteString.of("node")
|
||||
val LEVEL_KEY: ByteString = ByteString.of("level")
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
}
|
||||
}
|
||||
|
@ -215,6 +215,8 @@ class ServerWorld private constructor(
|
||||
it.client.enqueueWarp(WarpAlias.Return)
|
||||
}
|
||||
|
||||
callUninitOnEntities()
|
||||
|
||||
if (!uncleanShutdown) {
|
||||
saveMetadata()
|
||||
storage.commit()
|
||||
|
@ -4,7 +4,6 @@ import com.google.common.collect.ImmutableCollection
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import it.unimi.dsi.fastutil.bytes.ByteConsumer
|
||||
import org.classdump.luna.ByteString
|
||||
import ru.dbotthepony.kommons.util.IStruct2d
|
||||
import ru.dbotthepony.kommons.util.IStruct2f
|
||||
import ru.dbotthepony.kommons.util.IStruct2i
|
||||
@ -76,7 +75,6 @@ fun staticRandom32FromList(values: Iterable<Any?>): Int {
|
||||
for (value in values) {
|
||||
when (value) {
|
||||
is String -> digest.update(value.toByteArray())
|
||||
is ByteString -> digest.update(value.bytes)
|
||||
is Byte -> digest.update(value)
|
||||
is Boolean -> digest.update(if (value) 1 else 0)
|
||||
is Short -> toBytes(digest::update, value)
|
||||
@ -134,7 +132,6 @@ fun staticRandom64FromList(values: Iterable<Any?>): Long {
|
||||
for (value in values) {
|
||||
when (value) {
|
||||
is String -> digest.update(value.toByteArray())
|
||||
is ByteString -> digest.update(value.bytes)
|
||||
is Byte -> digest.update(value)
|
||||
is Boolean -> digest.update(if (value) 1 else 0)
|
||||
is Short -> toBytes(digest::update, value)
|
||||
|
@ -566,6 +566,16 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
||||
|
||||
}
|
||||
|
||||
protected fun callUninitOnEntities() {
|
||||
entityList.forEach {
|
||||
try {
|
||||
it.uninit(this)
|
||||
} catch (err: Throwable) {
|
||||
LOGGER.error("Exception while calling uninit on $it", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open var centralStructure: WorldStructure = WorldStructure()
|
||||
protected set
|
||||
|
||||
|
@ -175,10 +175,21 @@ abstract class AbstractEntity : Comparable<AbstractEntity> {
|
||||
protected open fun onJoinWorld(world: World<*, *>) { }
|
||||
|
||||
/**
|
||||
* Called upon being removed from [world], regardless if entity is local or not
|
||||
* Called upon being removed from [world], regardless if entity is local or not,
|
||||
* but **not** when world is being unloaded from memory ([uninit] should be used in latter case)
|
||||
*/
|
||||
protected open fun onRemove(world: World<*, *>, reason: RemovalReason) { }
|
||||
|
||||
/**
|
||||
* Called upon being removed from [world], regardless if entity is local or not,
|
||||
* including when world is being unloaded from memory
|
||||
*
|
||||
* When being removed from world, called right after [onRemove]
|
||||
*
|
||||
* Should be used to free native data structures, or remove data from global structures
|
||||
*/
|
||||
open fun uninit(world: World<*, *>) { }
|
||||
|
||||
val networkGroup = MasterElement(NetworkedGroup())
|
||||
abstract fun writeNetwork(stream: DataOutputStream, isLegacy: Boolean)
|
||||
|
||||
@ -286,6 +297,12 @@ abstract class AbstractEntity : Comparable<AbstractEntity> {
|
||||
LOGGER.error("Exception while removing $this from $world", err)
|
||||
}
|
||||
|
||||
try {
|
||||
uninit(world)
|
||||
} catch (err: Throwable) {
|
||||
LOGGER.error("Exception while calling uninit on $this from $world", err)
|
||||
}
|
||||
|
||||
spatialEntry?.remove()
|
||||
spatialEntry = null
|
||||
innerWorld = null
|
||||
|
@ -2,23 +2,15 @@ package ru.dbotthepony.kstarbound.world.entities
|
||||
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import org.classdump.luna.ByteString
|
||||
import org.classdump.luna.Table
|
||||
import ru.dbotthepony.kommons.util.IStruct2d
|
||||
import ru.dbotthepony.kstarbound.defs.DamageNotification
|
||||
import ru.dbotthepony.kstarbound.defs.DamageSource
|
||||
import com.google.gson.JsonNull
|
||||
import com.google.gson.JsonPrimitive
|
||||
import ru.dbotthepony.kstarbound.defs.Drawable
|
||||
import ru.dbotthepony.kstarbound.defs.InteractAction
|
||||
import ru.dbotthepony.kstarbound.defs.InteractRequest
|
||||
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
||||
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
||||
import ru.dbotthepony.kstarbound.lua.from
|
||||
import ru.dbotthepony.kstarbound.lua.get
|
||||
import ru.dbotthepony.kstarbound.lua.tableMapOf
|
||||
import ru.dbotthepony.kstarbound.lua.toJsonFromLua
|
||||
import ru.dbotthepony.kstarbound.lua.setTableValue
|
||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.network.packets.DamageNotificationPacket
|
||||
import ru.dbotthepony.kstarbound.network.packets.DamageRequestPacket
|
||||
import ru.dbotthepony.kstarbound.network.packets.HitRequestPacket
|
||||
import ru.dbotthepony.kstarbound.world.Direction
|
||||
import ru.dbotthepony.kstarbound.world.World
|
||||
@ -35,8 +27,6 @@ abstract class ActorEntity : DynamicEntity(), InteractiveEntity, ScriptedEntity
|
||||
// ticked manually in final classes, to ensure proper order of operations
|
||||
val effects = EffectEmitter(this)
|
||||
|
||||
abstract val lua: LuaEnvironment
|
||||
|
||||
override fun move(delta: Double) {
|
||||
statusController.applyMovementControls()
|
||||
super.move(delta)
|
||||
@ -66,6 +56,11 @@ abstract class ActorEntity : DynamicEntity(), InteractiveEntity, ScriptedEntity
|
||||
statusController.init()
|
||||
}
|
||||
|
||||
override fun uninit(world: World<*, *>) {
|
||||
super.uninit(world)
|
||||
statusController.uninit()
|
||||
}
|
||||
|
||||
override fun tick(delta: Double) {
|
||||
super.tick(delta)
|
||||
|
||||
@ -114,31 +109,34 @@ abstract class ActorEntity : DynamicEntity(), InteractiveEntity, ScriptedEntity
|
||||
}
|
||||
|
||||
override fun callScript(fnName: String, arguments: JsonArray): JsonElement {
|
||||
//require(isLocal) { "Calling script on remote entity" }
|
||||
//return lua.invokeGlobal(fnName, *arguments)
|
||||
TODO()
|
||||
require(isLocal) { "Calling script on remote entity" }
|
||||
return super.callScript(fnName, arguments)
|
||||
}
|
||||
|
||||
override fun evalScript(code: String): Array<Any?> {
|
||||
override fun evalScript(code: String): JsonElement {
|
||||
require(isLocal) { "Calling script on remote entity" }
|
||||
return lua.eval(code)
|
||||
return super.evalScript(code)
|
||||
}
|
||||
|
||||
override fun interact(request: InteractRequest): InteractAction {
|
||||
val result = lua.invokeGlobal("interact", lua.tableMapOf(
|
||||
"sourceId" to request.source,
|
||||
"sourcePosition" to lua.from(request.sourcePos)
|
||||
))
|
||||
val result = lua.invokeGlobal("interact", 1, {
|
||||
pushTable(hashSize = 2)
|
||||
|
||||
if (result.isEmpty() || result[0] == null)
|
||||
setTableValue("sourcePosition", request.sourcePos)
|
||||
setTableValue("sourceId", request.source)
|
||||
|
||||
1
|
||||
}, { getJson() ?: JsonNull.INSTANCE })
|
||||
|
||||
if (result.isEmpty || result.value.isJsonNull)
|
||||
return InteractAction.NONE
|
||||
|
||||
val value = result[0]
|
||||
val value = result.value
|
||||
|
||||
if (value is ByteString)
|
||||
return InteractAction(value.decode(), entityID)
|
||||
if (value is JsonPrimitive)
|
||||
return InteractAction(value.asString, entityID)
|
||||
|
||||
value as Table
|
||||
return InteractAction((value[1L] as ByteString).decode(), entityID, toJsonFromLua(value[2L]))
|
||||
value as JsonArray
|
||||
return InteractAction(value[1].asString, entityID, if (value.size() >= 2) value[2] else JsonNull.INSTANCE)
|
||||
}
|
||||
}
|
||||
|
@ -21,14 +21,14 @@ abstract class DynamicEntity() : AbstractEntity() {
|
||||
abstract val movement: MovementController
|
||||
|
||||
/**
|
||||
* Called in multiple threads
|
||||
* Called in multiple threads, when [isLocal] is true
|
||||
*/
|
||||
protected open fun move(delta: Double) {
|
||||
movement.move(delta)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called in multiple threads
|
||||
* Called in multiple threads, when [isLocal] is false
|
||||
*/
|
||||
protected open fun moveRemote(delta: Double) {
|
||||
movement.tickRemote(delta)
|
||||
|
@ -8,11 +8,8 @@ import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonNull
|
||||
import com.google.gson.JsonObject
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||
import org.classdump.luna.ByteString
|
||||
import org.classdump.luna.Table
|
||||
import ru.dbotthepony.kommons.gson.get
|
||||
import ru.dbotthepony.kommons.gson.set
|
||||
import ru.dbotthepony.kstarbound.io.map
|
||||
import ru.dbotthepony.kommons.util.Either
|
||||
import ru.dbotthepony.kommons.util.getValue
|
||||
import ru.dbotthepony.kommons.util.setValue
|
||||
@ -26,8 +23,6 @@ import ru.dbotthepony.kstarbound.defs.DamageSource
|
||||
import ru.dbotthepony.kstarbound.defs.EntityDamageTeam
|
||||
import ru.dbotthepony.kstarbound.defs.EntityType
|
||||
import ru.dbotthepony.kstarbound.defs.HitType
|
||||
import ru.dbotthepony.kstarbound.defs.InteractAction
|
||||
import ru.dbotthepony.kstarbound.defs.InteractRequest
|
||||
import ru.dbotthepony.kstarbound.defs.JumpProfile
|
||||
import ru.dbotthepony.kstarbound.defs.PhysicsForceRegion
|
||||
import ru.dbotthepony.kstarbound.defs.actor.StatModifier
|
||||
@ -38,21 +33,17 @@ import ru.dbotthepony.kstarbound.defs.monster.MonsterVariant
|
||||
import ru.dbotthepony.kstarbound.fromJsonFast
|
||||
import ru.dbotthepony.kstarbound.io.DoubleValueCodec
|
||||
import ru.dbotthepony.kstarbound.io.FloatValueCodec
|
||||
import ru.dbotthepony.kstarbound.io.map
|
||||
import ru.dbotthepony.kstarbound.io.nullable
|
||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
||||
import ru.dbotthepony.kstarbound.lua.LuaMessageHandlerComponent
|
||||
import ru.dbotthepony.kstarbound.lua.LuaThread
|
||||
import ru.dbotthepony.kstarbound.lua.LuaUpdateComponent
|
||||
import ru.dbotthepony.kstarbound.lua.bindings.MovementControllerBindings
|
||||
import ru.dbotthepony.kstarbound.lua.bindings.provideAnimatorBindings
|
||||
import ru.dbotthepony.kstarbound.lua.bindings.provideConfigBindings
|
||||
import ru.dbotthepony.kstarbound.lua.bindings.provideConfigBinding
|
||||
import ru.dbotthepony.kstarbound.lua.bindings.provideEntityBindings
|
||||
import ru.dbotthepony.kstarbound.lua.from
|
||||
import ru.dbotthepony.kstarbound.lua.get
|
||||
import ru.dbotthepony.kstarbound.lua.set
|
||||
import ru.dbotthepony.kstarbound.lua.tableMapOf
|
||||
import ru.dbotthepony.kstarbound.lua.toJsonFromLua
|
||||
import ru.dbotthepony.kstarbound.lua.userdata.BehaviorState
|
||||
import ru.dbotthepony.kstarbound.lua.userdata.provideBehaviorBindings
|
||||
import ru.dbotthepony.kstarbound.math.AABB
|
||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.network.packets.DamageRequestPacket
|
||||
@ -70,9 +61,9 @@ import ru.dbotthepony.kstarbound.util.random.MWCRandom
|
||||
import ru.dbotthepony.kstarbound.util.random.random
|
||||
import ru.dbotthepony.kstarbound.world.Direction
|
||||
import ru.dbotthepony.kstarbound.world.World
|
||||
import ru.dbotthepony.kstarbound.world.entities.api.ScriptedEntity
|
||||
import ru.dbotthepony.kstarbound.world.physics.Poly
|
||||
import java.io.DataOutputStream
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
class MonsterEntity(val variant: MonsterVariant, level: Double? = null) : ActorEntity() {
|
||||
override val type: EntityType
|
||||
@ -82,16 +73,17 @@ class MonsterEntity(val variant: MonsterVariant, level: Double? = null) : ActorE
|
||||
team.accept(EntityDamageTeam(variant.commonParameters.damageTeamType, variant.commonParameters.damageTeam))
|
||||
}
|
||||
|
||||
override val lua = LuaEnvironment()
|
||||
val luaUpdate = LuaUpdateComponent(lua)
|
||||
val luaMovement = MovementControllerBindings(movement)
|
||||
val luaMessages = LuaMessageHandlerComponent(lua) { toString() }
|
||||
private var scriptStorage = JsonObject()
|
||||
override var lua by Delegates.notNull<LuaThread>()
|
||||
private set
|
||||
var luaUpdate by Delegates.notNull<LuaUpdateComponent>()
|
||||
private set
|
||||
var luaMovement by Delegates.notNull<MovementControllerBindings>()
|
||||
private set
|
||||
var luaMessages by Delegates.notNull<LuaMessageHandlerComponent>()
|
||||
private set
|
||||
val animator = Animator(variant.animationConfig)
|
||||
|
||||
init {
|
||||
lua.globals["storage"] = lua.newTable()
|
||||
}
|
||||
|
||||
override fun move(delta: Double) {
|
||||
luaMovement.apply()
|
||||
super.move(delta)
|
||||
@ -169,7 +161,7 @@ class MonsterEntity(val variant: MonsterVariant, level: Double? = null) : ActorE
|
||||
this.uniqueID.value = deserialized.uniqueId
|
||||
this.team.value = deserialized.team
|
||||
|
||||
this.lua.globals["storage"] = lua.from(deserialized.scriptStorage)
|
||||
scriptStorage = deserialized.scriptStorage as? JsonObject ?: JsonObject()
|
||||
}
|
||||
|
||||
override fun serialize(data: JsonObject) {
|
||||
@ -188,7 +180,10 @@ class MonsterEntity(val variant: MonsterVariant, level: Double? = null) : ActorE
|
||||
uniqueID.value,
|
||||
team.value,
|
||||
effectEmitter = effects.serialize(),
|
||||
scriptStorage = toJsonFromLua(lua.globals["storage"]),
|
||||
scriptStorage = run {
|
||||
lua.loadGlobal("storage")
|
||||
lua.popJson() ?: JsonObject()
|
||||
},
|
||||
)
|
||||
|
||||
for ((k, v) in Starbound.gson.toJsonTree(moreData).asJsonObject.entrySet()) {
|
||||
@ -263,24 +258,41 @@ class MonsterEntity(val variant: MonsterVariant, level: Double? = null) : ActorE
|
||||
|
||||
monsterLevel = monsterLevel ?: world.template.threatLevel
|
||||
|
||||
if (!isRemote) {
|
||||
if (isLocal) {
|
||||
val healthMultiplier = (variant.commonParameters.healthLevelFunction.value?.evaluate(monsterLevel!!) ?: 1.0) * variant.commonParameters.healthMultiplier
|
||||
statusController.setPersistentEffects("innate", listOf(Either.left(StatModifier("maxHealth", healthMultiplier, StatModifierType.BASE_MULTIPLICATION))))
|
||||
|
||||
lua = LuaThread()
|
||||
luaUpdate = LuaUpdateComponent(lua, this)
|
||||
luaMovement = MovementControllerBindings(movement, lua)
|
||||
luaMessages = LuaMessageHandlerComponent(lua) { toString() }
|
||||
|
||||
lua.push(scriptStorage)
|
||||
lua.storeGlobal("storage")
|
||||
// free up memory
|
||||
scriptStorage = JsonObject()
|
||||
|
||||
provideEntityBindings(this, lua)
|
||||
provideAnimatorBindings(animator, lua)
|
||||
|
||||
provideConfigBindings(lua) { key, default ->
|
||||
key.find(variant.parameters) ?: default
|
||||
provideConfigBinding(lua) { key ->
|
||||
key.find(variant.parameters)
|
||||
}
|
||||
|
||||
lua.attach(variant.commonParameters.scripts)
|
||||
luaUpdate.stepCount = variant.commonParameters.initialScriptDelta
|
||||
|
||||
BehaviorState.provideBindings(lua)
|
||||
provideBehaviorBindings(lua)
|
||||
|
||||
luaMovement.init(lua)
|
||||
lua.init()
|
||||
lua.initScripts()
|
||||
}
|
||||
}
|
||||
|
||||
override fun uninit(world: World<*, *>) {
|
||||
super.uninit(world)
|
||||
|
||||
if (isLocal) {
|
||||
lua.close()
|
||||
}
|
||||
}
|
||||
|
||||
@ -320,12 +332,13 @@ class MonsterEntity(val variant: MonsterVariant, level: Double? = null) : ActorE
|
||||
|
||||
// TODO: hitDamageNotificationLimiter++ < Globals.npcs.hitDamageNotificationLimit maybe?
|
||||
if (totalDamage > 0.0) {
|
||||
lua.invokeGlobal("damage", lua.tableMapOf(
|
||||
"sourceId" to damage.request.sourceEntityId,
|
||||
"damage" to totalDamage,
|
||||
"sourceDamage" to damage.request.damage,
|
||||
"sourceKind" to damage.request.damageSourceKind
|
||||
))
|
||||
lua.pushTable(hashSize = 4)
|
||||
lua.setTableValue("sourceId", damage.request.sourceEntityId)
|
||||
lua.setTableValue("damage", totalDamage)
|
||||
lua.setTableValue("sourceDamage", damage.request.damage)
|
||||
lua.setTableValue("sourceKind", damage.request.damageSourceKind)
|
||||
|
||||
lua.invokeGlobal("damage", 1)
|
||||
}
|
||||
|
||||
if (health <= 0.0) {
|
||||
@ -336,8 +349,8 @@ class MonsterEntity(val variant: MonsterVariant, level: Double? = null) : ActorE
|
||||
}
|
||||
|
||||
private val shouldDie: Boolean get() {
|
||||
val result = lua.invokeGlobal("shouldDie")
|
||||
return result.isNotEmpty() && result[0] is Boolean && result[0] as Boolean || health <= 0.0 //|| lua.errorState
|
||||
val result = lua.invokeGlobal("shouldDie", 1, { 0 }, { getBoolean() == true }).orElse(false)
|
||||
return result || health <= 0.0 //|| lua.errorState
|
||||
}
|
||||
|
||||
override fun tickParallel(delta: Double) {
|
||||
|
@ -6,8 +6,6 @@ import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonPrimitive
|
||||
import org.classdump.luna.ByteString
|
||||
import org.classdump.luna.Table
|
||||
import ru.dbotthepony.kommons.collect.filterNotNull
|
||||
import ru.dbotthepony.kommons.gson.set
|
||||
import ru.dbotthepony.kommons.util.getValue
|
||||
@ -22,8 +20,6 @@ import ru.dbotthepony.kstarbound.defs.DamageSource
|
||||
import ru.dbotthepony.kstarbound.defs.EntityDamageTeam
|
||||
import ru.dbotthepony.kstarbound.defs.EntityType
|
||||
import ru.dbotthepony.kstarbound.defs.HitType
|
||||
import ru.dbotthepony.kstarbound.defs.InteractAction
|
||||
import ru.dbotthepony.kstarbound.defs.InteractRequest
|
||||
import ru.dbotthepony.kstarbound.defs.actor.HumanoidConfig
|
||||
import ru.dbotthepony.kstarbound.defs.actor.HumanoidEmote
|
||||
import ru.dbotthepony.kstarbound.defs.actor.HumanoidIdentity
|
||||
@ -35,19 +31,13 @@ import ru.dbotthepony.kstarbound.io.BinaryStringCodec
|
||||
import ru.dbotthepony.kstarbound.io.NullableBinaryStringCodec
|
||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||
import ru.dbotthepony.kstarbound.json.mergeJson
|
||||
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
||||
import ru.dbotthepony.kstarbound.lua.LuaMessageHandlerComponent
|
||||
import ru.dbotthepony.kstarbound.lua.LuaThread
|
||||
import ru.dbotthepony.kstarbound.lua.LuaUpdateComponent
|
||||
import ru.dbotthepony.kstarbound.lua.bindings.MovementControllerBindings
|
||||
import ru.dbotthepony.kstarbound.lua.bindings.provideConfigBindings
|
||||
import ru.dbotthepony.kstarbound.lua.bindings.provideConfigBinding
|
||||
import ru.dbotthepony.kstarbound.lua.bindings.provideEntityBindings
|
||||
import ru.dbotthepony.kstarbound.lua.from
|
||||
import ru.dbotthepony.kstarbound.lua.get
|
||||
import ru.dbotthepony.kstarbound.lua.set
|
||||
import ru.dbotthepony.kstarbound.lua.tableMapOf
|
||||
import ru.dbotthepony.kstarbound.lua.tableOf
|
||||
import ru.dbotthepony.kstarbound.lua.toJsonFromLua
|
||||
import ru.dbotthepony.kstarbound.lua.userdata.BehaviorState
|
||||
import ru.dbotthepony.kstarbound.lua.userdata.provideBehaviorBindings
|
||||
import ru.dbotthepony.kstarbound.math.AABB
|
||||
import ru.dbotthepony.kstarbound.math.Interpolator
|
||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||
@ -61,12 +51,10 @@ import ru.dbotthepony.kstarbound.network.syncher.networkedJsonElement
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedString
|
||||
import ru.dbotthepony.kstarbound.util.GameTimer
|
||||
import ru.dbotthepony.kstarbound.util.random.staticRandomInt
|
||||
import ru.dbotthepony.kstarbound.util.valueOf
|
||||
import ru.dbotthepony.kstarbound.world.World
|
||||
import ru.dbotthepony.kstarbound.world.entities.api.InteractiveEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.api.ScriptedEntity
|
||||
import java.io.DataOutputStream
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
class NPCEntity(val variant: NPCVariant) : HumanoidActorEntity() {
|
||||
override val type: EntityType
|
||||
@ -155,14 +143,15 @@ class NPCEntity(val variant: NPCVariant) : HumanoidActorEntity() {
|
||||
override var isPersistent: Boolean = variant.persistent
|
||||
override var keepAlive: Boolean = variant.keepAlive
|
||||
|
||||
override val lua = LuaEnvironment()
|
||||
val luaUpdate = LuaUpdateComponent(lua)
|
||||
val luaMovement = MovementControllerBindings(movement)
|
||||
val luaMessages = LuaMessageHandlerComponent(lua) { toString() }
|
||||
|
||||
init {
|
||||
lua.globals["storage"] = lua.tableOf()
|
||||
}
|
||||
private var scriptStorage = JsonObject()
|
||||
override var lua by Delegates.notNull<LuaThread>()
|
||||
private set
|
||||
var luaUpdate by Delegates.notNull<LuaUpdateComponent>()
|
||||
private set
|
||||
var luaMovement by Delegates.notNull<MovementControllerBindings>()
|
||||
private set
|
||||
var luaMessages by Delegates.notNull<LuaMessageHandlerComponent>()
|
||||
private set
|
||||
|
||||
override fun handleMessage(connection: Int, message: String, arguments: JsonArray): JsonElement? {
|
||||
return luaMessages.handle(message, connection == connectionID, arguments) ?: statusController.handleMessage(message, connection == connectionID, arguments)
|
||||
@ -201,6 +190,7 @@ class NPCEntity(val variant: NPCVariant) : HumanoidActorEntity() {
|
||||
val deathParticleBurst: String? = null,
|
||||
val dropPools: ImmutableList<Registry.Ref<TreasurePoolDefinition>> = ImmutableList.of(),
|
||||
val aggressive: Boolean = false,
|
||||
val scriptStorage: JsonObject = JsonObject(),
|
||||
)
|
||||
|
||||
override fun deserialize(data: JsonObject) {
|
||||
@ -231,6 +221,8 @@ class NPCEntity(val variant: NPCVariant) : HumanoidActorEntity() {
|
||||
dropPools.addAll(serialized.dropPools.filter { it.isPresent })
|
||||
isAggressive = serialized.aggressive
|
||||
blinkTimer.reset(0.0)
|
||||
|
||||
scriptStorage = serialized.scriptStorage
|
||||
}
|
||||
|
||||
override fun serialize(data: JsonObject) {
|
||||
@ -257,6 +249,10 @@ class NPCEntity(val variant: NPCVariant) : HumanoidActorEntity() {
|
||||
deathParticleBurst = deathParticleBurst,
|
||||
dropPools = dropPools.stream().filter { it.isPresent }.collect(ImmutableList.toImmutableList()),
|
||||
aggressive = isAggressive,
|
||||
scriptStorage = run {
|
||||
lua.loadGlobal("storage")
|
||||
lua.popJson() as? JsonObject ?: JsonObject()
|
||||
}
|
||||
)
|
||||
|
||||
mergeJson(data, Starbound.gson.toJsonTree(serialized))
|
||||
@ -265,24 +261,41 @@ class NPCEntity(val variant: NPCVariant) : HumanoidActorEntity() {
|
||||
override fun onJoinWorld(world: World<*, *>) {
|
||||
super.onJoinWorld(world)
|
||||
|
||||
if (!isRemote) {
|
||||
if (isLocal) {
|
||||
for ((slot, item) in variant.items) {
|
||||
setItem(slot, item)
|
||||
}
|
||||
|
||||
lua = LuaThread()
|
||||
luaUpdate = LuaUpdateComponent(lua, this)
|
||||
luaMovement = MovementControllerBindings(movement, lua)
|
||||
luaMessages = LuaMessageHandlerComponent(lua) { toString() }
|
||||
|
||||
lua.push(scriptStorage)
|
||||
lua.storeGlobal("storage")
|
||||
// free up memory
|
||||
scriptStorage = JsonObject()
|
||||
|
||||
provideEntityBindings(this, lua)
|
||||
|
||||
provideConfigBindings(lua) { key, default ->
|
||||
key.find(variant.scriptConfig) ?: default
|
||||
provideConfigBinding(lua) { key ->
|
||||
key.find(variant.scriptConfig)
|
||||
}
|
||||
|
||||
BehaviorState.provideBindings(lua)
|
||||
provideBehaviorBindings(lua)
|
||||
|
||||
luaUpdate.stepCount = variant.initialScriptDelta
|
||||
lua.attach(variant.scripts)
|
||||
|
||||
luaMovement.init(lua)
|
||||
lua.init()
|
||||
lua.initScripts()
|
||||
}
|
||||
}
|
||||
|
||||
override fun uninit(world: World<*, *>) {
|
||||
super.uninit(world)
|
||||
|
||||
if (isLocal) {
|
||||
lua.close()
|
||||
}
|
||||
}
|
||||
|
||||
@ -319,12 +332,13 @@ class NPCEntity(val variant: NPCVariant) : HumanoidActorEntity() {
|
||||
val totalDamage = notifications.sumOf { it.healthLost }
|
||||
|
||||
if (totalDamage > 0.0 && hitDamageNotificationLimiter++ < Globals.npcs.hitDamageNotificationLimit) {
|
||||
lua.invokeGlobal("damage", lua.tableMapOf(
|
||||
"sourceId" to damage.request.sourceEntityId,
|
||||
"damage" to totalDamage,
|
||||
"sourceDamage" to damage.request.damage,
|
||||
"sourceKind" to damage.request.damageSourceKind
|
||||
))
|
||||
lua.pushTable(hashSize = 4)
|
||||
lua.setTableValue("sourceId", damage.request.sourceEntityId)
|
||||
lua.setTableValue("damage", totalDamage)
|
||||
lua.setTableValue("sourceDamage", damage.request.damage)
|
||||
lua.setTableValue("sourceKind", damage.request.damageSourceKind)
|
||||
|
||||
lua.invokeGlobal("damage", 1)
|
||||
}
|
||||
|
||||
return notifications
|
||||
@ -388,14 +402,14 @@ class NPCEntity(val variant: NPCVariant) : HumanoidActorEntity() {
|
||||
override fun tick(delta: Double) {
|
||||
super.tick(delta)
|
||||
|
||||
if (!isRemote) {
|
||||
if (isDead || lua.errorState) {
|
||||
if (isLocal) {
|
||||
if (isDead) {
|
||||
remove(RemovalReason.DYING)
|
||||
return
|
||||
} else {
|
||||
val shouldDie = lua.invokeGlobal("shouldDie")
|
||||
val shouldDie = lua.invokeGlobal("shouldDie", 1, { 0 }, { getBoolean() == true }).orElse(false)
|
||||
|
||||
if (shouldDie.isNotEmpty() && shouldDie[0] is Boolean && shouldDie[0] as Boolean) {
|
||||
if (shouldDie) {
|
||||
remove(RemovalReason.DYING)
|
||||
return
|
||||
}
|
||||
@ -410,7 +424,8 @@ class NPCEntity(val variant: NPCVariant) : HumanoidActorEntity() {
|
||||
override fun onRemove(world: World<*, *>, reason: RemovalReason) {
|
||||
super.onRemove(world, reason)
|
||||
|
||||
lua.invokeGlobal("die")
|
||||
if (isLocal)
|
||||
lua.invokeGlobal("die", 0)
|
||||
|
||||
val dropPools by lazy { dropPools.stream().map { it.entry }.filterNotNull().toList() }
|
||||
|
||||
|
@ -1,36 +1,25 @@
|
||||
package ru.dbotthepony.kstarbound.world.entities
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.classdump.luna.Table
|
||||
import org.classdump.luna.TableFactory
|
||||
import ru.dbotthepony.kommons.collect.map
|
||||
import ru.dbotthepony.kommons.collect.reduce
|
||||
import ru.dbotthepony.kommons.util.KOptional
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.defs.ActorMovementParameters
|
||||
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||
import ru.dbotthepony.kstarbound.lua.LuaThread
|
||||
import ru.dbotthepony.kstarbound.lua.from
|
||||
import ru.dbotthepony.kstarbound.lua.set
|
||||
import ru.dbotthepony.kstarbound.lua.setTableValue
|
||||
import ru.dbotthepony.kstarbound.lua.tableOf
|
||||
import ru.dbotthepony.kstarbound.lua.toByteString
|
||||
import ru.dbotthepony.kstarbound.math.AABB
|
||||
import ru.dbotthepony.kstarbound.math.AABBi
|
||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.math.vector.Vector2i
|
||||
import ru.dbotthepony.kstarbound.util.CarriedExecutor
|
||||
import ru.dbotthepony.kstarbound.util.supplyAsync
|
||||
import ru.dbotthepony.kstarbound.world.World
|
||||
import ru.dbotthepony.kstarbound.world.physics.CollisionType
|
||||
import java.util.PriorityQueue
|
||||
import java.util.*
|
||||
import java.util.function.Supplier
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.math.sign
|
||||
import kotlin.math.sqrt
|
||||
|
||||
@ -48,8 +37,6 @@ class PathFinder(val world: World<*, *>, val start: Vector2d, val goal: Vector2d
|
||||
SWIM(false, "Swim"),
|
||||
FLY(false, "Fly"),
|
||||
LAND(false, "Land");
|
||||
|
||||
val luaName = jsonName.toByteString()!!
|
||||
}
|
||||
|
||||
data class Edge(
|
||||
@ -154,13 +141,6 @@ class PathFinder(val world: World<*, *>, val start: Vector2d, val goal: Vector2d
|
||||
var totalCost: Double = 0.0
|
||||
var parent: Edge? = null
|
||||
|
||||
fun toTable(tables: TableFactory): Table {
|
||||
val table = tables.newTable(2, 0)
|
||||
table[positionIndex] = tables.from(position)
|
||||
table[velocityIndex] = tables.from(velocity)
|
||||
return table
|
||||
}
|
||||
|
||||
fun store(lua: LuaThread) {
|
||||
lua.pushTable(hashSize = 2)
|
||||
lua.setTableValue("position", position)
|
||||
@ -721,8 +701,5 @@ class PathFinder(val world: World<*, *>, val start: Vector2d, val goal: Vector2d
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
const val ARC_SIMULATION_FIDELTITY = 0.5
|
||||
const val NODE_GRANULARITY = 1.0
|
||||
|
||||
private val positionIndex = "position".toByteString()!!
|
||||
private val velocityIndex = "velocity".toByteString()!!
|
||||
}
|
||||
}
|
||||
|
@ -19,13 +19,8 @@ import ru.dbotthepony.kstarbound.json.jsonArrayOf
|
||||
import ru.dbotthepony.kstarbound.json.putAll
|
||||
import ru.dbotthepony.kstarbound.json.readJsonElement
|
||||
import ru.dbotthepony.kstarbound.json.writeJsonElement
|
||||
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
||||
import ru.dbotthepony.kstarbound.lua.LuaThread
|
||||
import ru.dbotthepony.kstarbound.lua.LuaUpdateComponent
|
||||
import ru.dbotthepony.kstarbound.lua.from
|
||||
import ru.dbotthepony.kstarbound.lua.get
|
||||
import ru.dbotthepony.kstarbound.lua.set
|
||||
import ru.dbotthepony.kstarbound.lua.tableOf
|
||||
import ru.dbotthepony.kstarbound.lua.toJsonFromLua
|
||||
import ru.dbotthepony.kstarbound.math.AABB
|
||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedFloat
|
||||
@ -33,6 +28,7 @@ import ru.dbotthepony.kstarbound.world.World
|
||||
import ru.dbotthepony.kstarbound.world.entities.api.ScriptedEntity
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
class StagehandEntity(isRemote: Boolean = false) : AbstractEntity(), ScriptedEntity {
|
||||
constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readJsonElement().asJsonObject, true)
|
||||
@ -69,12 +65,11 @@ class StagehandEntity(isRemote: Boolean = false) : AbstractEntity(), ScriptedEnt
|
||||
var isScripted = false
|
||||
private set
|
||||
|
||||
val lua = LuaEnvironment()
|
||||
val luaUpdate = LuaUpdateComponent(lua)
|
||||
override var lua by Delegates.notNull<LuaThread>()
|
||||
private set
|
||||
|
||||
init {
|
||||
lua.globals["storage"] = lua.tableOf()
|
||||
}
|
||||
var luaUpdate by Delegates.notNull<LuaUpdateComponent>()
|
||||
private set
|
||||
|
||||
override fun deserialize(data: JsonObject) {
|
||||
super.deserialize(data)
|
||||
@ -98,22 +93,32 @@ class StagehandEntity(isRemote: Boolean = false) : AbstractEntity(), ScriptedEnt
|
||||
}
|
||||
|
||||
boundingBox = broadcastArea ?: AABB.withSide(Vector2d.ZERO, 5.0)
|
||||
|
||||
if (isScripted && isLocal) {
|
||||
lua.attach(config["scripts"].asJsonArray.map { AssetPath(it.asString) })
|
||||
luaUpdate.stepCount = config.get("scriptDelta", 5.0)
|
||||
|
||||
if ("scriptStorage" in config) {
|
||||
lua.globals["storage"] = lua.from(config["scriptStorage"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onJoinWorld(world: World<*, *>) {
|
||||
super.onJoinWorld(world)
|
||||
|
||||
if (isLocal && isScripted) {
|
||||
lua.init()
|
||||
lua = LuaThread()
|
||||
luaUpdate = LuaUpdateComponent(lua, this)
|
||||
lua.attach(config["scripts"].asJsonArray.map { AssetPath(it.asString) })
|
||||
luaUpdate.stepCount = config.get("scriptDelta", 5.0)
|
||||
|
||||
if ("scriptStorage" in config) {
|
||||
lua.push(config["scriptStorage"])
|
||||
lua.storeGlobal("storage")
|
||||
} else {
|
||||
lua.pushTable()
|
||||
lua.storeGlobal("storage")
|
||||
}
|
||||
|
||||
lua.initScripts()
|
||||
}
|
||||
}
|
||||
|
||||
override fun uninit(world: World<*, *>) {
|
||||
if (isLocal && isScripted) {
|
||||
lua.close()
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,8 +133,10 @@ class StagehandEntity(isRemote: Boolean = false) : AbstractEntity(), ScriptedEnt
|
||||
else
|
||||
data.remove("uniqueId")
|
||||
|
||||
if (isScripted)
|
||||
data["scriptStorage"] = toJsonFromLua(lua.globals["storage"])
|
||||
if (isScripted) {
|
||||
lua.loadGlobal("storage")
|
||||
data["scriptStorage"] = lua.popJson()
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeNetwork(stream: DataOutputStream, isLegacy: Boolean) {
|
||||
@ -146,10 +153,12 @@ class StagehandEntity(isRemote: Boolean = false) : AbstractEntity(), ScriptedEnt
|
||||
get() = "Technical entity responsible for doing cool stuff"
|
||||
|
||||
override fun callScript(fnName: String, arguments: JsonArray): JsonElement {
|
||||
TODO()
|
||||
require(isLocal) { "Calling script on remote entity" }
|
||||
return super.callScript(fnName, arguments)
|
||||
}
|
||||
|
||||
override fun evalScript(code: String): Array<Any?> {
|
||||
return lua.eval(code)
|
||||
override fun evalScript(code: String): JsonElement {
|
||||
require(isLocal) { "Calling script on remote entity" }
|
||||
return super.evalScript(code)
|
||||
}
|
||||
}
|
||||
|
@ -9,12 +9,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.classdump.luna.ByteString
|
||||
import org.classdump.luna.LuaRuntimeException
|
||||
import org.classdump.luna.Table
|
||||
import ru.dbotthepony.kommons.collect.ListenableMap
|
||||
import ru.dbotthepony.kommons.collect.collect
|
||||
import ru.dbotthepony.kommons.collect.map
|
||||
import ru.dbotthepony.kommons.gson.contains
|
||||
import ru.dbotthepony.kommons.gson.set
|
||||
import ru.dbotthepony.kommons.io.readKOptional
|
||||
@ -42,19 +37,14 @@ import ru.dbotthepony.kstarbound.io.StreamCodec
|
||||
import ru.dbotthepony.kstarbound.io.nullable
|
||||
import ru.dbotthepony.kstarbound.io.readInternedString
|
||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
||||
import ru.dbotthepony.kstarbound.lua.LuaMessageHandlerComponent
|
||||
import ru.dbotthepony.kstarbound.lua.LuaThread
|
||||
import ru.dbotthepony.kstarbound.lua.LuaUpdateComponent
|
||||
import ru.dbotthepony.kstarbound.lua.bindings.MovementControllerBindings
|
||||
import ru.dbotthepony.kstarbound.lua.bindings.createConfigBinding
|
||||
import ru.dbotthepony.kstarbound.lua.bindings.provideAnimatorBindings
|
||||
import ru.dbotthepony.kstarbound.lua.bindings.provideConfigBindings
|
||||
import ru.dbotthepony.kstarbound.lua.bindings.provideConfigBinding
|
||||
import ru.dbotthepony.kstarbound.lua.bindings.provideEntityBindings
|
||||
import ru.dbotthepony.kstarbound.lua.from
|
||||
import ru.dbotthepony.kstarbound.lua.iterator
|
||||
import ru.dbotthepony.kstarbound.lua.luaFunction
|
||||
import ru.dbotthepony.kstarbound.lua.set
|
||||
import ru.dbotthepony.kstarbound.lua.toJsonFromLua
|
||||
import ru.dbotthepony.kstarbound.math.Interpolator
|
||||
import ru.dbotthepony.kstarbound.network.packets.DamageNotificationPacket
|
||||
import ru.dbotthepony.kstarbound.network.syncher.NetworkedDynamicGroup
|
||||
@ -74,7 +64,7 @@ import ru.dbotthepony.kstarbound.world.entities.api.StatusEffectEntity
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
import java.util.*
|
||||
import java.util.stream.Collectors
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
// this is unnatural to have this class separated, but since it contains
|
||||
// lots of internal state, it would be nice to have it encapsulated;
|
||||
@ -131,26 +121,39 @@ class StatusController(val entity: ActorEntity, val config: StatusControllerConf
|
||||
networkGroup.add(statNetworkGroup)
|
||||
}
|
||||
|
||||
val lua = LuaEnvironment()
|
||||
val luaUpdate = LuaUpdateComponent(lua)
|
||||
val luaMovement = MovementControllerBindings(entity.movement)
|
||||
val luaMessages = LuaMessageHandlerComponent(lua) { toString() }
|
||||
var lua by Delegates.notNull<LuaThread>()
|
||||
private set
|
||||
var luaUpdate by Delegates.notNull<LuaUpdateComponent>()
|
||||
private set
|
||||
var luaMovement by Delegates.notNull<MovementControllerBindings>()
|
||||
private set
|
||||
var luaMessages by Delegates.notNull<LuaMessageHandlerComponent>()
|
||||
private set
|
||||
|
||||
private val animator: EffectAnimator?
|
||||
private val animatorID: Int?
|
||||
|
||||
fun uninit() {
|
||||
if (entity.isLocal) {
|
||||
lua.close()
|
||||
}
|
||||
}
|
||||
|
||||
fun init() {
|
||||
if (!entity.isRemote) {
|
||||
if (entity.isLocal) {
|
||||
lua = LuaThread()
|
||||
luaUpdate = LuaUpdateComponent(lua, this)
|
||||
luaMovement = MovementControllerBindings(entity.movement, lua)
|
||||
luaMessages = LuaMessageHandlerComponent(lua) { toString() }
|
||||
luaUpdate.stepCount = config.primaryScriptDelta
|
||||
lua.attach(config.primaryScriptSources)
|
||||
|
||||
// provideStatusControllerBindings(this, lua) // provided through provideEntityBindings
|
||||
provideEntityBindings(entity, lua)
|
||||
luaMovement.init(lua)
|
||||
|
||||
// TODO: Once we have brand new object-oriented Lua API, expose proper entity bindings here
|
||||
// TODO: Expose world bindings
|
||||
lua.init()
|
||||
lua.initScripts()
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,14 +183,17 @@ class StatusController(val entity: ActorEntity, val config: StatusControllerConf
|
||||
fun recentDamageDealt(since: Long = 0L) = recentDamageDealt.query(since)
|
||||
|
||||
fun experienceDamage(damage: DamageData): List<DamageNotification> {
|
||||
val results = lua.invokeGlobal("applyDamageRequest", lua.from(Starbound.gson.toJsonTree(damage)))
|
||||
val results = lua.invokeGlobal("applyDamageRequest", 1, {
|
||||
push(Starbound.gson.toJsonTree(damage))
|
||||
1
|
||||
}, { getJson() as? JsonArray }).flatMap { KOptional.ofNullable(it) }
|
||||
|
||||
if (results.isNotEmpty() && results[0] is Table) {
|
||||
if (results.isPresent) {
|
||||
val parsed = ArrayList<DamageNotification>()
|
||||
|
||||
for ((_, v) in (results[0] as Table)) {
|
||||
for (v in results.value) {
|
||||
try {
|
||||
parsed.add(Starbound.gson.fromJsonFast(toJsonFromLua(v), DamageNotification::class.java))
|
||||
parsed.add(Starbound.gson.fromJsonFast(v, DamageNotification::class.java))
|
||||
} catch (err: Throwable) {
|
||||
LOGGER.error("Exception while parsing results returned by applyDamageRequest (primary scripts: ${config.primaryScriptSources})", err)
|
||||
}
|
||||
@ -457,10 +463,14 @@ class StatusController(val entity: ActorEntity, val config: StatusControllerConf
|
||||
var parentDirectives: String = ""
|
||||
val modifierGroups = IntArrayList()
|
||||
|
||||
val lua = LuaEnvironment()
|
||||
val luaMessages = LuaMessageHandlerComponent(lua) { "Unique effect" }
|
||||
val luaMovement = MovementControllerBindings(entity.movement)
|
||||
val luaUpdate = LuaUpdateComponent(lua)
|
||||
var lua by Delegates.notNull<LuaThread>()
|
||||
private set
|
||||
var luaUpdate by Delegates.notNull<LuaUpdateComponent>()
|
||||
private set
|
||||
var luaMovement by Delegates.notNull<MovementControllerBindings>()
|
||||
private set
|
||||
var luaMessages by Delegates.notNull<LuaMessageHandlerComponent>()
|
||||
private set
|
||||
|
||||
val metadata = UniqueEffectNetworkedValues()
|
||||
val metadataNetworkID = uniqueEffectMetadata.add(metadata)
|
||||
@ -485,14 +495,18 @@ class StatusController(val entity: ActorEntity, val config: StatusControllerConf
|
||||
animatorNetworkID = null
|
||||
}
|
||||
|
||||
if (!entity.isRemote && effect.value.scripts.isNotEmpty()) {
|
||||
if (entity.isLocal && effect.value.scripts.isNotEmpty()) {
|
||||
lua = LuaThread()
|
||||
luaMessages = LuaMessageHandlerComponent(lua) { "Unique effect" }
|
||||
luaMovement = MovementControllerBindings(entity.movement, lua)
|
||||
luaUpdate = LuaUpdateComponent(lua, this)
|
||||
|
||||
// unique effects shouldn't be initialized on remotes in first place
|
||||
// but whatever
|
||||
lua.attach(effect.value.scripts)
|
||||
|
||||
// provideStatusControllerBindings(this@StatusController, lua) // provided through provideEntityBindings
|
||||
provideEntityBindings(entity, lua)
|
||||
luaMovement.init(lua)
|
||||
provideEffectBindings()
|
||||
|
||||
if (animator != null) {
|
||||
@ -502,84 +516,103 @@ class StatusController(val entity: ActorEntity, val config: StatusControllerConf
|
||||
provideAnimatorBindings(animator.animator, lua)
|
||||
}
|
||||
|
||||
provideConfigBindings(lua) { path, default ->
|
||||
path.find(effect.json) ?: default
|
||||
provideConfigBinding(lua) { path ->
|
||||
path.find(effect.json)
|
||||
}
|
||||
|
||||
lua.init()
|
||||
lua.initScripts()
|
||||
}
|
||||
}
|
||||
|
||||
private fun duration(args: LuaThread.ArgStack): Int {
|
||||
args.lua.push(metadata.duration)
|
||||
return 1
|
||||
}
|
||||
|
||||
private fun modifyDuration(args: LuaThread.ArgStack): Int {
|
||||
val duration = args.nextDouble()
|
||||
require(duration.isFinite()) { "Infinite duration provided" }
|
||||
require(!duration.isNaN()) { "NaN duration provided" }
|
||||
|
||||
if (!isPersistent)
|
||||
metadata.duration += duration
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
private fun expire(args: LuaThread.ArgStack): Int {
|
||||
if (!isPersistent)
|
||||
metadata.duration = 0.0
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
private fun sourceEntity(args: LuaThread.ArgStack): Int {
|
||||
args.lua.push(metadata.sourceEntity?.toLong() ?: entity.entityID.toLong())
|
||||
return 1
|
||||
}
|
||||
|
||||
private fun setParentDirectives(args: LuaThread.ArgStack): Int {
|
||||
parentDirectives = args.nextString().sbIntern()
|
||||
return 0
|
||||
}
|
||||
|
||||
private fun addStatModifierGroup(args: LuaThread.ArgStack): Int {
|
||||
val modifiers = args.readTableValues {
|
||||
Starbound.gson.fromJsonFast(getJson(it)!!, StatModifier::class.java)
|
||||
}
|
||||
|
||||
val id = statModifiers.add(ArrayList(modifiers))
|
||||
modifierGroups.add(id)
|
||||
args.lua.push(id.toLong())
|
||||
return 1
|
||||
}
|
||||
|
||||
private fun setStatModifierGroup(args: LuaThread.ArgStack): Int {
|
||||
val id = args.nextInt()
|
||||
|
||||
if (id !in modifierGroups)
|
||||
throw IllegalStateException("$id stat modifier group does not belong to this effect")
|
||||
|
||||
val modifiers = args.readTableValues {
|
||||
Starbound.gson.fromJsonFast(getJson(it)!!, StatModifier::class.java)
|
||||
}
|
||||
|
||||
statModifiers[id] = ArrayList(modifiers)
|
||||
return 0
|
||||
}
|
||||
|
||||
private fun removeStatModifierGroup(args: LuaThread.ArgStack): Int {
|
||||
val id = args.nextInt()
|
||||
|
||||
if (modifierGroups.rem(id)) {
|
||||
statModifiers.remove(id)
|
||||
} else {
|
||||
throw IllegalStateException("$id stat modifier group does not belong to this effect")
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
private fun provideEffectBindings() {
|
||||
val callbacks = lua.newTable()
|
||||
lua.globals["effect"] = callbacks
|
||||
lua.pushTable()
|
||||
lua.dup()
|
||||
lua.storeGlobal("effect")
|
||||
|
||||
callbacks["duration"] = luaFunction {
|
||||
returnBuffer.setTo(metadata.duration)
|
||||
}
|
||||
lua.setTableValue("duration", ::duration)
|
||||
lua.setTableValue("modifyDuration", ::modifyDuration)
|
||||
lua.setTableValue("expire", ::expire)
|
||||
lua.setTableValue("sourceEntity", ::sourceEntity)
|
||||
lua.setTableValue("setParentDirectives", ::setParentDirectives)
|
||||
lua.setTableValue("addStatModifierGroup", ::addStatModifierGroup)
|
||||
lua.setTableValue("setStatModifierGroup", ::setStatModifierGroup)
|
||||
lua.setTableValue("removeStatModifierGroup", ::removeStatModifierGroup)
|
||||
|
||||
callbacks["modifyDuration"] = luaFunction { duration: Number ->
|
||||
val value = duration.toDouble()
|
||||
check(value.isFinite()) { "Infinite duration provided" }
|
||||
check(!value.isNaN()) { "NaN duration provided" }
|
||||
lua.push("getParameter")
|
||||
createConfigBinding(lua) { path -> path.find(effect.json) }
|
||||
lua.setTableValue()
|
||||
|
||||
if (!isPersistent) {
|
||||
metadata.duration += value
|
||||
}
|
||||
}
|
||||
|
||||
callbacks["expire"] = luaFunction {
|
||||
if (!isPersistent) {
|
||||
metadata.duration = 0.0
|
||||
}
|
||||
}
|
||||
|
||||
callbacks["sourceEntity"] = luaFunction {
|
||||
returnBuffer.setTo(metadata.sourceEntity ?: entity.entityID)
|
||||
}
|
||||
|
||||
callbacks["setParentDirectives"] = luaFunction { directives: ByteString ->
|
||||
parentDirectives = directives.decode().sbIntern()
|
||||
}
|
||||
|
||||
callbacks["getParameter"] = createConfigBinding { path, default ->
|
||||
path.find(effect.json) ?: default
|
||||
}
|
||||
|
||||
callbacks["addStatModifierGroup"] = luaFunction { modifiers: Table ->
|
||||
val actual = modifiers
|
||||
.iterator()
|
||||
.map { (_, v) -> Starbound.gson.fromJsonFast(toJsonFromLua(v), StatModifier::class.java) }
|
||||
.collect(Collectors.toCollection(::ArrayList))
|
||||
|
||||
val id = statModifiers.add(actual)
|
||||
modifierGroups.add(id)
|
||||
returnBuffer.setTo(id)
|
||||
}
|
||||
|
||||
callbacks["setStatModifierGroup"] = luaFunction { groupID: Number, modifiers: Table ->
|
||||
val id = groupID.toInt()
|
||||
|
||||
if (id !in modifierGroups)
|
||||
throw LuaRuntimeException("$groupID stat modifier group does not belong to this effect")
|
||||
|
||||
val actual = modifiers
|
||||
.iterator()
|
||||
.map { (_, v) -> Starbound.gson.fromJsonFast(toJsonFromLua(v), StatModifier::class.java) }
|
||||
.collect(Collectors.toCollection(::ArrayList))
|
||||
|
||||
statModifiers[id] = actual
|
||||
}
|
||||
|
||||
callbacks["removeStatModifierGroup"] = luaFunction { groupID: Number ->
|
||||
val id = groupID.toInt()
|
||||
|
||||
if (modifierGroups.rem(id)) {
|
||||
statModifiers.remove(id)
|
||||
} else {
|
||||
throw LuaRuntimeException("$groupID stat modifier group does not belong to this effect")
|
||||
}
|
||||
}
|
||||
lua.pop()
|
||||
}
|
||||
|
||||
fun promoteToPersistent() {
|
||||
@ -591,7 +624,7 @@ class StatusController(val entity: ActorEntity, val config: StatusControllerConf
|
||||
// remove an ephemeral effect.
|
||||
|
||||
// Original engine only updates the duration (setting it to null),
|
||||
// but for proper promotion we also need to reset cause entity, otherwise
|
||||
// but for proper promotion we also need to reset "cause" entity, otherwise
|
||||
// persistent effect will be sourced to whoever put ephemeral effect
|
||||
metadata.duration = 0.0
|
||||
metadata.sourceEntity = null
|
||||
@ -599,7 +632,9 @@ class StatusController(val entity: ActorEntity, val config: StatusControllerConf
|
||||
}
|
||||
|
||||
fun remove() {
|
||||
lua.invokeGlobal("onExpire")
|
||||
if (entity.isLocal)
|
||||
lua.invokeGlobal("onExpire", 0)
|
||||
|
||||
uniqueEffectMetadata.remove(metadataNetworkID)
|
||||
|
||||
for (group in modifierGroups) {
|
||||
|
@ -2,13 +2,29 @@ package ru.dbotthepony.kstarbound.world.entities.api
|
||||
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonNull
|
||||
import ru.dbotthepony.kstarbound.lua.LuaThread
|
||||
|
||||
interface ScriptedEntity {
|
||||
val lua: LuaThread
|
||||
|
||||
// Call a script function directly with the given arguments, should return
|
||||
// nothing only on failure.
|
||||
fun callScript(fnName: String, arguments: JsonArray): JsonElement
|
||||
fun callScript(fnName: String, arguments: JsonArray): JsonElement {
|
||||
return lua.invokeGlobal(fnName, 1, {
|
||||
ensureExtraCapacity(arguments.size() + 4)
|
||||
|
||||
for (argument in arguments) {
|
||||
push(argument)
|
||||
}
|
||||
|
||||
arguments.size()
|
||||
}, { getJson() ?: JsonNull.INSTANCE }).orElse(JsonNull.INSTANCE)
|
||||
}
|
||||
|
||||
// Execute the given code directly in the underlying context, return nothing
|
||||
// on failure.
|
||||
fun evalScript(code: String): Array<Any?>
|
||||
fun evalScript(code: String): JsonElement {
|
||||
return lua.eval(code)
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ import ru.dbotthepony.kstarbound.defs.actor.HumanoidEmote
|
||||
import ru.dbotthepony.kstarbound.defs.actor.player.PlayerGamemode
|
||||
import ru.dbotthepony.kstarbound.io.readInternedString
|
||||
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
||||
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
||||
import ru.dbotthepony.kstarbound.lua.LuaThread
|
||||
import ru.dbotthepony.kstarbound.math.AABB
|
||||
import ru.dbotthepony.kstarbound.math.Interpolator
|
||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||
@ -101,7 +101,7 @@ class PlayerEntity() : HumanoidActorEntity() {
|
||||
val newChatMessage = networkGroup.upstream.add(networkedEventCounter())
|
||||
var emote by networkGroup.upstream.add(networkedEnumExtraStupid(HumanoidEmote.IDLE))
|
||||
|
||||
override val lua: LuaEnvironment
|
||||
override val lua: LuaThread
|
||||
get() = TODO("Not yet implemented")
|
||||
override val emoteCooldownTimer: GameTimer = GameTimer()
|
||||
override val danceTimer: GameTimer?
|
||||
|
@ -12,29 +12,20 @@ import com.google.gson.TypeAdapter
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.classdump.luna.ByteString
|
||||
import org.classdump.luna.Table
|
||||
import ru.dbotthepony.kommons.gson.JsonArrayCollector
|
||||
import ru.dbotthepony.kommons.gson.contains
|
||||
import ru.dbotthepony.kommons.math.RGBAColor
|
||||
import ru.dbotthepony.kstarbound.math.vector.Vector2i
|
||||
import ru.dbotthepony.kstarbound.Registries
|
||||
import ru.dbotthepony.kstarbound.Registry
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.defs.`object`.ObjectDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.`object`.ObjectOrientation
|
||||
import ru.dbotthepony.kommons.gson.get
|
||||
import ru.dbotthepony.kommons.gson.set
|
||||
import ru.dbotthepony.kommons.guava.immutableSet
|
||||
import ru.dbotthepony.kstarbound.io.StreamCodec
|
||||
import ru.dbotthepony.kstarbound.io.map
|
||||
import ru.dbotthepony.kommons.io.writeBinaryString
|
||||
import ru.dbotthepony.kstarbound.math.AABB
|
||||
import ru.dbotthepony.kommons.math.RGBAColor
|
||||
import ru.dbotthepony.kommons.util.getValue
|
||||
import ru.dbotthepony.kommons.util.setValue
|
||||
import ru.dbotthepony.kstarbound.Globals
|
||||
import ru.dbotthepony.kstarbound.Registries
|
||||
import ru.dbotthepony.kstarbound.Registry
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.defs.DamageNotification
|
||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.defs.DamageSource
|
||||
import ru.dbotthepony.kstarbound.defs.EntityType
|
||||
import ru.dbotthepony.kstarbound.defs.HitType
|
||||
@ -42,15 +33,30 @@ import ru.dbotthepony.kstarbound.defs.InteractAction
|
||||
import ru.dbotthepony.kstarbound.defs.InteractRequest
|
||||
import ru.dbotthepony.kstarbound.defs.animation.AnimationDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor
|
||||
import ru.dbotthepony.kstarbound.defs.`object`.ObjectDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.`object`.ObjectOrientation
|
||||
import ru.dbotthepony.kstarbound.defs.`object`.ObjectType
|
||||
import ru.dbotthepony.kstarbound.defs.quest.QuestArcDescriptor
|
||||
import ru.dbotthepony.kstarbound.defs.tile.TileDamage
|
||||
import ru.dbotthepony.kstarbound.io.RGBACodec
|
||||
import ru.dbotthepony.kstarbound.io.StreamCodec
|
||||
import ru.dbotthepony.kstarbound.io.Vector2iCodec
|
||||
import ru.dbotthepony.kstarbound.io.map
|
||||
import ru.dbotthepony.kstarbound.json.JsonPath
|
||||
import ru.dbotthepony.kstarbound.json.jsonArrayOf
|
||||
import ru.dbotthepony.kstarbound.json.mergeJson
|
||||
import ru.dbotthepony.kstarbound.json.stream
|
||||
import ru.dbotthepony.kstarbound.json.writeJsonElement
|
||||
import ru.dbotthepony.kstarbound.lua.LuaMessageHandlerComponent
|
||||
import ru.dbotthepony.kstarbound.lua.LuaThread
|
||||
import ru.dbotthepony.kstarbound.lua.LuaUpdateComponent
|
||||
import ru.dbotthepony.kstarbound.lua.bindings.provideAnimatorBindings
|
||||
import ru.dbotthepony.kstarbound.lua.bindings.provideEntityBindings
|
||||
import ru.dbotthepony.kstarbound.lua.setTableValue
|
||||
import ru.dbotthepony.kstarbound.math.AABB
|
||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||
import ru.dbotthepony.kstarbound.math.vector.Vector2i
|
||||
import ru.dbotthepony.kstarbound.network.packets.DamageRequestPacket
|
||||
import ru.dbotthepony.kstarbound.network.syncher.InternedStringCodec
|
||||
import ru.dbotthepony.kstarbound.network.syncher.JsonElementCodec
|
||||
import ru.dbotthepony.kstarbound.network.syncher.NetworkedList
|
||||
@ -64,21 +70,6 @@ import ru.dbotthepony.kstarbound.network.syncher.networkedFloat
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedJsonElement
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedPointer
|
||||
import ru.dbotthepony.kstarbound.network.syncher.networkedString
|
||||
import ru.dbotthepony.kstarbound.json.writeJsonElement
|
||||
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
||||
import ru.dbotthepony.kstarbound.lua.LuaMessageHandlerComponent
|
||||
import ru.dbotthepony.kstarbound.lua.LuaUpdateComponent
|
||||
import ru.dbotthepony.kstarbound.lua.bindings.provideAnimatorBindings
|
||||
import ru.dbotthepony.kstarbound.lua.bindings.provideEntityBindings
|
||||
import ru.dbotthepony.kstarbound.lua.from
|
||||
import ru.dbotthepony.kstarbound.lua.get
|
||||
import ru.dbotthepony.kstarbound.lua.set
|
||||
import ru.dbotthepony.kstarbound.lua.tableMapOf
|
||||
import ru.dbotthepony.kstarbound.lua.tableOf
|
||||
import ru.dbotthepony.kstarbound.lua.toJson
|
||||
import ru.dbotthepony.kstarbound.lua.toJsonFromLua
|
||||
import ru.dbotthepony.kstarbound.network.packets.DamageRequestPacket
|
||||
import ru.dbotthepony.kstarbound.server.world.LegacyWireProcessor
|
||||
import ru.dbotthepony.kstarbound.util.ManualLazy
|
||||
import ru.dbotthepony.kstarbound.util.asStringOrNull
|
||||
import ru.dbotthepony.kstarbound.util.random.random
|
||||
@ -92,19 +83,20 @@ import ru.dbotthepony.kstarbound.world.entities.api.InteractiveEntity
|
||||
import ru.dbotthepony.kstarbound.world.entities.api.ScriptedEntity
|
||||
import ru.dbotthepony.kstarbound.world.physics.Poly
|
||||
import java.io.DataOutputStream
|
||||
import java.util.Collections
|
||||
import java.util.HashMap
|
||||
import java.util.*
|
||||
import java.util.random.RandomGenerator
|
||||
import kotlin.math.min
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntity(), ScriptedEntity, InteractiveEntity {
|
||||
private var scriptStorage = JsonObject()
|
||||
|
||||
override fun deserialize(data: JsonObject) {
|
||||
super.deserialize(data)
|
||||
direction = data.get("direction", directions) { Direction.LEFT }
|
||||
orientationIndex = data.get("orientationIndex", -1).toLong()
|
||||
isInteractive = data.get("interactive", false)
|
||||
|
||||
lua.globals["storage"] = lua.from(data.get("scriptStorage") { JsonObject() })
|
||||
scriptStorage = data.get("scriptStorage") { JsonObject() }
|
||||
|
||||
loadParameters(data.get("parameters") { JsonObject() })
|
||||
|
||||
@ -159,10 +151,11 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
||||
data["inputWireNodes"] = inputNodes.stream().map { it.serialize() }.collect(JsonArrayCollector)
|
||||
data["outputWireNodes"] = outputNodes.stream().map { it.serialize() }.collect(JsonArrayCollector)
|
||||
|
||||
val scriptStorage = lua.globals["storage"]
|
||||
lua.loadGlobal("storage")
|
||||
val scriptStorage = lua.popJson()
|
||||
|
||||
if (scriptStorage != null && scriptStorage is Table) {
|
||||
data["scriptStorage"] = scriptStorage.toJson(true)
|
||||
if (scriptStorage != null && scriptStorage is JsonObject) {
|
||||
data["scriptStorage"] = scriptStorage
|
||||
}
|
||||
|
||||
uniqueID.get()?.let {
|
||||
@ -313,13 +306,13 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
||||
fun addConnection(connection: WireConnection) {
|
||||
if (connection !in connectionsInternal) {
|
||||
connectionsInternal.add(connection.copy())
|
||||
lua.invokeGlobal("onNodeConnectionChange")
|
||||
lua.invokeGlobal("onNodeConnectionChange", 0)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeConnection(connection: WireConnection) {
|
||||
if (connectionsInternal.remove(connection)) {
|
||||
lua.invokeGlobal("onNodeConnectionChange")
|
||||
lua.invokeGlobal("onNodeConnectionChange", 0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -332,20 +325,20 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
||||
val any = otherConnections?.getOrNull(it.index)?.connectionsInternal?.removeIf { it.entityLocation == tilePosition && it.index == index }
|
||||
|
||||
if (any == true) {
|
||||
otherEntity!!.lua.invokeGlobal("onNodeConnectionChange")
|
||||
otherEntity!!.lua.invokeGlobal("onNodeConnectionChange", 0)
|
||||
}
|
||||
|
||||
any == true
|
||||
}
|
||||
|
||||
if (any)
|
||||
lua.invokeGlobal("onNodeConnectionChange")
|
||||
lua.invokeGlobal("onNodeConnectionChange", 0)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeConnectionsTo(pos: Vector2i) {
|
||||
if (connectionsInternal.removeIf { it.entityLocation == pos }) {
|
||||
lua.invokeGlobal("onNodeConnectionChange")
|
||||
lua.invokeGlobal("onNodeConnectionChange", 0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -431,13 +424,12 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
||||
networkGroup.upstream.add(animator.networkGroup)
|
||||
}
|
||||
|
||||
val lua = LuaEnvironment()
|
||||
val luaUpdate = LuaUpdateComponent(lua)
|
||||
val luaMessageHandler = LuaMessageHandlerComponent(lua) { toString() }
|
||||
|
||||
init {
|
||||
lua.globals["storage"] = lua.newTable()
|
||||
}
|
||||
final override var lua by Delegates.notNull<LuaThread>()
|
||||
private set
|
||||
var luaUpdate by Delegates.notNull<LuaUpdateComponent>()
|
||||
private set
|
||||
var luaMessageHandler by Delegates.notNull<LuaMessageHandlerComponent>()
|
||||
private set
|
||||
|
||||
val unbreakable by ManualLazy {
|
||||
lookupProperty("unbreakable") { JsonPrimitive(false) }.asBoolean
|
||||
@ -553,14 +545,23 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
||||
animator.setPartTag(k, "partImage", v.asString)
|
||||
}
|
||||
|
||||
lua = LuaThread()
|
||||
lua.push(scriptStorage)
|
||||
lua.storeGlobal("storage")
|
||||
luaUpdate = LuaUpdateComponent(lua, this)
|
||||
luaMessageHandler = LuaMessageHandlerComponent(lua) { toString() }
|
||||
|
||||
updateMaterialSpacesNow()
|
||||
provideEntityBindings(this, lua)
|
||||
provideAnimatorBindings(animator, lua)
|
||||
lua.attach(config.value.scripts)
|
||||
luaUpdate.stepCount = lookupProperty(JsonPath("scriptDelta")) { JsonPrimitive(5) }.asDouble
|
||||
lua.init()
|
||||
lua.initScripts()
|
||||
}
|
||||
|
||||
// free up memory
|
||||
scriptStorage = JsonObject()
|
||||
|
||||
// as original code puts it:
|
||||
// Don't animate the initial state when first spawned IF you're dumb, which by default
|
||||
// you would be, and don't know how to use transition and static states properly. Someday
|
||||
@ -593,21 +594,25 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
||||
override fun interact(request: InteractRequest): InteractAction {
|
||||
val diff = world.geometry.diff(request.sourcePos, position)
|
||||
|
||||
val result = lua.invokeGlobal("onInteraction", lua.newTable().apply {
|
||||
this["source"] = lua.tableOf(diff.x, diff.y)
|
||||
this["sourceId"] = request.source
|
||||
})
|
||||
val result = lua.invokeGlobal("onInteraction", 1, {
|
||||
pushTable(hashSize = 2)
|
||||
|
||||
if (result.isNotEmpty()) {
|
||||
if (result[0] == null)
|
||||
setTableValue("source", diff)
|
||||
setTableValue("sourceId", request.source)
|
||||
|
||||
1
|
||||
}, { getJson() ?: JsonNull.INSTANCE })
|
||||
|
||||
if (result.isPresent) {
|
||||
if (result.value.isJsonNull)
|
||||
return InteractAction.NONE
|
||||
else if (result[0] is ByteString) {
|
||||
val decoded = (result[0] as ByteString).decode()
|
||||
else if (result.value is JsonPrimitive) {
|
||||
val decoded = result.value.asString
|
||||
return InteractAction(InteractAction.Type.entries.firstOrNull { it.jsonName == decoded } ?: throw NoSuchElementException("Unknown interaction action type $decoded!"), entityID)
|
||||
} else {
|
||||
val data = result[0] as Table
|
||||
val decoded = (data[1L] as ByteString).decode()
|
||||
return InteractAction(InteractAction.Type.entries.firstOrNull { it.jsonName == decoded } ?: throw NoSuchElementException("Unknown interaction action type $decoded!"), entityID, toJsonFromLua(data[2L]))
|
||||
val data = result.value as JsonArray
|
||||
val decoded = data[0].asString
|
||||
return InteractAction(InteractAction.Type.entries.firstOrNull { it.jsonName == decoded } ?: throw NoSuchElementException("Unknown interaction action type $decoded!"), entityID, if (data.size() >= 2) data[1] else JsonNull.INSTANCE)
|
||||
}
|
||||
}
|
||||
|
||||
@ -670,7 +675,7 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
||||
// break connection if other entity got removed
|
||||
if (connection.otherEntity?.removalReason?.removal == true) {
|
||||
itr.remove()
|
||||
lua.invokeGlobal("onNodeConnectionChange")
|
||||
lua.invokeGlobal("onNodeConnectionChange", 0)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -686,7 +691,7 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
||||
// break connection if we point at invalid node
|
||||
if (otherNode == null) {
|
||||
itr.remove()
|
||||
lua.invokeGlobal("onNodeConnectionChange")
|
||||
lua.invokeGlobal("onNodeConnectionChange", 0)
|
||||
} else {
|
||||
newState = newState!! || otherNode.state
|
||||
}
|
||||
@ -701,7 +706,10 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
||||
// otherwise, keep current node state
|
||||
if (newState != null && node.state != newState) {
|
||||
node.state = newState
|
||||
lua.invokeGlobal("onInputNodeChange", lua.tableMapOf(LegacyWireProcessor.NODE_KEY to i.toLong(), LegacyWireProcessor.LEVEL_KEY to newState))
|
||||
lua.pushTable(hashSize = 2)
|
||||
lua.setTableValue("node", i)
|
||||
lua.setTableValue("level", newState)
|
||||
lua.invokeGlobal("onInputNodeChange", 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -734,7 +742,7 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
||||
override fun onRemove(world: World<*, *>, reason: RemovalReason) {
|
||||
super.onRemove(world, reason)
|
||||
|
||||
val doSmash = config.value.smashable && (health <= 0.0 || lookupProperty("smashOnBreak") { JsonPrimitive(config.value.smashOnBreak) }.asBoolean)
|
||||
val doSmash = reason.dying && config.value.smashable && (health <= 0.0 || lookupProperty("smashOnBreak") { JsonPrimitive(config.value.smashOnBreak) }.asBoolean)
|
||||
|
||||
fun spawnRandomItems(poolName: String, optionsName: String, seedName: String): Boolean {
|
||||
val dropPool = lookupProperty(poolName) { JsonPrimitive("") }.asString
|
||||
@ -774,7 +782,8 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
||||
}
|
||||
|
||||
if (!isRemote && reason.dying) {
|
||||
lua.invokeGlobal("die", health <= 0.0)
|
||||
lua.push(health <= 0.0)
|
||||
lua.invokeGlobal("die", 1)
|
||||
|
||||
try {
|
||||
if (doSmash) {
|
||||
@ -791,7 +800,8 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
||||
}
|
||||
|
||||
parameters.remove("owner")
|
||||
parameters["scriptStorage"] = (lua.globals["storage"] as Table).toJson(true)
|
||||
lua.loadGlobal("storage")
|
||||
parameters["scriptStorage"] = lua.popJson()
|
||||
}
|
||||
|
||||
val entity = ItemDropEntity(ItemDescriptor(config.key, 1L, parameters))
|
||||
@ -805,6 +815,14 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
||||
}
|
||||
}
|
||||
|
||||
override fun uninit(world: World<*, *>) {
|
||||
super.uninit(world)
|
||||
|
||||
if (!isRemote) {
|
||||
lua.close()
|
||||
}
|
||||
}
|
||||
|
||||
override fun damageTileEntity(damageSpaces: List<Vector2i>, source: Vector2d, damage: TileDamage): Boolean {
|
||||
if (unbreakable)
|
||||
return false
|
||||
@ -864,15 +882,6 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
||||
return luaMessageHandler.handle(message, connectionID == connection, arguments)
|
||||
}
|
||||
|
||||
override fun callScript(fnName: String, arguments: JsonArray): JsonElement {
|
||||
//return lua.invokeGlobal(fnName, *arguments)
|
||||
TODO()
|
||||
}
|
||||
|
||||
override fun evalScript(code: String): Array<Any?> {
|
||||
return lua.eval(code)
|
||||
}
|
||||
|
||||
init {
|
||||
isInteractive = !interactAction.isJsonNull
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user