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.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.apache.logging.log4j.LogManager
|
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.kstarbound.math.AABBTypeAdapter
|
||||||
import ru.dbotthepony.kommons.gson.EitherTypeAdapter
|
import ru.dbotthepony.kommons.gson.EitherTypeAdapter
|
||||||
import ru.dbotthepony.kommons.gson.KOptionalTypeAdapter
|
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()
|
private val luaScriptCache = Caffeine.newBuilder()
|
||||||
.maximumSize(LUA_SCRIPT_CACHE_SIZE)
|
.maximumSize(LUA_SCRIPT_CACHE_SIZE)
|
||||||
.expireAfterAccess(Duration.ofHours(1L))
|
.expireAfterAccess(Duration.ofHours(1L))
|
||||||
|
@ -10,11 +10,6 @@ import com.google.gson.TypeAdapter
|
|||||||
import com.google.gson.stream.JsonReader
|
import com.google.gson.stream.JsonReader
|
||||||
import com.google.gson.stream.JsonWriter
|
import com.google.gson.stream.JsonWriter
|
||||||
import org.apache.logging.log4j.LogManager
|
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.contains
|
||||||
import ru.dbotthepony.kommons.gson.get
|
import ru.dbotthepony.kommons.gson.get
|
||||||
import ru.dbotthepony.kommons.gson.value
|
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.mergeJson
|
||||||
import ru.dbotthepony.kstarbound.json.readJsonElement
|
import ru.dbotthepony.kstarbound.json.readJsonElement
|
||||||
import ru.dbotthepony.kstarbound.json.writeJsonElement
|
import ru.dbotthepony.kstarbound.json.writeJsonElement
|
||||||
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
|
||||||
import ru.dbotthepony.kstarbound.lua.LuaThread
|
import ru.dbotthepony.kstarbound.lua.LuaThread
|
||||||
import ru.dbotthepony.kstarbound.lua.LuaType
|
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.DataInputStream
|
||||||
import java.io.DataOutputStream
|
import java.io.DataOutputStream
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
@ -190,74 +177,6 @@ fun ItemDescriptor(args: LuaThread.ArgStack): ItemDescriptor {
|
|||||||
return ItemDescriptor(args.lua, args.position)
|
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 {
|
fun ItemDescriptor(stream: DataInputStream): ItemDescriptor {
|
||||||
val name = stream.readInternedString()
|
val name = stream.readInternedString()
|
||||||
val count = stream.readVarLong()
|
val count = stream.readVarLong()
|
||||||
@ -324,18 +243,6 @@ data class ItemDescriptor(
|
|||||||
return !isEmpty
|
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 {
|
fun build(level: Double? = null, seed: Long? = null, random: RandomGenerator? = null): ItemStack {
|
||||||
try {
|
try {
|
||||||
val (jConfig, jParameters) = buildConfig(level, seed, random)
|
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.JsonPrimitive
|
||||||
import com.google.gson.TypeAdapter
|
import com.google.gson.TypeAdapter
|
||||||
import com.google.gson.annotations.JsonAdapter
|
import com.google.gson.annotations.JsonAdapter
|
||||||
import com.google.gson.internal.bind.TypeAdapters
|
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
import com.google.gson.stream.JsonReader
|
import com.google.gson.stream.JsonReader
|
||||||
import com.google.gson.stream.JsonWriter
|
import com.google.gson.stream.JsonWriter
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
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.consumeNull
|
||||||
import ru.dbotthepony.kommons.gson.contains
|
import ru.dbotthepony.kommons.gson.contains
|
||||||
import ru.dbotthepony.kommons.gson.get
|
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.io.writeVarLong
|
||||||
import ru.dbotthepony.kommons.math.RGBAColor
|
import ru.dbotthepony.kommons.math.RGBAColor
|
||||||
import ru.dbotthepony.kommons.util.Either
|
import ru.dbotthepony.kommons.util.Either
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2f
|
|
||||||
import ru.dbotthepony.kstarbound.Globals
|
import ru.dbotthepony.kstarbound.Globals
|
||||||
import ru.dbotthepony.kstarbound.Registry
|
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
import ru.dbotthepony.kstarbound.defs.Drawable
|
import ru.dbotthepony.kstarbound.defs.Drawable
|
||||||
import ru.dbotthepony.kstarbound.defs.actor.PersistentStatusEffect
|
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.mergeJson
|
||||||
import ru.dbotthepony.kstarbound.json.stream
|
import ru.dbotthepony.kstarbound.json.stream
|
||||||
import ru.dbotthepony.kstarbound.json.writeJsonElement
|
import ru.dbotthepony.kstarbound.json.writeJsonElement
|
||||||
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
|
||||||
import ru.dbotthepony.kstarbound.lua.LuaThread
|
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.network.syncher.NetworkedElement
|
||||||
import ru.dbotthepony.kstarbound.util.AssetPathStack
|
import ru.dbotthepony.kstarbound.util.AssetPathStack
|
||||||
import ru.dbotthepony.kstarbound.util.ManualLazy
|
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)
|
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 {
|
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 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 != updated) {
|
||||||
if (descriptor.name == updated.name) {
|
if (descriptor.name == updated.name) {
|
||||||
@ -374,14 +368,6 @@ open class ItemStack(val entry: ItemRegistry.Entry, val config: JsonObject, para
|
|||||||
return createDescriptor().toJson()
|
return createDescriptor().toJson()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toTable(allocator: TableFactory): Table? {
|
|
||||||
if (isEmpty) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return createDescriptor().toTable(allocator)
|
|
||||||
}
|
|
||||||
|
|
||||||
class Adapter(gson: Gson) : TypeAdapter<ItemStack>() {
|
class Adapter(gson: Gson) : TypeAdapter<ItemStack>() {
|
||||||
override fun write(out: JsonWriter, value: ItemStack?) {
|
override fun write(out: JsonWriter, value: ItemStack?) {
|
||||||
val json = value?.toJson()
|
val json = value?.toJson()
|
||||||
|
@ -1,22 +1,7 @@
|
|||||||
package ru.dbotthepony.kstarbound.lua
|
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.math.RGBAColor
|
||||||
import ru.dbotthepony.kommons.util.Either
|
import ru.dbotthepony.kommons.util.Either
|
||||||
import ru.dbotthepony.kstarbound.math.AABB
|
|
||||||
import ru.dbotthepony.kommons.util.IStruct2d
|
import ru.dbotthepony.kommons.util.IStruct2d
|
||||||
import ru.dbotthepony.kommons.util.IStruct2f
|
import ru.dbotthepony.kommons.util.IStruct2f
|
||||||
import ru.dbotthepony.kommons.util.IStruct2i
|
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.IStruct4f
|
||||||
import ru.dbotthepony.kommons.util.IStruct4i
|
import ru.dbotthepony.kommons.util.IStruct4i
|
||||||
import ru.dbotthepony.kstarbound.Registry
|
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.Vector2d
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2f
|
import ru.dbotthepony.kstarbound.math.vector.Vector2f
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2i
|
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
|
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
|
// TODO: error reporting when argument was provided, but it is malformed
|
||||||
// currently, invalid data gets silently discarded, and treated as "no value" aka null
|
// currently, invalid data gets silently discarded, and treated as "no value" aka null
|
||||||
fun LuaThread.getPoly(stackIndex: Int = -1): Poly? {
|
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
|
package ru.dbotthepony.kstarbound.lua
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray
|
||||||
import com.google.gson.JsonElement
|
import com.google.gson.JsonElement
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import ru.dbotthepony.kstarbound.util.ActionPacer
|
import ru.dbotthepony.kstarbound.util.ActionPacer
|
||||||
import ru.dbotthepony.kstarbound.util.sbIntern
|
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 val handlers = HashMap<String, LuaHandle>()
|
||||||
|
|
||||||
private fun setHandler(args: LuaThread.ArgStack): Int {
|
private fun setHandler(args: LuaThread.ArgStack): Int {
|
||||||
@ -37,7 +38,7 @@ class LuaMessageHandlerComponent(lua: LuaThread, val nameProvider: () -> String)
|
|||||||
return handlers[name]
|
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 handler = lookupHandler(message) ?: return null
|
||||||
val top = lua.stackTop
|
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 {
|
companion object {
|
||||||
val LOGGER = LogManager.getLogger()
|
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.LuaFuture
|
||||||
import ru.dbotthepony.kstarbound.lua.userdata.LuaPathFinder
|
import ru.dbotthepony.kstarbound.lua.userdata.LuaPathFinder
|
||||||
import ru.dbotthepony.kstarbound.util.random.random
|
import ru.dbotthepony.kstarbound.util.random.random
|
||||||
|
import java.io.Closeable
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue
|
import java.util.concurrent.ConcurrentLinkedQueue
|
||||||
import java.util.random.RandomGenerator
|
import java.util.random.RandomGenerator
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
class LuaSharedState(val handlesThread: LuaThread) {
|
class LuaSharedState(val handlesThread: LuaThread) : Closeable {
|
||||||
private val pendingFree = ConcurrentLinkedQueue<Int>()
|
private val pendingFree = ConcurrentLinkedQueue<Int>()
|
||||||
private val freeHandles = IntAVLTreeSet()
|
private val freeHandles = IntAVLTreeSet()
|
||||||
private var nextHandle = 0
|
private var nextHandle = 0
|
||||||
@ -21,6 +22,15 @@ class LuaSharedState(val handlesThread: LuaThread) {
|
|||||||
var commonHandles by Delegates.notNull<CommonHandleRegistry>()
|
var commonHandles by Delegates.notNull<CommonHandleRegistry>()
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
var isValid = true
|
||||||
|
private set
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
if (!isValid) return
|
||||||
|
isValid = false
|
||||||
|
namedHandles.clear()
|
||||||
|
}
|
||||||
|
|
||||||
fun initializeHandles(mainThread: LuaThread) {
|
fun initializeHandles(mainThread: LuaThread) {
|
||||||
val future = LuaFuture.initializeHandle(mainThread)
|
val future = LuaFuture.initializeHandle(mainThread)
|
||||||
val pathFinder = LuaPathFinder.initializeHandle(mainThread)
|
val pathFinder = LuaPathFinder.initializeHandle(mainThread)
|
||||||
@ -32,6 +42,7 @@ class LuaSharedState(val handlesThread: LuaThread) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun freeHandle(handle: Int, key: Any?) {
|
fun freeHandle(handle: Int, key: Any?) {
|
||||||
|
if (!isValid) return
|
||||||
pendingFree.add(handle)
|
pendingFree.add(handle)
|
||||||
|
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
@ -40,6 +51,7 @@ class LuaSharedState(val handlesThread: LuaThread) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun cleanup() {
|
fun cleanup() {
|
||||||
|
check(isValid) { "Shared state is no longer valid" }
|
||||||
if (handlesInUse == 0) return
|
if (handlesInUse == 0) return
|
||||||
var handle = pendingFree.poll()
|
var handle = pendingFree.poll()
|
||||||
|
|
||||||
@ -55,6 +67,7 @@ class LuaSharedState(val handlesThread: LuaThread) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun allocateHandle(name: Any?): LuaHandle {
|
fun allocateHandle(name: Any?): LuaHandle {
|
||||||
|
check(isValid) { "Shared state is no longer valid" }
|
||||||
require(name == null || name !in namedHandles) { "Named handle '$name' already exists" }
|
require(name == null || name !in namedHandles) { "Named handle '$name' already exists" }
|
||||||
handlesInUse++
|
handlesInUse++
|
||||||
|
|
||||||
@ -80,10 +93,12 @@ class LuaSharedState(val handlesThread: LuaThread) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getNamedHandle(key: Any): LuaHandle {
|
fun getNamedHandle(key: Any): LuaHandle {
|
||||||
|
check(isValid) { "Shared state is no longer valid" }
|
||||||
return namedHandles[key] ?: throw NoSuchElementException("No such handle: $key")
|
return namedHandles[key] ?: throw NoSuchElementException("No such handle: $key")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findNamedHandle(key: Any): LuaHandle? {
|
fun findNamedHandle(key: Any): LuaHandle? {
|
||||||
|
check(isValid) { "Shared state is no longer valid" }
|
||||||
return namedHandles[key]
|
return namedHandles[key]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,10 @@ class LuaThread private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
this.cleanable?.clean()
|
if (cleanable != null) {
|
||||||
|
cleanable!!.clean()
|
||||||
|
sharedState.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val stackTop: Int get() {
|
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
|
* 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
|
val top = stackTop
|
||||||
|
|
||||||
try {
|
try {
|
||||||
load(chunk, name)
|
load(chunk, name)
|
||||||
call()
|
call(numResults = 1)
|
||||||
|
return getJson() ?: JsonNull.INSTANCE
|
||||||
} finally {
|
} finally {
|
||||||
setTop(top)
|
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 ru.dbotthepony.kstarbound.world.physics.Poly
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
|
|
||||||
class MovementControllerBindings(val self: ActorMovementController) {
|
class MovementControllerBindings(val self: ActorMovementController, lua: LuaThread) {
|
||||||
private fun mass(args: LuaThread.ArgStack): Int {
|
private fun mass(args: LuaThread.ArgStack): Int {
|
||||||
args.lua.push(self.mass)
|
args.lua.push(self.mass)
|
||||||
return 1
|
return 1
|
||||||
@ -303,7 +303,7 @@ class MovementControllerBindings(val self: ActorMovementController) {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fun init(lua: LuaThread) {
|
init {
|
||||||
lua.pushTable()
|
lua.pushTable()
|
||||||
lua.dup()
|
lua.dup()
|
||||||
lua.storeGlobal("mcontroller")
|
lua.storeGlobal("mcontroller")
|
||||||
|
@ -4,7 +4,6 @@ import com.google.gson.JsonNull
|
|||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
|
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import org.classdump.luna.LuaRuntimeException
|
|
||||||
import ru.dbotthepony.kstarbound.Globals
|
import ru.dbotthepony.kstarbound.Globals
|
||||||
import ru.dbotthepony.kstarbound.Registries
|
import ru.dbotthepony.kstarbound.Registries
|
||||||
import ru.dbotthepony.kstarbound.Registry
|
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> {
|
private fun <T : Any> lookupStrict(registry: Registry<T>, args: LuaThread.ArgStack): Registry.Entry<T> {
|
||||||
return when (val type = args.peek()) {
|
return when (val type = args.peek()) {
|
||||||
LuaType.NUMBER -> {
|
LuaType.NUMBER -> {
|
||||||
val key = args.nextInt()
|
registry.getOrThrow(args.nextInt())
|
||||||
registry[key] ?: throw LuaRuntimeException("No such ${registry.name}: $key")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LuaType.STRING -> {
|
LuaType.STRING -> {
|
||||||
val key = args.nextString()
|
registry.getOrThrow(args.nextString())
|
||||||
registry[key] ?: throw LuaRuntimeException("No such ${registry.name}: $key")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> throw IllegalArgumentException("Invalid registry key type: $type")
|
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 {
|
private fun registryDef(registry: Registry<*>): LuaThread.Fn {
|
||||||
return LuaThread.Fn { args ->
|
return LuaThread.Fn { args ->
|
||||||
val name = args.nextString()
|
val value = registry.getOrThrow(args.nextString())
|
||||||
val value = registry[name] ?: throw LuaRuntimeException("No such ${registry.name}: $name")
|
|
||||||
args.lua.push(value.json)
|
args.lua.push(value.json)
|
||||||
return@Fn 1
|
return@Fn 1
|
||||||
}
|
}
|
||||||
@ -252,7 +248,7 @@ private fun createItem(args: LuaThread.ArgStack): Int {
|
|||||||
val seed = args.nextOptionalLong()
|
val seed = args.nextOptionalLong()
|
||||||
|
|
||||||
if (desc.name !in ItemRegistry) {
|
if (desc.name !in ItemRegistry) {
|
||||||
throw LuaRuntimeException("No such item ${desc.name}")
|
throw NoSuchElementException("No such item ${desc.name}")
|
||||||
} else {
|
} else {
|
||||||
val (_, params) = desc.buildConfig(level, seed)
|
val (_, params) = desc.buildConfig(level, seed)
|
||||||
|
|
||||||
@ -348,7 +344,7 @@ private fun loadVersionedJson(args: LuaThread.ArgStack): Int {
|
|||||||
private fun evalFunction(args: LuaThread.ArgStack): Int {
|
private fun evalFunction(args: LuaThread.ArgStack): Int {
|
||||||
val name = args.nextString()
|
val name = args.nextString()
|
||||||
val value = args.nextDouble()
|
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))
|
args.lua.push(fn.value.evaluate(value))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@ -357,7 +353,7 @@ private fun evalFunction2(args: LuaThread.ArgStack): Int {
|
|||||||
val name = args.nextString()
|
val name = args.nextString()
|
||||||
val value = args.nextDouble()
|
val value = args.nextDouble()
|
||||||
val value2 = 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))
|
args.lua.push(fn.value.evaluate(value, value2))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@ -365,7 +361,7 @@ private fun evalFunction2(args: LuaThread.ArgStack): Int {
|
|||||||
private fun imageSize(args: LuaThread.ArgStack): Int {
|
private fun imageSize(args: LuaThread.ArgStack): Int {
|
||||||
val name = args.nextString()
|
val name = args.nextString()
|
||||||
val ref = SpriteReference.create(name)
|
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.pushTable(2)
|
||||||
args.lua.setTableValue(1, sprite.width)
|
args.lua.setTableValue(1, sprite.width)
|
||||||
args.lua.setTableValue(2, sprite.height)
|
args.lua.setTableValue(2, sprite.height)
|
||||||
@ -374,7 +370,7 @@ private fun imageSize(args: LuaThread.ArgStack): Int {
|
|||||||
|
|
||||||
private fun imageSpaces(args: LuaThread.ArgStack): Int {
|
private fun imageSpaces(args: LuaThread.ArgStack): Int {
|
||||||
val name = args.nextString()
|
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 pixelOffset = args.nextVector2i()
|
||||||
val fillFactor = args.nextDouble()
|
val fillFactor = args.nextDouble()
|
||||||
@ -394,7 +390,7 @@ private fun imageSpaces(args: LuaThread.ArgStack): Int {
|
|||||||
|
|
||||||
private fun nonEmptyRegion(args: LuaThread.ArgStack): Int {
|
private fun nonEmptyRegion(args: LuaThread.ArgStack): Int {
|
||||||
val name = args.nextString()
|
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)
|
args.lua.push(image.nonEmptyRegion)
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package ru.dbotthepony.kstarbound.lua.bindings
|
package ru.dbotthepony.kstarbound.lua.bindings
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import org.classdump.luna.LuaRuntimeException
|
|
||||||
import ru.dbotthepony.kommons.util.KOptional
|
import ru.dbotthepony.kommons.util.KOptional
|
||||||
import ru.dbotthepony.kstarbound.Registries
|
import ru.dbotthepony.kstarbound.Registries
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
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.nextVector2d
|
||||||
import ru.dbotthepony.kstarbound.lua.nextVector2i
|
import ru.dbotthepony.kstarbound.lua.nextVector2i
|
||||||
import ru.dbotthepony.kstarbound.lua.push
|
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.LuaFuture
|
||||||
import ru.dbotthepony.kstarbound.lua.userdata.push
|
import ru.dbotthepony.kstarbound.lua.userdata.push
|
||||||
import ru.dbotthepony.kstarbound.server.world.ServerWorld
|
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 {
|
private fun setSkyTime(self: ServerWorld, args: LuaThread.ArgStack): Int {
|
||||||
val time = args.nextDouble()
|
val time = args.nextDouble()
|
||||||
|
require(time >= 0.0) { "Negative time? $time" }
|
||||||
if (time < 0.0)
|
|
||||||
throw LuaRuntimeException("Negative time? $time")
|
|
||||||
|
|
||||||
self.sky.time = time
|
self.sky.time = time
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -301,7 +296,7 @@ private fun setDungeonGravity(self: ServerWorld, args: LuaThread.ArgStack): Int
|
|||||||
} else if (peek == LuaType.TABLE) {
|
} else if (peek == LuaType.TABLE) {
|
||||||
self.setDungeonGravity(id, args.nextVector2d())
|
self.setDungeonGravity(id, args.nextVector2d())
|
||||||
} else {
|
} else {
|
||||||
throw LuaRuntimeException("Illegal gravity argument to setDungeonGravity: $peek")
|
throw IllegalArgumentException("Illegal gravity argument to setDungeonGravity: $peek")
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
@ -336,7 +331,7 @@ private fun enqueuePlacement(self: ServerWorld, args: LuaThread.ArgStack): Int {
|
|||||||
|
|
||||||
for (v in distributions) {
|
for (v in distributions) {
|
||||||
// original engine treats distributions table as if it was originating from biome json files
|
// 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))
|
val prepared = unprepared.create(BiomeDefinition.CreationParams(hueShift = 0.0, random = args.lua.random))
|
||||||
items.add(prepared)
|
items.add(prepared)
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ import ru.dbotthepony.kstarbound.json.mergeJson
|
|||||||
import ru.dbotthepony.kstarbound.lua.LuaHandle
|
import ru.dbotthepony.kstarbound.lua.LuaHandle
|
||||||
import ru.dbotthepony.kstarbound.lua.LuaThread
|
import ru.dbotthepony.kstarbound.lua.LuaThread
|
||||||
import ru.dbotthepony.kstarbound.lua.LuaType
|
import ru.dbotthepony.kstarbound.lua.LuaType
|
||||||
import ru.dbotthepony.kstarbound.lua.toJsonFromLua
|
|
||||||
import ru.dbotthepony.kstarbound.util.valueOf
|
import ru.dbotthepony.kstarbound.util.valueOf
|
||||||
|
|
||||||
private fun replaceBehaviorTag(parameter: NodeParameterValue, treeParameters: Map<String, NodeParameterValue>): NodeParameterValue {
|
private fun replaceBehaviorTag(parameter: NodeParameterValue, treeParameters: Map<String, NodeParameterValue>): NodeParameterValue {
|
||||||
@ -246,7 +245,7 @@ private fun createBehaviorTree(args: LuaThread.ArgStack): Int {
|
|||||||
} else {
|
} else {
|
||||||
mergedParams = Starbound.gson.fromJsonFast(base.json.deepCopy().also {
|
mergedParams = Starbound.gson.fromJsonFast(base.json.deepCopy().also {
|
||||||
it as JsonObject
|
it as JsonObject
|
||||||
it["parameters"] = mergeJson(it["parameters"] ?: JsonNull.INSTANCE, toJsonFromLua(parameters))
|
it["parameters"] = mergeJson(it["parameters"] ?: JsonNull.INSTANCE, parameters)
|
||||||
}, BehaviorDefinition::class.java)
|
}, BehaviorDefinition::class.java)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -5,8 +5,6 @@ import kotlinx.coroutines.coroutineScope
|
|||||||
import kotlinx.coroutines.future.await
|
import kotlinx.coroutines.future.await
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.apache.logging.log4j.LogManager
|
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.math.vector.Vector2i
|
||||||
import ru.dbotthepony.kstarbound.world.entities.tile.WorldObject
|
import ru.dbotthepony.kstarbound.world.entities.tile.WorldObject
|
||||||
|
|
||||||
@ -99,7 +97,10 @@ class LegacyWireProcessor(val world: ServerWorld) {
|
|||||||
if (newState != node.state) {
|
if (newState != node.state) {
|
||||||
try {
|
try {
|
||||||
node.state = newState
|
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) {
|
} catch (err: Throwable) {
|
||||||
LOGGER.error("Exception while updating wire state of $entity at ${entity.tilePosition} (input node index $i)", err)
|
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 {
|
companion object {
|
||||||
val NODE_KEY: ByteString = ByteString.of("node")
|
|
||||||
val LEVEL_KEY: ByteString = ByteString.of("level")
|
|
||||||
private val LOGGER = LogManager.getLogger()
|
private val LOGGER = LogManager.getLogger()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,6 +215,8 @@ class ServerWorld private constructor(
|
|||||||
it.client.enqueueWarp(WarpAlias.Return)
|
it.client.enqueueWarp(WarpAlias.Return)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callUninitOnEntities()
|
||||||
|
|
||||||
if (!uncleanShutdown) {
|
if (!uncleanShutdown) {
|
||||||
saveMetadata()
|
saveMetadata()
|
||||||
storage.commit()
|
storage.commit()
|
||||||
|
@ -4,7 +4,6 @@ import com.google.common.collect.ImmutableCollection
|
|||||||
import com.google.gson.JsonArray
|
import com.google.gson.JsonArray
|
||||||
import com.google.gson.JsonElement
|
import com.google.gson.JsonElement
|
||||||
import it.unimi.dsi.fastutil.bytes.ByteConsumer
|
import it.unimi.dsi.fastutil.bytes.ByteConsumer
|
||||||
import org.classdump.luna.ByteString
|
|
||||||
import ru.dbotthepony.kommons.util.IStruct2d
|
import ru.dbotthepony.kommons.util.IStruct2d
|
||||||
import ru.dbotthepony.kommons.util.IStruct2f
|
import ru.dbotthepony.kommons.util.IStruct2f
|
||||||
import ru.dbotthepony.kommons.util.IStruct2i
|
import ru.dbotthepony.kommons.util.IStruct2i
|
||||||
@ -76,7 +75,6 @@ fun staticRandom32FromList(values: Iterable<Any?>): Int {
|
|||||||
for (value in values) {
|
for (value in values) {
|
||||||
when (value) {
|
when (value) {
|
||||||
is String -> digest.update(value.toByteArray())
|
is String -> digest.update(value.toByteArray())
|
||||||
is ByteString -> digest.update(value.bytes)
|
|
||||||
is Byte -> digest.update(value)
|
is Byte -> digest.update(value)
|
||||||
is Boolean -> digest.update(if (value) 1 else 0)
|
is Boolean -> digest.update(if (value) 1 else 0)
|
||||||
is Short -> toBytes(digest::update, value)
|
is Short -> toBytes(digest::update, value)
|
||||||
@ -134,7 +132,6 @@ fun staticRandom64FromList(values: Iterable<Any?>): Long {
|
|||||||
for (value in values) {
|
for (value in values) {
|
||||||
when (value) {
|
when (value) {
|
||||||
is String -> digest.update(value.toByteArray())
|
is String -> digest.update(value.toByteArray())
|
||||||
is ByteString -> digest.update(value.bytes)
|
|
||||||
is Byte -> digest.update(value)
|
is Byte -> digest.update(value)
|
||||||
is Boolean -> digest.update(if (value) 1 else 0)
|
is Boolean -> digest.update(if (value) 1 else 0)
|
||||||
is Short -> toBytes(digest::update, value)
|
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()
|
open var centralStructure: WorldStructure = WorldStructure()
|
||||||
protected set
|
protected set
|
||||||
|
|
||||||
|
@ -175,10 +175,21 @@ abstract class AbstractEntity : Comparable<AbstractEntity> {
|
|||||||
protected open fun onJoinWorld(world: World<*, *>) { }
|
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) { }
|
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())
|
val networkGroup = MasterElement(NetworkedGroup())
|
||||||
abstract fun writeNetwork(stream: DataOutputStream, isLegacy: Boolean)
|
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)
|
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?.remove()
|
||||||
spatialEntry = null
|
spatialEntry = null
|
||||||
innerWorld = null
|
innerWorld = null
|
||||||
|
@ -2,23 +2,15 @@ package ru.dbotthepony.kstarbound.world.entities
|
|||||||
|
|
||||||
import com.google.gson.JsonArray
|
import com.google.gson.JsonArray
|
||||||
import com.google.gson.JsonElement
|
import com.google.gson.JsonElement
|
||||||
import org.classdump.luna.ByteString
|
import com.google.gson.JsonNull
|
||||||
import org.classdump.luna.Table
|
import com.google.gson.JsonPrimitive
|
||||||
import ru.dbotthepony.kommons.util.IStruct2d
|
|
||||||
import ru.dbotthepony.kstarbound.defs.DamageNotification
|
|
||||||
import ru.dbotthepony.kstarbound.defs.DamageSource
|
|
||||||
import ru.dbotthepony.kstarbound.defs.Drawable
|
import ru.dbotthepony.kstarbound.defs.Drawable
|
||||||
import ru.dbotthepony.kstarbound.defs.InteractAction
|
import ru.dbotthepony.kstarbound.defs.InteractAction
|
||||||
import ru.dbotthepony.kstarbound.defs.InteractRequest
|
import ru.dbotthepony.kstarbound.defs.InteractRequest
|
||||||
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
||||||
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
import ru.dbotthepony.kstarbound.lua.setTableValue
|
||||||
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.math.vector.Vector2d
|
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||||
import ru.dbotthepony.kstarbound.network.packets.DamageNotificationPacket
|
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.network.packets.HitRequestPacket
|
||||||
import ru.dbotthepony.kstarbound.world.Direction
|
import ru.dbotthepony.kstarbound.world.Direction
|
||||||
import ru.dbotthepony.kstarbound.world.World
|
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
|
// ticked manually in final classes, to ensure proper order of operations
|
||||||
val effects = EffectEmitter(this)
|
val effects = EffectEmitter(this)
|
||||||
|
|
||||||
abstract val lua: LuaEnvironment
|
|
||||||
|
|
||||||
override fun move(delta: Double) {
|
override fun move(delta: Double) {
|
||||||
statusController.applyMovementControls()
|
statusController.applyMovementControls()
|
||||||
super.move(delta)
|
super.move(delta)
|
||||||
@ -66,6 +56,11 @@ abstract class ActorEntity : DynamicEntity(), InteractiveEntity, ScriptedEntity
|
|||||||
statusController.init()
|
statusController.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun uninit(world: World<*, *>) {
|
||||||
|
super.uninit(world)
|
||||||
|
statusController.uninit()
|
||||||
|
}
|
||||||
|
|
||||||
override fun tick(delta: Double) {
|
override fun tick(delta: Double) {
|
||||||
super.tick(delta)
|
super.tick(delta)
|
||||||
|
|
||||||
@ -114,31 +109,34 @@ abstract class ActorEntity : DynamicEntity(), InteractiveEntity, ScriptedEntity
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun callScript(fnName: String, arguments: JsonArray): JsonElement {
|
override fun callScript(fnName: String, arguments: JsonArray): JsonElement {
|
||||||
//require(isLocal) { "Calling script on remote entity" }
|
require(isLocal) { "Calling script on remote entity" }
|
||||||
//return lua.invokeGlobal(fnName, *arguments)
|
return super.callScript(fnName, arguments)
|
||||||
TODO()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun evalScript(code: String): Array<Any?> {
|
override fun evalScript(code: String): JsonElement {
|
||||||
require(isLocal) { "Calling script on remote entity" }
|
require(isLocal) { "Calling script on remote entity" }
|
||||||
return lua.eval(code)
|
return super.evalScript(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun interact(request: InteractRequest): InteractAction {
|
override fun interact(request: InteractRequest): InteractAction {
|
||||||
val result = lua.invokeGlobal("interact", lua.tableMapOf(
|
val result = lua.invokeGlobal("interact", 1, {
|
||||||
"sourceId" to request.source,
|
pushTable(hashSize = 2)
|
||||||
"sourcePosition" to lua.from(request.sourcePos)
|
|
||||||
))
|
|
||||||
|
|
||||||
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
|
return InteractAction.NONE
|
||||||
|
|
||||||
val value = result[0]
|
val value = result.value
|
||||||
|
|
||||||
if (value is ByteString)
|
if (value is JsonPrimitive)
|
||||||
return InteractAction(value.decode(), entityID)
|
return InteractAction(value.asString, entityID)
|
||||||
|
|
||||||
value as Table
|
value as JsonArray
|
||||||
return InteractAction((value[1L] as ByteString).decode(), entityID, toJsonFromLua(value[2L]))
|
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
|
abstract val movement: MovementController
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called in multiple threads
|
* Called in multiple threads, when [isLocal] is true
|
||||||
*/
|
*/
|
||||||
protected open fun move(delta: Double) {
|
protected open fun move(delta: Double) {
|
||||||
movement.move(delta)
|
movement.move(delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called in multiple threads
|
* Called in multiple threads, when [isLocal] is false
|
||||||
*/
|
*/
|
||||||
protected open fun moveRemote(delta: Double) {
|
protected open fun moveRemote(delta: Double) {
|
||||||
movement.tickRemote(delta)
|
movement.tickRemote(delta)
|
||||||
|
@ -8,11 +8,8 @@ import com.google.gson.JsonElement
|
|||||||
import com.google.gson.JsonNull
|
import com.google.gson.JsonNull
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
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.get
|
||||||
import ru.dbotthepony.kommons.gson.set
|
import ru.dbotthepony.kommons.gson.set
|
||||||
import ru.dbotthepony.kstarbound.io.map
|
|
||||||
import ru.dbotthepony.kommons.util.Either
|
import ru.dbotthepony.kommons.util.Either
|
||||||
import ru.dbotthepony.kommons.util.getValue
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
import ru.dbotthepony.kommons.util.setValue
|
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.EntityDamageTeam
|
||||||
import ru.dbotthepony.kstarbound.defs.EntityType
|
import ru.dbotthepony.kstarbound.defs.EntityType
|
||||||
import ru.dbotthepony.kstarbound.defs.HitType
|
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.JumpProfile
|
||||||
import ru.dbotthepony.kstarbound.defs.PhysicsForceRegion
|
import ru.dbotthepony.kstarbound.defs.PhysicsForceRegion
|
||||||
import ru.dbotthepony.kstarbound.defs.actor.StatModifier
|
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.fromJsonFast
|
||||||
import ru.dbotthepony.kstarbound.io.DoubleValueCodec
|
import ru.dbotthepony.kstarbound.io.DoubleValueCodec
|
||||||
import ru.dbotthepony.kstarbound.io.FloatValueCodec
|
import ru.dbotthepony.kstarbound.io.FloatValueCodec
|
||||||
|
import ru.dbotthepony.kstarbound.io.map
|
||||||
import ru.dbotthepony.kstarbound.io.nullable
|
import ru.dbotthepony.kstarbound.io.nullable
|
||||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||||
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
|
||||||
import ru.dbotthepony.kstarbound.lua.LuaMessageHandlerComponent
|
import ru.dbotthepony.kstarbound.lua.LuaMessageHandlerComponent
|
||||||
|
import ru.dbotthepony.kstarbound.lua.LuaThread
|
||||||
import ru.dbotthepony.kstarbound.lua.LuaUpdateComponent
|
import ru.dbotthepony.kstarbound.lua.LuaUpdateComponent
|
||||||
import ru.dbotthepony.kstarbound.lua.bindings.MovementControllerBindings
|
import ru.dbotthepony.kstarbound.lua.bindings.MovementControllerBindings
|
||||||
import ru.dbotthepony.kstarbound.lua.bindings.provideAnimatorBindings
|
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.bindings.provideEntityBindings
|
||||||
import ru.dbotthepony.kstarbound.lua.from
|
import ru.dbotthepony.kstarbound.lua.userdata.provideBehaviorBindings
|
||||||
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.math.AABB
|
import ru.dbotthepony.kstarbound.math.AABB
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||||
import ru.dbotthepony.kstarbound.network.packets.DamageRequestPacket
|
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.util.random.random
|
||||||
import ru.dbotthepony.kstarbound.world.Direction
|
import ru.dbotthepony.kstarbound.world.Direction
|
||||||
import ru.dbotthepony.kstarbound.world.World
|
import ru.dbotthepony.kstarbound.world.World
|
||||||
import ru.dbotthepony.kstarbound.world.entities.api.ScriptedEntity
|
|
||||||
import ru.dbotthepony.kstarbound.world.physics.Poly
|
import ru.dbotthepony.kstarbound.world.physics.Poly
|
||||||
import java.io.DataOutputStream
|
import java.io.DataOutputStream
|
||||||
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
class MonsterEntity(val variant: MonsterVariant, level: Double? = null) : ActorEntity() {
|
class MonsterEntity(val variant: MonsterVariant, level: Double? = null) : ActorEntity() {
|
||||||
override val type: EntityType
|
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))
|
team.accept(EntityDamageTeam(variant.commonParameters.damageTeamType, variant.commonParameters.damageTeam))
|
||||||
}
|
}
|
||||||
|
|
||||||
override val lua = LuaEnvironment()
|
private var scriptStorage = JsonObject()
|
||||||
val luaUpdate = LuaUpdateComponent(lua)
|
override var lua by Delegates.notNull<LuaThread>()
|
||||||
val luaMovement = MovementControllerBindings(movement)
|
private set
|
||||||
val luaMessages = LuaMessageHandlerComponent(lua) { toString() }
|
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)
|
val animator = Animator(variant.animationConfig)
|
||||||
|
|
||||||
init {
|
|
||||||
lua.globals["storage"] = lua.newTable()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun move(delta: Double) {
|
override fun move(delta: Double) {
|
||||||
luaMovement.apply()
|
luaMovement.apply()
|
||||||
super.move(delta)
|
super.move(delta)
|
||||||
@ -169,7 +161,7 @@ class MonsterEntity(val variant: MonsterVariant, level: Double? = null) : ActorE
|
|||||||
this.uniqueID.value = deserialized.uniqueId
|
this.uniqueID.value = deserialized.uniqueId
|
||||||
this.team.value = deserialized.team
|
this.team.value = deserialized.team
|
||||||
|
|
||||||
this.lua.globals["storage"] = lua.from(deserialized.scriptStorage)
|
scriptStorage = deserialized.scriptStorage as? JsonObject ?: JsonObject()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun serialize(data: JsonObject) {
|
override fun serialize(data: JsonObject) {
|
||||||
@ -188,7 +180,10 @@ class MonsterEntity(val variant: MonsterVariant, level: Double? = null) : ActorE
|
|||||||
uniqueID.value,
|
uniqueID.value,
|
||||||
team.value,
|
team.value,
|
||||||
effectEmitter = effects.serialize(),
|
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()) {
|
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
|
monsterLevel = monsterLevel ?: world.template.threatLevel
|
||||||
|
|
||||||
if (!isRemote) {
|
if (isLocal) {
|
||||||
val healthMultiplier = (variant.commonParameters.healthLevelFunction.value?.evaluate(monsterLevel!!) ?: 1.0) * variant.commonParameters.healthMultiplier
|
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))))
|
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)
|
provideEntityBindings(this, lua)
|
||||||
provideAnimatorBindings(animator, lua)
|
provideAnimatorBindings(animator, lua)
|
||||||
|
|
||||||
provideConfigBindings(lua) { key, default ->
|
provideConfigBinding(lua) { key ->
|
||||||
key.find(variant.parameters) ?: default
|
key.find(variant.parameters)
|
||||||
}
|
}
|
||||||
|
|
||||||
lua.attach(variant.commonParameters.scripts)
|
lua.attach(variant.commonParameters.scripts)
|
||||||
luaUpdate.stepCount = variant.commonParameters.initialScriptDelta
|
luaUpdate.stepCount = variant.commonParameters.initialScriptDelta
|
||||||
|
|
||||||
BehaviorState.provideBindings(lua)
|
provideBehaviorBindings(lua)
|
||||||
|
|
||||||
luaMovement.init(lua)
|
lua.initScripts()
|
||||||
lua.init()
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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?
|
// TODO: hitDamageNotificationLimiter++ < Globals.npcs.hitDamageNotificationLimit maybe?
|
||||||
if (totalDamage > 0.0) {
|
if (totalDamage > 0.0) {
|
||||||
lua.invokeGlobal("damage", lua.tableMapOf(
|
lua.pushTable(hashSize = 4)
|
||||||
"sourceId" to damage.request.sourceEntityId,
|
lua.setTableValue("sourceId", damage.request.sourceEntityId)
|
||||||
"damage" to totalDamage,
|
lua.setTableValue("damage", totalDamage)
|
||||||
"sourceDamage" to damage.request.damage,
|
lua.setTableValue("sourceDamage", damage.request.damage)
|
||||||
"sourceKind" to damage.request.damageSourceKind
|
lua.setTableValue("sourceKind", damage.request.damageSourceKind)
|
||||||
))
|
|
||||||
|
lua.invokeGlobal("damage", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (health <= 0.0) {
|
if (health <= 0.0) {
|
||||||
@ -336,8 +349,8 @@ class MonsterEntity(val variant: MonsterVariant, level: Double? = null) : ActorE
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val shouldDie: Boolean get() {
|
private val shouldDie: Boolean get() {
|
||||||
val result = lua.invokeGlobal("shouldDie")
|
val result = lua.invokeGlobal("shouldDie", 1, { 0 }, { getBoolean() == true }).orElse(false)
|
||||||
return result.isNotEmpty() && result[0] is Boolean && result[0] as Boolean || health <= 0.0 //|| lua.errorState
|
return result || health <= 0.0 //|| lua.errorState
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun tickParallel(delta: Double) {
|
override fun tickParallel(delta: Double) {
|
||||||
|
@ -6,8 +6,6 @@ import com.google.gson.JsonArray
|
|||||||
import com.google.gson.JsonElement
|
import com.google.gson.JsonElement
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import com.google.gson.JsonPrimitive
|
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.collect.filterNotNull
|
||||||
import ru.dbotthepony.kommons.gson.set
|
import ru.dbotthepony.kommons.gson.set
|
||||||
import ru.dbotthepony.kommons.util.getValue
|
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.EntityDamageTeam
|
||||||
import ru.dbotthepony.kstarbound.defs.EntityType
|
import ru.dbotthepony.kstarbound.defs.EntityType
|
||||||
import ru.dbotthepony.kstarbound.defs.HitType
|
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.HumanoidConfig
|
||||||
import ru.dbotthepony.kstarbound.defs.actor.HumanoidEmote
|
import ru.dbotthepony.kstarbound.defs.actor.HumanoidEmote
|
||||||
import ru.dbotthepony.kstarbound.defs.actor.HumanoidIdentity
|
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.io.NullableBinaryStringCodec
|
||||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||||
import ru.dbotthepony.kstarbound.json.mergeJson
|
import ru.dbotthepony.kstarbound.json.mergeJson
|
||||||
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
|
||||||
import ru.dbotthepony.kstarbound.lua.LuaMessageHandlerComponent
|
import ru.dbotthepony.kstarbound.lua.LuaMessageHandlerComponent
|
||||||
|
import ru.dbotthepony.kstarbound.lua.LuaThread
|
||||||
import ru.dbotthepony.kstarbound.lua.LuaUpdateComponent
|
import ru.dbotthepony.kstarbound.lua.LuaUpdateComponent
|
||||||
import ru.dbotthepony.kstarbound.lua.bindings.MovementControllerBindings
|
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.bindings.provideEntityBindings
|
||||||
import ru.dbotthepony.kstarbound.lua.from
|
import ru.dbotthepony.kstarbound.lua.userdata.provideBehaviorBindings
|
||||||
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.math.AABB
|
import ru.dbotthepony.kstarbound.math.AABB
|
||||||
import ru.dbotthepony.kstarbound.math.Interpolator
|
import ru.dbotthepony.kstarbound.math.Interpolator
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
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.network.syncher.networkedString
|
||||||
import ru.dbotthepony.kstarbound.util.GameTimer
|
import ru.dbotthepony.kstarbound.util.GameTimer
|
||||||
import ru.dbotthepony.kstarbound.util.random.staticRandomInt
|
import ru.dbotthepony.kstarbound.util.random.staticRandomInt
|
||||||
import ru.dbotthepony.kstarbound.util.valueOf
|
|
||||||
import ru.dbotthepony.kstarbound.world.World
|
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 java.io.DataOutputStream
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
class NPCEntity(val variant: NPCVariant) : HumanoidActorEntity() {
|
class NPCEntity(val variant: NPCVariant) : HumanoidActorEntity() {
|
||||||
override val type: EntityType
|
override val type: EntityType
|
||||||
@ -155,14 +143,15 @@ class NPCEntity(val variant: NPCVariant) : HumanoidActorEntity() {
|
|||||||
override var isPersistent: Boolean = variant.persistent
|
override var isPersistent: Boolean = variant.persistent
|
||||||
override var keepAlive: Boolean = variant.keepAlive
|
override var keepAlive: Boolean = variant.keepAlive
|
||||||
|
|
||||||
override val lua = LuaEnvironment()
|
private var scriptStorage = JsonObject()
|
||||||
val luaUpdate = LuaUpdateComponent(lua)
|
override var lua by Delegates.notNull<LuaThread>()
|
||||||
val luaMovement = MovementControllerBindings(movement)
|
private set
|
||||||
val luaMessages = LuaMessageHandlerComponent(lua) { toString() }
|
var luaUpdate by Delegates.notNull<LuaUpdateComponent>()
|
||||||
|
private set
|
||||||
init {
|
var luaMovement by Delegates.notNull<MovementControllerBindings>()
|
||||||
lua.globals["storage"] = lua.tableOf()
|
private set
|
||||||
}
|
var luaMessages by Delegates.notNull<LuaMessageHandlerComponent>()
|
||||||
|
private set
|
||||||
|
|
||||||
override fun handleMessage(connection: Int, message: String, arguments: JsonArray): JsonElement? {
|
override fun handleMessage(connection: Int, message: String, arguments: JsonArray): JsonElement? {
|
||||||
return luaMessages.handle(message, connection == connectionID, arguments) ?: statusController.handleMessage(message, connection == connectionID, arguments)
|
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 deathParticleBurst: String? = null,
|
||||||
val dropPools: ImmutableList<Registry.Ref<TreasurePoolDefinition>> = ImmutableList.of(),
|
val dropPools: ImmutableList<Registry.Ref<TreasurePoolDefinition>> = ImmutableList.of(),
|
||||||
val aggressive: Boolean = false,
|
val aggressive: Boolean = false,
|
||||||
|
val scriptStorage: JsonObject = JsonObject(),
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun deserialize(data: JsonObject) {
|
override fun deserialize(data: JsonObject) {
|
||||||
@ -231,6 +221,8 @@ class NPCEntity(val variant: NPCVariant) : HumanoidActorEntity() {
|
|||||||
dropPools.addAll(serialized.dropPools.filter { it.isPresent })
|
dropPools.addAll(serialized.dropPools.filter { it.isPresent })
|
||||||
isAggressive = serialized.aggressive
|
isAggressive = serialized.aggressive
|
||||||
blinkTimer.reset(0.0)
|
blinkTimer.reset(0.0)
|
||||||
|
|
||||||
|
scriptStorage = serialized.scriptStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun serialize(data: JsonObject) {
|
override fun serialize(data: JsonObject) {
|
||||||
@ -257,6 +249,10 @@ class NPCEntity(val variant: NPCVariant) : HumanoidActorEntity() {
|
|||||||
deathParticleBurst = deathParticleBurst,
|
deathParticleBurst = deathParticleBurst,
|
||||||
dropPools = dropPools.stream().filter { it.isPresent }.collect(ImmutableList.toImmutableList()),
|
dropPools = dropPools.stream().filter { it.isPresent }.collect(ImmutableList.toImmutableList()),
|
||||||
aggressive = isAggressive,
|
aggressive = isAggressive,
|
||||||
|
scriptStorage = run {
|
||||||
|
lua.loadGlobal("storage")
|
||||||
|
lua.popJson() as? JsonObject ?: JsonObject()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
mergeJson(data, Starbound.gson.toJsonTree(serialized))
|
mergeJson(data, Starbound.gson.toJsonTree(serialized))
|
||||||
@ -265,24 +261,41 @@ class NPCEntity(val variant: NPCVariant) : HumanoidActorEntity() {
|
|||||||
override fun onJoinWorld(world: World<*, *>) {
|
override fun onJoinWorld(world: World<*, *>) {
|
||||||
super.onJoinWorld(world)
|
super.onJoinWorld(world)
|
||||||
|
|
||||||
if (!isRemote) {
|
if (isLocal) {
|
||||||
for ((slot, item) in variant.items) {
|
for ((slot, item) in variant.items) {
|
||||||
setItem(slot, item)
|
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)
|
provideEntityBindings(this, lua)
|
||||||
|
|
||||||
provideConfigBindings(lua) { key, default ->
|
provideConfigBinding(lua) { key ->
|
||||||
key.find(variant.scriptConfig) ?: default
|
key.find(variant.scriptConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
BehaviorState.provideBindings(lua)
|
provideBehaviorBindings(lua)
|
||||||
|
|
||||||
luaUpdate.stepCount = variant.initialScriptDelta
|
luaUpdate.stepCount = variant.initialScriptDelta
|
||||||
lua.attach(variant.scripts)
|
lua.attach(variant.scripts)
|
||||||
|
|
||||||
luaMovement.init(lua)
|
lua.initScripts()
|
||||||
lua.init()
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 }
|
val totalDamage = notifications.sumOf { it.healthLost }
|
||||||
|
|
||||||
if (totalDamage > 0.0 && hitDamageNotificationLimiter++ < Globals.npcs.hitDamageNotificationLimit) {
|
if (totalDamage > 0.0 && hitDamageNotificationLimiter++ < Globals.npcs.hitDamageNotificationLimit) {
|
||||||
lua.invokeGlobal("damage", lua.tableMapOf(
|
lua.pushTable(hashSize = 4)
|
||||||
"sourceId" to damage.request.sourceEntityId,
|
lua.setTableValue("sourceId", damage.request.sourceEntityId)
|
||||||
"damage" to totalDamage,
|
lua.setTableValue("damage", totalDamage)
|
||||||
"sourceDamage" to damage.request.damage,
|
lua.setTableValue("sourceDamage", damage.request.damage)
|
||||||
"sourceKind" to damage.request.damageSourceKind
|
lua.setTableValue("sourceKind", damage.request.damageSourceKind)
|
||||||
))
|
|
||||||
|
lua.invokeGlobal("damage", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
return notifications
|
return notifications
|
||||||
@ -388,14 +402,14 @@ class NPCEntity(val variant: NPCVariant) : HumanoidActorEntity() {
|
|||||||
override fun tick(delta: Double) {
|
override fun tick(delta: Double) {
|
||||||
super.tick(delta)
|
super.tick(delta)
|
||||||
|
|
||||||
if (!isRemote) {
|
if (isLocal) {
|
||||||
if (isDead || lua.errorState) {
|
if (isDead) {
|
||||||
remove(RemovalReason.DYING)
|
remove(RemovalReason.DYING)
|
||||||
return
|
return
|
||||||
} else {
|
} 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)
|
remove(RemovalReason.DYING)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -410,7 +424,8 @@ class NPCEntity(val variant: NPCVariant) : HumanoidActorEntity() {
|
|||||||
override fun onRemove(world: World<*, *>, reason: RemovalReason) {
|
override fun onRemove(world: World<*, *>, reason: RemovalReason) {
|
||||||
super.onRemove(world, reason)
|
super.onRemove(world, reason)
|
||||||
|
|
||||||
lua.invokeGlobal("die")
|
if (isLocal)
|
||||||
|
lua.invokeGlobal("die", 0)
|
||||||
|
|
||||||
val dropPools by lazy { dropPools.stream().map { it.entry }.filterNotNull().toList() }
|
val dropPools by lazy { dropPools.stream().map { it.entry }.filterNotNull().toList() }
|
||||||
|
|
||||||
|
@ -1,36 +1,25 @@
|
|||||||
package ru.dbotthepony.kstarbound.world.entities
|
package ru.dbotthepony.kstarbound.world.entities
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
|
|
||||||
import org.apache.logging.log4j.LogManager
|
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.map
|
||||||
import ru.dbotthepony.kommons.collect.reduce
|
import ru.dbotthepony.kommons.collect.reduce
|
||||||
import ru.dbotthepony.kommons.util.KOptional
|
import ru.dbotthepony.kommons.util.KOptional
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
|
||||||
import ru.dbotthepony.kstarbound.defs.ActorMovementParameters
|
import ru.dbotthepony.kstarbound.defs.ActorMovementParameters
|
||||||
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
||||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||||
import ru.dbotthepony.kstarbound.lua.LuaThread
|
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.setTableValue
|
||||||
import ru.dbotthepony.kstarbound.lua.tableOf
|
|
||||||
import ru.dbotthepony.kstarbound.lua.toByteString
|
|
||||||
import ru.dbotthepony.kstarbound.math.AABB
|
import ru.dbotthepony.kstarbound.math.AABB
|
||||||
import ru.dbotthepony.kstarbound.math.AABBi
|
import ru.dbotthepony.kstarbound.math.AABBi
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2i
|
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.World
|
||||||
import ru.dbotthepony.kstarbound.world.physics.CollisionType
|
import ru.dbotthepony.kstarbound.world.physics.CollisionType
|
||||||
import java.util.PriorityQueue
|
import java.util.*
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.math.roundToInt
|
|
||||||
import kotlin.math.sign
|
import kotlin.math.sign
|
||||||
import kotlin.math.sqrt
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
@ -48,8 +37,6 @@ class PathFinder(val world: World<*, *>, val start: Vector2d, val goal: Vector2d
|
|||||||
SWIM(false, "Swim"),
|
SWIM(false, "Swim"),
|
||||||
FLY(false, "Fly"),
|
FLY(false, "Fly"),
|
||||||
LAND(false, "Land");
|
LAND(false, "Land");
|
||||||
|
|
||||||
val luaName = jsonName.toByteString()!!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Edge(
|
data class Edge(
|
||||||
@ -154,13 +141,6 @@ class PathFinder(val world: World<*, *>, val start: Vector2d, val goal: Vector2d
|
|||||||
var totalCost: Double = 0.0
|
var totalCost: Double = 0.0
|
||||||
var parent: Edge? = null
|
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) {
|
fun store(lua: LuaThread) {
|
||||||
lua.pushTable(hashSize = 2)
|
lua.pushTable(hashSize = 2)
|
||||||
lua.setTableValue("position", position)
|
lua.setTableValue("position", position)
|
||||||
@ -721,8 +701,5 @@ class PathFinder(val world: World<*, *>, val start: Vector2d, val goal: Vector2d
|
|||||||
private val LOGGER = LogManager.getLogger()
|
private val LOGGER = LogManager.getLogger()
|
||||||
const val ARC_SIMULATION_FIDELTITY = 0.5
|
const val ARC_SIMULATION_FIDELTITY = 0.5
|
||||||
const val NODE_GRANULARITY = 1.0
|
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.putAll
|
||||||
import ru.dbotthepony.kstarbound.json.readJsonElement
|
import ru.dbotthepony.kstarbound.json.readJsonElement
|
||||||
import ru.dbotthepony.kstarbound.json.writeJsonElement
|
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.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.AABB
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||||
import ru.dbotthepony.kstarbound.network.syncher.networkedFloat
|
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 ru.dbotthepony.kstarbound.world.entities.api.ScriptedEntity
|
||||||
import java.io.DataInputStream
|
import java.io.DataInputStream
|
||||||
import java.io.DataOutputStream
|
import java.io.DataOutputStream
|
||||||
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
class StagehandEntity(isRemote: Boolean = false) : AbstractEntity(), ScriptedEntity {
|
class StagehandEntity(isRemote: Boolean = false) : AbstractEntity(), ScriptedEntity {
|
||||||
constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readJsonElement().asJsonObject, true)
|
constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readJsonElement().asJsonObject, true)
|
||||||
@ -69,12 +65,11 @@ class StagehandEntity(isRemote: Boolean = false) : AbstractEntity(), ScriptedEnt
|
|||||||
var isScripted = false
|
var isScripted = false
|
||||||
private set
|
private set
|
||||||
|
|
||||||
val lua = LuaEnvironment()
|
override var lua by Delegates.notNull<LuaThread>()
|
||||||
val luaUpdate = LuaUpdateComponent(lua)
|
private set
|
||||||
|
|
||||||
init {
|
var luaUpdate by Delegates.notNull<LuaUpdateComponent>()
|
||||||
lua.globals["storage"] = lua.tableOf()
|
private set
|
||||||
}
|
|
||||||
|
|
||||||
override fun deserialize(data: JsonObject) {
|
override fun deserialize(data: JsonObject) {
|
||||||
super.deserialize(data)
|
super.deserialize(data)
|
||||||
@ -98,22 +93,32 @@ class StagehandEntity(isRemote: Boolean = false) : AbstractEntity(), ScriptedEnt
|
|||||||
}
|
}
|
||||||
|
|
||||||
boundingBox = broadcastArea ?: AABB.withSide(Vector2d.ZERO, 5.0)
|
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<*, *>) {
|
override fun onJoinWorld(world: World<*, *>) {
|
||||||
super.onJoinWorld(world)
|
super.onJoinWorld(world)
|
||||||
|
|
||||||
if (isLocal && isScripted) {
|
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
|
else
|
||||||
data.remove("uniqueId")
|
data.remove("uniqueId")
|
||||||
|
|
||||||
if (isScripted)
|
if (isScripted) {
|
||||||
data["scriptStorage"] = toJsonFromLua(lua.globals["storage"])
|
lua.loadGlobal("storage")
|
||||||
|
data["scriptStorage"] = lua.popJson()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun writeNetwork(stream: DataOutputStream, isLegacy: Boolean) {
|
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"
|
get() = "Technical entity responsible for doing cool stuff"
|
||||||
|
|
||||||
override fun callScript(fnName: String, arguments: JsonArray): JsonElement {
|
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?> {
|
override fun evalScript(code: String): JsonElement {
|
||||||
return lua.eval(code)
|
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.ints.IntArrayList
|
||||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
|
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
|
||||||
import org.apache.logging.log4j.LogManager
|
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.ListenableMap
|
||||||
import ru.dbotthepony.kommons.collect.collect
|
|
||||||
import ru.dbotthepony.kommons.collect.map
|
|
||||||
import ru.dbotthepony.kommons.gson.contains
|
import ru.dbotthepony.kommons.gson.contains
|
||||||
import ru.dbotthepony.kommons.gson.set
|
import ru.dbotthepony.kommons.gson.set
|
||||||
import ru.dbotthepony.kommons.io.readKOptional
|
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.nullable
|
||||||
import ru.dbotthepony.kstarbound.io.readInternedString
|
import ru.dbotthepony.kstarbound.io.readInternedString
|
||||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||||
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
|
||||||
import ru.dbotthepony.kstarbound.lua.LuaMessageHandlerComponent
|
import ru.dbotthepony.kstarbound.lua.LuaMessageHandlerComponent
|
||||||
|
import ru.dbotthepony.kstarbound.lua.LuaThread
|
||||||
import ru.dbotthepony.kstarbound.lua.LuaUpdateComponent
|
import ru.dbotthepony.kstarbound.lua.LuaUpdateComponent
|
||||||
import ru.dbotthepony.kstarbound.lua.bindings.MovementControllerBindings
|
import ru.dbotthepony.kstarbound.lua.bindings.MovementControllerBindings
|
||||||
import ru.dbotthepony.kstarbound.lua.bindings.createConfigBinding
|
import ru.dbotthepony.kstarbound.lua.bindings.createConfigBinding
|
||||||
import ru.dbotthepony.kstarbound.lua.bindings.provideAnimatorBindings
|
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.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.math.Interpolator
|
||||||
import ru.dbotthepony.kstarbound.network.packets.DamageNotificationPacket
|
import ru.dbotthepony.kstarbound.network.packets.DamageNotificationPacket
|
||||||
import ru.dbotthepony.kstarbound.network.syncher.NetworkedDynamicGroup
|
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.DataInputStream
|
||||||
import java.io.DataOutputStream
|
import java.io.DataOutputStream
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.stream.Collectors
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
// this is unnatural to have this class separated, but since it contains
|
// this is unnatural to have this class separated, but since it contains
|
||||||
// lots of internal state, it would be nice to have it encapsulated;
|
// 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)
|
networkGroup.add(statNetworkGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
val lua = LuaEnvironment()
|
var lua by Delegates.notNull<LuaThread>()
|
||||||
val luaUpdate = LuaUpdateComponent(lua)
|
private set
|
||||||
val luaMovement = MovementControllerBindings(entity.movement)
|
var luaUpdate by Delegates.notNull<LuaUpdateComponent>()
|
||||||
val luaMessages = LuaMessageHandlerComponent(lua) { toString() }
|
private set
|
||||||
|
var luaMovement by Delegates.notNull<MovementControllerBindings>()
|
||||||
|
private set
|
||||||
|
var luaMessages by Delegates.notNull<LuaMessageHandlerComponent>()
|
||||||
|
private set
|
||||||
|
|
||||||
private val animator: EffectAnimator?
|
private val animator: EffectAnimator?
|
||||||
private val animatorID: Int?
|
private val animatorID: Int?
|
||||||
|
|
||||||
|
fun uninit() {
|
||||||
|
if (entity.isLocal) {
|
||||||
|
lua.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun init() {
|
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
|
luaUpdate.stepCount = config.primaryScriptDelta
|
||||||
lua.attach(config.primaryScriptSources)
|
lua.attach(config.primaryScriptSources)
|
||||||
|
|
||||||
// provideStatusControllerBindings(this, lua) // provided through provideEntityBindings
|
// provideStatusControllerBindings(this, lua) // provided through provideEntityBindings
|
||||||
provideEntityBindings(entity, lua)
|
provideEntityBindings(entity, lua)
|
||||||
luaMovement.init(lua)
|
|
||||||
|
|
||||||
// TODO: Once we have brand new object-oriented Lua API, expose proper entity bindings here
|
// TODO: Once we have brand new object-oriented Lua API, expose proper entity bindings here
|
||||||
// TODO: Expose world bindings
|
// 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 recentDamageDealt(since: Long = 0L) = recentDamageDealt.query(since)
|
||||||
|
|
||||||
fun experienceDamage(damage: DamageData): List<DamageNotification> {
|
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>()
|
val parsed = ArrayList<DamageNotification>()
|
||||||
|
|
||||||
for ((_, v) in (results[0] as Table)) {
|
for (v in results.value) {
|
||||||
try {
|
try {
|
||||||
parsed.add(Starbound.gson.fromJsonFast(toJsonFromLua(v), DamageNotification::class.java))
|
parsed.add(Starbound.gson.fromJsonFast(v, DamageNotification::class.java))
|
||||||
} catch (err: Throwable) {
|
} catch (err: Throwable) {
|
||||||
LOGGER.error("Exception while parsing results returned by applyDamageRequest (primary scripts: ${config.primaryScriptSources})", err)
|
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 = ""
|
var parentDirectives: String = ""
|
||||||
val modifierGroups = IntArrayList()
|
val modifierGroups = IntArrayList()
|
||||||
|
|
||||||
val lua = LuaEnvironment()
|
var lua by Delegates.notNull<LuaThread>()
|
||||||
val luaMessages = LuaMessageHandlerComponent(lua) { "Unique effect" }
|
private set
|
||||||
val luaMovement = MovementControllerBindings(entity.movement)
|
var luaUpdate by Delegates.notNull<LuaUpdateComponent>()
|
||||||
val luaUpdate = LuaUpdateComponent(lua)
|
private set
|
||||||
|
var luaMovement by Delegates.notNull<MovementControllerBindings>()
|
||||||
|
private set
|
||||||
|
var luaMessages by Delegates.notNull<LuaMessageHandlerComponent>()
|
||||||
|
private set
|
||||||
|
|
||||||
val metadata = UniqueEffectNetworkedValues()
|
val metadata = UniqueEffectNetworkedValues()
|
||||||
val metadataNetworkID = uniqueEffectMetadata.add(metadata)
|
val metadataNetworkID = uniqueEffectMetadata.add(metadata)
|
||||||
@ -485,14 +495,18 @@ class StatusController(val entity: ActorEntity, val config: StatusControllerConf
|
|||||||
animatorNetworkID = null
|
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
|
// unique effects shouldn't be initialized on remotes in first place
|
||||||
// but whatever
|
// but whatever
|
||||||
lua.attach(effect.value.scripts)
|
lua.attach(effect.value.scripts)
|
||||||
|
|
||||||
// provideStatusControllerBindings(this@StatusController, lua) // provided through provideEntityBindings
|
// provideStatusControllerBindings(this@StatusController, lua) // provided through provideEntityBindings
|
||||||
provideEntityBindings(entity, lua)
|
provideEntityBindings(entity, lua)
|
||||||
luaMovement.init(lua)
|
|
||||||
provideEffectBindings()
|
provideEffectBindings()
|
||||||
|
|
||||||
if (animator != null) {
|
if (animator != null) {
|
||||||
@ -502,84 +516,103 @@ class StatusController(val entity: ActorEntity, val config: StatusControllerConf
|
|||||||
provideAnimatorBindings(animator.animator, lua)
|
provideAnimatorBindings(animator.animator, lua)
|
||||||
}
|
}
|
||||||
|
|
||||||
provideConfigBindings(lua) { path, default ->
|
provideConfigBinding(lua) { path ->
|
||||||
path.find(effect.json) ?: default
|
path.find(effect.json)
|
||||||
}
|
}
|
||||||
|
|
||||||
lua.init()
|
lua.initScripts()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun provideEffectBindings() {
|
private fun duration(args: LuaThread.ArgStack): Int {
|
||||||
val callbacks = lua.newTable()
|
args.lua.push(metadata.duration)
|
||||||
lua.globals["effect"] = callbacks
|
return 1
|
||||||
|
|
||||||
callbacks["duration"] = luaFunction {
|
|
||||||
returnBuffer.setTo(metadata.duration)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["modifyDuration"] = luaFunction { duration: Number ->
|
private fun modifyDuration(args: LuaThread.ArgStack): Int {
|
||||||
val value = duration.toDouble()
|
val duration = args.nextDouble()
|
||||||
check(value.isFinite()) { "Infinite duration provided" }
|
require(duration.isFinite()) { "Infinite duration provided" }
|
||||||
check(!value.isNaN()) { "NaN duration provided" }
|
require(!duration.isNaN()) { "NaN duration provided" }
|
||||||
|
|
||||||
if (!isPersistent) {
|
if (!isPersistent)
|
||||||
metadata.duration += value
|
metadata.duration += duration
|
||||||
}
|
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["expire"] = luaFunction {
|
private fun expire(args: LuaThread.ArgStack): Int {
|
||||||
if (!isPersistent) {
|
if (!isPersistent)
|
||||||
metadata.duration = 0.0
|
metadata.duration = 0.0
|
||||||
}
|
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["sourceEntity"] = luaFunction {
|
private fun sourceEntity(args: LuaThread.ArgStack): Int {
|
||||||
returnBuffer.setTo(metadata.sourceEntity ?: entity.entityID)
|
args.lua.push(metadata.sourceEntity?.toLong() ?: entity.entityID.toLong())
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["setParentDirectives"] = luaFunction { directives: ByteString ->
|
private fun setParentDirectives(args: LuaThread.ArgStack): Int {
|
||||||
parentDirectives = directives.decode().sbIntern()
|
parentDirectives = args.nextString().sbIntern()
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["getParameter"] = createConfigBinding { path, default ->
|
private fun addStatModifierGroup(args: LuaThread.ArgStack): Int {
|
||||||
path.find(effect.json) ?: default
|
val modifiers = args.readTableValues {
|
||||||
|
Starbound.gson.fromJsonFast(getJson(it)!!, StatModifier::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["addStatModifierGroup"] = luaFunction { modifiers: Table ->
|
val id = statModifiers.add(ArrayList(modifiers))
|
||||||
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)
|
modifierGroups.add(id)
|
||||||
returnBuffer.setTo(id)
|
args.lua.push(id.toLong())
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["setStatModifierGroup"] = luaFunction { groupID: Number, modifiers: Table ->
|
private fun setStatModifierGroup(args: LuaThread.ArgStack): Int {
|
||||||
val id = groupID.toInt()
|
val id = args.nextInt()
|
||||||
|
|
||||||
if (id !in modifierGroups)
|
if (id !in modifierGroups)
|
||||||
throw LuaRuntimeException("$groupID stat modifier group does not belong to this effect")
|
throw IllegalStateException("$id stat modifier group does not belong to this effect")
|
||||||
|
|
||||||
val actual = modifiers
|
val modifiers = args.readTableValues {
|
||||||
.iterator()
|
Starbound.gson.fromJsonFast(getJson(it)!!, StatModifier::class.java)
|
||||||
.map { (_, v) -> Starbound.gson.fromJsonFast(toJsonFromLua(v), StatModifier::class.java) }
|
|
||||||
.collect(Collectors.toCollection(::ArrayList))
|
|
||||||
|
|
||||||
statModifiers[id] = actual
|
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["removeStatModifierGroup"] = luaFunction { groupID: Number ->
|
statModifiers[id] = ArrayList(modifiers)
|
||||||
val id = groupID.toInt()
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeStatModifierGroup(args: LuaThread.ArgStack): Int {
|
||||||
|
val id = args.nextInt()
|
||||||
|
|
||||||
if (modifierGroups.rem(id)) {
|
if (modifierGroups.rem(id)) {
|
||||||
statModifiers.remove(id)
|
statModifiers.remove(id)
|
||||||
} else {
|
} else {
|
||||||
throw LuaRuntimeException("$groupID stat modifier group does not belong to this effect")
|
throw IllegalStateException("$id stat modifier group does not belong to this effect")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun provideEffectBindings() {
|
||||||
|
lua.pushTable()
|
||||||
|
lua.dup()
|
||||||
|
lua.storeGlobal("effect")
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
lua.push("getParameter")
|
||||||
|
createConfigBinding(lua) { path -> path.find(effect.json) }
|
||||||
|
lua.setTableValue()
|
||||||
|
|
||||||
|
lua.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun promoteToPersistent() {
|
fun promoteToPersistent() {
|
||||||
@ -591,7 +624,7 @@ class StatusController(val entity: ActorEntity, val config: StatusControllerConf
|
|||||||
// remove an ephemeral effect.
|
// remove an ephemeral effect.
|
||||||
|
|
||||||
// Original engine only updates the duration (setting it to null),
|
// 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
|
// persistent effect will be sourced to whoever put ephemeral effect
|
||||||
metadata.duration = 0.0
|
metadata.duration = 0.0
|
||||||
metadata.sourceEntity = null
|
metadata.sourceEntity = null
|
||||||
@ -599,7 +632,9 @@ class StatusController(val entity: ActorEntity, val config: StatusControllerConf
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun remove() {
|
fun remove() {
|
||||||
lua.invokeGlobal("onExpire")
|
if (entity.isLocal)
|
||||||
|
lua.invokeGlobal("onExpire", 0)
|
||||||
|
|
||||||
uniqueEffectMetadata.remove(metadataNetworkID)
|
uniqueEffectMetadata.remove(metadataNetworkID)
|
||||||
|
|
||||||
for (group in modifierGroups) {
|
for (group in modifierGroups) {
|
||||||
|
@ -2,13 +2,29 @@ package ru.dbotthepony.kstarbound.world.entities.api
|
|||||||
|
|
||||||
import com.google.gson.JsonArray
|
import com.google.gson.JsonArray
|
||||||
import com.google.gson.JsonElement
|
import com.google.gson.JsonElement
|
||||||
|
import com.google.gson.JsonNull
|
||||||
|
import ru.dbotthepony.kstarbound.lua.LuaThread
|
||||||
|
|
||||||
interface ScriptedEntity {
|
interface ScriptedEntity {
|
||||||
|
val lua: LuaThread
|
||||||
|
|
||||||
// Call a script function directly with the given arguments, should return
|
// Call a script function directly with the given arguments, should return
|
||||||
// nothing only on failure.
|
// 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
|
// Execute the given code directly in the underlying context, return nothing
|
||||||
// on failure.
|
// 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.defs.actor.player.PlayerGamemode
|
||||||
import ru.dbotthepony.kstarbound.io.readInternedString
|
import ru.dbotthepony.kstarbound.io.readInternedString
|
||||||
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
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.AABB
|
||||||
import ru.dbotthepony.kstarbound.math.Interpolator
|
import ru.dbotthepony.kstarbound.math.Interpolator
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||||
@ -101,7 +101,7 @@ class PlayerEntity() : HumanoidActorEntity() {
|
|||||||
val newChatMessage = networkGroup.upstream.add(networkedEventCounter())
|
val newChatMessage = networkGroup.upstream.add(networkedEventCounter())
|
||||||
var emote by networkGroup.upstream.add(networkedEnumExtraStupid(HumanoidEmote.IDLE))
|
var emote by networkGroup.upstream.add(networkedEnumExtraStupid(HumanoidEmote.IDLE))
|
||||||
|
|
||||||
override val lua: LuaEnvironment
|
override val lua: LuaThread
|
||||||
get() = TODO("Not yet implemented")
|
get() = TODO("Not yet implemented")
|
||||||
override val emoteCooldownTimer: GameTimer = GameTimer()
|
override val emoteCooldownTimer: GameTimer = GameTimer()
|
||||||
override val danceTimer: GameTimer?
|
override val danceTimer: GameTimer?
|
||||||
|
@ -12,29 +12,20 @@ import com.google.gson.TypeAdapter
|
|||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
||||||
import org.apache.logging.log4j.LogManager
|
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.JsonArrayCollector
|
||||||
import ru.dbotthepony.kommons.gson.contains
|
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.get
|
||||||
import ru.dbotthepony.kommons.gson.set
|
import ru.dbotthepony.kommons.gson.set
|
||||||
import ru.dbotthepony.kommons.guava.immutableSet
|
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.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.getValue
|
||||||
import ru.dbotthepony.kommons.util.setValue
|
import ru.dbotthepony.kommons.util.setValue
|
||||||
import ru.dbotthepony.kstarbound.Globals
|
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.defs.DamageNotification
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
|
||||||
import ru.dbotthepony.kstarbound.defs.DamageSource
|
import ru.dbotthepony.kstarbound.defs.DamageSource
|
||||||
import ru.dbotthepony.kstarbound.defs.EntityType
|
import ru.dbotthepony.kstarbound.defs.EntityType
|
||||||
import ru.dbotthepony.kstarbound.defs.HitType
|
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.InteractRequest
|
||||||
import ru.dbotthepony.kstarbound.defs.animation.AnimationDefinition
|
import ru.dbotthepony.kstarbound.defs.animation.AnimationDefinition
|
||||||
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor
|
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.`object`.ObjectType
|
||||||
import ru.dbotthepony.kstarbound.defs.quest.QuestArcDescriptor
|
import ru.dbotthepony.kstarbound.defs.quest.QuestArcDescriptor
|
||||||
import ru.dbotthepony.kstarbound.defs.tile.TileDamage
|
import ru.dbotthepony.kstarbound.defs.tile.TileDamage
|
||||||
import ru.dbotthepony.kstarbound.io.RGBACodec
|
import ru.dbotthepony.kstarbound.io.RGBACodec
|
||||||
|
import ru.dbotthepony.kstarbound.io.StreamCodec
|
||||||
import ru.dbotthepony.kstarbound.io.Vector2iCodec
|
import ru.dbotthepony.kstarbound.io.Vector2iCodec
|
||||||
|
import ru.dbotthepony.kstarbound.io.map
|
||||||
import ru.dbotthepony.kstarbound.json.JsonPath
|
import ru.dbotthepony.kstarbound.json.JsonPath
|
||||||
import ru.dbotthepony.kstarbound.json.jsonArrayOf
|
import ru.dbotthepony.kstarbound.json.jsonArrayOf
|
||||||
import ru.dbotthepony.kstarbound.json.mergeJson
|
import ru.dbotthepony.kstarbound.json.mergeJson
|
||||||
import ru.dbotthepony.kstarbound.json.stream
|
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.InternedStringCodec
|
||||||
import ru.dbotthepony.kstarbound.network.syncher.JsonElementCodec
|
import ru.dbotthepony.kstarbound.network.syncher.JsonElementCodec
|
||||||
import ru.dbotthepony.kstarbound.network.syncher.NetworkedList
|
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.networkedJsonElement
|
||||||
import ru.dbotthepony.kstarbound.network.syncher.networkedPointer
|
import ru.dbotthepony.kstarbound.network.syncher.networkedPointer
|
||||||
import ru.dbotthepony.kstarbound.network.syncher.networkedString
|
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.ManualLazy
|
||||||
import ru.dbotthepony.kstarbound.util.asStringOrNull
|
import ru.dbotthepony.kstarbound.util.asStringOrNull
|
||||||
import ru.dbotthepony.kstarbound.util.random.random
|
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.entities.api.ScriptedEntity
|
||||||
import ru.dbotthepony.kstarbound.world.physics.Poly
|
import ru.dbotthepony.kstarbound.world.physics.Poly
|
||||||
import java.io.DataOutputStream
|
import java.io.DataOutputStream
|
||||||
import java.util.Collections
|
import java.util.*
|
||||||
import java.util.HashMap
|
|
||||||
import java.util.random.RandomGenerator
|
import java.util.random.RandomGenerator
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntity(), ScriptedEntity, InteractiveEntity {
|
open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntity(), ScriptedEntity, InteractiveEntity {
|
||||||
|
private var scriptStorage = JsonObject()
|
||||||
|
|
||||||
override fun deserialize(data: JsonObject) {
|
override fun deserialize(data: JsonObject) {
|
||||||
super.deserialize(data)
|
super.deserialize(data)
|
||||||
direction = data.get("direction", directions) { Direction.LEFT }
|
direction = data.get("direction", directions) { Direction.LEFT }
|
||||||
orientationIndex = data.get("orientationIndex", -1).toLong()
|
orientationIndex = data.get("orientationIndex", -1).toLong()
|
||||||
isInteractive = data.get("interactive", false)
|
isInteractive = data.get("interactive", false)
|
||||||
|
scriptStorage = data.get("scriptStorage") { JsonObject() }
|
||||||
lua.globals["storage"] = lua.from(data.get("scriptStorage") { JsonObject() })
|
|
||||||
|
|
||||||
loadParameters(data.get("parameters") { 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["inputWireNodes"] = inputNodes.stream().map { it.serialize() }.collect(JsonArrayCollector)
|
||||||
data["outputWireNodes"] = outputNodes.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) {
|
if (scriptStorage != null && scriptStorage is JsonObject) {
|
||||||
data["scriptStorage"] = scriptStorage.toJson(true)
|
data["scriptStorage"] = scriptStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
uniqueID.get()?.let {
|
uniqueID.get()?.let {
|
||||||
@ -313,13 +306,13 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
|||||||
fun addConnection(connection: WireConnection) {
|
fun addConnection(connection: WireConnection) {
|
||||||
if (connection !in connectionsInternal) {
|
if (connection !in connectionsInternal) {
|
||||||
connectionsInternal.add(connection.copy())
|
connectionsInternal.add(connection.copy())
|
||||||
lua.invokeGlobal("onNodeConnectionChange")
|
lua.invokeGlobal("onNodeConnectionChange", 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeConnection(connection: WireConnection) {
|
fun removeConnection(connection: WireConnection) {
|
||||||
if (connectionsInternal.remove(connection)) {
|
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 }
|
val any = otherConnections?.getOrNull(it.index)?.connectionsInternal?.removeIf { it.entityLocation == tilePosition && it.index == index }
|
||||||
|
|
||||||
if (any == true) {
|
if (any == true) {
|
||||||
otherEntity!!.lua.invokeGlobal("onNodeConnectionChange")
|
otherEntity!!.lua.invokeGlobal("onNodeConnectionChange", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
any == true
|
any == true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (any)
|
if (any)
|
||||||
lua.invokeGlobal("onNodeConnectionChange")
|
lua.invokeGlobal("onNodeConnectionChange", 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeConnectionsTo(pos: Vector2i) {
|
fun removeConnectionsTo(pos: Vector2i) {
|
||||||
if (connectionsInternal.removeIf { it.entityLocation == pos }) {
|
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)
|
networkGroup.upstream.add(animator.networkGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
val lua = LuaEnvironment()
|
final override var lua by Delegates.notNull<LuaThread>()
|
||||||
val luaUpdate = LuaUpdateComponent(lua)
|
private set
|
||||||
val luaMessageHandler = LuaMessageHandlerComponent(lua) { toString() }
|
var luaUpdate by Delegates.notNull<LuaUpdateComponent>()
|
||||||
|
private set
|
||||||
init {
|
var luaMessageHandler by Delegates.notNull<LuaMessageHandlerComponent>()
|
||||||
lua.globals["storage"] = lua.newTable()
|
private set
|
||||||
}
|
|
||||||
|
|
||||||
val unbreakable by ManualLazy {
|
val unbreakable by ManualLazy {
|
||||||
lookupProperty("unbreakable") { JsonPrimitive(false) }.asBoolean
|
lookupProperty("unbreakable") { JsonPrimitive(false) }.asBoolean
|
||||||
@ -553,14 +545,23 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
|||||||
animator.setPartTag(k, "partImage", v.asString)
|
animator.setPartTag(k, "partImage", v.asString)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lua = LuaThread()
|
||||||
|
lua.push(scriptStorage)
|
||||||
|
lua.storeGlobal("storage")
|
||||||
|
luaUpdate = LuaUpdateComponent(lua, this)
|
||||||
|
luaMessageHandler = LuaMessageHandlerComponent(lua) { toString() }
|
||||||
|
|
||||||
updateMaterialSpacesNow()
|
updateMaterialSpacesNow()
|
||||||
provideEntityBindings(this, lua)
|
provideEntityBindings(this, lua)
|
||||||
provideAnimatorBindings(animator, lua)
|
provideAnimatorBindings(animator, lua)
|
||||||
lua.attach(config.value.scripts)
|
lua.attach(config.value.scripts)
|
||||||
luaUpdate.stepCount = lookupProperty(JsonPath("scriptDelta")) { JsonPrimitive(5) }.asDouble
|
luaUpdate.stepCount = lookupProperty(JsonPath("scriptDelta")) { JsonPrimitive(5) }.asDouble
|
||||||
lua.init()
|
lua.initScripts()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// free up memory
|
||||||
|
scriptStorage = JsonObject()
|
||||||
|
|
||||||
// as original code puts it:
|
// as original code puts it:
|
||||||
// Don't animate the initial state when first spawned IF you're dumb, which by default
|
// 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
|
// 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 {
|
override fun interact(request: InteractRequest): InteractAction {
|
||||||
val diff = world.geometry.diff(request.sourcePos, position)
|
val diff = world.geometry.diff(request.sourcePos, position)
|
||||||
|
|
||||||
val result = lua.invokeGlobal("onInteraction", lua.newTable().apply {
|
val result = lua.invokeGlobal("onInteraction", 1, {
|
||||||
this["source"] = lua.tableOf(diff.x, diff.y)
|
pushTable(hashSize = 2)
|
||||||
this["sourceId"] = request.source
|
|
||||||
})
|
|
||||||
|
|
||||||
if (result.isNotEmpty()) {
|
setTableValue("source", diff)
|
||||||
if (result[0] == null)
|
setTableValue("sourceId", request.source)
|
||||||
|
|
||||||
|
1
|
||||||
|
}, { getJson() ?: JsonNull.INSTANCE })
|
||||||
|
|
||||||
|
if (result.isPresent) {
|
||||||
|
if (result.value.isJsonNull)
|
||||||
return InteractAction.NONE
|
return InteractAction.NONE
|
||||||
else if (result[0] is ByteString) {
|
else if (result.value is JsonPrimitive) {
|
||||||
val decoded = (result[0] as ByteString).decode()
|
val decoded = result.value.asString
|
||||||
return InteractAction(InteractAction.Type.entries.firstOrNull { it.jsonName == decoded } ?: throw NoSuchElementException("Unknown interaction action type $decoded!"), entityID)
|
return InteractAction(InteractAction.Type.entries.firstOrNull { it.jsonName == decoded } ?: throw NoSuchElementException("Unknown interaction action type $decoded!"), entityID)
|
||||||
} else {
|
} else {
|
||||||
val data = result[0] as Table
|
val data = result.value as JsonArray
|
||||||
val decoded = (data[1L] as ByteString).decode()
|
val decoded = data[0].asString
|
||||||
return InteractAction(InteractAction.Type.entries.firstOrNull { it.jsonName == decoded } ?: throw NoSuchElementException("Unknown interaction action type $decoded!"), entityID, toJsonFromLua(data[2L]))
|
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
|
// break connection if other entity got removed
|
||||||
if (connection.otherEntity?.removalReason?.removal == true) {
|
if (connection.otherEntity?.removalReason?.removal == true) {
|
||||||
itr.remove()
|
itr.remove()
|
||||||
lua.invokeGlobal("onNodeConnectionChange")
|
lua.invokeGlobal("onNodeConnectionChange", 0)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -686,7 +691,7 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
|||||||
// break connection if we point at invalid node
|
// break connection if we point at invalid node
|
||||||
if (otherNode == null) {
|
if (otherNode == null) {
|
||||||
itr.remove()
|
itr.remove()
|
||||||
lua.invokeGlobal("onNodeConnectionChange")
|
lua.invokeGlobal("onNodeConnectionChange", 0)
|
||||||
} else {
|
} else {
|
||||||
newState = newState!! || otherNode.state
|
newState = newState!! || otherNode.state
|
||||||
}
|
}
|
||||||
@ -701,7 +706,10 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
|||||||
// otherwise, keep current node state
|
// otherwise, keep current node state
|
||||||
if (newState != null && node.state != newState) {
|
if (newState != null && node.state != newState) {
|
||||||
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) {
|
override fun onRemove(world: World<*, *>, reason: RemovalReason) {
|
||||||
super.onRemove(world, reason)
|
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 {
|
fun spawnRandomItems(poolName: String, optionsName: String, seedName: String): Boolean {
|
||||||
val dropPool = lookupProperty(poolName) { JsonPrimitive("") }.asString
|
val dropPool = lookupProperty(poolName) { JsonPrimitive("") }.asString
|
||||||
@ -774,7 +782,8 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!isRemote && reason.dying) {
|
if (!isRemote && reason.dying) {
|
||||||
lua.invokeGlobal("die", health <= 0.0)
|
lua.push(health <= 0.0)
|
||||||
|
lua.invokeGlobal("die", 1)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (doSmash) {
|
if (doSmash) {
|
||||||
@ -791,7 +800,8 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
|||||||
}
|
}
|
||||||
|
|
||||||
parameters.remove("owner")
|
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))
|
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 {
|
override fun damageTileEntity(damageSpaces: List<Vector2i>, source: Vector2d, damage: TileDamage): Boolean {
|
||||||
if (unbreakable)
|
if (unbreakable)
|
||||||
return false
|
return false
|
||||||
@ -864,15 +882,6 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
|||||||
return luaMessageHandler.handle(message, connectionID == connection, arguments)
|
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 {
|
init {
|
||||||
isInteractive = !interactAction.isJsonNull
|
isInteractive = !interactAction.isJsonNull
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user