Some initial fixes for Lua bindings and stuff

This commit is contained in:
DBotThePony 2024-04-14 11:48:19 +07:00
parent c49c0e4393
commit 403aac63de
Signed by: DBot
GPG Key ID: DCC23B5715498507
9 changed files with 117 additions and 23 deletions

View File

@ -1,18 +1,23 @@
package ru.dbotthepony.kstarbound package ru.dbotthepony.kstarbound
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
import com.google.gson.GsonBuilder import com.google.gson.GsonBuilder
import com.google.gson.JsonElement import com.google.gson.JsonElement
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.google.gson.JsonSyntaxException import com.google.gson.JsonSyntaxException
import com.google.gson.TypeAdapterFactory import com.google.gson.TypeAdapterFactory
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader import com.google.gson.stream.JsonReader
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kommons.util.KOptional import ru.dbotthepony.kommons.util.KOptional
import ru.dbotthepony.kstarbound.defs.AssetReference
import ru.dbotthepony.kstarbound.defs.Json2Function import ru.dbotthepony.kstarbound.defs.Json2Function
import ru.dbotthepony.kstarbound.defs.JsonConfigFunction import ru.dbotthepony.kstarbound.defs.JsonConfigFunction
import ru.dbotthepony.kstarbound.defs.JsonFunction import ru.dbotthepony.kstarbound.defs.JsonFunction
import ru.dbotthepony.kstarbound.defs.Species import ru.dbotthepony.kstarbound.defs.Species
import ru.dbotthepony.kstarbound.defs.StatusEffectDefinition import ru.dbotthepony.kstarbound.defs.StatusEffectDefinition
import ru.dbotthepony.kstarbound.defs.ThingDescription
import ru.dbotthepony.kstarbound.defs.item.TreasurePoolDefinition import ru.dbotthepony.kstarbound.defs.item.TreasurePoolDefinition
import ru.dbotthepony.kstarbound.defs.item.api.IItemDefinition import ru.dbotthepony.kstarbound.defs.item.api.IItemDefinition
import ru.dbotthepony.kstarbound.defs.item.impl.BackArmorItemDefinition import ru.dbotthepony.kstarbound.defs.item.impl.BackArmorItemDefinition
@ -36,14 +41,18 @@ import ru.dbotthepony.kstarbound.defs.dungeon.DungeonDefinition
import ru.dbotthepony.kstarbound.defs.projectile.ProjectileDefinition import ru.dbotthepony.kstarbound.defs.projectile.ProjectileDefinition
import ru.dbotthepony.kstarbound.defs.quest.QuestTemplate import ru.dbotthepony.kstarbound.defs.quest.QuestTemplate
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
import ru.dbotthepony.kstarbound.defs.tile.RenderParameters
import ru.dbotthepony.kstarbound.defs.tile.TileDamageConfig
import ru.dbotthepony.kstarbound.defs.tile.TileModifierDefinition import ru.dbotthepony.kstarbound.defs.tile.TileModifierDefinition
import ru.dbotthepony.kstarbound.defs.tile.TileDefinition import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
import ru.dbotthepony.kstarbound.defs.world.BushVariant import ru.dbotthepony.kstarbound.defs.world.BushVariant
import ru.dbotthepony.kstarbound.defs.world.GrassVariant import ru.dbotthepony.kstarbound.defs.world.GrassVariant
import ru.dbotthepony.kstarbound.defs.world.TreeVariant import ru.dbotthepony.kstarbound.defs.world.TreeVariant
import ru.dbotthepony.kstarbound.defs.world.BiomeDefinition import ru.dbotthepony.kstarbound.defs.world.BiomeDefinition
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
import ru.dbotthepony.kstarbound.world.terrain.TerrainSelectorType import ru.dbotthepony.kstarbound.world.terrain.TerrainSelectorType
import ru.dbotthepony.kstarbound.util.AssetPathStack import ru.dbotthepony.kstarbound.util.AssetPathStack
import ru.dbotthepony.kstarbound.world.physics.CollisionType
import java.util.* import java.util.*
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
import java.util.concurrent.Future import java.util.concurrent.Future
@ -156,6 +165,9 @@ object Registries {
tasks.addAll(loadRegistry(tileModifiers, fileTree["matmod"] ?: listOf(), key(TileModifierDefinition::modName, TileModifierDefinition::modId))) tasks.addAll(loadRegistry(tileModifiers, fileTree["matmod"] ?: listOf(), key(TileModifierDefinition::modName, TileModifierDefinition::modId)))
tasks.addAll(loadRegistry(liquid, fileTree["liquid"] ?: listOf(), key(LiquidDefinition::name, LiquidDefinition::liquidId))) tasks.addAll(loadRegistry(liquid, fileTree["liquid"] ?: listOf(), key(LiquidDefinition::name, LiquidDefinition::liquidId)))
tasks.add(loadMetaMaterials())
tasks.addAll(loadRegistry(dungeons, fileTree["dungeon"] ?: listOf(), key(DungeonDefinition::name)))
tasks.addAll(loadRegistry(worldObjects, fileTree["object"] ?: listOf(), key(ObjectDefinition::objectName))) tasks.addAll(loadRegistry(worldObjects, fileTree["object"] ?: listOf(), key(ObjectDefinition::objectName)))
tasks.addAll(loadRegistry(statusEffects, fileTree["statuseffect"] ?: listOf(), key(StatusEffectDefinition::name))) tasks.addAll(loadRegistry(statusEffects, fileTree["statuseffect"] ?: listOf(), key(StatusEffectDefinition::name)))
tasks.addAll(loadRegistry(species, fileTree["species"] ?: listOf(), key(Species::kind))) tasks.addAll(loadRegistry(species, fileTree["species"] ?: listOf(), key(Species::kind)))
@ -169,7 +181,6 @@ object Registries {
tasks.addAll(loadRegistry(treeStemVariants, fileTree["modularstem"] ?: listOf(), key(TreeVariant.StemData::name))) tasks.addAll(loadRegistry(treeStemVariants, fileTree["modularstem"] ?: listOf(), key(TreeVariant.StemData::name)))
tasks.addAll(loadRegistry(treeFoliageVariants, fileTree["modularfoliage"] ?: listOf(), key(TreeVariant.FoliageData::name))) tasks.addAll(loadRegistry(treeFoliageVariants, fileTree["modularfoliage"] ?: listOf(), key(TreeVariant.FoliageData::name)))
tasks.addAll(loadRegistry(bushVariants, fileTree["bush"] ?: listOf(), key(BushVariant.Data::name))) tasks.addAll(loadRegistry(bushVariants, fileTree["bush"] ?: listOf(), key(BushVariant.Data::name)))
tasks.addAll(loadRegistry(dungeons, fileTree["dungeon"] ?: listOf(), key(DungeonDefinition::name)))
tasks.addAll(loadCombined(jsonFunctions, fileTree["functions"] ?: listOf())) tasks.addAll(loadCombined(jsonFunctions, fileTree["functions"] ?: listOf()))
tasks.addAll(loadCombined(json2Functions, fileTree["2functions"] ?: listOf())) tasks.addAll(loadCombined(json2Functions, fileTree["2functions"] ?: listOf()))
@ -282,4 +293,44 @@ object Registries {
return tasks return tasks
} }
@JsonFactory
data class MetaMaterialDef(
val materialId: Int,
val name: String,
val collisionKind: CollisionType,
val blocksLiquidFlow: Boolean = collisionKind.isSolidCollision,
val isConnectable: Boolean = true,
val supportsMods: Boolean = false,
)
private fun loadMetaMaterials(): Future<*> {
return Starbound.EXECUTOR.submit {
val read = Starbound.loadJsonAsset("/metamaterials.config") ?: return@submit
val read2 = Starbound.gson.getAdapter(object : TypeToken<ImmutableList<MetaMaterialDef>>() {}).fromJsonTree(read)
for (def in read2) {
tiles.add {
tiles.add(key = "metamaterial:${def.name}", id = KOptional(def.materialId), value = TileDefinition(
isMeta = true,
materialId = def.materialId,
materialName = "metamaterial:${def.name}",
descriptionData = ThingDescription.EMPTY,
category = "meta",
renderTemplate = AssetReference.empty(),
renderParameters = RenderParameters.META,
isConnectable = def.isConnectable,
supportsMods = def.supportsMods,
damageTable = AssetReference(TileDamageConfig(
damageFactors = ImmutableMap.of(),
damageRecovery = Double.MAX_VALUE,
maximumEffectTime = 0.0,
totalHealth = Double.MAX_VALUE,
harvestLevel = Int.MAX_VALUE,
))
))
}
}
}
}
} }

View File

@ -47,7 +47,7 @@ data class AnimatedPartsDefinition(
init { init {
var index = 0 var index = 0
for ((k, v) in states) { for ((k, v) in sortedStates) {
v.name = k v.name = k
v.index = index++ v.index = index++

View File

@ -315,7 +315,7 @@ class Image private constructor(
if (xpixel !in 0 until width) if (xpixel !in 0 until width)
continue continue
if (!isTransparent(xpixel, ypixel, flip)) { if (!isTransparent(xpixel, height - ypixel - 1, flip)) {
fillRatio += 1.0 / (PIXELS_IN_STARBOUND_UNIT * PIXELS_IN_STARBOUND_UNIT) fillRatio += 1.0 / (PIXELS_IN_STARBOUND_UNIT * PIXELS_IN_STARBOUND_UNIT)
} }
} }

View File

@ -120,7 +120,7 @@ const val PROTECTED_ZERO_GRAVITY_DUNGEON_ID = 65524
const val FIRST_RESERVED_DUNGEON_ID = 65520 const val FIRST_RESERVED_DUNGEON_ID = 65520
object BuiltinMetaMaterials { object BuiltinMetaMaterials {
private fun make(id: Int, name: String, collisionType: CollisionType, isConnectable: Boolean = true) = Registries.tiles.add(key = name, id = KOptional(id), value = TileDefinition( private fun make(id: Int, name: String, collisionType: CollisionType, isConnectable: Boolean = true) = Registries.tiles.add(key = "metamaterial:$name", id = KOptional(id), value = TileDefinition(
materialId = id, materialId = id,
materialName = "metamaterial:$name", materialName = "metamaterial:$name",
descriptionData = ThingDescription.EMPTY, descriptionData = ThingDescription.EMPTY,
@ -140,7 +140,7 @@ object BuiltinMetaMaterials {
)) ))
), isBuiltin = true) ), isBuiltin = true)
private fun makeMod(id: Int, name: String) = Registries.tileModifiers.add(key = name, id = KOptional(id), value = TileModifierDefinition( private fun makeMod(id: Int, name: String) = Registries.tileModifiers.add(key = "metamod:$name", id = KOptional(id), value = TileModifierDefinition(
modId = id, modId = id,
modName = "metamod:$name", modName = "metamod:$name",
descriptionData = ThingDescription.EMPTY, descriptionData = ThingDescription.EMPTY,
@ -176,7 +176,7 @@ object BuiltinMetaMaterials {
val BIOME_MOD = makeMod(65534, "biome") val BIOME_MOD = makeMod(65534, "biome")
val UNDERGROUND_BIOME_MOD = makeMod(65533, "underground_biome") val UNDERGROUND_BIOME_MOD = makeMod(65533, "underground_biome")
val NO_LIQUID = Registries.liquid.add(key = "empty", id = KOptional(0), value = LiquidDefinition( val NO_LIQUID = Registries.liquid.add(key = "metaliquid:empty", id = KOptional(0), value = LiquidDefinition(
name = "metaliquid:empty", name = "metaliquid:empty",
liquidId = 0, liquidId = 0,
color = RGBAColor.TRANSPARENT_BLACK, color = RGBAColor.TRANSPARENT_BLACK,

View File

@ -4,6 +4,7 @@ import com.google.gson.JsonElement
import it.unimi.dsi.fastutil.objects.ObjectIterators import it.unimi.dsi.fastutil.objects.ObjectIterators
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.runtime.AbstractFunction0 import org.classdump.luna.runtime.AbstractFunction0
@ -99,6 +100,16 @@ operator fun Table.iterator(): Iterator<Map.Entry<Any, Any>> {
} }
} }
fun TableFactory.tableOf(vararg values: Any?): Table {
val table = newTable(values.size, 0)
for ((i, v) in values.withIndex()) {
table[i + 1L] = v
}
return table
}
@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?>() {

View File

@ -242,20 +242,23 @@ class LuaEnvironment : StateContext {
return true return true
} }
fun invokeGlobal(name: String) { fun invokeGlobal(name: String, vararg arguments: Any?): Array<out Any?> {
if (errorState) if (errorState)
return return arrayOf()
val load = globals[name] val load = globals[name]
if (load is LuaFunction<*, *, *, *, *>) { if (load is LuaFunction<*, *, *, *, *>) {
try { return try {
executor.call(this, load) executor.call(this, load, *arguments)
} catch (err: Throwable) { } catch (err: Throwable) {
errorState = true errorState = true
throw err LOGGER.error("Exception while calling global $name", err)
arrayOf()
} }
} }
return arrayOf()
} }
companion object { companion object {

View File

@ -3,8 +3,8 @@ package ru.dbotthepony.kstarbound.lua
import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.Starbound
class LuaUpdateComponent(val lua: LuaEnvironment) { class LuaUpdateComponent(val lua: LuaEnvironment) {
var stepCount = 1 var stepCount = 1.0
private var steps = 0 private var steps = 0.0
init { init {
val script = lua.newTable() val script = lua.newTable()
@ -14,15 +14,20 @@ class LuaUpdateComponent(val lua: LuaEnvironment) {
returnBuffer.setTo(stepCount * Starbound.TIMESTEP) returnBuffer.setTo(stepCount * Starbound.TIMESTEP)
} }
script["setUpdateDelta"] = luaFunction { ticks: Int -> script["setUpdateDelta"] = luaFunction { ticks: Number ->
stepCount = ticks stepCount = ticks.toDouble()
} }
} }
fun update() { fun update(delta: Double, vararg arguments: Any?) {
if (steps++ >= stepCount) { if (stepCount == 0.0)
steps = 0 return
lua.invokeGlobal("update")
steps += delta / Starbound.TIMESTEP
if (steps >= stepCount) {
steps %= stepCount
lua.invokeGlobal("update", stepCount * Starbound.TIMESTEP, *arguments)
} }
} }
} }

View File

@ -172,7 +172,7 @@ class Animator() {
// sorted by key // sorted by key
// Basically, this is definition of each separate state // Basically, this is definition of each separate state
val states = config.states val states = config.sortedStates
var timer = 0.0 var timer = 0.0
var frame = 0 var frame = 0
@ -192,7 +192,7 @@ class Animator() {
private var activeStateChanged = false private var activeStateChanged = false
private var frameChanged = false private var frameChanged = false
private var firstTime = true private var firstTime = false
var activeState: AnimatedPartsDefinition.StateType.State? = null var activeState: AnimatedPartsDefinition.StateType.State? = null
set(value) { set(value) {
@ -233,7 +233,7 @@ class Animator() {
activeState = getState activeState = getState
timer = 0.0 timer = 0.0
startedEvent.trigger() startedEvent.trigger()
firstTime = true firstTime = false
return true return true
} }
@ -304,6 +304,8 @@ class Animator() {
} }
fun finishAnimations() { fun finishAnimations() {
firstTime = true
while (true) { while (true) {
val active = activeState ?: break val active = activeState ?: break

View File

@ -11,6 +11,7 @@ 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.apache.logging.log4j.LogManager
import org.classdump.luna.ByteString
import org.classdump.luna.Table 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
@ -71,7 +72,9 @@ import ru.dbotthepony.kstarbound.lua.bindings.provideWorldObjectBindings
import ru.dbotthepony.kstarbound.lua.from import ru.dbotthepony.kstarbound.lua.from
import ru.dbotthepony.kstarbound.lua.get import ru.dbotthepony.kstarbound.lua.get
import ru.dbotthepony.kstarbound.lua.set import ru.dbotthepony.kstarbound.lua.set
import ru.dbotthepony.kstarbound.lua.tableOf
import ru.dbotthepony.kstarbound.lua.toJson import ru.dbotthepony.kstarbound.lua.toJson
import ru.dbotthepony.kstarbound.lua.toJsonFromLua
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
@ -412,6 +415,7 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
provideEntityBindings(this, lua) provideEntityBindings(this, lua)
provideAnimatorBindings(animator, lua) provideAnimatorBindings(animator, lua)
lua.attach(config.value.scripts) lua.attach(config.value.scripts)
luaUpdate.stepCount = lookupProperty(JsonPath("scriptDelta")) { JsonPrimitive(5) }.asDouble
lua.init() lua.init()
} }
@ -451,6 +455,24 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
override fun interact(request: InteractRequest): InteractAction { override fun interact(request: InteractRequest): InteractAction {
val diff = world.geometry.diff(request.sourcePos, position) val diff = world.geometry.diff(request.sourcePos, position)
val result = lua.invokeGlobal("onInteraction", lua.newTable().apply {
this["source"] = lua.tableOf(diff.x, diff.y)
this["sourceId"] = request.source
})
if (result.isNotEmpty()) {
if (result[0] == null)
return InteractAction.NONE
else if (result[0] is ByteString) {
val decoded = (result[0] as ByteString).decode()
return InteractAction(InteractAction.Type.entries.firstOrNull { it.jsonName == decoded } ?: throw NoSuchElementException("Unknown interaction action type $decoded!"))
} else {
val data = result[0] as Table
val decoded = (data[1L] as ByteString).decode()
return InteractAction(InteractAction.Type.entries.firstOrNull { it.jsonName == decoded } ?: throw NoSuchElementException("Unknown interaction action type $decoded!"), 0, toJsonFromLua(data[2L]))
}
}
if (!interactAction.isJsonNull) { if (!interactAction.isJsonNull) {
return InteractAction(interactAction.asString, entityID, interactData) return InteractAction(interactAction.asString, entityID, interactData)
} }
@ -493,7 +515,7 @@ open class WorldObject(val config: Registry.Entry<ObjectDefinition>) : TileEntit
} }
try { try {
luaUpdate.update() luaUpdate.update(delta)
} catch (err: Throwable) { } catch (err: Throwable) {
LogManager.getLogger().error("Error running update", err) LogManager.getLogger().error("Error running update", err)
} }