Some initial Lua bindings (object, animator, root, sb)
This commit is contained in:
parent
ba05b27deb
commit
8ee60c0f4d
21
ADDITIONS.md
21
ADDITIONS.md
@ -39,7 +39,7 @@ val color: TileColor = TileColor.DEFAULT
|
|||||||
```
|
```
|
||||||
* `item` brush now can accept proper item descriptors (in json object tag),
|
* `item` brush now can accept proper item descriptors (in json object tag),
|
||||||
* Previous behavior remains unchanged (if specified as string, creates _randomized_ item, if as object, creates _exactly_ what have been specified)
|
* Previous behavior remains unchanged (if specified as string, creates _randomized_ item, if as object, creates _exactly_ what have been specified)
|
||||||
* To stop randomizing as Tiled tileset brush, specify `"randomize"` as `false`
|
* To stop randomizing as Tiled tileset brush, specify `"dont_randomize"` as anything (e.g. as `""`)
|
||||||
* `liquid` brush now can accept 'level' as second argument
|
* `liquid` brush now can accept 'level' as second argument
|
||||||
* Previous behavior is unchanged, `["liquid", "water", true]` will result into infinite water as before, but `["liquid", "water", 0.5, false]` will spawn half-filled water
|
* Previous behavior is unchanged, `["liquid", "water", true]` will result into infinite water as before, but `["liquid", "water", 0.5, false]` will spawn half-filled water
|
||||||
* In tiled, you already can do this using `"quantity"` property
|
* In tiled, you already can do this using `"quantity"` property
|
||||||
@ -62,3 +62,22 @@ val color: TileColor = TileColor.DEFAULT
|
|||||||
* Used by object and plant anchoring code to determine valid placement
|
* Used by object and plant anchoring code to determine valid placement
|
||||||
* Used by world tile rendering code (render piece rule `Connects`)
|
* Used by world tile rendering code (render piece rule `Connects`)
|
||||||
* And finally, used by `canPlaceMaterial` to determine whenever player can place blocks next to it (at least one such tile should be present for player to be able to place blocks next to it)
|
* And finally, used by `canPlaceMaterial` to determine whenever player can place blocks next to it (at least one such tile should be present for player to be able to place blocks next to it)
|
||||||
|
|
||||||
|
---------------
|
||||||
|
|
||||||
|
### Scripting
|
||||||
|
|
||||||
|
#### animator
|
||||||
|
|
||||||
|
* Added `animator.targetRotationAngle(rotationGroup: string): double`
|
||||||
|
* Added `animator.hasRotationGroup(rotationGroup: string): boolean`
|
||||||
|
* Added `animator.rotationGroups(): List<string>` (returns valid names for `rotateGroup`, `currentRotationAngle` and `targetRotationAngle`)
|
||||||
|
* Added `animator.transformationGroups(): List<string>`
|
||||||
|
* Added `animator.particleEmitters(): List<string>`
|
||||||
|
* Added `animator.hasParticleEmitter(emitter: string): boolean`
|
||||||
|
* Added `animator.lights(): List<string>`
|
||||||
|
* Added `animator.hasLight(light: string): boolean`
|
||||||
|
* Added `animator.sounds(): List<string>`
|
||||||
|
* Added `animator.effects(): List<string>`
|
||||||
|
* Added `animator.hasEffect(effect: string): boolean`
|
||||||
|
* Added `animator.parts(): List<string>`
|
9
README.md
Normal file
9
README.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
### Starbound engine recreation project
|
||||||
|
|
||||||
|
Make sure to specify next settings as startup options to JVM:
|
||||||
|
|
||||||
|
```
|
||||||
|
-Dfile.encoding=UTF8
|
||||||
|
```
|
||||||
|
|
@ -83,7 +83,7 @@ dependencies {
|
|||||||
implementation("ru.dbotthepony.kommons:kommons-gson-linear-algebra:[$kommonsVersion,)") { setTransitive(false) }
|
implementation("ru.dbotthepony.kommons:kommons-gson-linear-algebra:[$kommonsVersion,)") { setTransitive(false) }
|
||||||
|
|
||||||
implementation("com.github.ben-manes.caffeine:caffeine:3.1.5")
|
implementation("com.github.ben-manes.caffeine:caffeine:3.1.5")
|
||||||
implementation("org.classdump.luna:luna-all-shaded:0.4.1")
|
implementation(project(":luna"))
|
||||||
|
|
||||||
implementation("io.netty:netty-transport:4.1.105.Final")
|
implementation("io.netty:netty-transport:4.1.105.Final")
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
rootProject.name = "KStarBound"
|
rootProject.name = "KStarBound"
|
||||||
|
include("luna")
|
||||||
|
@ -7,6 +7,9 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
|||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
||||||
import kotlinx.coroutines.asCoroutineDispatcher
|
import kotlinx.coroutines.asCoroutineDispatcher
|
||||||
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.kommons.gson.AABBTypeAdapter
|
import ru.dbotthepony.kommons.gson.AABBTypeAdapter
|
||||||
import ru.dbotthepony.kommons.gson.AABBiTypeAdapter
|
import ru.dbotthepony.kommons.gson.AABBiTypeAdapter
|
||||||
import ru.dbotthepony.kommons.gson.EitherTypeAdapter
|
import ru.dbotthepony.kommons.gson.EitherTypeAdapter
|
||||||
@ -68,6 +71,7 @@ import java.io.*
|
|||||||
import java.lang.ref.Cleaner
|
import java.lang.ref.Cleaner
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.concurrent.Executor
|
import java.util.concurrent.Executor
|
||||||
import java.util.concurrent.ExecutorService
|
import java.util.concurrent.ExecutorService
|
||||||
import java.util.concurrent.ForkJoinPool
|
import java.util.concurrent.ForkJoinPool
|
||||||
@ -150,7 +154,7 @@ object Starbound : BlockableEventLoop("Universe Thread"), Scheduler, ISBFileLoca
|
|||||||
// Hrm.
|
// Hrm.
|
||||||
// val strings: Interner<String> = Interner.newWeakInterner()
|
// val strings: Interner<String> = Interner.newWeakInterner()
|
||||||
// val strings: Interner<String> = Interner { it }
|
// val strings: Interner<String> = Interner { it }
|
||||||
@JvmField
|
@JvmField
|
||||||
val STRINGS: Interner<String> = interner(5)
|
val STRINGS: Interner<String> = interner(5)
|
||||||
|
|
||||||
// immeasurably lazy and fragile solution, too bad!
|
// immeasurably lazy and fragile solution, too bad!
|
||||||
@ -220,6 +224,27 @@ object Starbound : BlockableEventLoop("Universe Thread"), Scheduler, ISBFileLoca
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
return loader.compileTextChunk(path, find.readToString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadScript(path: String): ChunkFactory {
|
||||||
|
return scriptCache.computeIfAbsent(path, ::loadScript0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun compileScriptChunk(name: String, chunk: String): ChunkFactory {
|
||||||
|
return loader.compileTextChunk(name, chunk)
|
||||||
|
}
|
||||||
|
|
||||||
val gson: Gson = with(GsonBuilder()) {
|
val gson: Gson = with(GsonBuilder()) {
|
||||||
// serializeNulls()
|
// serializeNulls()
|
||||||
setDateFormat(DateFormat.LONG)
|
setDateFormat(DateFormat.LONG)
|
||||||
|
@ -66,7 +66,7 @@ data class AnimatedPartsDefinition(
|
|||||||
@JsonFactory
|
@JsonFactory
|
||||||
data class State(
|
data class State(
|
||||||
val properties: JsonObject = JsonObject(),
|
val properties: JsonObject = JsonObject(),
|
||||||
val frameProperties: JsonObject = JsonObject(),
|
val frameProperties: ImmutableMap<String, JsonArray> = ImmutableMap.of(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -239,7 +239,7 @@ enum class DungeonBrushType(override val jsonName: String) : IStringSerializable
|
|||||||
|
|
||||||
override fun readTiled(json: JsonObject): DungeonBrush? {
|
override fun readTiled(json: JsonObject): DungeonBrush? {
|
||||||
if ("item" in json) {
|
if ("item" in json) {
|
||||||
return DungeonBrush.DropItem(ItemDescriptor(json["item"].asString, json.get("count", 1L), json.get("parameters") { JsonObject() }), json.get("randomize", true))
|
return DungeonBrush.DropItem(ItemDescriptor(json["item"].asString, json.get("count", 1L), json.get("parameters") { JsonObject() }), "dont_randomize" !in json)
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
|
@ -15,12 +15,11 @@ import org.classdump.luna.ByteString
|
|||||||
import org.classdump.luna.LuaRuntimeException
|
import org.classdump.luna.LuaRuntimeException
|
||||||
import org.classdump.luna.Table
|
import org.classdump.luna.Table
|
||||||
import org.classdump.luna.TableFactory
|
import org.classdump.luna.TableFactory
|
||||||
import ru.dbotthepony.kommons.gson.consumeNull
|
import org.classdump.luna.runtime.ExecutionContext
|
||||||
import ru.dbotthepony.kommons.gson.contains
|
import ru.dbotthepony.kommons.gson.contains
|
||||||
import ru.dbotthepony.kommons.util.KOptional
|
import ru.dbotthepony.kommons.util.KOptional
|
||||||
import ru.dbotthepony.kstarbound.lua.StateMachine
|
import ru.dbotthepony.kstarbound.lua.StateMachine
|
||||||
import ru.dbotthepony.kstarbound.lua.from
|
import ru.dbotthepony.kstarbound.lua.from
|
||||||
import ru.dbotthepony.kstarbound.lua.toJsonObject
|
|
||||||
import ru.dbotthepony.kommons.gson.get
|
import ru.dbotthepony.kommons.gson.get
|
||||||
import ru.dbotthepony.kommons.gson.value
|
import ru.dbotthepony.kommons.gson.value
|
||||||
import ru.dbotthepony.kommons.io.StreamCodec
|
import ru.dbotthepony.kommons.io.StreamCodec
|
||||||
@ -36,6 +35,8 @@ import ru.dbotthepony.kstarbound.io.readInternedString
|
|||||||
import ru.dbotthepony.kstarbound.item.ItemStack
|
import ru.dbotthepony.kstarbound.item.ItemStack
|
||||||
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.indexNoYield
|
||||||
|
import ru.dbotthepony.kstarbound.lua.toJson
|
||||||
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
|
||||||
@ -80,17 +81,34 @@ fun ItemDescriptor(data: Table, stateMachine: StateMachine): Supplier<ItemDescri
|
|||||||
stateMachine.add {
|
stateMachine.add {
|
||||||
val iname = name.get()
|
val iname = name.get()
|
||||||
val icount = count.get().orElse(1L)
|
val icount = count.get().orElse(1L)
|
||||||
val iparameters = parameters.get().map { if (it is Table) it.toJsonObject() else throw LuaRuntimeException("Invalid item descriptor parameters ($it)") }.orElse { JsonObject() }
|
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 (iname !is ByteString) throw LuaRuntimeException("Invalid item descriptor name (${iname})")
|
||||||
if (icount !is Number) throw LuaRuntimeException("Invalid item descriptor count (${icount})")
|
if (icount !is Number) throw LuaRuntimeException("Invalid item descriptor count (${icount})")
|
||||||
|
|
||||||
result = KOptional(ItemDescriptor(iname.decode(), icount.toLong(), iparameters))
|
result = KOptional(ItemDescriptor(iname.decode(), icount.toLong(), iparameters as JsonObject))
|
||||||
}
|
}
|
||||||
|
|
||||||
return Supplier { result.value }
|
return Supplier { result.value }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun ExecutionContext.ItemDescriptor(data: Table): ItemDescriptor {
|
||||||
|
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 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()
|
||||||
|
@ -38,11 +38,11 @@ enum class SkyType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class FlyingType {
|
enum class FlyingType(override val jsonName: String) : IStringSerializable {
|
||||||
NONE,
|
NONE("none"),
|
||||||
DISEMBARKING,
|
DISEMBARKING("disembarking"),
|
||||||
WARP,
|
WARP("warp"),
|
||||||
ARRIVING;
|
ARRIVING("arriving");
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val CODEC = StreamCodec.Enum(FlyingType::class.java)
|
val CODEC = StreamCodec.Enum(FlyingType::class.java)
|
||||||
|
375
src/main/kotlin/ru/dbotthepony/kstarbound/lua/Conversions.kt
Normal file
375
src/main/kotlin/ru/dbotthepony/kstarbound/lua/Conversions.kt
Normal file
@ -0,0 +1,375 @@
|
|||||||
|
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.Conversions
|
||||||
|
import org.classdump.luna.Table
|
||||||
|
import org.classdump.luna.TableFactory
|
||||||
|
import org.classdump.luna.impl.NonsuspendableFunctionException
|
||||||
|
import org.classdump.luna.runtime.AbstractFunction3
|
||||||
|
import org.classdump.luna.runtime.ExecutionContext
|
||||||
|
import ru.dbotthepony.kommons.gson.set
|
||||||
|
import ru.dbotthepony.kommons.math.RGBAColor
|
||||||
|
import ru.dbotthepony.kommons.util.AABB
|
||||||
|
import ru.dbotthepony.kommons.util.IStruct2d
|
||||||
|
import ru.dbotthepony.kommons.util.IStruct2i
|
||||||
|
import ru.dbotthepony.kommons.util.IStruct3i
|
||||||
|
import ru.dbotthepony.kommons.util.IStruct4i
|
||||||
|
import ru.dbotthepony.kommons.vector.Vector2d
|
||||||
|
import ru.dbotthepony.kommons.vector.Vector2f
|
||||||
|
import ru.dbotthepony.kommons.vector.Vector2i
|
||||||
|
import ru.dbotthepony.kstarbound.json.InternedJsonElementAdapter
|
||||||
|
import ru.dbotthepony.kstarbound.world.physics.Poly
|
||||||
|
|
||||||
|
fun ExecutionContext.toVector2i(table: Any): Vector2i {
|
||||||
|
val x = indexNoYield(table, 1L)
|
||||||
|
val y = indexNoYield(table, 2L)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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.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)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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 toJsonFromLua(value: Any?): JsonElement {
|
||||||
|
return when (value) {
|
||||||
|
null, is JsonNull -> JsonNull.INSTANCE
|
||||||
|
is String -> JsonPrimitive(value)
|
||||||
|
is ByteString -> JsonPrimitive(value.decode())
|
||||||
|
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 value.asDouble
|
||||||
|
} 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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: 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 {
|
||||||
|
return newTable(2, 0).apply {
|
||||||
|
this[1L] = value.component1()
|
||||||
|
this[2L] = value.component2()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun TableFactory.from(value: Poly): Table {
|
||||||
|
return newTable(value.vertices.size, 0).apply {
|
||||||
|
value.vertices.withIndex().forEach { (i, v) -> this[i + 1L] = from(v) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun TableFactory.fromCollection(value: Collection<JsonElement?>): Table {
|
||||||
|
val table = newTable(value.size, 0)
|
||||||
|
|
||||||
|
for ((k, v) in value.withIndex()) {
|
||||||
|
table.rawset(Conversions.normaliseKey(k + 1), from(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
return table
|
||||||
|
}
|
||||||
|
|
||||||
|
fun TableFactory.from(value: IStruct2i): Table {
|
||||||
|
return newTable(2, 0).also {
|
||||||
|
it.rawset(1L, value.component1())
|
||||||
|
it.rawset(2L, value.component2())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun TableFactory.from(value: IStruct3i): Table {
|
||||||
|
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 {
|
||||||
|
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 {
|
||||||
|
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 {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
@ -1,33 +1,38 @@
|
|||||||
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 com.google.gson.JsonNull
|
|
||||||
import com.google.gson.JsonObject
|
|
||||||
import com.google.gson.JsonPrimitive
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectAVLTreeMap
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectIterators
|
import it.unimi.dsi.fastutil.objects.ObjectIterators
|
||||||
import org.classdump.luna.ByteString
|
|
||||||
import org.classdump.luna.Conversions
|
|
||||||
import org.classdump.luna.LuaRuntimeException
|
import org.classdump.luna.LuaRuntimeException
|
||||||
import org.classdump.luna.Table
|
import org.classdump.luna.Table
|
||||||
import org.classdump.luna.TableFactory
|
|
||||||
import org.classdump.luna.impl.NonsuspendableFunctionException
|
import org.classdump.luna.impl.NonsuspendableFunctionException
|
||||||
import org.classdump.luna.lib.ArgumentIterator
|
import org.classdump.luna.lib.ArgumentIterator
|
||||||
import org.classdump.luna.lib.BadArgumentException
|
|
||||||
import org.classdump.luna.runtime.AbstractFunction0
|
import org.classdump.luna.runtime.AbstractFunction0
|
||||||
import org.classdump.luna.runtime.AbstractFunction1
|
import org.classdump.luna.runtime.AbstractFunction1
|
||||||
import org.classdump.luna.runtime.AbstractFunction2
|
import org.classdump.luna.runtime.AbstractFunction2
|
||||||
import org.classdump.luna.runtime.AbstractFunction3
|
import org.classdump.luna.runtime.AbstractFunction3
|
||||||
import org.classdump.luna.runtime.AbstractFunction4
|
import org.classdump.luna.runtime.AbstractFunction4
|
||||||
import org.classdump.luna.runtime.AbstractFunctionAnyArg
|
import org.classdump.luna.runtime.AbstractFunctionAnyArg
|
||||||
|
import org.classdump.luna.runtime.Dispatch
|
||||||
import org.classdump.luna.runtime.ExecutionContext
|
import org.classdump.luna.runtime.ExecutionContext
|
||||||
import org.classdump.luna.runtime.LuaFunction
|
import org.classdump.luna.runtime.LuaFunction
|
||||||
import ru.dbotthepony.kommons.util.IStruct2i
|
import org.classdump.luna.runtime.UnresolvedControlThrowable
|
||||||
import ru.dbotthepony.kommons.util.IStruct3i
|
|
||||||
import ru.dbotthepony.kommons.util.IStruct4i
|
fun ExecutionContext.indexNoYield(table: Any, key: Any): Any? {
|
||||||
import ru.dbotthepony.kstarbound.json.InternedJsonElementAdapter
|
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? {
|
fun ArgumentIterator.nextOptionalFloat(): Double? {
|
||||||
if (hasNext()) {
|
if (hasNext()) {
|
||||||
@ -94,161 +99,6 @@ operator fun Table.iterator(): Iterator<Map.Entry<Any, Any>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Table.toJson(): JsonElement {
|
|
||||||
val arrayValues = Long2ObjectAVLTreeMap<Any>()
|
|
||||||
val hashValues = HashMap<Any, Any>()
|
|
||||||
|
|
||||||
for ((k, v) in this) {
|
|
||||||
if (k is Number) {
|
|
||||||
arrayValues[k.toLong()] = v
|
|
||||||
} else {
|
|
||||||
hashValues[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hashValues.isEmpty()) {
|
|
||||||
return JsonArray(arrayValues.size).also {
|
|
||||||
for (v in arrayValues.values) {
|
|
||||||
if (v is ByteString) {
|
|
||||||
it.add(JsonPrimitive(v.decode()))
|
|
||||||
} else if (v is Number) {
|
|
||||||
it.add(JsonPrimitive(v))
|
|
||||||
} else if (v is Boolean) {
|
|
||||||
it.add(InternedJsonElementAdapter.of(v))
|
|
||||||
} else if (v is Table) {
|
|
||||||
it.add(v.toJson())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return JsonObject().also {
|
|
||||||
for ((k, v) in arrayValues) {
|
|
||||||
if (v is ByteString) {
|
|
||||||
it.add(k.toString(), JsonPrimitive(v.decode()))
|
|
||||||
} else if (v is Number) {
|
|
||||||
it.add(k.toString(), JsonPrimitive(v))
|
|
||||||
} else if (v is Boolean) {
|
|
||||||
it.add(k.toString(), InternedJsonElementAdapter.of(v))
|
|
||||||
} else if (v is Table) {
|
|
||||||
it.add(k.toString(), v.toJson())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for ((k, v) in hashValues) {
|
|
||||||
if (v is ByteString) {
|
|
||||||
it.add(k.toString(), JsonPrimitive(v.decode()))
|
|
||||||
} else if (v is Number) {
|
|
||||||
it.add(k.toString(), JsonPrimitive(v))
|
|
||||||
} else if (v is Boolean) {
|
|
||||||
it.add(k.toString(), InternedJsonElementAdapter.of(v))
|
|
||||||
} else if (v is Table) {
|
|
||||||
it.add(k.toString(), v.toJson())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Table.toJsonObject(): JsonObject {
|
|
||||||
return JsonObject().also {
|
|
||||||
for ((k, v) in this) {
|
|
||||||
if (v is ByteString) {
|
|
||||||
it.add(k.toString(), JsonPrimitive(v.decode()))
|
|
||||||
} else if (v is Number) {
|
|
||||||
it.add(k.toString(), JsonPrimitive(v))
|
|
||||||
} else if (v is Boolean) {
|
|
||||||
it.add(k.toString(), InternedJsonElementAdapter.of(v))
|
|
||||||
} else if (v is Table) {
|
|
||||||
it.add(k.toString(), v.toJson())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun TableFactory.from(value: JsonElement?): Any? {
|
|
||||||
when (value) {
|
|
||||||
is JsonPrimitive -> {
|
|
||||||
if (value.isNumber) {
|
|
||||||
return value.asDouble
|
|
||||||
} else if (value.isString) {
|
|
||||||
return ByteString.of(value.asString)
|
|
||||||
} else if (value.isBoolean) {
|
|
||||||
return value.asBoolean
|
|
||||||
} else {
|
|
||||||
throw RuntimeException("unreachable code")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is JsonNull -> return null
|
|
||||||
is JsonArray -> return from(value)
|
|
||||||
is JsonObject -> return from(value)
|
|
||||||
null -> return null
|
|
||||||
else -> throw RuntimeException(value::class.qualifiedName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun TableFactory.from(value: JsonObject): Table {
|
|
||||||
val table = newTable(0, value.size())
|
|
||||||
|
|
||||||
for ((k, v) in value.entrySet()) {
|
|
||||||
table.rawset(Conversions.normaliseKey(k), from(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
return table
|
|
||||||
}
|
|
||||||
|
|
||||||
fun TableFactory.from(value: JsonArray): Table {
|
|
||||||
val table = newTable(value.size(), 0)
|
|
||||||
|
|
||||||
for ((k, v) in value.withIndex()) {
|
|
||||||
table.rawset(Conversions.normaliseKey(k + 1), from(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
return table
|
|
||||||
}
|
|
||||||
|
|
||||||
fun TableFactory.fromCollection(value: Collection<JsonElement?>): Table {
|
|
||||||
val table = newTable(value.size, 0)
|
|
||||||
|
|
||||||
for ((k, v) in value.withIndex()) {
|
|
||||||
table.rawset(Conversions.normaliseKey(k + 1), from(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
return table
|
|
||||||
}
|
|
||||||
|
|
||||||
fun TableFactory.from(value: IStruct2i): Table {
|
|
||||||
return newTable(2, 0).also {
|
|
||||||
it.rawset(1L, value.component1())
|
|
||||||
it.rawset(2L, value.component2())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun TableFactory.from(value: IStruct3i): Table {
|
|
||||||
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 {
|
|
||||||
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: Collection<Any>): Table {
|
|
||||||
return newTable(value.size, 0).also {
|
|
||||||
for ((i, v) in value.withIndex()) {
|
|
||||||
it.rawset(i + 1L, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated("Function is a stub")
|
@Deprecated("Function is a stub")
|
||||||
fun luaStub(message: String = "not yet implemented"): LuaFunction<Any?, Any?, Any?, Any?, Any?> {
|
fun luaStub(message: String = "not yet implemented"): LuaFunction<Any?, Any?, Any?, Any?, Any?> {
|
||||||
return object : LuaFunction<Any?, Any?, Any?, Any?, Any?>() {
|
return object : LuaFunction<Any?, Any?, Any?, Any?, Any?>() {
|
||||||
@ -286,42 +136,19 @@ fun luaStub(message: String = "not yet implemented"): LuaFunction<Any?, Any?, An
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun luaFunction0String(name: String, callable: (String) -> Any?): LuaFunction<Any?, *, *, *, *> {
|
fun luaFunctionN(name: String, callable: ExecutionContext.(ArgumentIterator) -> Unit): LuaFunction<Any, Any, Any, Any, Any> {
|
||||||
return object : AbstractFunction1<Any?>() {
|
|
||||||
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
|
||||||
throw NonsuspendableFunctionException(this::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun invoke(context: ExecutionContext, arg: Any?) {
|
|
||||||
if (arg !is ByteString)
|
|
||||||
throw BadArgumentException(1, name, "string expected, got ${if (arg == null) "nil" else arg::class.qualifiedName}")
|
|
||||||
|
|
||||||
val result = callable.invoke(arg.decode())
|
|
||||||
|
|
||||||
if (result != null && result !== Unit) {
|
|
||||||
context.returnBuffer.setTo(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun luaFunctionN(name: String, callable: (ExecutionContext, ArgumentIterator) -> Any?): LuaFunction<Any, Any, Any, Any, Any> {
|
|
||||||
return object : AbstractFunctionAnyArg() {
|
return object : AbstractFunctionAnyArg() {
|
||||||
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
||||||
throw NonsuspendableFunctionException(this::class.java)
|
throw NonsuspendableFunctionException(this::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invoke(context: ExecutionContext, args: Array<out Any>) {
|
override fun invoke(context: ExecutionContext, args: Array<out Any>) {
|
||||||
val result = callable.invoke(context, ArgumentIterator.of(context, name, args))
|
callable.invoke(context, ArgumentIterator.of(context, name, args))
|
||||||
|
|
||||||
if (result != null && result !== Unit) {
|
|
||||||
context.returnBuffer.setTo(result)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun luaFunctionNS(name: String, callable: (ExecutionContext, ArgumentIterator) -> StateMachine): LuaFunction<Any, Any, Any, Any, Any> {
|
fun luaFunctionNS(name: String, callable: ExecutionContext.(ArgumentIterator) -> StateMachine): LuaFunction<Any, Any, Any, Any, Any> {
|
||||||
return object : AbstractFunctionAnyArg() {
|
return object : AbstractFunctionAnyArg() {
|
||||||
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
||||||
(suspendedState as StateMachine).run(context)
|
(suspendedState as StateMachine).run(context)
|
||||||
@ -333,7 +160,19 @@ fun luaFunctionNS(name: String, callable: (ExecutionContext, ArgumentIterator) -
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun luaFunction(callable: (ExecutionContext) -> Unit): LuaFunction<*, *, *, *, *> {
|
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?>) {
|
||||||
|
callable.invoke(context, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun luaFunction(callable: ExecutionContext.() -> Unit): LuaFunction<*, *, *, *, *> {
|
||||||
return object : AbstractFunction0() {
|
return object : AbstractFunction0() {
|
||||||
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
||||||
throw NonsuspendableFunctionException(this::class.java)
|
throw NonsuspendableFunctionException(this::class.java)
|
||||||
@ -345,7 +184,7 @@ fun luaFunction(callable: (ExecutionContext) -> Unit): LuaFunction<*, *, *, *, *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> luaFunction(callable: (ExecutionContext, T) -> Unit): LuaFunction<T, *, *, *, *> {
|
fun <T> luaFunction(callable: ExecutionContext.(T) -> Unit): LuaFunction<T, *, *, *, *> {
|
||||||
return object : AbstractFunction1<T>() {
|
return object : AbstractFunction1<T>() {
|
||||||
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
||||||
throw NonsuspendableFunctionException(this::class.java)
|
throw NonsuspendableFunctionException(this::class.java)
|
||||||
@ -357,7 +196,7 @@ fun <T> luaFunction(callable: (ExecutionContext, T) -> Unit): LuaFunction<T, *,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T, T2> luaFunction(callable: (ExecutionContext, T, T2) -> Unit): LuaFunction<T, T2, *, *, *> {
|
fun <T, T2> luaFunction(callable: ExecutionContext.(T, T2) -> Unit): LuaFunction<T, T2, *, *, *> {
|
||||||
return object : AbstractFunction2<T, T2>() {
|
return object : AbstractFunction2<T, T2>() {
|
||||||
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
||||||
throw NonsuspendableFunctionException(this::class.java)
|
throw NonsuspendableFunctionException(this::class.java)
|
||||||
@ -369,7 +208,7 @@ fun <T, T2> luaFunction(callable: (ExecutionContext, T, T2) -> Unit): LuaFunctio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T, T2, T3> luaFunction(callable: (ExecutionContext, T, T2, T3) -> Unit): LuaFunction<T, T2, T3, *, *> {
|
fun <T, T2, T3> luaFunction(callable: ExecutionContext.(T, T2, T3) -> Unit): LuaFunction<T, T2, T3, *, *> {
|
||||||
return object : AbstractFunction3<T, T2, T3>() {
|
return object : AbstractFunction3<T, T2, T3>() {
|
||||||
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
||||||
throw NonsuspendableFunctionException(this::class.java)
|
throw NonsuspendableFunctionException(this::class.java)
|
||||||
@ -381,7 +220,7 @@ fun <T, T2, T3> luaFunction(callable: (ExecutionContext, T, T2, T3) -> Unit): Lu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T, T2, T3, T4> luaFunction(callable: (ExecutionContext, T, T2, T3, T4) -> Unit): LuaFunction<T, T2, T3, T4, *> {
|
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>() {
|
return object : AbstractFunction4<T, T2, T3, T4>() {
|
||||||
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
override fun resume(context: ExecutionContext, suspendedState: Any) {
|
||||||
throw NonsuspendableFunctionException(this::class.java)
|
throw NonsuspendableFunctionException(this::class.java)
|
||||||
|
264
src/main/kotlin/ru/dbotthepony/kstarbound/lua/LuaEnvironment.kt
Normal file
264
src/main/kotlin/ru/dbotthepony/kstarbound/lua/LuaEnvironment.kt
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
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.LuaType
|
||||||
|
import org.classdump.luna.StateContext
|
||||||
|
import org.classdump.luna.Table
|
||||||
|
import org.classdump.luna.Variable
|
||||||
|
import org.classdump.luna.env.RuntimeEnvironments
|
||||||
|
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.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
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
globals["require"] = LuaRequire()
|
||||||
|
|
||||||
|
// 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())
|
||||||
|
|
||||||
|
// 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>()
|
||||||
|
|
||||||
|
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)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun attach(script: ChunkFactory) {
|
||||||
|
scripts.add(script)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun attach(scripts: Collection<AssetPath>) {
|
||||||
|
for (script in scripts) {
|
||||||
|
attach(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 init(): 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()
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (errorState)
|
||||||
|
return
|
||||||
|
|
||||||
|
val load = globals[name]
|
||||||
|
|
||||||
|
if (load is LuaFunction<*, *, *, *, *>) {
|
||||||
|
try {
|
||||||
|
executor.call(this, load)
|
||||||
|
} catch (err: Throwable) {
|
||||||
|
errorState = true
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val LOGGER = LogManager.getLogger()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.lua
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray
|
||||||
|
import com.google.gson.JsonElement
|
||||||
|
import com.google.gson.JsonNull
|
||||||
|
import org.apache.logging.log4j.LogManager
|
||||||
|
import org.classdump.luna.ByteString
|
||||||
|
import org.classdump.luna.exec.CallPausedException
|
||||||
|
import org.classdump.luna.runtime.LuaFunction
|
||||||
|
|
||||||
|
class LuaMessageHandler(val lua: LuaEnvironment) {
|
||||||
|
private val handlers = HashMap<String, LuaFunction<*, *, *, *, *>>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
val table = lua.newTable()
|
||||||
|
|
||||||
|
table["setHandler"] = luaFunction { name: ByteString, handler: LuaFunction<*, *, *, *, *>? ->
|
||||||
|
if (handler == null) {
|
||||||
|
handlers.remove(name.decode())
|
||||||
|
} else {
|
||||||
|
handlers[name.decode()] = handler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lua.globals["message"] = table
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handle(message: String, isLocal: Boolean, parameters: JsonArray): JsonElement {
|
||||||
|
if (lua.errorState)
|
||||||
|
return JsonNull.INSTANCE
|
||||||
|
|
||||||
|
val handler = handlers[message] ?: return JsonNull.INSTANCE
|
||||||
|
|
||||||
|
try {
|
||||||
|
val unpacked = arrayOfNulls<Any>(parameters.size() + 2)
|
||||||
|
|
||||||
|
unpacked[0] = ByteString.of(message)
|
||||||
|
unpacked[1] = isLocal
|
||||||
|
|
||||||
|
for ((i, v) in parameters.withIndex()) {
|
||||||
|
unpacked[i + 2] = lua.from(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
val result = lua.executor.call(lua, handler, *unpacked)
|
||||||
|
|
||||||
|
if (result.isEmpty()) {
|
||||||
|
return JsonNull.INSTANCE
|
||||||
|
} else if (result.size == 1) {
|
||||||
|
return toJsonFromLua(result[0])
|
||||||
|
} else {
|
||||||
|
val array = JsonArray()
|
||||||
|
|
||||||
|
for (v in result) {
|
||||||
|
array.add(toJsonFromLua(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
return array
|
||||||
|
}
|
||||||
|
} catch (err: CallPausedException) {
|
||||||
|
lua.markErrored()
|
||||||
|
LOGGER.error("Message handler for $message tried to yield", err)
|
||||||
|
return JsonNull.INSTANCE
|
||||||
|
} catch (err: Throwable) {
|
||||||
|
lua.markErrored()
|
||||||
|
LOGGER.error("Message handler for $message errored", err)
|
||||||
|
return JsonNull.INSTANCE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val LOGGER = LogManager.getLogger()
|
||||||
|
}
|
||||||
|
}
|
@ -1,30 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.lua
|
|
||||||
|
|
||||||
import org.classdump.luna.ByteString
|
|
||||||
import org.classdump.luna.LuaRuntimeException
|
|
||||||
import org.classdump.luna.impl.NonsuspendableFunctionException
|
|
||||||
import org.classdump.luna.runtime.AbstractFunction1
|
|
||||||
import org.classdump.luna.runtime.ExecutionContext
|
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
|
||||||
|
|
||||||
class LuaRequire(private val state: NewLuaState) : 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()
|
|
||||||
|
|
||||||
val file = Starbound.locate(name)
|
|
||||||
|
|
||||||
if (!file.exists) {
|
|
||||||
throw LuaRuntimeException("File $name does not exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!file.isFile) {
|
|
||||||
throw LuaRuntimeException("File $name is a directory")
|
|
||||||
}
|
|
||||||
|
|
||||||
state.load(context, file.readToString(), file.computeFullPath())
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,28 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.lua
|
||||||
|
|
||||||
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
|
|
||||||
|
class LuaUpdateComponent(val lua: LuaEnvironment) {
|
||||||
|
var stepCount = 1
|
||||||
|
private var steps = 0
|
||||||
|
|
||||||
|
init {
|
||||||
|
val script = lua.newTable()
|
||||||
|
lua.globals["script"] = script
|
||||||
|
|
||||||
|
script["updateDt"] = luaFunction {
|
||||||
|
returnBuffer.setTo(stepCount * Starbound.TIMESTEP)
|
||||||
|
}
|
||||||
|
|
||||||
|
script["setUpdateDelta"] = luaFunction { ticks: Int ->
|
||||||
|
stepCount = ticks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update() {
|
||||||
|
if (steps++ >= stepCount) {
|
||||||
|
steps = 0
|
||||||
|
lua.invokeGlobal("update")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,77 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.lua
|
|
||||||
|
|
||||||
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.DirectCallExecutor
|
|
||||||
import org.classdump.luna.impl.StateContexts
|
|
||||||
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.runtime.Dispatch
|
|
||||||
import org.classdump.luna.runtime.ExecutionContext
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
|
||||||
|
|
||||||
class NewLuaState {
|
|
||||||
val state: StateContext = StateContexts.newDefaultInstance()
|
|
||||||
val chunkLoader: CompilerChunkLoader = CompilerChunkLoader.of(CompilerSettings.defaultNoAccountingSettings(), "kstarbound_${COUNTER.getAndIncrement()}_")
|
|
||||||
val env: Table = state.newTable()
|
|
||||||
val executor: DirectCallExecutor = DirectCallExecutor.newExecutor()
|
|
||||||
|
|
||||||
init {
|
|
||||||
env["_G"] = env
|
|
||||||
|
|
||||||
env["assert"] = BasicLib.assertFn()
|
|
||||||
env["error"] = BasicLib.error()
|
|
||||||
env["getmetatable"] = BasicLib.getmetatable()
|
|
||||||
env["ipairs"] = BasicLib.ipairs()
|
|
||||||
env["next"] = BasicLib.next()
|
|
||||||
env["pairs"] = BasicLib.pairs()
|
|
||||||
env["pcall"] = BasicLib.pcall()
|
|
||||||
|
|
||||||
env["rawequal"] = BasicLib.rawequal()
|
|
||||||
env["rawget"] = BasicLib.rawget()
|
|
||||||
env["rawlen"] = BasicLib.rawlen()
|
|
||||||
env["rawset"] = BasicLib.rawset()
|
|
||||||
env["select"] = BasicLib.select()
|
|
||||||
env["setmetatable"] = BasicLib.setmetatable()
|
|
||||||
env["tostring"] = BasicLib.tostring()
|
|
||||||
env["tonumber"] = BasicLib.tonumber()
|
|
||||||
env["type"] = BasicLib.type()
|
|
||||||
env["_VERSION"] = BasicLib._VERSION
|
|
||||||
env["xpcall"] = BasicLib.xpcall()
|
|
||||||
|
|
||||||
env["print"] = PrintFunction(env)
|
|
||||||
|
|
||||||
env["require"] = LuaRequire(this)
|
|
||||||
|
|
||||||
CoroutineLib.installInto(state, env)
|
|
||||||
TableLib.installInto(state, env)
|
|
||||||
MathLib.installInto(state, env)
|
|
||||||
StringLib.installInto(state, env)
|
|
||||||
OsLib.installInto(state, env, RuntimeEnvironments.system())
|
|
||||||
|
|
||||||
// TODO: NYI, maybe polyfill?
|
|
||||||
Utf8Lib.installInto(state, env)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun load(code: String, name: String = "main chunk") {
|
|
||||||
val main = chunkLoader.loadTextChunk(Variable(env), name, code)
|
|
||||||
executor.call(state, main)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun load(context: ExecutionContext, code: String, name: String = "main chunk") {
|
|
||||||
Dispatch.call(context, chunkLoader.loadTextChunk(Variable(env), name, code))
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val COUNTER = AtomicInteger()
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,260 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.lua.bindings
|
||||||
|
|
||||||
|
import org.classdump.luna.ByteString
|
||||||
|
import org.classdump.luna.Table
|
||||||
|
import ru.dbotthepony.kommons.collect.map
|
||||||
|
import ru.dbotthepony.kommons.collect.toList
|
||||||
|
import ru.dbotthepony.kommons.vector.Vector2f
|
||||||
|
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
||||||
|
import ru.dbotthepony.kstarbound.lua.from
|
||||||
|
import ru.dbotthepony.kstarbound.lua.get
|
||||||
|
import ru.dbotthepony.kstarbound.lua.iterator
|
||||||
|
import ru.dbotthepony.kstarbound.lua.luaFunction
|
||||||
|
import ru.dbotthepony.kstarbound.lua.luaFunctionArray
|
||||||
|
import ru.dbotthepony.kstarbound.lua.set
|
||||||
|
import ru.dbotthepony.kstarbound.lua.toAABB
|
||||||
|
import ru.dbotthepony.kstarbound.lua.toColor
|
||||||
|
import ru.dbotthepony.kstarbound.lua.toPoly
|
||||||
|
import ru.dbotthepony.kstarbound.lua.toVector2d
|
||||||
|
import ru.dbotthepony.kstarbound.lua.toVector2f
|
||||||
|
import ru.dbotthepony.kstarbound.world.entities.Animator
|
||||||
|
|
||||||
|
fun provideAnimatorBindings(self: Animator, lua: LuaEnvironment) {
|
||||||
|
val callbacks = lua.newTable()
|
||||||
|
lua.globals["animator"] = callbacks
|
||||||
|
|
||||||
|
callbacks["setAnimationState"] = luaFunction { type: ByteString, state: ByteString, alwaysStart: Boolean? ->
|
||||||
|
returnBuffer.setTo(self.setActiveState(type.decode(), state.decode(), alwaysStart ?: false))
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["animationState"] = luaFunction { type: ByteString ->
|
||||||
|
returnBuffer.setTo(self.animationState(type.decode()))
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["animationStateProperty"] = luaFunction { type: ByteString, key: ByteString ->
|
||||||
|
returnBuffer.setTo(self.stateProperty(type.decode(), key.decode()))
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["setGlobalTag"] = luaFunction { key: ByteString, value: ByteString ->
|
||||||
|
self.setGlobalTag(key.decode(), value.decode())
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["setPartTag"] = luaFunction { part: ByteString, key: ByteString, value: ByteString ->
|
||||||
|
self.setPartTag(part.decode(), key.decode(), value.decode())
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["setFlipped"] = luaFunction { isFlipped: Boolean, centerLine: Double? ->
|
||||||
|
self.isFlipped = isFlipped
|
||||||
|
self.flippedRelativeCenterLine = centerLine ?: 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["setAnimationRate"] = luaFunction { rate: Double ->
|
||||||
|
self.animationRate = rate
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["rotateGroup"] = luaFunction { group: ByteString, rotation: Number, immediate: Boolean? ->
|
||||||
|
self.rotateGroup(group.decode(), rotation.toDouble(), immediate ?: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["currentRotationAngle"] = luaFunction { group: ByteString ->
|
||||||
|
returnBuffer.setTo(self.currentRotationAngle(group.decode()))
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["targetRotationAngle"] = luaFunction { group: ByteString ->
|
||||||
|
returnBuffer.setTo(self.targetRotationAngle(group.decode()))
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["hasRotationGroup"] = luaFunction { group: ByteString ->
|
||||||
|
returnBuffer.setTo(self.hasRotationGroup(group.decode()))
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["rotationGroups"] = luaFunction {
|
||||||
|
val groups = self.rotationGroups()
|
||||||
|
val keys = newTable(groups.size, 0)
|
||||||
|
groups.withIndex().forEach { (i, v) -> keys[i + 1L] = v }
|
||||||
|
returnBuffer.setTo(keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["hasTransformationGroup"] = luaFunction { group: ByteString ->
|
||||||
|
returnBuffer.setTo(self.hasTransformationGroup(group.decode()))
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["transformationGroups"] = luaFunction {
|
||||||
|
val groups = self.transformationGroups()
|
||||||
|
val keys = newTable(groups.size, 0)
|
||||||
|
groups.withIndex().forEach { (i, v) -> keys[i + 1L] = v }
|
||||||
|
returnBuffer.setTo(keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["translateTransformGroup"] = luaFunction { group: ByteString, translation: Table ->
|
||||||
|
self.translateTransformGroup(group.decode(), toVector2f(translation))
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["rotateTransformGroup"] = luaFunction { group: ByteString, rotation: Double, center: Table? ->
|
||||||
|
self.rotateTransformGroup(group.decode(), rotation.toFloat(), if (center == null) Vector2f.ZERO else toVector2f(center))
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["scaleTransformationGroup"] = luaFunction { group: ByteString, scale: Any, center: Table? ->
|
||||||
|
if (scale is Number) {
|
||||||
|
self.scaleTransformationGroup(group.decode(), Vector2f(scale.toFloat(), scale.toFloat()), if (center == null) Vector2f.ZERO else toVector2f(center))
|
||||||
|
} else {
|
||||||
|
self.scaleTransformationGroup(group.decode(), toVector2f(scale), if (center == null) Vector2f.ZERO else toVector2f(center))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["transformTransformationGroup"] = luaFunctionArray { arguments: Array<out Any?> ->
|
||||||
|
val group = arguments[0] as ByteString
|
||||||
|
val r00 = arguments[1] as Number
|
||||||
|
val r01 = arguments[2] as Number
|
||||||
|
val r02 = arguments[3] as Number
|
||||||
|
val r10 = arguments[4] as Number
|
||||||
|
val r11 = arguments[5] as Number
|
||||||
|
val r12 = arguments[6] as Number
|
||||||
|
|
||||||
|
self.transformTransformationGroup(group.decode(), r00.toFloat(),
|
||||||
|
r01.toFloat(),
|
||||||
|
r02.toFloat(),
|
||||||
|
r10.toFloat(),
|
||||||
|
r11.toFloat(),
|
||||||
|
r12.toFloat())
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["resetTransformationGroup"] = luaFunction { group: ByteString ->
|
||||||
|
self.resetTransformationGroup(group.decode())
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["particleEmitters"] = luaFunction {
|
||||||
|
val groups = self.particleEmitters()
|
||||||
|
val keys = newTable(groups.size, 0)
|
||||||
|
groups.withIndex().forEach { (i, v) -> keys[i + 1L] = v }
|
||||||
|
returnBuffer.setTo(keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["hasParticleEmitter"] = luaFunction { group: ByteString ->
|
||||||
|
returnBuffer.setTo(self.hasParticleEmitter(group.decode()))
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["setParticleEmitterActive"] = luaFunction { emitter: ByteString, state: Boolean ->
|
||||||
|
self.setParticleEmitterActive(emitter.decode(), state)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["setParticleEmitterEmissionRate"] = luaFunction { emitter: ByteString, rate: Number ->
|
||||||
|
self.setParticleEmitterEmissionRate(emitter.decode(), rate.toDouble())
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["setParticleEmitterBurstCount"] = luaFunction { emitter: ByteString, count: Number ->
|
||||||
|
self.setParticleEmitterBurstCount(emitter.decode(), count.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["setParticleEmitterOffsetRegion"] = luaFunction { emitter: ByteString, region: Table ->
|
||||||
|
self.setParticleEmitterOffsetRegion(emitter.decode(), toAABB(region))
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["burstParticleEmitter"] = luaFunction { emitter: ByteString ->
|
||||||
|
self.burstParticleEmitter(emitter.decode())
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["lights"] = luaFunction {
|
||||||
|
val groups = self.lights()
|
||||||
|
val keys = newTable(groups.size, 0)
|
||||||
|
groups.withIndex().forEach { (i, v) -> keys[i + 1L] = v }
|
||||||
|
returnBuffer.setTo(keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["hasLight"] = luaFunction { light: ByteString ->
|
||||||
|
returnBuffer.setTo(self.hasLight(light.decode()))
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["setLightActive"] = luaFunction { light: ByteString, state: Boolean ->
|
||||||
|
self.setLightActive(light.decode(), state)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["setLightPosition"] = luaFunction { light: ByteString, position: Table ->
|
||||||
|
self.setLightPosition(light.decode(), toVector2d(position))
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["setLightColor"] = luaFunction { light: ByteString, color: Table ->
|
||||||
|
self.setLightColor(light.decode(), toColor(color))
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["setLightPointAngle"] = luaFunction { light: ByteString, angle: Number ->
|
||||||
|
self.setLightPointAngle(light.decode(), angle.toDouble())
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["sounds"] = luaFunction {
|
||||||
|
val groups = self.sounds()
|
||||||
|
val keys = newTable(groups.size, 0)
|
||||||
|
groups.withIndex().forEach { (i, v) -> keys[i + 1L] = v }
|
||||||
|
returnBuffer.setTo(keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["hasSound"] = luaFunction { sound: ByteString ->
|
||||||
|
returnBuffer.setTo(self.hasSound(sound.decode()))
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["setSoundPool"] = luaFunction { sound: ByteString, sounds: Table ->
|
||||||
|
self.setSoundPool(sound.decode(), sounds.iterator().map { (it.value as ByteString).decode() }.toList())
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["setSoundPosition"] = luaFunction { sound: ByteString, position: Table ->
|
||||||
|
self.setSoundPosition(sound.decode(), toVector2d(position))
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["playSound"] = luaFunction { sound: ByteString, loops: Number? ->
|
||||||
|
self.playSound(sound.decode(), loops?.toInt() ?: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["setSoundVolume"] = luaFunction { sound: ByteString, volume: Number, rampTime: Number? ->
|
||||||
|
self.setSoundVolume(sound.decode(), volume.toDouble(), rampTime?.toDouble() ?: 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["setSoundPitch"] = luaFunction { sound: ByteString, pitch: Number, rampTime: Number? ->
|
||||||
|
self.setSoundPitch(sound.decode(), pitch.toDouble(), rampTime?.toDouble() ?: 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["stopAllSounds"] = luaFunction { sound: ByteString, rampTime: Number? ->
|
||||||
|
self.stopAllSounds(sound.decode(), rampTime?.toDouble() ?: 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["effects"] = luaFunction {
|
||||||
|
val groups = self.effects()
|
||||||
|
val keys = newTable(groups.size, 0)
|
||||||
|
groups.withIndex().forEach { (i, v) -> keys[i + 1L] = v }
|
||||||
|
returnBuffer.setTo(keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["hasEffect"] = luaFunction { effect: ByteString ->
|
||||||
|
returnBuffer.setTo(self.hasEffect(effect.decode()))
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["setEffectActive"] = luaFunction { effect: ByteString, state: Boolean ->
|
||||||
|
self.setEffectActive(effect.decode(), state)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["parts"] = luaFunction {
|
||||||
|
val groups = self.parts()
|
||||||
|
val keys = newTable(groups.size, 0)
|
||||||
|
groups.withIndex().forEach { (i, v) -> keys[i + 1L] = v }
|
||||||
|
returnBuffer.setTo(keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["partPoint"] = luaFunction { part: ByteString, property: ByteString ->
|
||||||
|
returnBuffer.setTo(self.partPoint(part.decode(), property.decode())?.let { from(it) })
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["partPoly"] = luaFunction { part: ByteString, property: ByteString ->
|
||||||
|
returnBuffer.setTo(self.partPoly(part.decode(), property.decode())?.let { from(it) })
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["partProperty"] = luaFunction { part: ByteString, property: ByteString ->
|
||||||
|
returnBuffer.setTo(from(self.partProperty(part.decode(), property.decode())))
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["transformPoint"] = luaFunction { point: Table, part: ByteString ->
|
||||||
|
returnBuffer.setTo(from(toVector2d(point).times(self.partTransformation(part.decode()))))
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["transformPoly"] = luaFunction { poly: Table, part: ByteString ->
|
||||||
|
returnBuffer.setTo(from(toPoly(poly).transform(self.partTransformation(part.decode()))))
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package ru.dbotthepony.kstarbound.lua
|
package ru.dbotthepony.kstarbound.lua.bindings
|
||||||
|
|
||||||
import org.classdump.luna.ByteString
|
import org.classdump.luna.ByteString
|
||||||
import org.classdump.luna.LuaRuntimeException
|
import org.classdump.luna.LuaRuntimeException
|
||||||
@ -7,6 +7,7 @@ import org.classdump.luna.impl.NonsuspendableFunctionException
|
|||||||
import org.classdump.luna.runtime.AbstractFunction1
|
import org.classdump.luna.runtime.AbstractFunction1
|
||||||
import org.classdump.luna.runtime.ExecutionContext
|
import org.classdump.luna.runtime.ExecutionContext
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
|
import ru.dbotthepony.kstarbound.lua.from
|
||||||
|
|
||||||
class AssetJsonFunction(private val tables: TableFactory) : AbstractFunction1<ByteString>() {
|
class AssetJsonFunction(private val tables: TableFactory) : AbstractFunction1<ByteString>() {
|
||||||
override fun resume(context: ExecutionContext?, suspendedState: Any?) {
|
override fun resume(context: ExecutionContext?, suspendedState: Any?) {
|
@ -0,0 +1,42 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.lua.bindings
|
||||||
|
|
||||||
|
import ru.dbotthepony.kommons.vector.Vector2d
|
||||||
|
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
||||||
|
import ru.dbotthepony.kstarbound.lua.from
|
||||||
|
import ru.dbotthepony.kstarbound.lua.get
|
||||||
|
import ru.dbotthepony.kstarbound.lua.luaFunction
|
||||||
|
import ru.dbotthepony.kstarbound.lua.set
|
||||||
|
import ru.dbotthepony.kstarbound.world.entities.AbstractEntity
|
||||||
|
|
||||||
|
fun provideEntityBindings(self: AbstractEntity, lua: LuaEnvironment) {
|
||||||
|
val table = lua.newTable()
|
||||||
|
lua.globals["entity"] = table
|
||||||
|
|
||||||
|
table["id"] = luaFunction { returnBuffer.setTo(self.entityID) }
|
||||||
|
table["position"] = luaFunction { returnBuffer.setTo(from(self.position)) }
|
||||||
|
table["entityType"] = luaFunction { returnBuffer.setTo(self.type.jsonName) }
|
||||||
|
table["uniqueId"] = luaFunction { returnBuffer.setTo(self.uniqueID.get().orNull()) }
|
||||||
|
table["persistent"] = luaFunction { returnBuffer.setTo(self.isPersistent) }
|
||||||
|
|
||||||
|
table["entityInSight"] = luaFunction { TODO() }
|
||||||
|
table["isValidTarget"] = luaFunction { TODO() }
|
||||||
|
|
||||||
|
table["damageTeam"] = luaFunction {
|
||||||
|
val result = newTable()
|
||||||
|
|
||||||
|
result["team"] = self.team.get().team
|
||||||
|
result["type"] = self.type.jsonName
|
||||||
|
|
||||||
|
returnBuffer.setTo(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
table["distanceToEntity"] = luaFunction { entity: Number ->
|
||||||
|
val find = self.world.entities[entity.toInt()]
|
||||||
|
|
||||||
|
if (find != null) {
|
||||||
|
returnBuffer.setTo(from(self.world.geometry.diff(find.position, self.position)))
|
||||||
|
} else {
|
||||||
|
returnBuffer.setTo(from(Vector2d.ZERO))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package ru.dbotthepony.kstarbound.lua.bindings
|
package ru.dbotthepony.kstarbound.lua.bindings
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongArrayList
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
|
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
|
||||||
import org.classdump.luna.ByteString
|
import org.classdump.luna.ByteString
|
||||||
import org.classdump.luna.LuaRuntimeException
|
import org.classdump.luna.LuaRuntimeException
|
||||||
@ -14,10 +15,15 @@ import ru.dbotthepony.kstarbound.Registry
|
|||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
import ru.dbotthepony.kstarbound.defs.image.Image
|
import ru.dbotthepony.kstarbound.defs.image.Image
|
||||||
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor
|
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor
|
||||||
import ru.dbotthepony.kstarbound.lua.AssetJsonFunction
|
import ru.dbotthepony.kstarbound.lua.LUA_HINT_ARRAY
|
||||||
import ru.dbotthepony.kstarbound.lua.NewLuaState
|
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
||||||
import ru.dbotthepony.kstarbound.lua.StateMachine
|
import ru.dbotthepony.kstarbound.lua.StateMachine
|
||||||
|
import ru.dbotthepony.kstarbound.lua.createJsonArray
|
||||||
|
import ru.dbotthepony.kstarbound.lua.createJsonObject
|
||||||
import ru.dbotthepony.kstarbound.lua.from
|
import ru.dbotthepony.kstarbound.lua.from
|
||||||
|
import ru.dbotthepony.kstarbound.lua.get
|
||||||
|
import ru.dbotthepony.kstarbound.lua.indexNoYield
|
||||||
|
import ru.dbotthepony.kstarbound.lua.indexSetNoYield
|
||||||
import ru.dbotthepony.kstarbound.lua.iterator
|
import ru.dbotthepony.kstarbound.lua.iterator
|
||||||
import ru.dbotthepony.kstarbound.lua.luaFunction
|
import ru.dbotthepony.kstarbound.lua.luaFunction
|
||||||
import ru.dbotthepony.kstarbound.lua.luaFunctionN
|
import ru.dbotthepony.kstarbound.lua.luaFunctionN
|
||||||
@ -26,12 +32,14 @@ import ru.dbotthepony.kstarbound.lua.luaStub
|
|||||||
import ru.dbotthepony.kstarbound.lua.nextOptionalFloat
|
import ru.dbotthepony.kstarbound.lua.nextOptionalFloat
|
||||||
import ru.dbotthepony.kstarbound.lua.nextOptionalInteger
|
import ru.dbotthepony.kstarbound.lua.nextOptionalInteger
|
||||||
import ru.dbotthepony.kstarbound.lua.set
|
import ru.dbotthepony.kstarbound.lua.set
|
||||||
|
import ru.dbotthepony.kstarbound.lua.toLuaInteger
|
||||||
import kotlin.collections.component1
|
import kotlin.collections.component1
|
||||||
import kotlin.collections.component2
|
import kotlin.collections.component2
|
||||||
import kotlin.collections.isNotEmpty
|
import kotlin.collections.isNotEmpty
|
||||||
import kotlin.collections.random
|
import kotlin.collections.random
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
import kotlin.collections.withIndex
|
import kotlin.collections.withIndex
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
private fun <T : Any> lookup(registry: Registry<T>, key: Any?): Registry.Entry<T>? {
|
private fun <T : Any> lookup(registry: Registry<T>, key: Any?): Registry.Entry<T>? {
|
||||||
if (key is ByteString) {
|
if (key is ByteString) {
|
||||||
@ -94,30 +102,30 @@ private fun nonEmptyRegion(context: ExecutionContext, name: ByteString) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun registryDef(registry: Registry<*>): LuaFunction<ByteString, *, *, *, *> {
|
private fun registryDef(registry: Registry<*>): LuaFunction<ByteString, *, *, *, *> {
|
||||||
return luaFunction { context, name ->
|
return luaFunction { name ->
|
||||||
val value = registry[name.decode()] ?: throw LuaRuntimeException("No such NPC type $name")
|
val value = registry[name.decode()] ?: throw LuaRuntimeException("No such NPC type $name")
|
||||||
context.returnBuffer.setTo(context.from(value.json))
|
returnBuffer.setTo(from(value.json))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun registryDef2(registry: Registry<*>): LuaFunction<Any?, *, *, *, *> {
|
private fun registryDef2(registry: Registry<*>): LuaFunction<Any?, *, *, *, *> {
|
||||||
return luaFunction { context, name ->
|
return luaFunction { name ->
|
||||||
val def = lookup(registry, name)
|
val def = lookup(registry, name)
|
||||||
|
|
||||||
if (def != null) {
|
if (def != null) {
|
||||||
context.returnBuffer.setTo(context.newTable(0, 2).also {
|
returnBuffer.setTo(newTable(0, 2).also {
|
||||||
it["path"] = def.file?.computeFullPath()
|
it["path"] = def.file?.computeFullPath()
|
||||||
it["config"] = context.from(def.json)
|
it["config"] = from(def.json)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
context.returnBuffer.setTo()
|
returnBuffer.setTo()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun registryDefExists(registry: Registry<*>): LuaFunction<ByteString, *, *, *, *> {
|
private fun registryDefExists(registry: Registry<*>): LuaFunction<ByteString, *, *, *, *> {
|
||||||
return luaFunction { context, name ->
|
return luaFunction { name ->
|
||||||
context.returnBuffer.setTo(name.decode() in registry)
|
returnBuffer.setTo(name.decode() in registry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,11 +286,105 @@ private fun techConfig(context: ExecutionContext, arguments: ArgumentIterator) {
|
|||||||
context.returnBuffer.setTo(lookupStrict(Registries.techs, arguments.nextAny()).json)
|
context.returnBuffer.setTo(lookupStrict(Registries.techs, arguments.nextAny()).json)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun provideRootBindings(state: NewLuaState) {
|
private val jobject = luaFunction { returnBuffer.setTo(createJsonObject()) }
|
||||||
val table = state.state.newTable()
|
private val jarray = luaFunction { returnBuffer.setTo(createJsonArray()) }
|
||||||
state.env["root"] = table
|
|
||||||
|
|
||||||
table["assetJson"] = AssetJsonFunction(state.state)
|
private val jremove = luaFunction { self: Table, key: Any ->
|
||||||
|
val nils = self.metatable?.rawget("__nils") as? Table
|
||||||
|
|
||||||
|
if (nils != null) {
|
||||||
|
nils[key] = 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
self[key] = null as Any?
|
||||||
|
}
|
||||||
|
|
||||||
|
private val jsize = luaFunction { self: Table ->
|
||||||
|
var elemCount = 0L
|
||||||
|
var highestIndex = 0L
|
||||||
|
var hintList = false
|
||||||
|
|
||||||
|
val meta = self.metatable
|
||||||
|
|
||||||
|
if (meta != null) {
|
||||||
|
if (meta["__typehint"] == LUA_HINT_ARRAY) {
|
||||||
|
hintList = true
|
||||||
|
}
|
||||||
|
|
||||||
|
val nils = meta["__nils"]
|
||||||
|
|
||||||
|
if (nils is Table) {
|
||||||
|
for ((k, v) in nils) {
|
||||||
|
val ik = k.toLuaInteger()
|
||||||
|
|
||||||
|
if (ik != null) {
|
||||||
|
highestIndex = max(ik, highestIndex)
|
||||||
|
} else {
|
||||||
|
hintList = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ((k, v) in self) {
|
||||||
|
val ik = k.toLuaInteger()
|
||||||
|
|
||||||
|
if (ik != null) {
|
||||||
|
highestIndex = max(ik, highestIndex)
|
||||||
|
} else {
|
||||||
|
hintList = false
|
||||||
|
}
|
||||||
|
|
||||||
|
elemCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hintList) {
|
||||||
|
returnBuffer.setTo(highestIndex)
|
||||||
|
} else {
|
||||||
|
returnBuffer.setTo(elemCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// why is this a thing?
|
||||||
|
private val jresize = luaFunction { self: Table, target: Long ->
|
||||||
|
val nils = self.metatable?.rawget("__nils") as? Table
|
||||||
|
|
||||||
|
if (nils != null) {
|
||||||
|
val keysToRemove = ArrayList<Any>()
|
||||||
|
|
||||||
|
for ((k, v) in nils) {
|
||||||
|
val ik = k.toLuaInteger()
|
||||||
|
|
||||||
|
if (ik != null && ik > 0L && ik > target)
|
||||||
|
keysToRemove.add(k)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (k in keysToRemove) {
|
||||||
|
nils[k] = null as Any?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val keysToRemove = ArrayList<Any>()
|
||||||
|
|
||||||
|
for ((k, v) in self) {
|
||||||
|
val ik = k.toLuaInteger()
|
||||||
|
|
||||||
|
if (ik != null && ik > 0L && ik > target)
|
||||||
|
keysToRemove.add(k)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (k in keysToRemove) {
|
||||||
|
self[k] = null as Any?
|
||||||
|
}
|
||||||
|
|
||||||
|
indexSetNoYield(self, target, indexNoYield(self, target))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun provideRootBindings(lua: LuaEnvironment) {
|
||||||
|
val table = lua.newTable()
|
||||||
|
lua.globals["root"] = table
|
||||||
|
|
||||||
|
table["assetJson"] = AssetJsonFunction(lua)
|
||||||
table["makeCurrentVersionedJson"] = luaStub("makeCurrentVersionedJson")
|
table["makeCurrentVersionedJson"] = luaStub("makeCurrentVersionedJson")
|
||||||
table["loadVersionedJson"] = luaStub("loadVersionedJson")
|
table["loadVersionedJson"] = luaStub("loadVersionedJson")
|
||||||
|
|
||||||
@ -346,6 +448,9 @@ fun provideRootBindings(state: NewLuaState) {
|
|||||||
table["dungeonMetadata"] = luaStub("dungeonMetadata")
|
table["dungeonMetadata"] = luaStub("dungeonMetadata")
|
||||||
table["behavior"] = luaStub("behavior")
|
table["behavior"] = luaStub("behavior")
|
||||||
|
|
||||||
state.env["jobject"] = luaFunction { executionContext -> executionContext.returnBuffer.setTo(executionContext.newTable()) }
|
lua.globals["jobject"] = jobject
|
||||||
state.env["jarray"] = luaFunction { executionContext -> executionContext.returnBuffer.setTo(executionContext.newTable()) }
|
lua.globals["jarray"] = jarray
|
||||||
|
lua.globals["jremove"] = jremove
|
||||||
|
lua.globals["jsize"] = jsize
|
||||||
|
lua.globals["jresize"] = jresize
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,135 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.lua.bindings
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager
|
||||||
|
import org.classdump.luna.ByteString
|
||||||
|
import org.classdump.luna.Table
|
||||||
|
import ru.dbotthepony.kommons.vector.Vector2d
|
||||||
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
|
import ru.dbotthepony.kstarbound.defs.PerlinNoiseParameters
|
||||||
|
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
||||||
|
import ru.dbotthepony.kstarbound.lua.from
|
||||||
|
import ru.dbotthepony.kstarbound.lua.get
|
||||||
|
import ru.dbotthepony.kstarbound.lua.luaFunction
|
||||||
|
import ru.dbotthepony.kstarbound.lua.luaFunctionArray
|
||||||
|
import ru.dbotthepony.kstarbound.lua.luaFunctionN
|
||||||
|
import ru.dbotthepony.kstarbound.lua.nextOptionalFloat
|
||||||
|
import ru.dbotthepony.kstarbound.lua.set
|
||||||
|
import ru.dbotthepony.kstarbound.lua.toJson
|
||||||
|
import ru.dbotthepony.kstarbound.lua.toVector2d
|
||||||
|
import ru.dbotthepony.kstarbound.lua.userdata.LuaPerlinNoise
|
||||||
|
import ru.dbotthepony.kstarbound.lua.userdata.LuaRandomGenerator
|
||||||
|
import ru.dbotthepony.kstarbound.math.Interpolator
|
||||||
|
import ru.dbotthepony.kstarbound.util.SBPattern
|
||||||
|
import ru.dbotthepony.kstarbound.util.random.AbstractPerlinNoise
|
||||||
|
import ru.dbotthepony.kstarbound.util.random.nextNormalDouble
|
||||||
|
import ru.dbotthepony.kstarbound.util.random.random
|
||||||
|
import ru.dbotthepony.kstarbound.util.random.staticRandom32
|
||||||
|
import ru.dbotthepony.kstarbound.util.random.staticRandomDouble
|
||||||
|
import ru.dbotthepony.kstarbound.util.random.staticRandomLong
|
||||||
|
import ru.dbotthepony.kstarbound.util.toStarboundString
|
||||||
|
import java.util.*
|
||||||
|
import java.util.random.RandomGenerator
|
||||||
|
|
||||||
|
private val LOGGER = LogManager.getLogger()
|
||||||
|
|
||||||
|
private val logInfo = luaFunctionN("logInfo") { args ->
|
||||||
|
LOGGER.info(args.nextString().toString().format(*args.copyRemaining()))
|
||||||
|
}
|
||||||
|
|
||||||
|
private val logWarn = luaFunctionN("logWarn") { args ->
|
||||||
|
LOGGER.warn(args.nextString().toString().format(*args.copyRemaining()))
|
||||||
|
}
|
||||||
|
|
||||||
|
private val logError = luaFunctionN("logError") { args ->
|
||||||
|
LOGGER.error(args.nextString().toString().format(*args.copyRemaining()))
|
||||||
|
}
|
||||||
|
|
||||||
|
private val interpolateSinEase = luaFunctionArray { args ->
|
||||||
|
if (args.size < 3)
|
||||||
|
throw IllegalArgumentException("Invalid amount of arguments to interpolateSinEase: ${args.size}")
|
||||||
|
|
||||||
|
val offset = args[0] as? Number ?: throw IllegalArgumentException("Invalid 'offset' argument: ${args[0]}")
|
||||||
|
|
||||||
|
if (args[1] is Number && args[2] is Number) {
|
||||||
|
returnBuffer.setTo(Interpolator.Sin.interpolate(offset.toDouble(), (args[1] as Number).toDouble(), (args[2] as Number).toDouble()))
|
||||||
|
} else {
|
||||||
|
// assume vectors
|
||||||
|
val a = toVector2d(args[1]!!)
|
||||||
|
val b = toVector2d(args[2]!!)
|
||||||
|
|
||||||
|
val result = Vector2d(
|
||||||
|
Interpolator.Sin.interpolate(offset.toDouble(), a.x, b.x),
|
||||||
|
Interpolator.Sin.interpolate(offset.toDouble(), a.y, b.y),
|
||||||
|
)
|
||||||
|
|
||||||
|
returnBuffer.setTo(from(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val replaceTags = luaFunction { string: ByteString, tags: Table ->
|
||||||
|
returnBuffer.setTo(SBPattern.of(string.toString()).resolveOrSkip({ tags[it]?.toString() }))
|
||||||
|
}
|
||||||
|
|
||||||
|
private val makePerlinSource = luaFunction { settings: Table ->
|
||||||
|
returnBuffer.setTo(LuaPerlinNoise(AbstractPerlinNoise.of(Starbound.gson.fromJson(settings.toJson(), PerlinNoiseParameters::class.java))))
|
||||||
|
}
|
||||||
|
|
||||||
|
private val staticRandomI32 = luaFunctionArray {
|
||||||
|
returnBuffer.setTo(staticRandom32(*it))
|
||||||
|
}
|
||||||
|
|
||||||
|
private val staticRandomDouble = luaFunctionArray {
|
||||||
|
returnBuffer.setTo(staticRandomDouble(*it))
|
||||||
|
}
|
||||||
|
|
||||||
|
private val staticRandomDoubleRange = luaFunctionN("staticRandomDoubleRange") {
|
||||||
|
val min = it.nextFloat()
|
||||||
|
val max = it.nextFloat()
|
||||||
|
returnBuffer.setTo(staticRandomDouble(*it.copyRemaining()) * (max - min) + min)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val staticRandomI32Range = luaFunctionN("staticRandomI32Range") {
|
||||||
|
val min = it.nextInteger()
|
||||||
|
val max = it.nextInteger()
|
||||||
|
returnBuffer.setTo(staticRandomLong(min, max, *it.copyRemaining()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun provideUtilityBindings(
|
||||||
|
lua: LuaEnvironment,
|
||||||
|
random: RandomGenerator = random()
|
||||||
|
) {
|
||||||
|
val table = lua.newTable()
|
||||||
|
lua.globals["sb"] = table
|
||||||
|
|
||||||
|
table["makeUuid"] = luaFunction {
|
||||||
|
returnBuffer.setTo(UUID(random.nextLong(), random.nextLong()).toStarboundString())
|
||||||
|
}
|
||||||
|
|
||||||
|
table["logInfo"] = logInfo
|
||||||
|
table["logWarn"] = logWarn
|
||||||
|
table["logError"] = logError
|
||||||
|
|
||||||
|
table["nrand"] = luaFunctionN("nrand") { args ->
|
||||||
|
val stdev = args.nextOptionalFloat() ?: 1.0
|
||||||
|
val mean = args.nextOptionalFloat() ?: 0.0
|
||||||
|
random.nextNormalDouble(stdev, mean)
|
||||||
|
}
|
||||||
|
|
||||||
|
table["print"] = lua.globals["tostring"]
|
||||||
|
table["printJson"] = lua.globals["tostring"]
|
||||||
|
table["interpolateSinEase"] = interpolateSinEase
|
||||||
|
table["replaceTags"] = replaceTags
|
||||||
|
table["makeRandomSource"] = luaFunction { seed: Long? ->
|
||||||
|
returnBuffer.setTo(LuaRandomGenerator(random(seed ?: random.nextLong())))
|
||||||
|
}
|
||||||
|
|
||||||
|
table["makePerlinSource"] = makePerlinSource
|
||||||
|
|
||||||
|
table["staticRandomI32"] = staticRandomI32
|
||||||
|
table["staticRandomI64"] = staticRandomI32
|
||||||
|
table["staticRandomDouble"] = staticRandomDouble
|
||||||
|
|
||||||
|
table["staticRandomDoubleRange"] = staticRandomDoubleRange
|
||||||
|
table["staticRandomI32Range"] = staticRandomI32Range
|
||||||
|
table["staticRandomI64Range"] = staticRandomI32Range
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.lua.bindings
|
||||||
|
|
||||||
|
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
||||||
|
import ru.dbotthepony.kstarbound.lua.get
|
||||||
|
import ru.dbotthepony.kstarbound.lua.luaFunction
|
||||||
|
import ru.dbotthepony.kstarbound.lua.set
|
||||||
|
import ru.dbotthepony.kstarbound.world.World
|
||||||
|
|
||||||
|
fun provideWorldBindings(self: World<*, *>, lua: LuaEnvironment) {
|
||||||
|
val callbacks = lua.newTable()
|
||||||
|
lua.globals["world"] = callbacks
|
||||||
|
|
||||||
|
callbacks["flyingType"] = luaFunction {
|
||||||
|
returnBuffer.setTo(self.sky.flyingType.jsonName)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,235 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.lua.bindings
|
||||||
|
|
||||||
|
import com.google.gson.JsonPrimitive
|
||||||
|
import org.classdump.luna.ByteString
|
||||||
|
import org.classdump.luna.Table
|
||||||
|
import ru.dbotthepony.kommons.util.KOptional
|
||||||
|
import ru.dbotthepony.kstarbound.Registries
|
||||||
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
|
import ru.dbotthepony.kstarbound.defs.DamageSource
|
||||||
|
import ru.dbotthepony.kstarbound.defs.quest.QuestArcDescriptor
|
||||||
|
import ru.dbotthepony.kstarbound.json.JsonPath
|
||||||
|
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
||||||
|
import ru.dbotthepony.kstarbound.lua.from
|
||||||
|
import ru.dbotthepony.kstarbound.lua.get
|
||||||
|
import ru.dbotthepony.kstarbound.lua.indexNoYield
|
||||||
|
import ru.dbotthepony.kstarbound.lua.iterator
|
||||||
|
import ru.dbotthepony.kstarbound.lua.luaFunction
|
||||||
|
import ru.dbotthepony.kstarbound.lua.set
|
||||||
|
import ru.dbotthepony.kstarbound.lua.toColor
|
||||||
|
import ru.dbotthepony.kstarbound.lua.toJson
|
||||||
|
import ru.dbotthepony.kstarbound.lua.toJsonFromLua
|
||||||
|
import ru.dbotthepony.kstarbound.lua.toVector2d
|
||||||
|
import ru.dbotthepony.kstarbound.lua.toVector2i
|
||||||
|
import ru.dbotthepony.kstarbound.util.SBPattern
|
||||||
|
import ru.dbotthepony.kstarbound.world.entities.tile.WorldObject
|
||||||
|
|
||||||
|
fun provideWorldObjectBindings(self: WorldObject, lua: LuaEnvironment) {
|
||||||
|
val config = lua.newTable()
|
||||||
|
lua.globals["config"] = config
|
||||||
|
|
||||||
|
config["getParameter"] = luaFunction { name: ByteString, default: Any? ->
|
||||||
|
val path = JsonPath.query(name.decode())
|
||||||
|
val find = self.lookupProperty(path)
|
||||||
|
|
||||||
|
if (find.isJsonNull) {
|
||||||
|
returnBuffer.setTo(default)
|
||||||
|
} else {
|
||||||
|
returnBuffer.setTo(from(find))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val table = lua.newTable()
|
||||||
|
lua.globals["object"] = table
|
||||||
|
|
||||||
|
table["name"] = luaFunction { returnBuffer.setTo(self.config.key) }
|
||||||
|
table["direction"] = luaFunction { returnBuffer.setTo(self.direction.luaValue) }
|
||||||
|
table["position"] = luaFunction { returnBuffer.setTo(from(self.tilePosition)) }
|
||||||
|
table["setInteractive"] = luaFunction { interactive: Boolean -> self.isInteractive = interactive }
|
||||||
|
table["uniqueId"] = luaFunction { returnBuffer.setTo(self.uniqueID.get().orNull()) }
|
||||||
|
table["setUniqueId"] = luaFunction { id: ByteString? -> self.uniqueID.accept(KOptional.ofNullable(id?.decode())) }
|
||||||
|
table["boundBox"] = luaFunction { returnBuffer.setTo(from(self.metaBoundingBox)) }
|
||||||
|
|
||||||
|
// original engine parity, it returns occupied spaces in local coordinates
|
||||||
|
table["spaces"] = luaFunction { returnBuffer.setTo(from(self.occupySpaces.map { from(it - self.tilePosition) })) }
|
||||||
|
|
||||||
|
table["setProcessingDirectives"] = luaFunction { directives: ByteString -> self.animator.processingDirectives = directives.decode() }
|
||||||
|
table["setSoundEffectEnabled"] = luaFunction { state: Boolean -> self.soundEffectEnabled = state }
|
||||||
|
table["smash"] = luaFunction { smash: Boolean? -> self.callBreak(smash ?: false) }
|
||||||
|
table["level"] = luaFunction { returnBuffer.setTo(self.lookupProperty(JsonPath("level")) { JsonPrimitive(self.world.template.threatLevel) }.asDouble) }
|
||||||
|
table["toAbsolutePosition"] = luaFunction { pos: Table -> returnBuffer.setTo(from(toVector2d(pos) + self.position)) }
|
||||||
|
|
||||||
|
table["say"] = luaFunction { line: ByteString, tags: Table?, config: Table ->
|
||||||
|
if (tags == null) {
|
||||||
|
if (line.isEmpty) {
|
||||||
|
returnBuffer.setTo(false)
|
||||||
|
} else {
|
||||||
|
self.addChatMessage(line.decode(), config.toJson())
|
||||||
|
returnBuffer.setTo(true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (line.isEmpty) {
|
||||||
|
returnBuffer.setTo(false)
|
||||||
|
} else {
|
||||||
|
self.addChatMessage(SBPattern.of(line.decode()).resolveOrSkip({ tags[it]?.toString() }), config.toJson())
|
||||||
|
returnBuffer.setTo(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table["sayPortrait"] = luaFunction { line: ByteString, portrait: ByteString, tags: Table?, config: Table ->
|
||||||
|
if (tags == null) {
|
||||||
|
if (line.isEmpty) {
|
||||||
|
returnBuffer.setTo(false)
|
||||||
|
} else {
|
||||||
|
self.addChatMessage(line.decode(), config.toJson(), portrait.decode())
|
||||||
|
returnBuffer.setTo(true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (line.isEmpty) {
|
||||||
|
returnBuffer.setTo(false)
|
||||||
|
} else {
|
||||||
|
self.addChatMessage(SBPattern.of(line.decode()).resolveOrSkip({ tags[it]?.toString() }), config.toJson(), portrait.decode())
|
||||||
|
returnBuffer.setTo(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table["isTouching"] = luaFunction { entity: Number ->
|
||||||
|
val find = self.world.entities[entity]
|
||||||
|
|
||||||
|
if (find != null) {
|
||||||
|
returnBuffer.setTo(find.collisionArea.intersect(self.volumeBoundingBox))
|
||||||
|
} else {
|
||||||
|
returnBuffer.setTo(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table["setLightColor"] = luaFunction { color: Table ->
|
||||||
|
self.lightSourceColor = toColor(color)
|
||||||
|
}
|
||||||
|
|
||||||
|
table["getLightColor"] = luaFunction {
|
||||||
|
returnBuffer.setTo(from(self.lightSourceColor))
|
||||||
|
}
|
||||||
|
|
||||||
|
table["inputNodeCount"] = luaFunction { returnBuffer.setTo(self.inputNodes.size) }
|
||||||
|
table["outputNodeCount"] = luaFunction { returnBuffer.setTo(self.outputNodes.size) }
|
||||||
|
|
||||||
|
table["getInputNodePosition"] = luaFunction { index: Long ->
|
||||||
|
returnBuffer.setTo(from(self.inputNodes[index.toInt()].position))
|
||||||
|
}
|
||||||
|
|
||||||
|
table["getOutputNodePosition"] = luaFunction { index: Long ->
|
||||||
|
returnBuffer.setTo(from(self.outputNodes[index.toInt()].position))
|
||||||
|
}
|
||||||
|
|
||||||
|
table["getInputNodeLevel"] = luaFunction { index: Long ->
|
||||||
|
returnBuffer.setTo(self.inputNodes[index.toInt()].state)
|
||||||
|
}
|
||||||
|
|
||||||
|
table["getOutputNodeLevel"] = luaFunction { index: Long ->
|
||||||
|
returnBuffer.setTo(self.outputNodes[index.toInt()].state)
|
||||||
|
}
|
||||||
|
|
||||||
|
table["isInputNodeConnected"] = luaFunction { index: Long ->
|
||||||
|
returnBuffer.setTo(self.inputNodes[index.toInt()].connections.isNotEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
table["isOutputNodeConnected"] = luaFunction { index: Long ->
|
||||||
|
returnBuffer.setTo(self.outputNodes[index.toInt()].connections.isNotEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
table["getInputNodeIds"] = luaFunction { index: Long ->
|
||||||
|
val results = newTable()
|
||||||
|
|
||||||
|
for (connection in self.inputNodes[index.toInt()].connections) {
|
||||||
|
val entity = self.world.entityIndex.tileEntityAt(connection.entityLocation) as? WorldObject
|
||||||
|
|
||||||
|
if (entity != null) {
|
||||||
|
results[entity.entityID] = connection.index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
returnBuffer.setTo(results)
|
||||||
|
}
|
||||||
|
|
||||||
|
table["getOutputNodeIds"] = luaFunction { index: Long ->
|
||||||
|
val results = newTable()
|
||||||
|
|
||||||
|
for (connection in self.outputNodes[index.toInt()].connections) {
|
||||||
|
val entity = self.world.entityIndex.tileEntityAt(connection.entityLocation) as? WorldObject
|
||||||
|
|
||||||
|
if (entity != null) {
|
||||||
|
results[entity.entityID] = connection.index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
returnBuffer.setTo(results)
|
||||||
|
}
|
||||||
|
|
||||||
|
table["setOutputNodeLevel"] = luaFunction { index: Long, state: Boolean ->
|
||||||
|
self.outputNodes[index.toInt()].state = state
|
||||||
|
}
|
||||||
|
|
||||||
|
table["setAllOutputNodes"] = luaFunction { state: Boolean ->
|
||||||
|
self.outputNodes.forEach { it.state = state }
|
||||||
|
}
|
||||||
|
|
||||||
|
table["setOfferedQuests"] = luaFunction { quests: Table? ->
|
||||||
|
self.offeredQuests.clear()
|
||||||
|
|
||||||
|
if (quests != null) {
|
||||||
|
for ((_, v) in quests) {
|
||||||
|
v as Table
|
||||||
|
self.offeredQuests.add(Starbound.gson.fromJson(v.toJson(), QuestArcDescriptor::class.java))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table["setTurnInQuests"] = luaFunction { quests: Table? ->
|
||||||
|
self.turnInQuests.clear()
|
||||||
|
|
||||||
|
if (quests != null) {
|
||||||
|
for ((_, v) in quests) {
|
||||||
|
self.turnInQuests.add((v as ByteString).decode())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table["setConfigParameter"] = luaFunction { key: ByteString, value: Any? ->
|
||||||
|
self.parameters[key.decode()] = toJsonFromLua(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
table["setAnimationParameter"] = luaFunction { key: ByteString, value: Any? ->
|
||||||
|
self.scriptedAnimationParameters[key.decode()] = toJsonFromLua(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
table["setMaterialSpaces"] = luaFunction { spaces: Table ->
|
||||||
|
self.networkedMaterialSpaces.clear()
|
||||||
|
|
||||||
|
for ((i, pair) in spaces) {
|
||||||
|
pair as Table
|
||||||
|
|
||||||
|
val position = toVector2i(indexNoYield(pair, 1L) ?: throw NullPointerException("invalid space at $i"))
|
||||||
|
val material = indexNoYield(pair, 2L) as? ByteString ?: throw NullPointerException("invalid space at $i")
|
||||||
|
|
||||||
|
self.networkedMaterialSpaces.add(position to Registries.tiles.ref(material.decode()))
|
||||||
|
}
|
||||||
|
|
||||||
|
self.markSpacesDirty()
|
||||||
|
}
|
||||||
|
|
||||||
|
table["setDamageSources"] = luaFunction { sources: Table? ->
|
||||||
|
self.damageSources.clear()
|
||||||
|
|
||||||
|
if (sources != null) {
|
||||||
|
for ((_, v) in sources) {
|
||||||
|
self.damageSources.add(Starbound.gson.fromJson((v as Table).toJson(), DamageSource::class.java))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table["health"] = luaFunction { returnBuffer.setTo(self.health) }
|
||||||
|
table["setHealth"] = luaFunction { health: Double -> self.health = health }
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.lua.userdata
|
||||||
|
|
||||||
|
import org.classdump.luna.Table
|
||||||
|
import org.classdump.luna.Userdata
|
||||||
|
import org.classdump.luna.impl.ImmutableTable
|
||||||
|
import ru.dbotthepony.kstarbound.lua.luaFunction
|
||||||
|
import ru.dbotthepony.kstarbound.util.random.AbstractPerlinNoise
|
||||||
|
|
||||||
|
class LuaPerlinNoise(val noise: AbstractPerlinNoise) : Userdata<AbstractPerlinNoise>() {
|
||||||
|
private var metatable: Table? = Companion.metatable
|
||||||
|
|
||||||
|
override fun getMetatable(): Table? {
|
||||||
|
return metatable
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setMetatable(mt: Table?): Table? {
|
||||||
|
val old = metatable
|
||||||
|
metatable = mt
|
||||||
|
return old
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getUserValue(): AbstractPerlinNoise {
|
||||||
|
return noise
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setUserValue(value: AbstractPerlinNoise?): AbstractPerlinNoise {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val metatable = ImmutableTable.Builder()
|
||||||
|
.add("get", luaFunction { self: LuaPerlinNoise, x: Number, y: Number?, z: Number? ->
|
||||||
|
if (y != null && z != null) {
|
||||||
|
returnBuffer.setTo(self.noise[x.toDouble(), y.toDouble(), z.toDouble()])
|
||||||
|
} else if (y != null) {
|
||||||
|
returnBuffer.setTo(self.noise[x.toDouble(), y.toDouble()])
|
||||||
|
} else {
|
||||||
|
returnBuffer.setTo(self.noise[x.toDouble()])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.lua.userdata
|
||||||
|
|
||||||
|
import org.classdump.luna.Table
|
||||||
|
import org.classdump.luna.Userdata
|
||||||
|
import org.classdump.luna.impl.ImmutableTable
|
||||||
|
import ru.dbotthepony.kstarbound.lua.luaFunction
|
||||||
|
import ru.dbotthepony.kstarbound.util.random.random
|
||||||
|
import java.util.random.RandomGenerator
|
||||||
|
|
||||||
|
class LuaRandomGenerator(var random: RandomGenerator) : Userdata<RandomGenerator>() {
|
||||||
|
private var metatable: Table? = Companion.metatable
|
||||||
|
|
||||||
|
override fun getMetatable(): Table? {
|
||||||
|
return metatable
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setMetatable(mt: Table?): Table? {
|
||||||
|
val old = metatable
|
||||||
|
metatable = mt
|
||||||
|
return old
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getUserValue(): RandomGenerator {
|
||||||
|
return random
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setUserValue(value: RandomGenerator?): RandomGenerator {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun randf(origin: Double?, bound: Double?): Double {
|
||||||
|
if (origin != null && bound != null) {
|
||||||
|
if (origin == bound) {
|
||||||
|
random.nextDouble() // to keep old behavior
|
||||||
|
return origin
|
||||||
|
} else {
|
||||||
|
return random.nextDouble()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return random.nextDouble()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun randomInt(arg1: Long, arg2: Long?): Long {
|
||||||
|
if (arg2 == null) {
|
||||||
|
if (arg1 == 0L) {
|
||||||
|
random.nextLong() // to keep old behavior
|
||||||
|
return 0L
|
||||||
|
} else if (arg1 < 0L) {
|
||||||
|
return random.nextLong(arg1, 0L)
|
||||||
|
} else {
|
||||||
|
return random.nextLong(0L, arg1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (arg2 == arg1) {
|
||||||
|
random.nextLong() // to keep old behavior
|
||||||
|
return arg1
|
||||||
|
} else {
|
||||||
|
return random.nextLong(arg1, arg2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val metatable = ImmutableTable.Builder()
|
||||||
|
.add("init", luaFunction { self: LuaRandomGenerator, seed: Long? ->
|
||||||
|
self.random = random(seed ?: System.nanoTime())
|
||||||
|
})
|
||||||
|
.add("addEntropy", luaFunction { self: LuaRandomGenerator ->
|
||||||
|
throw UnsupportedOperationException("Adding entropy is not supported on new engine. If you have legitimate usecase for this, please let us know at issue tracker or discord")
|
||||||
|
})
|
||||||
|
// TODO: Lua, by definition, has no unsigned numbers,
|
||||||
|
// and before 5.3, there were only doubles, longs (integers) were added
|
||||||
|
// in 5.3
|
||||||
|
.add("randu32", luaFunction { self: LuaRandomGenerator ->
|
||||||
|
returnBuffer.setTo(self.random.nextLong(Int.MIN_VALUE.toLong(), Int.MAX_VALUE.toLong()))
|
||||||
|
})
|
||||||
|
.add("randi32", luaFunction { self: LuaRandomGenerator ->
|
||||||
|
returnBuffer.setTo(self.random.nextLong(Int.MIN_VALUE.toLong(), Int.MAX_VALUE.toLong()))
|
||||||
|
})
|
||||||
|
.add("randu64", luaFunction { self: LuaRandomGenerator ->
|
||||||
|
returnBuffer.setTo(self.random.nextLong())
|
||||||
|
})
|
||||||
|
.add("randi64", luaFunction { self: LuaRandomGenerator ->
|
||||||
|
returnBuffer.setTo(self.random.nextLong())
|
||||||
|
})
|
||||||
|
.add("randf", luaFunction { self: LuaRandomGenerator, origin: Double?, bound: Double? ->
|
||||||
|
returnBuffer.setTo(self.randf(origin, bound))
|
||||||
|
})
|
||||||
|
.add("randd", luaFunction { self: LuaRandomGenerator, origin: Double?, bound: Double? ->
|
||||||
|
returnBuffer.setTo(self.randf(origin, bound))
|
||||||
|
})
|
||||||
|
.add("randb", luaFunction { self: LuaRandomGenerator ->
|
||||||
|
returnBuffer.setTo(self.random.nextBoolean())
|
||||||
|
})
|
||||||
|
.add("randInt", luaFunction { self: LuaRandomGenerator, arg1: Long, arg2: Long? ->
|
||||||
|
returnBuffer.setTo(self.randomInt(arg1, arg2))
|
||||||
|
})
|
||||||
|
.add("randUInt", luaFunction { self: LuaRandomGenerator, arg1: Long, arg2: Long? ->
|
||||||
|
returnBuffer.setTo(self.randomInt(arg1, arg2))
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
@ -511,9 +511,9 @@ class ServerConnection(val server: StarboundServer, type: ConnectionType) : Conn
|
|||||||
scope.launch { shipFlightEventLoop() }
|
scope.launch { shipFlightEventLoop() }
|
||||||
scope.launch { warpEventLoop() }
|
scope.launch { warpEventLoop() }
|
||||||
|
|
||||||
//if (server.channels.connections.size > 1) {
|
if (server.channels.connections.size > 1) {
|
||||||
// enqueueWarp(WarpAction.Player(server.channels.connections.first().uuid!!))
|
enqueueWarp(WarpAction.Player(server.channels.connections.first().uuid!!))
|
||||||
//}
|
}
|
||||||
}
|
}
|
||||||
}.exceptionally {
|
}.exceptionally {
|
||||||
LOGGER.error("Error while initializing shipworld for $this", it)
|
LOGGER.error("Error while initializing shipworld for $this", it)
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package ru.dbotthepony.kstarbound.server.world
|
package ru.dbotthepony.kstarbound.server.world
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectAVLTreeSet
|
import it.unimi.dsi.fastutil.objects.ObjectAVLTreeSet
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.future.await
|
import kotlinx.coroutines.future.await
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -536,7 +535,7 @@ class ServerChunk(world: ServerWorld, pos: ChunkPos) : Chunk<ServerWorld, Server
|
|||||||
val unloadable = world.entityIndex
|
val unloadable = world.entityIndex
|
||||||
.query(
|
.query(
|
||||||
aabbd,
|
aabbd,
|
||||||
filter = Predicate { it.isApplicableForUnloading && !it.isRemote && aabbd.isInside(it.position) },
|
filter = Predicate { it.isPersistent && !it.isRemote && aabbd.isInside(it.position) },
|
||||||
withEdges = false)
|
withEdges = false)
|
||||||
|
|
||||||
world.storage.saveCells(pos, copyCells())
|
world.storage.saveCells(pos, copyCells())
|
||||||
|
@ -57,6 +57,48 @@ class SBPattern private constructor(
|
|||||||
return hash
|
return hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Different from regular resolve that this always returns non-null string,
|
||||||
|
* even if we didn't replace all tags (not replaced tags appear as `<tags>` or as [defaultValue],
|
||||||
|
* depending on [replaceWithDefault])
|
||||||
|
*/
|
||||||
|
fun resolveOrSkip(values: (String) -> String?, replaceWithDefault: Boolean = false, defaultValue: String = ""): String {
|
||||||
|
if (namesSet.isEmpty()) {
|
||||||
|
return raw
|
||||||
|
} else if (pieces.size == 1) {
|
||||||
|
val resolve = pieces[0].resolve(values, this::getParam)
|
||||||
|
?: if (replaceWithDefault) {
|
||||||
|
return defaultValue
|
||||||
|
} else {
|
||||||
|
return raw
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolve
|
||||||
|
}
|
||||||
|
|
||||||
|
val buffer = ArrayList<String>(pieces.size)
|
||||||
|
|
||||||
|
for (piece in pieces) {
|
||||||
|
var resolve = piece.resolve(values, this::getParam)
|
||||||
|
|
||||||
|
if (resolve == null) {
|
||||||
|
if (replaceWithDefault) {
|
||||||
|
resolve = defaultValue
|
||||||
|
} else {
|
||||||
|
resolve = "<${piece.name!!}>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.add(resolve)
|
||||||
|
}
|
||||||
|
|
||||||
|
var count = 0
|
||||||
|
for (piece in buffer) count += piece.length
|
||||||
|
val builder = StringBuilder(count)
|
||||||
|
for (piece in buffer) builder.append(piece)
|
||||||
|
return String(builder)
|
||||||
|
}
|
||||||
|
|
||||||
fun resolve(values: (String) -> String?): String? {
|
fun resolve(values: (String) -> String?): String? {
|
||||||
if (namesSet.isEmpty()) {
|
if (namesSet.isEmpty()) {
|
||||||
return raw
|
return raw
|
||||||
@ -146,7 +188,7 @@ class SBPattern private constructor(
|
|||||||
check(!(name != null && contents != null)) { "Both name and contents are not null" }
|
check(!(name != null && contents != null)) { "Both name and contents are not null" }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resolve(map0: (String) -> String?, map1: (String) -> String?): String? {
|
inline fun resolve(map0: (String) -> String?, map1: (String) -> String?): String? {
|
||||||
return contents ?: map0.invoke(name!!) ?: map1.invoke(name!!)
|
return contents ?: map0.invoke(name!!) ?: map1.invoke(name!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ private fun toBytes(accept: ByteConsumer, value: Float) {
|
|||||||
toBytes(accept, value.toBits())
|
toBytes(accept, value.toBits())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun staticRandom32(vararg values: Any): Int {
|
fun staticRandom32(vararg values: Any?): Int {
|
||||||
val digest = XXHash32(2938728349.toInt())
|
val digest = XXHash32(2938728349.toInt())
|
||||||
|
|
||||||
for (value in values) {
|
for (value in values) {
|
||||||
@ -68,6 +68,7 @@ fun staticRandom32(vararg values: Any): Int {
|
|||||||
is Long -> toBytes(digest::update, value)
|
is Long -> toBytes(digest::update, value)
|
||||||
is Double -> toBytes(digest::update, value)
|
is Double -> toBytes(digest::update, value)
|
||||||
is Float -> toBytes(digest::update, value)
|
is Float -> toBytes(digest::update, value)
|
||||||
|
null -> {} // do nothing?
|
||||||
else -> throw IllegalArgumentException("Can't hash value of type ${value::class.qualifiedName}")
|
else -> throw IllegalArgumentException("Can't hash value of type ${value::class.qualifiedName}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -75,20 +76,25 @@ fun staticRandom32(vararg values: Any): Int {
|
|||||||
return digest.digestAsInt()
|
return digest.digestAsInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun staticRandomFloat(vararg values: Any): Float {
|
fun staticRandomFloat(vararg values: Any?): Float {
|
||||||
return staticRandom32(*values).ushr(8) * 5.9604645E-8f
|
return staticRandom32(*values).ushr(8) * 5.9604645E-8f
|
||||||
}
|
}
|
||||||
|
|
||||||
fun staticRandomDouble(vararg values: Any): Double {
|
fun staticRandomDouble(vararg values: Any?): Double {
|
||||||
return staticRandom64(*values).ushr(11) * 1.1102230246251565E-16
|
return staticRandom64(*values).ushr(11) * 1.1102230246251565E-16
|
||||||
}
|
}
|
||||||
|
|
||||||
fun staticRandomInt(origin: Int, bound: Int, vararg values: Any): Int {
|
fun staticRandomInt(origin: Int, bound: Int, vararg values: Any?): Int {
|
||||||
val rand = staticRandomDouble(*values)
|
val rand = staticRandomDouble(*values)
|
||||||
return origin + ((bound - origin) * rand).toInt()
|
return origin + ((bound - origin) * rand).toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun staticRandom64(vararg values: Any): Long {
|
fun staticRandomLong(origin: Long, bound: Long, vararg values: Any?): Long {
|
||||||
|
val rand = staticRandomDouble(*values)
|
||||||
|
return origin + ((bound - origin) * rand).toLong()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun staticRandom64(vararg values: Any?): Long {
|
||||||
val digest = XXHash64(1997293021376312589L)
|
val digest = XXHash64(1997293021376312589L)
|
||||||
|
|
||||||
for (value in values) {
|
for (value in values) {
|
||||||
@ -101,6 +107,7 @@ fun staticRandom64(vararg values: Any): Long {
|
|||||||
is Long -> toBytes(digest::update, value)
|
is Long -> toBytes(digest::update, value)
|
||||||
is Double -> toBytes(digest::update, value)
|
is Double -> toBytes(digest::update, value)
|
||||||
is Float -> toBytes(digest::update, value)
|
is Float -> toBytes(digest::update, value)
|
||||||
|
null -> {} // do nothing?
|
||||||
else -> throw IllegalArgumentException("Can't hash value of type ${value::class.qualifiedName}")
|
else -> throw IllegalArgumentException("Can't hash value of type ${value::class.qualifiedName}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,12 @@ import ru.dbotthepony.kommons.io.StreamCodec
|
|||||||
import ru.dbotthepony.kommons.vector.Vector2d
|
import ru.dbotthepony.kommons.vector.Vector2d
|
||||||
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
||||||
|
|
||||||
enum class Direction(val normal: Vector2d, override val jsonName: String) : IStringSerializable {
|
enum class Direction(val normal: Vector2d, override val jsonName: String, val luaValue: Long) : IStringSerializable {
|
||||||
LEFT(Vector2d.NEGATIVE_X, "left") {
|
LEFT(Vector2d.NEGATIVE_X, "left", -1L) {
|
||||||
override val opposite: Direction
|
override val opposite: Direction
|
||||||
get() = RIGHT
|
get() = RIGHT
|
||||||
},
|
},
|
||||||
RIGHT(Vector2d.POSITIVE_X, "right") {
|
RIGHT(Vector2d.POSITIVE_X, "right", 1L) {
|
||||||
override val opposite: Direction
|
override val opposite: Direction
|
||||||
get() = LEFT
|
get() = LEFT
|
||||||
};
|
};
|
||||||
|
@ -110,7 +110,7 @@ sealed class TileModification {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!allowEntityOverlap && world.entityIndex.any(rect, Predicate { it is DynamicEntity && it.movement.computeCollisionAABB().intersect(rect) })) {
|
if (!allowEntityOverlap && world.entityIndex.any(rect, Predicate { it.collisionArea.intersect(rect) })) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import com.google.gson.JsonObject
|
|||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||||
import it.unimi.dsi.fastutil.ints.IntArraySet
|
import it.unimi.dsi.fastutil.ints.IntArraySet
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectAVLTreeSet
|
|
||||||
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 ru.dbotthepony.kommons.arrays.Object2DArray
|
import ru.dbotthepony.kommons.arrays.Object2DArray
|
||||||
@ -247,6 +246,8 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
|||||||
var ticks = 0L
|
var ticks = 0L
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
abstract val eventLoop: BlockableEventLoop
|
||||||
|
|
||||||
open fun broadcast(packet: IPacket) {
|
open fun broadcast(packet: IPacket) {
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -312,8 +313,6 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
|||||||
|
|
||||||
protected abstract fun chunkFactory(pos: ChunkPos): ChunkType
|
protected abstract fun chunkFactory(pos: ChunkPos): ChunkType
|
||||||
|
|
||||||
abstract val eventLoop: BlockableEventLoop
|
|
||||||
|
|
||||||
fun entitiesAtTile(pos: Vector2i, filter: Predicate<TileEntity> = Predicate { true }): List<TileEntity> {
|
fun entitiesAtTile(pos: Vector2i, filter: Predicate<TileEntity> = Predicate { true }): List<TileEntity> {
|
||||||
return entityIndex.query(
|
return entityIndex.query(
|
||||||
AABBi(pos, pos + Vector2i.POSITIVE_XY).toDoubleAABB(),
|
AABBi(pos, pos + Vector2i.POSITIVE_XY).toDoubleAABB(),
|
||||||
|
@ -10,10 +10,12 @@ import ru.dbotthepony.kommons.vector.Vector2d
|
|||||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
|
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
|
||||||
import ru.dbotthepony.kstarbound.client.world.ClientWorld
|
import ru.dbotthepony.kstarbound.client.world.ClientWorld
|
||||||
|
import ru.dbotthepony.kstarbound.defs.EntityDamageTeam
|
||||||
import ru.dbotthepony.kstarbound.defs.EntityType
|
import ru.dbotthepony.kstarbound.defs.EntityType
|
||||||
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.defs.JsonDriven
|
import ru.dbotthepony.kstarbound.defs.JsonDriven
|
||||||
|
import ru.dbotthepony.kstarbound.defs.`object`.DamageTeam
|
||||||
import ru.dbotthepony.kstarbound.network.syncher.InternedStringCodec
|
import ru.dbotthepony.kstarbound.network.syncher.InternedStringCodec
|
||||||
import ru.dbotthepony.kstarbound.network.syncher.MasterElement
|
import ru.dbotthepony.kstarbound.network.syncher.MasterElement
|
||||||
import ru.dbotthepony.kstarbound.network.syncher.NetworkedGroup
|
import ru.dbotthepony.kstarbound.network.syncher.NetworkedGroup
|
||||||
@ -83,9 +85,11 @@ abstract class AbstractEntity(path: String) : JsonDriven(path), Comparable<Abstr
|
|||||||
* Returning false will also stop entity from being saved to disk, and render entity orphaned
|
* Returning false will also stop entity from being saved to disk, and render entity orphaned
|
||||||
* when chunk containing it will get unloaded
|
* when chunk containing it will get unloaded
|
||||||
*/
|
*/
|
||||||
open val isApplicableForUnloading: Boolean
|
open val isPersistent: Boolean
|
||||||
get() = true
|
get() = true
|
||||||
|
|
||||||
|
val team = networkedData(EntityDamageTeam(), EntityDamageTeam.CODEC, EntityDamageTeam.LEGACY_CODEC)
|
||||||
|
|
||||||
enum class RemovalReason(val removal: Boolean, val dying: Boolean, val remote: Boolean) {
|
enum class RemovalReason(val removal: Boolean, val dying: Boolean, val remote: Boolean) {
|
||||||
UNLOADED(false, false, false), // Being saved to disk
|
UNLOADED(false, false, false), // Being saved to disk
|
||||||
REMOVED(true, false, false), // Got removed from world
|
REMOVED(true, false, false), // Got removed from world
|
||||||
@ -111,6 +115,9 @@ abstract class AbstractEntity(path: String) : JsonDriven(path), Comparable<Abstr
|
|||||||
*/
|
*/
|
||||||
abstract val metaBoundingBox: AABB
|
abstract val metaBoundingBox: AABB
|
||||||
|
|
||||||
|
open val collisionArea: AABB
|
||||||
|
get() = NEVER
|
||||||
|
|
||||||
open fun onNetworkUpdate() {
|
open fun onNetworkUpdate() {
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -189,5 +196,6 @@ abstract class AbstractEntity(path: String) : JsonDriven(path), Comparable<Abstr
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val LOGGER = LogManager.getLogger()
|
private val LOGGER = LogManager.getLogger()
|
||||||
|
private val NEVER = AABB(Vector2d(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY), Vector2d(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package ru.dbotthepony.kstarbound.world.entities
|
package ru.dbotthepony.kstarbound.world.entities
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement
|
||||||
|
import com.google.gson.JsonNull
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap
|
import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
|
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
|
||||||
@ -9,15 +11,20 @@ import ru.dbotthepony.kommons.gson.getArray
|
|||||||
import ru.dbotthepony.kommons.gson.set
|
import ru.dbotthepony.kommons.gson.set
|
||||||
import ru.dbotthepony.kommons.io.IntValueCodec
|
import ru.dbotthepony.kommons.io.IntValueCodec
|
||||||
import ru.dbotthepony.kommons.io.map
|
import ru.dbotthepony.kommons.io.map
|
||||||
|
import ru.dbotthepony.kommons.math.RGBAColor
|
||||||
|
import ru.dbotthepony.kommons.matrix.Matrix3d
|
||||||
import ru.dbotthepony.kommons.matrix.Matrix3f
|
import ru.dbotthepony.kommons.matrix.Matrix3f
|
||||||
|
import ru.dbotthepony.kommons.util.AABB
|
||||||
import ru.dbotthepony.kommons.util.KOptional
|
import ru.dbotthepony.kommons.util.KOptional
|
||||||
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.kommons.vector.Vector2d
|
import ru.dbotthepony.kommons.vector.Vector2d
|
||||||
|
import ru.dbotthepony.kommons.vector.Vector2f
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
import ru.dbotthepony.kstarbound.defs.animation.AnimatedPartsDefinition
|
import ru.dbotthepony.kstarbound.defs.animation.AnimatedPartsDefinition
|
||||||
import ru.dbotthepony.kstarbound.defs.animation.AnimationDefinition
|
import ru.dbotthepony.kstarbound.defs.animation.AnimationDefinition
|
||||||
import ru.dbotthepony.kstarbound.defs.animation.ParticleFactory
|
import ru.dbotthepony.kstarbound.defs.animation.ParticleFactory
|
||||||
|
import ru.dbotthepony.kstarbound.json.JsonPath
|
||||||
import ru.dbotthepony.kstarbound.json.mergeJson
|
import ru.dbotthepony.kstarbound.json.mergeJson
|
||||||
import ru.dbotthepony.kstarbound.math.Interpolator
|
import ru.dbotthepony.kstarbound.math.Interpolator
|
||||||
import ru.dbotthepony.kstarbound.math.PeriodicFunction
|
import ru.dbotthepony.kstarbound.math.PeriodicFunction
|
||||||
@ -39,8 +46,10 @@ import ru.dbotthepony.kstarbound.network.syncher.networkedSignedInt
|
|||||||
import ru.dbotthepony.kstarbound.network.syncher.networkedString
|
import ru.dbotthepony.kstarbound.network.syncher.networkedString
|
||||||
import ru.dbotthepony.kstarbound.network.syncher.networkedUnsignedInt
|
import ru.dbotthepony.kstarbound.network.syncher.networkedUnsignedInt
|
||||||
import ru.dbotthepony.kstarbound.util.random.random
|
import ru.dbotthepony.kstarbound.util.random.random
|
||||||
|
import ru.dbotthepony.kstarbound.world.physics.Poly
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
|
import kotlin.NoSuchElementException
|
||||||
import kotlin.math.atan2
|
import kotlin.math.atan2
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
@ -183,6 +192,7 @@ class Animator() {
|
|||||||
|
|
||||||
private var activeStateChanged = false
|
private var activeStateChanged = false
|
||||||
private var frameChanged = false
|
private var frameChanged = false
|
||||||
|
private var firstTime = true
|
||||||
|
|
||||||
var activeState: AnimatedPartsDefinition.StateType.State? = null
|
var activeState: AnimatedPartsDefinition.StateType.State? = null
|
||||||
set(value) {
|
set(value) {
|
||||||
@ -216,9 +226,14 @@ class Animator() {
|
|||||||
fun set(state: String, alwaysStart: Boolean): Boolean {
|
fun set(state: String, alwaysStart: Boolean): Boolean {
|
||||||
val getState = states[state] ?: return false
|
val getState = states[state] ?: return false
|
||||||
|
|
||||||
if (activeState != getState || alwaysStart) {
|
// allow to reset active state if calling for first time
|
||||||
|
// without this client and server will disagree what state is active
|
||||||
|
// if there default state present
|
||||||
|
if (activeState != getState || alwaysStart || firstTime) {
|
||||||
activeState = getState
|
activeState = getState
|
||||||
timer = 0.0
|
timer = 0.0
|
||||||
|
startedEvent.trigger()
|
||||||
|
firstTime = true
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,8 +242,11 @@ class Animator() {
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
if (default in states) {
|
if (default in states) {
|
||||||
|
noPropagate = true
|
||||||
activeState = states[default]!!
|
activeState = states[default]!!
|
||||||
stateIndex.accept(activeState!!.index.toLong())
|
stateIndex.accept(activeState!!.index.toLong())
|
||||||
|
startedEvent.trigger()
|
||||||
|
noPropagate = false
|
||||||
}
|
}
|
||||||
|
|
||||||
stateIndex.addListener(Consumer {
|
stateIndex.addListener(Consumer {
|
||||||
@ -284,12 +302,74 @@ class Animator() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun finishAnimations() {
|
||||||
|
while (true) {
|
||||||
|
val active = activeState ?: break
|
||||||
|
|
||||||
|
if (active.mode == AnimatedPartsDefinition.AnimationMode.END) {
|
||||||
|
timer = active.cycle
|
||||||
|
} else if (active.mode == AnimatedPartsDefinition.AnimationMode.TRANSITION) {
|
||||||
|
activeState = states[active.transition]!! // validity of 'transition' is checked during json load
|
||||||
|
this.activeState = activeState
|
||||||
|
timer = 0.0
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Part(config: AnimatedPartsDefinition.Part) {
|
private inner class Part(private val config: AnimatedPartsDefinition.Part) {
|
||||||
val partProperties = config.properties
|
|
||||||
var activePartDirty = true
|
var activePartDirty = true
|
||||||
val partStates = config.partStates
|
var properties: JsonObject = config.properties.deepCopy()
|
||||||
|
private set
|
||||||
|
|
||||||
|
var activeState: AnimatedPartsDefinition.Part.State? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun freshen() {
|
||||||
|
if (!activePartDirty)
|
||||||
|
return
|
||||||
|
|
||||||
|
activePartDirty = false
|
||||||
|
|
||||||
|
// First reset all the active part information assuming that no state type
|
||||||
|
// x state match exists.
|
||||||
|
activeState = null
|
||||||
|
properties = config.properties.deepCopy()
|
||||||
|
|
||||||
|
// Then go through each of the state types and states and look for a part
|
||||||
|
// state match in order of priority.
|
||||||
|
|
||||||
|
// honestly, i have no idea what next code does
|
||||||
|
for ((stateTypeName, stateType) in stateTypes) {
|
||||||
|
// Skip disabled state types
|
||||||
|
if (!stateType.enabled)
|
||||||
|
continue
|
||||||
|
|
||||||
|
val partStateType = config.partStates[stateTypeName] ?: continue
|
||||||
|
val stateName = stateType.activeState?.name ?: continue
|
||||||
|
val partState = partStateType[stateName] ?: continue
|
||||||
|
|
||||||
|
activeState = partState
|
||||||
|
val frame = stateType.frame
|
||||||
|
|
||||||
|
// Then set the part state data, as well as any part state frame data if
|
||||||
|
// the current frame is within the list size.
|
||||||
|
mergeJson(properties, partState.properties)
|
||||||
|
|
||||||
|
for ((key, props) in partState.frameProperties) {
|
||||||
|
if (props.size() > frame) {
|
||||||
|
properties[key] = props.get(frame).deepCopy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each part can only have one state type x state match, so we are done.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class RotationGroup {
|
private class RotationGroup {
|
||||||
@ -342,6 +422,15 @@ class Animator() {
|
|||||||
xShear = atan2(value[1, 0].toDouble(), value[0, 0].toDouble())
|
xShear = atan2(value[1, 0].toDouble(), value[0, 0].toDouble())
|
||||||
yShear = atan2(value[0, 1].toDouble(), value[1, 1].toDouble())
|
yShear = atan2(value[0, 1].toDouble(), value[1, 1].toDouble())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun reset() {
|
||||||
|
xTranslation = 0.0
|
||||||
|
yTranslation = 0.0
|
||||||
|
xScale = 1.0
|
||||||
|
yScale = 1.0
|
||||||
|
xShear = 0.0
|
||||||
|
yShear = 0.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ParticleEmitter {
|
private class ParticleEmitter {
|
||||||
@ -376,7 +465,7 @@ class Animator() {
|
|||||||
private val elements = ArrayList<NetworkedElement>()
|
private val elements = ArrayList<NetworkedElement>()
|
||||||
|
|
||||||
var processingDirectives by networkedString().also { elements.add(it) }
|
var processingDirectives by networkedString().also { elements.add(it) }
|
||||||
var zoom by networkedFloat().also { elements.add(it) }
|
var zoom by networkedFloat(1.0).also { elements.add(it) }
|
||||||
var isFlipped by networkedBoolean().also { elements.add(it) }
|
var isFlipped by networkedBoolean().also { elements.add(it) }
|
||||||
var flippedRelativeCenterLine by networkedFloat().also { elements.add(it) }
|
var flippedRelativeCenterLine by networkedFloat().also { elements.add(it) }
|
||||||
var animationRate by networkedFloat(1.0).also { elements.add(it); it.interpolator = Interpolator.Linear }
|
var animationRate by networkedFloat(1.0).also { elements.add(it); it.interpolator = Interpolator.Linear }
|
||||||
@ -518,13 +607,7 @@ class Animator() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setPartTag(partName: String, tagKey: String, tagValue: String) {
|
fun setPartTag(partName: String, tagKey: String, tagValue: String) {
|
||||||
var tags = partTags[partName]
|
val tags = partTags[partName] ?: throw IllegalArgumentException("Unknown part $partName!")
|
||||||
|
|
||||||
if (tags == null) {
|
|
||||||
tags = NetworkedMap(InternedStringCodec, InternedStringCodec)
|
|
||||||
partTags[partName] = tags
|
|
||||||
}
|
|
||||||
|
|
||||||
tags[tagKey] = tagValue
|
tags[tagKey] = tagValue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -574,15 +657,280 @@ class Animator() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setActiveState(type: String, state: String, alwaysStart: Boolean = false): Boolean {
|
fun setActiveState(type: String, state: String, alwaysStart: Boolean = false): Boolean {
|
||||||
val getType = stateTypes[type] ?: return false
|
return stateTypes[type]?.set(state, alwaysStart) ?: return false
|
||||||
val getState = getType.states[state] ?: return false
|
}
|
||||||
|
|
||||||
if (getType.activeState != getState || alwaysStart) {
|
fun animationState(type: String): String {
|
||||||
getType.timer = 0.0
|
val get = stateTypes[type] ?: throw NoSuchElementException("No such animation part $type")
|
||||||
return true
|
return get.activeState?.name ?: "" // original engine parity...............
|
||||||
|
// it will return empty string if state type has no active state (
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stateProperty(type: String, path: String): JsonElement {
|
||||||
|
val get = stateTypes[type] ?: throw NoSuchElementException("No such animation part $type")
|
||||||
|
return get.activeState?.properties?.get(path) ?: JsonNull.INSTANCE // очень и очень грустно
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stateProperty(type: String, path: JsonPath): JsonElement {
|
||||||
|
val get = stateTypes[type] ?: throw NoSuchElementException("No such animation part $type")
|
||||||
|
val props = get.activeState?.properties ?: return JsonNull.INSTANCE
|
||||||
|
return path.find(props) ?: JsonNull.INSTANCE // очень и очень грустно
|
||||||
|
}
|
||||||
|
|
||||||
|
fun rotateGroup(group: String, targetAngle: Double, immediate: Boolean = false) {
|
||||||
|
val get = rotationGroups[group] ?: throw NoSuchElementException("No such rotation group $group")
|
||||||
|
get.targetAngle.accept(targetAngle)
|
||||||
|
|
||||||
|
if (immediate) {
|
||||||
|
get.currentAngle = targetAngle
|
||||||
|
get.immediateEvent.trigger()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
fun currentRotationAngle(group: String): Double {
|
||||||
|
return rotationGroups[group]?.currentAngle ?: throw NoSuchElementException("No such rotation group $group")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun targetRotationAngle(group: String): Double {
|
||||||
|
return rotationGroups[group]?.targetAngle?.get() ?: throw NoSuchElementException("No such rotation group $group")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasRotationGroup(group: String): Boolean {
|
||||||
|
return group in rotationGroups
|
||||||
|
}
|
||||||
|
|
||||||
|
fun rotationGroups(): Collection<String> {
|
||||||
|
return Collections.unmodifiableCollection(rotationGroups.keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun transformationGroups(): Collection<String> {
|
||||||
|
return Collections.unmodifiableCollection(transformationGroups.keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasTransformationGroup(group: String): Boolean {
|
||||||
|
return group in transformationGroups
|
||||||
|
}
|
||||||
|
|
||||||
|
fun translateTransformGroup(group: String, translation: Vector2f) {
|
||||||
|
val get = transformationGroups[group] ?: throw NoSuchElementException("No such transformation group $group")
|
||||||
|
get.setAffineTransform(Matrix3f.rowMajor(r02 = translation.x, r12 = translation.y).mul(get.affineTransform()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun rotateTransformGroup(group: String, rotation: Float, center: Vector2f = Vector2f.ZERO) {
|
||||||
|
val get = transformationGroups[group] ?: throw NoSuchElementException("No such transformation group $group")
|
||||||
|
val sin = sin(rotation)
|
||||||
|
val cos = cos(rotation)
|
||||||
|
|
||||||
|
val matrix = Matrix3f.rowMajor(
|
||||||
|
cos, -sin, center.x - cos * center.x + sin * center.y,
|
||||||
|
sin, cos, center.y - sin * center.y - cos * center.x
|
||||||
|
)
|
||||||
|
|
||||||
|
get.setAffineTransform(matrix.mul(get.affineTransform()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun scaleTransformationGroup(group: String, scale: Vector2f, center: Vector2f = Vector2f.ZERO) {
|
||||||
|
val get = transformationGroups[group] ?: throw NoSuchElementException("No such transformation group $group")
|
||||||
|
|
||||||
|
val matrix = Matrix3f.rowMajor(
|
||||||
|
scale.x, 0f, center.x - center.x * scale.x,
|
||||||
|
0f, scale.y, center.y - center.y * scale.y
|
||||||
|
)
|
||||||
|
|
||||||
|
get.setAffineTransform(matrix.mul(get.affineTransform()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun transformTransformationGroup(
|
||||||
|
group: String,
|
||||||
|
r00: Float,
|
||||||
|
r01: Float,
|
||||||
|
r02: Float,
|
||||||
|
r10: Float,
|
||||||
|
r11: Float,
|
||||||
|
r12: Float,
|
||||||
|
) {
|
||||||
|
val get = transformationGroups[group] ?: throw NoSuchElementException("No such transformation group $group")
|
||||||
|
val matrix = Matrix3f.rowMajor(r00, r01, r02, r10, r11, r12)
|
||||||
|
get.setAffineTransform(matrix.mul(get.affineTransform()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetTransformationGroup(group: String) {
|
||||||
|
val get = transformationGroups[group] ?: throw NoSuchElementException("No such transformation group $group")
|
||||||
|
get.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasParticleEmitter(emitter: String): Boolean {
|
||||||
|
return emitter in particleEmitters
|
||||||
|
}
|
||||||
|
|
||||||
|
fun particleEmitters(): Collection<String> {
|
||||||
|
return Collections.unmodifiableCollection(particleEmitters.keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setParticleEmitterActive(emitter: String, state: Boolean = true) {
|
||||||
|
val get = particleEmitters[emitter] ?: throw NoSuchElementException("No such particle emitter $emitter")
|
||||||
|
get.active = state
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setParticleEmitterEmissionRate(emitter: String, rate: Double) {
|
||||||
|
val get = particleEmitters[emitter] ?: throw NoSuchElementException("No such particle emitter $emitter")
|
||||||
|
get.emissionRate = rate
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setParticleEmitterBurstCount(emitter: String, count: Int) {
|
||||||
|
val get = particleEmitters[emitter] ?: throw NoSuchElementException("No such particle emitter $emitter")
|
||||||
|
require(count >= 0) { "Negative burst count: $count" }
|
||||||
|
get.burstCount = count
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setParticleEmitterOffsetRegion(emitter: String, region: AABB) {
|
||||||
|
val get = particleEmitters[emitter] ?: throw NoSuchElementException("No such particle emitter $emitter")
|
||||||
|
get.offsetRegion = KOptional(region)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setParticleEmitterOffsetRegion(emitter: String) {
|
||||||
|
val get = particleEmitters[emitter] ?: throw NoSuchElementException("No such particle emitter $emitter")
|
||||||
|
get.offsetRegion = KOptional()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun burstParticleEmitter(emitter: String) {
|
||||||
|
val get = particleEmitters[emitter] ?: throw NoSuchElementException("No such particle emitter $emitter")
|
||||||
|
get.burstEvent.trigger()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun lights(): Collection<String> {
|
||||||
|
return Collections.unmodifiableCollection(lights.keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasLight(light: String): Boolean {
|
||||||
|
return light in lights
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setLightActive(light: String, state: Boolean = true) {
|
||||||
|
val get = lights[light] ?: throw NoSuchElementException("No such light source $light")
|
||||||
|
get.active = state
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setLightPosition(light: String, position: Vector2d) {
|
||||||
|
val get = lights[light] ?: throw NoSuchElementException("No such light source $light")
|
||||||
|
get.xPosition = position.x
|
||||||
|
get.yPosition = position.y
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setLightPosition(light: String, x: Double, y: Double) {
|
||||||
|
val get = lights[light] ?: throw NoSuchElementException("No such light source $light")
|
||||||
|
get.xPosition = x
|
||||||
|
get.yPosition = y
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setLightColor(light: String, color: RGBAColor) {
|
||||||
|
val get = lights[light] ?: throw NoSuchElementException("No such light source $light")
|
||||||
|
get.color = color
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setLightPointAngle(light: String, angle: Double) {
|
||||||
|
val get = lights[light] ?: throw NoSuchElementException("No such light source $light")
|
||||||
|
get.pointAngle = angle
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sounds(): Collection<String> {
|
||||||
|
return Collections.unmodifiableCollection(sounds.keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasSound(sound: String): Boolean {
|
||||||
|
return sound in sounds
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSoundPool(sound: String, pool: Collection<String>) {
|
||||||
|
val get = sounds[sound] ?: throw NoSuchElementException("No such sound source $sound")
|
||||||
|
|
||||||
|
get.soundPool.clear()
|
||||||
|
get.soundPool.addAll(pool)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSoundPosition(sound: String, position: Vector2d) {
|
||||||
|
val get = sounds[sound] ?: throw NoSuchElementException("No such sound source $sound")
|
||||||
|
|
||||||
|
get.xPosition = position.x
|
||||||
|
get.yPosition = position.y
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSoundPosition(sound: String, x: Double, y: Double) {
|
||||||
|
val get = sounds[sound] ?: throw NoSuchElementException("No such sound source $sound")
|
||||||
|
|
||||||
|
get.xPosition = x
|
||||||
|
get.yPosition = y
|
||||||
|
}
|
||||||
|
|
||||||
|
fun playSound(sound: String, loops: Int = 0) {
|
||||||
|
val get = sounds[sound] ?: throw NoSuchElementException("No such sound source $sound")
|
||||||
|
require(loops >= 0) { "Negative amount of loops: $loops" }
|
||||||
|
get.loops = loops
|
||||||
|
get.signals.push(SoundSignal.PLAY)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSoundVolume(sound: String, volume: Double, rampTime: Double = 0.0) {
|
||||||
|
val get = sounds[sound] ?: throw NoSuchElementException("No such sound source $sound")
|
||||||
|
get.volumeTarget = volume
|
||||||
|
get.volumeRampTime = rampTime
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSoundPitch(sound: String, pitch: Double, rampTime: Double = 0.0) {
|
||||||
|
val get = sounds[sound] ?: throw NoSuchElementException("No such sound source $sound")
|
||||||
|
get.pitchMultiplierTarget = pitch
|
||||||
|
get.pitchMultiplierRampTime = rampTime
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stopAllSounds(sound: String, rampTime: Double = 0.0) {
|
||||||
|
val get = sounds[sound] ?: throw NoSuchElementException("No such sound source $sound")
|
||||||
|
get.volumeRampTime = rampTime
|
||||||
|
get.signals.push(SoundSignal.STOP_ALL)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun effects(): Collection<String> {
|
||||||
|
return Collections.unmodifiableCollection(effects.keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasEffect(effect: String): Boolean {
|
||||||
|
return effect in effects
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setEffectActive(effect: String, state: Boolean = true) {
|
||||||
|
val get = effects[effect] ?: throw NoSuchElementException("No such effect source $effect")
|
||||||
|
get.enabled.accept(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parts(): Collection<String> {
|
||||||
|
return Collections.unmodifiableCollection(parts.keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun partPoint(part: String, property: String): Vector2d? {
|
||||||
|
val get = parts[part] ?: throw NoSuchElementException("No such animation part $part")
|
||||||
|
get.freshen()
|
||||||
|
val lookup = get.properties[property] ?: return null
|
||||||
|
return vectors.fromJsonTree(lookup)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun partPoly(part: String, property: String): Poly? {
|
||||||
|
val get = parts[part] ?: throw NoSuchElementException("No such animation part $part")
|
||||||
|
get.freshen()
|
||||||
|
val lookup = get.properties[property] ?: return null
|
||||||
|
return polies.fromJsonTree(lookup)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun partProperty(part: String, property: String): JsonElement {
|
||||||
|
val get = parts[part] ?: throw NoSuchElementException("No such animation part $part")
|
||||||
|
get.freshen()
|
||||||
|
return get.properties[property] ?: JsonNull.INSTANCE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun partTransformation(part: String): Matrix3d {
|
||||||
|
val get = parts[part] ?: throw NoSuchElementException("No such animation part $part")
|
||||||
|
val result = Matrix3d.rowMajor()
|
||||||
|
|
||||||
|
val active = get.activeState ?: return result
|
||||||
|
// TODO
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Dynamic target
|
// TODO: Dynamic target
|
||||||
@ -618,6 +966,8 @@ class Animator() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parts.values.forEach { it.activePartDirty = true }
|
||||||
|
|
||||||
for (rotationGroup in rotationGroups.values) {
|
for (rotationGroup in rotationGroups.values) {
|
||||||
rotationGroup.tick(delta)
|
rotationGroup.tick(delta)
|
||||||
}
|
}
|
||||||
@ -631,6 +981,14 @@ class Animator() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun finishAnimations() {
|
||||||
|
for (state in stateTypes.values) {
|
||||||
|
state.finishAnimations()
|
||||||
|
}
|
||||||
|
|
||||||
|
parts.values.forEach { it.activePartDirty = true }
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// lame
|
// lame
|
||||||
fun load(path: String): Animator {
|
fun load(path: String): Animator {
|
||||||
@ -649,5 +1007,7 @@ class Animator() {
|
|||||||
|
|
||||||
private val LOGGER = LogManager.getLogger()
|
private val LOGGER = LogManager.getLogger()
|
||||||
private val missing = Collections.synchronizedSet(ObjectOpenHashSet<String>())
|
private val missing = Collections.synchronizedSet(ObjectOpenHashSet<String>())
|
||||||
|
private val vectors by lazy { Starbound.gson.getAdapter(Vector2d::class.java) }
|
||||||
|
private val polies by lazy { Starbound.gson.getAdapter(Poly::class.java) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,9 @@ abstract class DynamicEntity(path: String) : AbstractEntity(path) {
|
|||||||
|
|
||||||
abstract val movement: MovementController
|
abstract val movement: MovementController
|
||||||
|
|
||||||
|
override val collisionArea: AABB
|
||||||
|
get() = movement.computeCollisionAABB()
|
||||||
|
|
||||||
override fun onNetworkUpdate() {
|
override fun onNetworkUpdate() {
|
||||||
super.onNetworkUpdate()
|
super.onNetworkUpdate()
|
||||||
movement.updateFixtures()
|
movement.updateFixtures()
|
||||||
|
@ -82,7 +82,11 @@ class PlayerEntity() : HumanoidActorEntity("/") {
|
|||||||
private var xAimPosition by networkGroup.upstream.add(networkedFixedPoint(0.003125))
|
private var xAimPosition by networkGroup.upstream.add(networkedFixedPoint(0.003125))
|
||||||
private var yAimPosition by networkGroup.upstream.add(networkedFixedPoint(0.003125).also { it.interpolator = Interpolator.Linear })
|
private var yAimPosition by networkGroup.upstream.add(networkedFixedPoint(0.003125).also { it.interpolator = Interpolator.Linear })
|
||||||
var humanoidData by networkGroup.upstream.add(networkedData(HumanoidData(), HumanoidData.CODEC, HumanoidData.LEGACY_CODEC))
|
var humanoidData by networkGroup.upstream.add(networkedData(HumanoidData(), HumanoidData.CODEC, HumanoidData.LEGACY_CODEC))
|
||||||
var teamState by networkGroup.upstream.add(networkedData(EntityDamageTeam(), EntityDamageTeam.CODEC, EntityDamageTeam.LEGACY_CODEC))
|
|
||||||
|
init {
|
||||||
|
networkGroup.upstream.add(team)
|
||||||
|
}
|
||||||
|
|
||||||
val landed = networkGroup.upstream.add(networkedEventCounter())
|
val landed = networkGroup.upstream.add(networkedEventCounter())
|
||||||
var chatMessage by networkGroup.upstream.add(networkedString())
|
var chatMessage by networkGroup.upstream.add(networkedString())
|
||||||
val newChatMessage = networkGroup.upstream.add(networkedEventCounter())
|
val newChatMessage = networkGroup.upstream.add(networkedEventCounter())
|
||||||
@ -106,7 +110,7 @@ class PlayerEntity() : HumanoidActorEntity("/") {
|
|||||||
override val aimPosition: Vector2d
|
override val aimPosition: Vector2d
|
||||||
get() = Vector2d(xAimPosition, yAimPosition)
|
get() = Vector2d(xAimPosition, yAimPosition)
|
||||||
|
|
||||||
override val isApplicableForUnloading: Boolean
|
override val isPersistent: Boolean
|
||||||
get() = false
|
get() = false
|
||||||
|
|
||||||
var uuid: UUID by Delegates.notNull()
|
var uuid: UUID by Delegates.notNull()
|
||||||
|
@ -83,11 +83,11 @@ abstract class TileEntity(path: String) : AbstractEntity(path) {
|
|||||||
private val currentMaterialSpaces = HashSet<Pair<Vector2i, Registry.Ref<TileDefinition>>>()
|
private val currentMaterialSpaces = HashSet<Pair<Vector2i, Registry.Ref<TileDefinition>>>()
|
||||||
private val currentRoots = HashSet<Vector2i>()
|
private val currentRoots = HashSet<Vector2i>()
|
||||||
|
|
||||||
protected open fun markSpacesDirty() {
|
open fun markSpacesDirty() {
|
||||||
needToUpdateSpaces = true
|
needToUpdateSpaces = true
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun markRootsDirty() {
|
open fun markRootsDirty() {
|
||||||
needToUpdateRoots = true
|
needToUpdateRoots = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,6 +147,8 @@ abstract class TileEntity(path: String) : AbstractEntity(path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected fun updateMaterialSpaces(desired: Collection<Pair<Vector2i, Registry.Ref<TileDefinition>>>): Boolean {
|
protected fun updateMaterialSpaces(desired: Collection<Pair<Vector2i, Registry.Ref<TileDefinition>>>): Boolean {
|
||||||
|
check(world.isServer) { "Invalid realm" }
|
||||||
|
|
||||||
val toRemove = ArrayList<Pair<Vector2i, Registry.Ref<TileDefinition>>>()
|
val toRemove = ArrayList<Pair<Vector2i, Registry.Ref<TileDefinition>>>()
|
||||||
val toPlace = ArrayList<Pair<Vector2i, Registry.Ref<TileDefinition>>>()
|
val toPlace = ArrayList<Pair<Vector2i, Registry.Ref<TileDefinition>>>()
|
||||||
|
|
||||||
@ -219,7 +221,7 @@ abstract class TileEntity(path: String) : AbstractEntity(path) {
|
|||||||
override fun tick(delta: Double) {
|
override fun tick(delta: Double) {
|
||||||
super.tick(delta)
|
super.tick(delta)
|
||||||
|
|
||||||
if (needToUpdateSpaces) {
|
if (world.isServer && needToUpdateSpaces) {
|
||||||
updateMaterialSpacesNow()
|
updateMaterialSpacesNow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,8 @@ 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.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
|
import org.apache.logging.log4j.LogManager
|
||||||
|
import org.classdump.luna.Table
|
||||||
import ru.dbotthepony.kommons.math.RGBAColor
|
import ru.dbotthepony.kommons.math.RGBAColor
|
||||||
import ru.dbotthepony.kommons.vector.Vector2i
|
import ru.dbotthepony.kommons.vector.Vector2i
|
||||||
import ru.dbotthepony.kstarbound.Registries
|
import ru.dbotthepony.kstarbound.Registries
|
||||||
@ -59,6 +61,17 @@ 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.json.writeJsonElement
|
||||||
|
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
||||||
|
import ru.dbotthepony.kstarbound.lua.LuaMessageHandler
|
||||||
|
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.bindings.provideWorldBindings
|
||||||
|
import ru.dbotthepony.kstarbound.lua.bindings.provideWorldObjectBindings
|
||||||
|
import ru.dbotthepony.kstarbound.lua.from
|
||||||
|
import ru.dbotthepony.kstarbound.lua.get
|
||||||
|
import ru.dbotthepony.kstarbound.lua.set
|
||||||
|
import ru.dbotthepony.kstarbound.lua.toJson
|
||||||
import ru.dbotthepony.kstarbound.util.asStringOrNull
|
import ru.dbotthepony.kstarbound.util.asStringOrNull
|
||||||
import ru.dbotthepony.kstarbound.world.Direction
|
import ru.dbotthepony.kstarbound.world.Direction
|
||||||
import ru.dbotthepony.kstarbound.world.LightCalculator
|
import ru.dbotthepony.kstarbound.world.LightCalculator
|
||||||
@ -78,6 +91,8 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
|||||||
isInteractive = data.get("interactive", false)
|
isInteractive = data.get("interactive", false)
|
||||||
tilePosition = data.get("tilePosition", vectors)
|
tilePosition = data.get("tilePosition", vectors)
|
||||||
|
|
||||||
|
lua.globals["storage"] = lua.from(data.get("scriptStorage") { JsonObject() })
|
||||||
|
|
||||||
uniqueID.accept(KOptional.ofNullable(data["uniqueId"]?.asStringOrNull))
|
uniqueID.accept(KOptional.ofNullable(data["uniqueId"]?.asStringOrNull))
|
||||||
|
|
||||||
loadParameters(data.get("parameters") { JsonObject() })
|
loadParameters(data.get("parameters") { JsonObject() })
|
||||||
@ -97,6 +112,12 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
|||||||
into["orientationIndex"] = orientationIndex
|
into["orientationIndex"] = orientationIndex
|
||||||
into["interactive"] = isInteractive
|
into["interactive"] = isInteractive
|
||||||
|
|
||||||
|
val scriptStorage = lua.globals["storage"]
|
||||||
|
|
||||||
|
if (scriptStorage != null && scriptStorage is Table) {
|
||||||
|
into["scriptStorage"] = scriptStorage.toJson()
|
||||||
|
}
|
||||||
|
|
||||||
uniqueID.get().ifPresent {
|
uniqueID.get().ifPresent {
|
||||||
into["uniqueId"] = it
|
into["uniqueId"] = it
|
||||||
}
|
}
|
||||||
@ -243,6 +264,14 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
|||||||
networkGroup.upstream.add(animator.networkGroup)
|
networkGroup.upstream.add(animator.networkGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val lua = LuaEnvironment()
|
||||||
|
val luaUpdate = LuaUpdateComponent(lua)
|
||||||
|
val luaMessageHandler = LuaMessageHandler(lua)
|
||||||
|
|
||||||
|
init {
|
||||||
|
lua.globals["storage"] = lua.newTable()
|
||||||
|
}
|
||||||
|
|
||||||
val unbreakable by LazyData {
|
val unbreakable by LazyData {
|
||||||
lookupProperty(JsonPath("unbreakable")) { JsonPrimitive(false) }.asBoolean
|
lookupProperty(JsonPath("unbreakable")) { JsonPrimitive(false) }.asBoolean
|
||||||
}
|
}
|
||||||
@ -280,10 +309,20 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
|||||||
//
|
//
|
||||||
var color: TileColor by Property(JsonPath("color"), TileColor.DEFAULT)
|
var color: TileColor by Property(JsonPath("color"), TileColor.DEFAULT)
|
||||||
|
|
||||||
var animationParts: ImmutableMap<String, SpriteReference> by Property(JsonPath("animationParts"))
|
|
||||||
var imagePosition: Vector2i by Property(JsonPath("imagePosition"), Vector2i.ZERO)
|
var imagePosition: Vector2i by Property(JsonPath("imagePosition"), Vector2i.ZERO)
|
||||||
var animationPosition: Vector2i by Property(JsonPath("animationPosition"), Vector2i.ZERO)
|
var animationPosition: Vector2i by Property(JsonPath("animationPosition"), Vector2i.ZERO)
|
||||||
|
|
||||||
|
// ????????
|
||||||
|
val volumeBoundingBox: AABB get() {
|
||||||
|
val orientation = orientation
|
||||||
|
|
||||||
|
if (orientation == null) {
|
||||||
|
return AABB(position, position + Vector2d.POSITIVE_XY)
|
||||||
|
} else {
|
||||||
|
return AABB(orientation.boundingBox.mins.toDoubleVector(), orientation.boundingBox.maxs.toDoubleVector() + Vector2d.POSITIVE_XY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val drawablesCache = LazyData {
|
private val drawablesCache = LazyData {
|
||||||
orientation?.drawables?.map { it.with(::getRenderParam) } ?: listOf()
|
orientation?.drawables?.map { it.with(::getRenderParam) } ?: listOf()
|
||||||
}
|
}
|
||||||
@ -336,6 +375,11 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun markSpacesDirty() {
|
||||||
|
super.markSpacesDirty()
|
||||||
|
materialSpaces0.invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onJoinWorld(world: World<*, *>) {
|
override fun onJoinWorld(world: World<*, *>) {
|
||||||
super.onJoinWorld(world)
|
super.onJoinWorld(world)
|
||||||
|
|
||||||
@ -352,13 +396,32 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
|||||||
networkedMaterialSpaces.addAll(orientation.materialSpaces)
|
networkedMaterialSpaces.addAll(orientation.materialSpaces)
|
||||||
}
|
}
|
||||||
|
|
||||||
setImageKey("color", lookupProperty(JsonPath("color")) { JsonPrimitive("default") }.asString)
|
if (isRemote) {
|
||||||
|
|
||||||
for ((k, v) in lookupProperty(JsonPath("animationParts")) { JsonObject() }.asJsonObject.entrySet()) {
|
} else {
|
||||||
animator.setPartTag(k, "partImage", v.asString)
|
setImageKey("color", lookupProperty(JsonPath("color")) { JsonPrimitive("default") }.asString)
|
||||||
|
|
||||||
|
for ((k, v) in lookupProperty(JsonPath("animationParts")) { JsonObject() }.asJsonObject.entrySet()) {
|
||||||
|
animator.setPartTag(k, "partImage", v.asString)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMaterialSpacesNow()
|
||||||
|
|
||||||
|
provideWorldBindings(world, lua)
|
||||||
|
provideWorldObjectBindings(this, lua)
|
||||||
|
provideEntityBindings(this, lua)
|
||||||
|
provideAnimatorBindings(animator, lua)
|
||||||
|
lua.attach(config.value.scripts)
|
||||||
|
lua.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
updateMaterialSpacesNow()
|
// as original code puts it:
|
||||||
|
// Don't animate the initial state when first spawned IF you're dumb, which by default
|
||||||
|
// you would be, and don't know how to use transition and static states properly. Someday
|
||||||
|
// I'll be brave and delete shit garbage entirely and we'll see what breaks.
|
||||||
|
if (lookupProperty(JsonPath("forceFinishAnimationsInInit")) { JsonPrimitive(true) }.asBoolean) {
|
||||||
|
animator.finishAnimations()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getRenderParam(key: String): String {
|
fun getRenderParam(key: String): String {
|
||||||
@ -400,6 +463,14 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
|||||||
drawablesCache.invalidate()
|
drawablesCache.invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun callBreak(smash: Boolean = false) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addChatMessage(message: String, config: JsonElement, portrait: String? = null) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
override fun tick(delta: Double) {
|
override fun tick(delta: Double) {
|
||||||
super.tick(delta)
|
super.tick(delta)
|
||||||
|
|
||||||
@ -420,6 +491,12 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
|
|||||||
setImageKey("frame", frame.toString())
|
setImageKey("frame", frame.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
luaUpdate.update()
|
||||||
|
} catch (err: Throwable) {
|
||||||
|
LogManager.getLogger().error("Error running update", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (world.isServer && !unbreakable) {
|
if (world.isServer && !unbreakable) {
|
||||||
|
@ -24,6 +24,7 @@ import ru.dbotthepony.kommons.io.readVector2f
|
|||||||
import ru.dbotthepony.kommons.io.writeCollection
|
import ru.dbotthepony.kommons.io.writeCollection
|
||||||
import ru.dbotthepony.kommons.io.writeStruct2d
|
import ru.dbotthepony.kommons.io.writeStruct2d
|
||||||
import ru.dbotthepony.kommons.io.writeStruct2f
|
import ru.dbotthepony.kommons.io.writeStruct2f
|
||||||
|
import ru.dbotthepony.kommons.matrix.Matrix3d
|
||||||
import ru.dbotthepony.kstarbound.json.listAdapter
|
import ru.dbotthepony.kstarbound.json.listAdapter
|
||||||
import ru.dbotthepony.kstarbound.math.Line2d
|
import ru.dbotthepony.kstarbound.math.Line2d
|
||||||
import ru.dbotthepony.kstarbound.network.syncher.legacyCodec
|
import ru.dbotthepony.kstarbound.network.syncher.legacyCodec
|
||||||
@ -213,6 +214,10 @@ class Poly private constructor(val edges: ImmutableList<Line2d>, val vertices: I
|
|||||||
return Vector2d(min, max)
|
return Vector2d(min, max)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun transform(transformation: Matrix3d): Poly {
|
||||||
|
return Poly(vertices.map { it * transformation })
|
||||||
|
}
|
||||||
|
|
||||||
fun windingNumber(point: IStruct2d): Int {
|
fun windingNumber(point: IStruct2d): Int {
|
||||||
val (x, y) = point
|
val (x, y) = point
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user