Compare commits
10 Commits
918b6ff95f
...
3b454374ec
Author | SHA1 | Date | |
---|---|---|---|
3b454374ec | |||
aeca7836cd | |||
11e4efb2af | |||
c5fa4e4b59 | |||
de34b8ac5f | |||
3efedd7d22 | |||
45e95f7f71 | |||
885bccb2d2 | |||
2b698be166 | |||
9947138eac |
@ -22,6 +22,7 @@ val guavaVersion: String by project
|
|||||||
val junitVersion: String by project
|
val junitVersion: String by project
|
||||||
val sqliteVersion: String by project
|
val sqliteVersion: String by project
|
||||||
val picocliVersion: String by project
|
val picocliVersion: String by project
|
||||||
|
val zstdVersion: String by project
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
maven {
|
maven {
|
||||||
@ -59,6 +60,7 @@ dependencies {
|
|||||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
|
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
|
||||||
|
|
||||||
implementation("com.google.code.gson:gson:$gsonVersion")
|
implementation("com.google.code.gson:gson:$gsonVersion")
|
||||||
|
implementation("com.github.luben:zstd-jni:$zstdVersion")
|
||||||
|
|
||||||
implementation("it.unimi.dsi:fastutil:$fastutilVersion")
|
implementation("it.unimi.dsi:fastutil:$fastutilVersion")
|
||||||
|
|
||||||
|
@ -17,5 +17,6 @@ log4jVersion=2.17.1
|
|||||||
guavaVersion=33.0.0-jre
|
guavaVersion=33.0.0-jre
|
||||||
junitVersion=5.8.2
|
junitVersion=5.8.2
|
||||||
sqliteVersion=3.45.3.0
|
sqliteVersion=3.45.3.0
|
||||||
|
zstdVersion=1.5.6-8
|
||||||
|
|
||||||
picocliVersion=4.7.6
|
picocliVersion=4.7.6
|
||||||
|
@ -50,6 +50,7 @@ operator fun <K : Any, V : Any> ImmutableMap.Builder<K, V>.set(key: K, value: V)
|
|||||||
inline fun <reified T> Gson.fromJson(reader: JsonReader): T? = fromJson<T>(reader, T::class.java)
|
inline fun <reified T> Gson.fromJson(reader: JsonReader): T? = fromJson<T>(reader, T::class.java)
|
||||||
inline fun <reified T> Gson.fromJson(reader: JsonElement): T? = getAdapter(T::class.java).read(FastJsonTreeReader(reader))
|
inline fun <reified T> Gson.fromJson(reader: JsonElement): T? = getAdapter(T::class.java).read(FastJsonTreeReader(reader))
|
||||||
|
|
||||||
|
// TODO: mark these nullable
|
||||||
fun <T> Gson.fromJsonFast(reader: JsonElement, type: Class<T>): T = getAdapter(type).read(FastJsonTreeReader(reader))
|
fun <T> Gson.fromJsonFast(reader: JsonElement, type: Class<T>): T = getAdapter(type).read(FastJsonTreeReader(reader))
|
||||||
fun <T> Gson.fromJsonFast(reader: JsonElement, type: TypeToken<T>): T = getAdapter(type).read(FastJsonTreeReader(reader))
|
fun <T> Gson.fromJsonFast(reader: JsonElement, type: TypeToken<T>): T = getAdapter(type).read(FastJsonTreeReader(reader))
|
||||||
inline fun <reified T> Gson.fromJsonFast(reader: JsonElement): T = getAdapter(object : TypeToken<T>() {}).read(FastJsonTreeReader(reader))
|
inline fun <reified T> Gson.fromJsonFast(reader: JsonElement): T = getAdapter(object : TypeToken<T>() {}).read(FastJsonTreeReader(reader))
|
||||||
|
@ -117,6 +117,33 @@ object Registries {
|
|||||||
|
|
||||||
private val monsterParts = HashMap<Pair<String, String>, HashMap<String, Pair<MonsterPartDefinition, IStarboundFile>>>()
|
private val monsterParts = HashMap<Pair<String, String>, HashMap<String, Pair<MonsterPartDefinition, IStarboundFile>>>()
|
||||||
private val loggedMonsterPartMisses = Collections.synchronizedSet(ObjectOpenHashSet<Pair<String, String>>())
|
private val loggedMonsterPartMisses = Collections.synchronizedSet(ObjectOpenHashSet<Pair<String, String>>())
|
||||||
|
private val stagehands = HashMap<String, Pair<JsonObject, IStarboundFile>>()
|
||||||
|
|
||||||
|
fun makeStagehandConfig(type: String, overrides: JsonElement? = JsonNull.INSTANCE): JsonObject {
|
||||||
|
val (data) = stagehands[type] ?: throw NoSuchElementException("No such stagehand: $type")
|
||||||
|
return mergeJson(data.deepCopy(), overrides ?: JsonNull.INSTANCE)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadStagehands(files: Collection<IStarboundFile>, patches: Map<String, Collection<IStarboundFile>>): List<Future<*>> {
|
||||||
|
return files.map { listedFile ->
|
||||||
|
Starbound.GLOBAL_SCOPE.launch {
|
||||||
|
try {
|
||||||
|
val elem = JsonPatch.applyAsync(listedFile.asyncJsonReader(), patches[listedFile.computeFullPath()]).asJsonObject
|
||||||
|
val type = elem["type"].asString
|
||||||
|
|
||||||
|
Starbound.submit {
|
||||||
|
val existing = stagehands.put(type, elem to listedFile)
|
||||||
|
|
||||||
|
if (existing != null) {
|
||||||
|
LOGGER.warn("Redefining stagehand prototype $type (new def originate from $listedFile, existing originate from ${existing.second})")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err: Throwable) {
|
||||||
|
LOGGER.error("Loading stagehand definition file $listedFile", err)
|
||||||
|
}
|
||||||
|
}.asCompletableFuture()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun selectMonsterPart(category: String, type: String, random: RandomGenerator): MonsterPartDefinition? {
|
fun selectMonsterPart(category: String, type: String, random: RandomGenerator): MonsterPartDefinition? {
|
||||||
val key = category to type
|
val key = category to type
|
||||||
@ -275,6 +302,7 @@ object Registries {
|
|||||||
tasks.addAll(loadRegistry(dungeons, patchTree, fileTree["dungeon"] ?: listOf(), key(DungeonDefinition::name)))
|
tasks.addAll(loadRegistry(dungeons, patchTree, fileTree["dungeon"] ?: listOf(), key(DungeonDefinition::name)))
|
||||||
|
|
||||||
tasks.addAll(loadMonsterParts(fileTree["monsterpart"] ?: listOf(), patchTree))
|
tasks.addAll(loadMonsterParts(fileTree["monsterpart"] ?: listOf(), patchTree))
|
||||||
|
tasks.addAll(loadStagehands(fileTree["stagehand"] ?: listOf(), patchTree))
|
||||||
|
|
||||||
tasks.addAll(loadRegistry(worldObjects, patchTree, fileTree["object"] ?: listOf(), key(ObjectDefinition::objectName)))
|
tasks.addAll(loadRegistry(worldObjects, patchTree, fileTree["object"] ?: listOf(), key(ObjectDefinition::objectName)))
|
||||||
tasks.addAll(loadRegistry(monsterTypes, patchTree, fileTree["monstertype"] ?: listOf(), key(MonsterTypeDefinition::type)))
|
tasks.addAll(loadRegistry(monsterTypes, patchTree, fileTree["monstertype"] ?: listOf(), key(MonsterTypeDefinition::type)))
|
||||||
|
@ -16,6 +16,7 @@ import ru.dbotthepony.kstarbound.world.entities.ItemDropEntity
|
|||||||
import ru.dbotthepony.kstarbound.world.entities.MonsterEntity
|
import ru.dbotthepony.kstarbound.world.entities.MonsterEntity
|
||||||
import ru.dbotthepony.kstarbound.world.entities.NPCEntity
|
import ru.dbotthepony.kstarbound.world.entities.NPCEntity
|
||||||
import ru.dbotthepony.kstarbound.world.entities.ProjectileEntity
|
import ru.dbotthepony.kstarbound.world.entities.ProjectileEntity
|
||||||
|
import ru.dbotthepony.kstarbound.world.entities.StagehandEntity
|
||||||
import ru.dbotthepony.kstarbound.world.entities.player.PlayerEntity
|
import ru.dbotthepony.kstarbound.world.entities.player.PlayerEntity
|
||||||
import ru.dbotthepony.kstarbound.world.entities.tile.ContainerObject
|
import ru.dbotthepony.kstarbound.world.entities.tile.ContainerObject
|
||||||
import ru.dbotthepony.kstarbound.world.entities.tile.LoungeableObject
|
import ru.dbotthepony.kstarbound.world.entities.tile.LoungeableObject
|
||||||
@ -102,11 +103,11 @@ enum class EntityType(override val jsonName: String, val storeName: String, val
|
|||||||
|
|
||||||
STAGEHAND("stagehand", "StagehandEntity", true, true) {
|
STAGEHAND("stagehand", "StagehandEntity", true, true) {
|
||||||
override suspend fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity {
|
override suspend fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity {
|
||||||
TODO("STAGEHAND")
|
return StagehandEntity(stream, isLegacy)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun fromStorage(data: JsonObject): AbstractEntity {
|
override fun fromStorage(data: JsonObject): AbstractEntity {
|
||||||
TODO("STAGEHAND")
|
return StagehandEntity(data)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -130,7 +130,7 @@ class DungeonWorld(
|
|||||||
private val boundingBoxes = ArrayList<AABBi>()
|
private val boundingBoxes = ArrayList<AABBi>()
|
||||||
|
|
||||||
fun clearTileEntityAt(x: Int, y: Int) {
|
fun clearTileEntityAt(x: Int, y: Int) {
|
||||||
clearTileEntitiesAt.add(geometry.wrap(Vector2i(x, y)))
|
clearTileEntitiesAt.add(geometry.wrap(x, y))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearTileEntityAt(position: Vector2i) {
|
fun clearTileEntityAt(position: Vector2i) {
|
||||||
@ -138,7 +138,7 @@ class DungeonWorld(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun isClearingTileEntityAt(x: Int, y: Int): Boolean {
|
fun isClearingTileEntityAt(x: Int, y: Int): Boolean {
|
||||||
return geometry.wrap(Vector2i(x, y)) in clearTileEntitiesAt
|
return geometry.wrap(x, y) in clearTileEntitiesAt
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearTileEntity(entity: TileEntity) {
|
fun clearTileEntity(entity: TileEntity) {
|
||||||
@ -163,15 +163,15 @@ class DungeonWorld(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun isTouched(x: Int, y: Int): Boolean {
|
fun isTouched(x: Int, y: Int): Boolean {
|
||||||
return touchedTiles.contains(geometry.wrap(Vector2i(x, y)))
|
return touchedTiles.contains(geometry.wrap(x, y))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun touchPlacement(x: Int, y: Int) {
|
fun touchPlacement(x: Int, y: Int) {
|
||||||
protectTile.add(geometry.wrap(Vector2i(x, y)))
|
protectTile.add(geometry.wrap(x, y))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasPlacement(x: Int, y: Int): Boolean {
|
fun hasPlacement(x: Int, y: Int): Boolean {
|
||||||
return protectTile.contains(geometry.wrap(Vector2i(x, y)))
|
return protectTile.contains(geometry.wrap(x, y))
|
||||||
}
|
}
|
||||||
|
|
||||||
private val biomeItems = HashSet<Vector2i>(8192, 0.5f)
|
private val biomeItems = HashSet<Vector2i>(8192, 0.5f)
|
||||||
@ -206,32 +206,32 @@ class DungeonWorld(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun placeObject(x: Int, y: Int, prototype: Registry.Entry<ObjectDefinition>, direction: Direction = Direction.LEFT, parameters: JsonObject = JsonObject()) {
|
fun placeObject(x: Int, y: Int, prototype: Registry.Entry<ObjectDefinition>, direction: Direction = Direction.LEFT, parameters: JsonObject = JsonObject()) {
|
||||||
placedObjects[geometry.wrap(Vector2i(x, y))] = PlacedObject(prototype, direction, parameters)
|
placedObjects[geometry.wrap(x, y)] = PlacedObject(prototype, direction, parameters)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun placeWiring(x: Int, y: Int, group: String, partLocal: Boolean) {
|
fun placeWiring(x: Int, y: Int, group: String, partLocal: Boolean) {
|
||||||
val table = if (partLocal) openLocalWires else globalWires
|
val table = if (partLocal) openLocalWires else globalWires
|
||||||
table.computeIfAbsent(group) { LinkedHashSet() }.add(geometry.wrap(Vector2i(x, y)))
|
table.computeIfAbsent(group) { LinkedHashSet() }.add(geometry.wrap(x, y))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun placeNPC(x: Int, y: Int, species: Registry.Entry<Species>, type: String, seed: Long, overrides: JsonElement = JsonNull.INSTANCE) {
|
fun placeNPC(x: Int, y: Int, species: Registry.Entry<Species>, type: String, seed: Long, overrides: JsonElement = JsonNull.INSTANCE) {
|
||||||
placedNPCs.computeIfAbsent(geometry.wrap(Vector2i(x, y))) { ArrayList() }.add(NPCData(species, type, seed, overrides))
|
placedNPCs.computeIfAbsent(geometry.wrap(x, y)) { ArrayList() }.add(NPCData(species, type, seed, overrides))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun placeMonster(x: Int, y: Int, type: Registry.Entry<MonsterTypeDefinition>, seed: Long, overrides: JsonObject = JsonObject()) {
|
fun placeMonster(x: Int, y: Int, type: Registry.Entry<MonsterTypeDefinition>, seed: Long, overrides: JsonObject = JsonObject()) {
|
||||||
placedMonsters.computeIfAbsent(geometry.wrap(Vector2i(x, y))) { ArrayList() }.add(MonsterData(type, seed, overrides))
|
placedMonsters.computeIfAbsent(geometry.wrap(x, y)) { ArrayList() }.add(MonsterData(type, seed, overrides))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun requestLiquid(x: Int, y: Int, liquid: AbstractLiquidState) {
|
fun requestLiquid(x: Int, y: Int, liquid: AbstractLiquidState) {
|
||||||
pendingLiquids[geometry.wrap(Vector2i(x, y))] = liquid
|
pendingLiquids[geometry.wrap(x, y)] = liquid
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setDungeonID(x: Int, y: Int, id: Int) {
|
fun setDungeonID(x: Int, y: Int, id: Int) {
|
||||||
dungeonIDs[geometry.wrap(Vector2i(x, y))] = id
|
dungeonIDs[geometry.wrap(x, y)] = id
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setDungeonID(x: Int, y: Int) {
|
fun setDungeonID(x: Int, y: Int) {
|
||||||
dungeonIDs.remove(geometry.wrap(Vector2i(x, y)))
|
dungeonIDs.remove(geometry.wrap(x, y))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setDungeonID(id: Int) {
|
fun setDungeonID(id: Int) {
|
||||||
@ -244,35 +244,35 @@ class DungeonWorld(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun placeBiomeTree(x: Int, y: Int) {
|
fun placeBiomeTree(x: Int, y: Int) {
|
||||||
biomeTrees.add(geometry.wrap(Vector2i(x, y)))
|
biomeTrees.add(geometry.wrap(x, y))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun placeBiomeItems(x: Int, y: Int) {
|
fun placeBiomeItems(x: Int, y: Int) {
|
||||||
biomeItems.add(geometry.wrap(Vector2i(x, y)))
|
biomeItems.add(geometry.wrap(x, y))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun dropItem(x: Int, y: Int, item: ItemDescriptor) {
|
fun dropItem(x: Int, y: Int, item: ItemDescriptor) {
|
||||||
itemDrops.computeIfAbsent(geometry.wrap(Vector2i(x, y))) { ArrayList() }.add(item)
|
itemDrops.computeIfAbsent(geometry.wrap(x, y)) { ArrayList() }.add(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun dropRandomizedItem(x: Int, y: Int, item: ItemDescriptor) {
|
fun dropRandomizedItem(x: Int, y: Int, item: ItemDescriptor) {
|
||||||
randomizedItemDrops.computeIfAbsent(geometry.wrap(Vector2i(x, y))) { ArrayList() }.add(item)
|
randomizedItemDrops.computeIfAbsent(geometry.wrap(x, y)) { ArrayList() }.add(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setLiquid(x: Int, y: Int, liquid: AbstractLiquidState) {
|
fun setLiquid(x: Int, y: Int, liquid: AbstractLiquidState) {
|
||||||
this.liquid[geometry.wrap(Vector2i(x, y))] = liquid
|
this.liquid[geometry.wrap(x, y)] = liquid
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLiquid(x: Int, y: Int): AbstractLiquidState {
|
fun getLiquid(x: Int, y: Int): AbstractLiquidState {
|
||||||
return this.liquid[geometry.wrap(Vector2i(x, y))] ?: AbstractLiquidState.EMPTY
|
return this.liquid[geometry.wrap(x, y)] ?: AbstractLiquidState.EMPTY
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setForeground(x: Int, y: Int, tile: Material) {
|
fun setForeground(x: Int, y: Int, tile: Material) {
|
||||||
this.foregroundMaterial[geometry.wrap(Vector2i(x, y))] = tile
|
this.foregroundMaterial[geometry.wrap(x, y)] = tile
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setForeground(x: Int, y: Int, tile: Modifier) {
|
fun setForeground(x: Int, y: Int, tile: Modifier) {
|
||||||
this.foregroundModifier[geometry.wrap(Vector2i(x, y))] = tile
|
this.foregroundModifier[geometry.wrap(x, y)] = tile
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setForeground(x: Int, y: Int, material: Registry.Entry<TileDefinition>, hueShift: Float = 0f, color: TileColor = TileColor.DEFAULT) {
|
fun setForeground(x: Int, y: Int, material: Registry.Entry<TileDefinition>, hueShift: Float = 0f, color: TileColor = TileColor.DEFAULT) {
|
||||||
@ -284,11 +284,11 @@ class DungeonWorld(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setBackground(x: Int, y: Int, tile: Material) {
|
fun setBackground(x: Int, y: Int, tile: Material) {
|
||||||
this.backgroundMaterial[geometry.wrap(Vector2i(x, y))] = tile
|
this.backgroundMaterial[geometry.wrap(x, y)] = tile
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setBackground(x: Int, y: Int, tile: Modifier) {
|
fun setBackground(x: Int, y: Int, tile: Modifier) {
|
||||||
this.backgroundModifier[geometry.wrap(Vector2i(x, y))] = tile
|
this.backgroundModifier[geometry.wrap(x, y)] = tile
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setBackground(x: Int, y: Int, material: Registry.Entry<TileDefinition>, hueShift: Float = 0f, color: TileColor = TileColor.DEFAULT) {
|
fun setBackground(x: Int, y: Int, material: Registry.Entry<TileDefinition>, hueShift: Float = 0f, color: TileColor = TileColor.DEFAULT) {
|
||||||
@ -313,7 +313,7 @@ class DungeonWorld(
|
|||||||
setBackgroundMaterial: Boolean = true,
|
setBackgroundMaterial: Boolean = true,
|
||||||
setBackgroundModifier: Boolean = true,
|
setBackgroundModifier: Boolean = true,
|
||||||
) {
|
) {
|
||||||
val pos = geometry.wrap(Vector2i(x, y))
|
val pos = geometry.wrap(x, y)
|
||||||
|
|
||||||
if (foreground != null) {
|
if (foreground != null) {
|
||||||
if (setForegroundMaterial)
|
if (setForegroundMaterial)
|
||||||
@ -336,7 +336,7 @@ class DungeonWorld(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun clearTile(x: Int, y: Int) {
|
fun clearTile(x: Int, y: Int) {
|
||||||
val pos = geometry.wrap(Vector2i(x, y))
|
val pos = geometry.wrap(x, y)
|
||||||
|
|
||||||
this.foregroundMaterial[pos] = emptyMaterial
|
this.foregroundMaterial[pos] = emptyMaterial
|
||||||
this.foregroundModifier[pos] = emptyModifier
|
this.foregroundModifier[pos] = emptyModifier
|
||||||
@ -348,24 +348,24 @@ class DungeonWorld(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun needsForegroundBiomeMod(x: Int, y: Int): Boolean {
|
fun needsForegroundBiomeMod(x: Int, y: Int): Boolean {
|
||||||
val pos = geometry.wrap(Vector2i(x, y))
|
val pos = geometry.wrap(x, y)
|
||||||
val material = foregroundMaterial[pos] ?: return false
|
val material = foregroundMaterial[pos] ?: return false
|
||||||
|
|
||||||
if (material.material !in BuiltinMetaMaterials.BIOME_META_MATERIALS)
|
if (material.material !in BuiltinMetaMaterials.BIOME_META_MATERIALS)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
val above = geometry.wrap(Vector2i(x, y + 1))
|
val above = geometry.wrap(x, y + 1)
|
||||||
return foregroundMaterial[above]?.material?.isNotEmptyTile == false
|
return foregroundMaterial[above]?.material?.isNotEmptyTile == false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun needsBackgroundBiomeMod(x: Int, y: Int): Boolean {
|
fun needsBackgroundBiomeMod(x: Int, y: Int): Boolean {
|
||||||
val pos = geometry.wrap(Vector2i(x, y))
|
val pos = geometry.wrap(x, y)
|
||||||
val material = backgroundMaterial[pos] ?: return false
|
val material = backgroundMaterial[pos] ?: return false
|
||||||
|
|
||||||
if (material.material !in BuiltinMetaMaterials.BIOME_META_MATERIALS)
|
if (material.material !in BuiltinMetaMaterials.BIOME_META_MATERIALS)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
val above = geometry.wrap(Vector2i(x, y + 1))
|
val above = geometry.wrap(x, y + 1)
|
||||||
return backgroundMaterial[above]?.material?.isNotEmptyTile == false
|
return backgroundMaterial[above]?.material?.isNotEmptyTile == false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
package ru.dbotthepony.kstarbound.defs.`object`
|
package ru.dbotthepony.kstarbound.defs.`object`
|
||||||
|
|
||||||
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
||||||
|
import ru.dbotthepony.kstarbound.world.entities.HumanoidActorEntity
|
||||||
|
|
||||||
// int32_t
|
// int32_t
|
||||||
enum class LoungeOrientation(override val jsonName: String) : IStringSerializable {
|
enum class LoungeOrientation(override val jsonName: String, val humanoidState: HumanoidActorEntity.HumanoidState) : IStringSerializable {
|
||||||
NONE("none"),
|
NONE("none", HumanoidActorEntity.HumanoidState.IDLE),
|
||||||
SIT("sit"),
|
SIT("sit", HumanoidActorEntity.HumanoidState.SIT),
|
||||||
LAY("lay"),
|
LAY("lay", HumanoidActorEntity.HumanoidState.LAY),
|
||||||
STAND("stand");
|
STAND("stand", HumanoidActorEntity.HumanoidState.IDLE);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package ru.dbotthepony.kstarbound.json
|
package ru.dbotthepony.kstarbound.json
|
||||||
|
|
||||||
|
import com.github.luben.zstd.ZstdInputStreamNoFinalizer
|
||||||
import com.google.gson.JsonArray
|
import com.google.gson.JsonArray
|
||||||
import com.google.gson.JsonElement
|
import com.google.gson.JsonElement
|
||||||
import com.google.gson.JsonNull
|
import com.google.gson.JsonNull
|
||||||
@ -29,31 +30,55 @@ import java.util.zip.DeflaterOutputStream
|
|||||||
import java.util.zip.Inflater
|
import java.util.zip.Inflater
|
||||||
import java.util.zip.InflaterInputStream
|
import java.util.zip.InflaterInputStream
|
||||||
|
|
||||||
private fun <T> ByteArray.callRead(inflate: Boolean, callable: DataInputStream.() -> T): T {
|
private enum class InflateType {
|
||||||
|
ZLIB,
|
||||||
|
ZSTD,
|
||||||
|
NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> ByteArray.callRead(inflate: InflateType, callable: DataInputStream.() -> T): T {
|
||||||
val stream = FastByteArrayInputStream(this)
|
val stream = FastByteArrayInputStream(this)
|
||||||
|
|
||||||
if (inflate) {
|
when (inflate) {
|
||||||
val inflater = Inflater()
|
InflateType.ZLIB -> {
|
||||||
|
val inflater = Inflater()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val data = DataInputStream(BufferedInputStream(InflaterInputStream(stream, inflater, 0x4000), 0x10000))
|
val data = DataInputStream(BufferedInputStream(InflaterInputStream(stream, inflater, 0x4000), 0x10000))
|
||||||
val t = callable(data)
|
val t = callable(data)
|
||||||
return t
|
return t
|
||||||
} finally {
|
} finally {
|
||||||
inflater.end()
|
inflater.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InflateType.ZSTD -> {
|
||||||
|
val data = DataInputStream(BufferedInputStream(ZstdInputStreamNoFinalizer(stream), 0x10000))
|
||||||
|
|
||||||
|
try {
|
||||||
|
return callable(data)
|
||||||
|
} finally {
|
||||||
|
data.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InflateType.NONE -> {
|
||||||
|
return callable(DataInputStream(stream))
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return callable(DataInputStream(stream))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ByteArray.readJsonElement(): JsonElement = callRead(false) { readJsonElement() }
|
fun ByteArray.readJsonElement(): JsonElement = callRead(InflateType.NONE) { readJsonElement() }
|
||||||
fun ByteArray.readJsonObject(): JsonObject = callRead(false) { readJsonObject() }
|
fun ByteArray.readJsonObject(): JsonObject = callRead(InflateType.NONE) { readJsonObject() }
|
||||||
fun ByteArray.readJsonArray(): JsonArray = callRead(false) { readJsonArray() }
|
fun ByteArray.readJsonArray(): JsonArray = callRead(InflateType.NONE) { readJsonArray() }
|
||||||
|
|
||||||
fun ByteArray.readJsonElementInflated(): JsonElement = callRead(true) { readJsonElement() }
|
fun ByteArray.readJsonElementInflated(): JsonElement = callRead(InflateType.ZLIB) { readJsonElement() }
|
||||||
fun ByteArray.readJsonObjectInflated(): JsonObject = callRead(true) { readJsonObject() }
|
fun ByteArray.readJsonObjectInflated(): JsonObject = callRead(InflateType.ZLIB) { readJsonObject() }
|
||||||
fun ByteArray.readJsonArrayInflated(): JsonArray = callRead(true) { readJsonArray() }
|
fun ByteArray.readJsonArrayInflated(): JsonArray = callRead(InflateType.ZLIB) { readJsonArray() }
|
||||||
|
|
||||||
|
fun ByteArray.readJsonElementZstd(): JsonElement = callRead(InflateType.ZSTD) { readJsonElement() }
|
||||||
|
fun ByteArray.readJsonObjectZstd(): JsonObject = callRead(InflateType.ZSTD) { readJsonObject() }
|
||||||
|
fun ByteArray.readJsonArrayZstd(): JsonArray = callRead(InflateType.ZSTD) { readJsonArray() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Позволяет читать двоичный JSON прямиком в [JsonElement]
|
* Позволяет читать двоичный JSON прямиком в [JsonElement]
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package ru.dbotthepony.kstarbound.json
|
package ru.dbotthepony.kstarbound.json
|
||||||
|
|
||||||
|
import com.github.luben.zstd.ZstdOutputStreamNoFinalizer
|
||||||
import com.google.gson.JsonArray
|
import com.google.gson.JsonArray
|
||||||
import com.google.gson.JsonElement
|
import com.google.gson.JsonElement
|
||||||
import com.google.gson.JsonNull
|
import com.google.gson.JsonNull
|
||||||
@ -14,31 +15,51 @@ import java.io.DataOutputStream
|
|||||||
import java.util.zip.Deflater
|
import java.util.zip.Deflater
|
||||||
import java.util.zip.DeflaterOutputStream
|
import java.util.zip.DeflaterOutputStream
|
||||||
|
|
||||||
private fun <T> T.callWrite(deflate: Boolean, callable: DataOutputStream.(T) -> Unit): ByteArray {
|
private enum class DeflateType {
|
||||||
|
ZLIB,
|
||||||
|
ZSTD,
|
||||||
|
NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> T.callWrite(deflate: DeflateType, zstdCompressionLevel: Int = 6, callable: DataOutputStream.(T) -> Unit): ByteArray {
|
||||||
val stream = FastByteArrayOutputStream()
|
val stream = FastByteArrayOutputStream()
|
||||||
|
|
||||||
if (deflate) {
|
when (deflate) {
|
||||||
val deflater = Deflater()
|
DeflateType.ZLIB -> {
|
||||||
val data = DataOutputStream(BufferedOutputStream(DeflaterOutputStream(stream, deflater, 0x4000), 0x10000))
|
val deflater = Deflater()
|
||||||
|
val data = DataOutputStream(BufferedOutputStream(DeflaterOutputStream(stream, deflater, 0x4000), 0x10000))
|
||||||
|
|
||||||
data.use {
|
data.use {
|
||||||
callable(data, this)
|
callable(data, this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
DeflateType.ZSTD -> {
|
||||||
callable(DataOutputStream(stream), this)
|
val s = ZstdOutputStreamNoFinalizer(stream)
|
||||||
|
s.setLevel(zstdCompressionLevel)
|
||||||
|
|
||||||
|
DataOutputStream(BufferedOutputStream(s, 0x10000)).use {
|
||||||
|
callable(it, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeflateType.NONE -> callable(DataOutputStream(stream), this)
|
||||||
}
|
}
|
||||||
|
|
||||||
return stream.array.copyOf(stream.length)
|
return stream.array.copyOf(stream.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun JsonElement.writeJsonElement(): ByteArray = callWrite(false) { writeJsonElement(it) }
|
fun JsonElement.writeJsonElement(): ByteArray = callWrite(DeflateType.NONE) { writeJsonElement(it) }
|
||||||
fun JsonObject.writeJsonObject(): ByteArray = callWrite(false) { writeJsonObject(it) }
|
fun JsonObject.writeJsonObject(): ByteArray = callWrite(DeflateType.NONE) { writeJsonObject(it) }
|
||||||
fun JsonArray.writeJsonArray(): ByteArray = callWrite(false) { writeJsonArray(it) }
|
fun JsonArray.writeJsonArray(): ByteArray = callWrite(DeflateType.NONE) { writeJsonArray(it) }
|
||||||
|
|
||||||
fun JsonElement.writeJsonElementDeflated(): ByteArray = callWrite(true) { writeJsonElement(it) }
|
fun JsonElement.writeJsonElementDeflated(): ByteArray = callWrite(DeflateType.ZLIB) { writeJsonElement(it) }
|
||||||
fun JsonObject.writeJsonObjectDeflated(): ByteArray = callWrite(true) { writeJsonObject(it) }
|
fun JsonObject.writeJsonObjectDeflated(): ByteArray = callWrite(DeflateType.ZLIB) { writeJsonObject(it) }
|
||||||
fun JsonArray.writeJsonArrayDeflated(): ByteArray = callWrite(true) { writeJsonArray(it) }
|
fun JsonArray.writeJsonArrayDeflated(): ByteArray = callWrite(DeflateType.ZLIB) { writeJsonArray(it) }
|
||||||
|
|
||||||
|
fun JsonElement.writeJsonElementZstd(level: Int = 6): ByteArray = callWrite(DeflateType.ZSTD, zstdCompressionLevel = level) { writeJsonElement(it) }
|
||||||
|
fun JsonObject.writeJsonObjectZstd(level: Int = 6): ByteArray = callWrite(DeflateType.ZSTD, zstdCompressionLevel = level) { writeJsonObject(it) }
|
||||||
|
fun JsonArray.writeJsonArrayZstd(level: Int = 6): ByteArray = callWrite(DeflateType.ZSTD, zstdCompressionLevel = level) { writeJsonArray(it) }
|
||||||
|
|
||||||
fun DataOutputStream.writeJsonElement(value: JsonElement) {
|
fun DataOutputStream.writeJsonElement(value: JsonElement) {
|
||||||
when (value) {
|
when (value) {
|
||||||
|
@ -102,7 +102,7 @@ fun provideNPCBindings(self: NPCEntity, lua: LuaEnvironment) {
|
|||||||
val anchorIndex = oAnchorIndex?.toInt() ?: 0
|
val anchorIndex = oAnchorIndex?.toInt() ?: 0
|
||||||
val entity = self.world.entities[loungeable.toInt()] as? LoungeableEntity
|
val entity = self.world.entities[loungeable.toInt()] as? LoungeableEntity
|
||||||
|
|
||||||
if (entity == null || anchorIndex !in 0 until entity.sitPositions.size || entity.entitiesLoungingIn(anchorIndex).isNotEmpty()) {
|
if (entity == null || anchorIndex !in 0 until entity.anchors.size || entity.entitiesLoungingIn(anchorIndex).isNotEmpty()) {
|
||||||
returnBuffer.setTo(false)
|
returnBuffer.setTo(false)
|
||||||
} else {
|
} else {
|
||||||
self.movement.anchorNetworkState = AnchorNetworkState(loungeable.toInt(), anchorIndex)
|
self.movement.anchorNetworkState = AnchorNetworkState(loungeable.toInt(), anchorIndex)
|
||||||
|
@ -3,6 +3,7 @@ package ru.dbotthepony.kstarbound.lua.bindings
|
|||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList
|
import it.unimi.dsi.fastutil.ints.IntArrayList
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import org.classdump.luna.ByteString
|
import org.classdump.luna.ByteString
|
||||||
import org.classdump.luna.LuaRuntimeException
|
import org.classdump.luna.LuaRuntimeException
|
||||||
@ -11,17 +12,22 @@ import org.classdump.luna.runtime.ExecutionContext
|
|||||||
import org.classdump.luna.runtime.LuaFunction
|
import org.classdump.luna.runtime.LuaFunction
|
||||||
import ru.dbotthepony.kommons.collect.map
|
import ru.dbotthepony.kommons.collect.map
|
||||||
import ru.dbotthepony.kommons.collect.toList
|
import ru.dbotthepony.kommons.collect.toList
|
||||||
|
import ru.dbotthepony.kommons.gson.contains
|
||||||
|
import ru.dbotthepony.kommons.gson.set
|
||||||
import ru.dbotthepony.kstarbound.Registries
|
import ru.dbotthepony.kstarbound.Registries
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
import ru.dbotthepony.kstarbound.defs.ActorMovementParameters
|
import ru.dbotthepony.kstarbound.defs.ActorMovementParameters
|
||||||
import ru.dbotthepony.kstarbound.defs.EntityType
|
import ru.dbotthepony.kstarbound.defs.EntityType
|
||||||
|
import ru.dbotthepony.kstarbound.defs.actor.NPCVariant
|
||||||
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor
|
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor
|
||||||
|
import ru.dbotthepony.kstarbound.defs.monster.MonsterVariant
|
||||||
import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials
|
import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials
|
||||||
import ru.dbotthepony.kstarbound.defs.tile.isNotEmptyLiquid
|
import ru.dbotthepony.kstarbound.defs.tile.isNotEmptyLiquid
|
||||||
import ru.dbotthepony.kstarbound.defs.tile.isNotEmptyTile
|
import ru.dbotthepony.kstarbound.defs.tile.isNotEmptyTile
|
||||||
import ru.dbotthepony.kstarbound.defs.world.TerrestrialWorldParameters
|
import ru.dbotthepony.kstarbound.defs.world.TerrestrialWorldParameters
|
||||||
import ru.dbotthepony.kstarbound.fromJsonFast
|
import ru.dbotthepony.kstarbound.fromJsonFast
|
||||||
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
||||||
|
import ru.dbotthepony.kstarbound.json.mergeJson
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||||
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
||||||
import ru.dbotthepony.kstarbound.lua.contains
|
import ru.dbotthepony.kstarbound.lua.contains
|
||||||
@ -63,7 +69,10 @@ import ru.dbotthepony.kstarbound.world.api.AbstractLiquidState
|
|||||||
import ru.dbotthepony.kstarbound.world.castRay
|
import ru.dbotthepony.kstarbound.world.castRay
|
||||||
import ru.dbotthepony.kstarbound.world.entities.AbstractEntity
|
import ru.dbotthepony.kstarbound.world.entities.AbstractEntity
|
||||||
import ru.dbotthepony.kstarbound.world.entities.ItemDropEntity
|
import ru.dbotthepony.kstarbound.world.entities.ItemDropEntity
|
||||||
|
import ru.dbotthepony.kstarbound.world.entities.MonsterEntity
|
||||||
|
import ru.dbotthepony.kstarbound.world.entities.NPCEntity
|
||||||
import ru.dbotthepony.kstarbound.world.entities.PathFinder
|
import ru.dbotthepony.kstarbound.world.entities.PathFinder
|
||||||
|
import ru.dbotthepony.kstarbound.world.entities.StagehandEntity
|
||||||
import ru.dbotthepony.kstarbound.world.entities.api.ScriptedEntity
|
import ru.dbotthepony.kstarbound.world.entities.api.ScriptedEntity
|
||||||
import ru.dbotthepony.kstarbound.world.entities.tile.WorldObject
|
import ru.dbotthepony.kstarbound.world.entities.tile.WorldObject
|
||||||
import ru.dbotthepony.kstarbound.world.physics.CollisionType
|
import ru.dbotthepony.kstarbound.world.physics.CollisionType
|
||||||
@ -430,7 +439,7 @@ fun provideWorldBindings(self: World<*, *>, lua: LuaEnvironment) {
|
|||||||
LOGGER.debug("Lua script tried to create non existing item {} at {}", itemType, pos)
|
LOGGER.debug("Lua script tried to create non existing item {} at {}", itemType, pos)
|
||||||
returnBuffer.setTo()
|
returnBuffer.setTo()
|
||||||
} else {
|
} else {
|
||||||
val create = ItemDropEntity(descriptor)
|
val create = ItemDropEntity(descriptor, lua.random)
|
||||||
create.movement.velocity = initialVelocity
|
create.movement.velocity = initialVelocity
|
||||||
|
|
||||||
if (intangibleTime is Number) {
|
if (intangibleTime is Number) {
|
||||||
@ -453,8 +462,7 @@ fun provideWorldBindings(self: World<*, *>, lua: LuaEnvironment) {
|
|||||||
val items = Registries.treasurePools
|
val items = Registries.treasurePools
|
||||||
.getOrThrow(pool.decode())
|
.getOrThrow(pool.decode())
|
||||||
.value
|
.value
|
||||||
// not using lua.random because we are, well, world's bindings
|
.evaluate(if (seed != null) random(seed.toLong()) else lua.random, level.toDouble())
|
||||||
.evaluate(if (seed != null) random(seed.toLong()) else self.random, level.toDouble())
|
|
||||||
|
|
||||||
val pos = toVector2d(position)
|
val pos = toVector2d(position)
|
||||||
|
|
||||||
@ -465,18 +473,79 @@ fun provideWorldBindings(self: World<*, *>, lua: LuaEnvironment) {
|
|||||||
entities.add(entity.entityID)
|
entities.add(entity.entityID)
|
||||||
}
|
}
|
||||||
} catch (err: Throwable) {
|
} catch (err: Throwable) {
|
||||||
LOGGER.error("Exception while spawning treasure from $pool at $position", err)
|
LOGGER.error("Exception while spawning treasure from pool '$pool' at $position", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
returnBuffer.setTo(tableOf(*entities.toTypedArray()))
|
returnBuffer.setTo(tableOf(*entities.toTypedArray()))
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["spawnMonster"] = luaFunction {
|
callbacks["spawnMonster"] = luaFunction { type: ByteString, position: Table, overrides: Any? ->
|
||||||
// TODO
|
try {
|
||||||
returnBuffer.setTo(0L)
|
val parameters = JsonObject()
|
||||||
|
parameters["aggressive"] = lua.random.nextBoolean()
|
||||||
|
|
||||||
|
if (overrides != null) {
|
||||||
|
mergeJson(parameters, toJsonFromLua(overrides))
|
||||||
|
}
|
||||||
|
|
||||||
|
val level = parameters["level"]?.asDouble ?: 1.0
|
||||||
|
val seed: Long
|
||||||
|
|
||||||
|
if ("seed" !in parameters || !parameters["seed"].asJsonPrimitive.isNumber) {
|
||||||
|
seed = lua.random.nextLong()
|
||||||
|
} else {
|
||||||
|
seed = parameters["seed"].asLong
|
||||||
|
}
|
||||||
|
|
||||||
|
val variant = Registries.monsterTypes.getOrThrow(type.decode()).value.create(seed, parameters)
|
||||||
|
val monster = MonsterEntity(variant, level)
|
||||||
|
monster.position = toVector2d(position)
|
||||||
|
monster.joinWorld(self)
|
||||||
|
returnBuffer.setTo(monster.entityID)
|
||||||
|
} catch (err: Throwable) {
|
||||||
|
LOGGER.error("Exception caught while spawning Monster type $type", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
callbacks["spawnNpc"] = luaStub("spawnNpc")
|
|
||||||
callbacks["spawnStagehand"] = luaStub("spawnStagehand")
|
callbacks["spawnNpc"] = luaFunctionN("spawnNpc") {
|
||||||
|
val position = it.nextTable()
|
||||||
|
val species = it.nextString().decode()
|
||||||
|
val type = it.nextString().decode()
|
||||||
|
val level = it.nextFloat()
|
||||||
|
val seed = it.nextOptionalInteger() ?: lua.random.nextLong()
|
||||||
|
val overrides = toJsonFromLua(it.nextOptionalAny(null))
|
||||||
|
|
||||||
|
try {
|
||||||
|
// TODO: this blocks world thread
|
||||||
|
val npc = runBlocking {
|
||||||
|
NPCEntity(NPCVariant.create(
|
||||||
|
Registries.species.getOrThrow(species),
|
||||||
|
type,
|
||||||
|
level,
|
||||||
|
seed,
|
||||||
|
overrides
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
npc.position = toVector2d(position)
|
||||||
|
npc.joinWorld(self)
|
||||||
|
returnBuffer.setTo(npc.entityID)
|
||||||
|
} catch (err: Throwable) {
|
||||||
|
LOGGER.error("Exception caught while spawning NPC $type with species $species", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["spawnStagehand"] = luaFunction { position: Table, type: ByteString, overrides: Any? ->
|
||||||
|
try {
|
||||||
|
val stagehand = StagehandEntity(type.decode(), toJsonFromLua(overrides))
|
||||||
|
stagehand.position = toVector2d(position)
|
||||||
|
stagehand.joinWorld(self)
|
||||||
|
returnBuffer.setTo(stagehand.entityID)
|
||||||
|
} catch (err: Throwable) {
|
||||||
|
LOGGER.error("Exception caught while spawning stagehand of type '$type'", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
callbacks["spawnProjectile"] = luaStub("spawnProjectile")
|
callbacks["spawnProjectile"] = luaStub("spawnProjectile")
|
||||||
callbacks["spawnVehicle"] = luaStub("spawnVehicle")
|
callbacks["spawnVehicle"] = luaStub("spawnVehicle")
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ private val centerStr = ByteString.of("center")
|
|||||||
private val boundModeStr = ByteString.of("boundMode")
|
private val boundModeStr = ByteString.of("boundMode")
|
||||||
private val orderStr = ByteString.of("order")
|
private val orderStr = ByteString.of("order")
|
||||||
|
|
||||||
private fun ExecutionContext.entityQueryImpl(self: World<*, *>, options: Table, predicate: Predicate<AbstractEntity> = Predicate { true }): Table {
|
private fun ExecutionContext.entityQueryImpl(self: World<*, *>, lua: LuaEnvironment, options: Table, predicate: Predicate<AbstractEntity> = Predicate { true }): Table {
|
||||||
val withoutEntityId = (indexNoYield(options, withoutEntityIdStr) as Number?)?.toInt()
|
val withoutEntityId = (indexNoYield(options, withoutEntityIdStr) as Number?)?.toInt()
|
||||||
|
|
||||||
val includedTypes = EnumSet.allOf(EntityType::class.java)
|
val includedTypes = EnumSet.allOf(EntityType::class.java)
|
||||||
@ -200,7 +200,7 @@ private fun ExecutionContext.entityQueryImpl(self: World<*, *>, options: Table,
|
|||||||
|
|
||||||
when (val order = (indexNoYield(options, orderStr) as ByteString?)?.decode()?.lowercase()) {
|
when (val order = (indexNoYield(options, orderStr) as ByteString?)?.decode()?.lowercase()) {
|
||||||
null -> {} // do nothing
|
null -> {} // do nothing
|
||||||
"random" -> entitites.shuffle(self.random)
|
"random" -> entitites.shuffle(lua.random)
|
||||||
"nearest" -> {
|
"nearest" -> {
|
||||||
val nearestPosition = lineQuery?.p0 ?: polyQuery?.centre ?: rectQuery?.centre ?: radiusQuery?.first ?: Vector2d.ZERO
|
val nearestPosition = lineQuery?.p0 ?: polyQuery?.centre ?: rectQuery?.centre ?: radiusQuery?.first ?: Vector2d.ZERO
|
||||||
|
|
||||||
@ -213,7 +213,7 @@ private fun ExecutionContext.entityQueryImpl(self: World<*, *>, options: Table,
|
|||||||
return tableOf(*entitites.map { it.entityID.toLong() }.toTypedArray())
|
return tableOf(*entitites.map { it.entityID.toLong() }.toTypedArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ExecutionContext.intermediateQueryFunction(self: World<*, *>, pos1: Table, pos2OrRadius: Any, options: Table?, predicate: Predicate<AbstractEntity>) {
|
private fun ExecutionContext.intermediateQueryFunction(self: World<*, *>, lua: LuaEnvironment, pos1: Table, pos2OrRadius: Any, options: Table?, predicate: Predicate<AbstractEntity>) {
|
||||||
val actualOptions = options ?: tableOf()
|
val actualOptions = options ?: tableOf()
|
||||||
|
|
||||||
if (pos2OrRadius is Number) {
|
if (pos2OrRadius is Number) {
|
||||||
@ -224,35 +224,35 @@ private fun ExecutionContext.intermediateQueryFunction(self: World<*, *>, pos1:
|
|||||||
actualOptions[rectStr] = tableOf(pos1[1L], pos1[2L], pos2OrRadius[1L], pos2OrRadius[2L])
|
actualOptions[rectStr] = tableOf(pos1[1L], pos1[2L], pos2OrRadius[1L], pos2OrRadius[2L])
|
||||||
}
|
}
|
||||||
|
|
||||||
returnBuffer.setTo(entityQueryImpl(self, actualOptions, predicate))
|
returnBuffer.setTo(entityQueryImpl(self, lua, actualOptions, predicate))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ExecutionContext.intermediateLineQueryFunction(self: World<*, *>, pos1: Table, pos2: Table, options: Table?, predicate: Predicate<AbstractEntity>) {
|
private fun ExecutionContext.intermediateLineQueryFunction(self: World<*, *>, lua: LuaEnvironment, pos1: Table, pos2: Table, options: Table?, predicate: Predicate<AbstractEntity>) {
|
||||||
val actualOptions = options ?: tableOf()
|
val actualOptions = options ?: tableOf()
|
||||||
actualOptions[lineStr] = tableOf(pos1, pos2)
|
actualOptions[lineStr] = tableOf(pos1, pos2)
|
||||||
returnBuffer.setTo(entityQueryImpl(self, actualOptions, predicate))
|
returnBuffer.setTo(entityQueryImpl(self, lua, actualOptions, predicate))
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun <reified T : AbstractEntity> createQueryFunction(self: World<*, *>) = luaFunction { pos1: Table, pos2OrRadius: Any, options: Table? ->
|
private inline fun <reified T : AbstractEntity> createQueryFunction(self: World<*, *>, lua: LuaEnvironment) = luaFunction { pos1: Table, pos2OrRadius: Any, options: Table? ->
|
||||||
intermediateQueryFunction(self, pos1, pos2OrRadius, options, Predicate { it is T })
|
intermediateQueryFunction(self, lua, pos1, pos2OrRadius, options, Predicate { it is T })
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun <reified T : AbstractEntity> createLineQueryFunction(self: World<*, *>) = luaFunction { pos1: Table, pos2: Table, options: Table? ->
|
private inline fun <reified T : AbstractEntity> createLineQueryFunction(self: World<*, *>, lua: LuaEnvironment) = luaFunction { pos1: Table, pos2: Table, options: Table? ->
|
||||||
intermediateLineQueryFunction(self, pos1, pos2, options, Predicate { it is T })
|
intermediateLineQueryFunction(self, lua, pos1, pos2, options, Predicate { it is T })
|
||||||
}
|
}
|
||||||
|
|
||||||
fun provideWorldEntitiesBindings(self: World<*, *>, callbacks: Table, lua: LuaEnvironment) {
|
fun provideWorldEntitiesBindings(self: World<*, *>, callbacks: Table, lua: LuaEnvironment) {
|
||||||
callbacks["entityQuery"] = createQueryFunction<AbstractEntity>(self)
|
callbacks["entityQuery"] = createQueryFunction<AbstractEntity>(self, lua)
|
||||||
callbacks["monsterQuery"] = createQueryFunction<AbstractEntity>(self) // TODO
|
callbacks["monsterQuery"] = createQueryFunction<AbstractEntity>(self, lua) // TODO
|
||||||
callbacks["npcQuery"] = createQueryFunction<AbstractEntity>(self) // TODO
|
callbacks["npcQuery"] = createQueryFunction<AbstractEntity>(self, lua) // TODO
|
||||||
callbacks["itemDropQuery"] = createQueryFunction<ItemDropEntity>(self)
|
callbacks["itemDropQuery"] = createQueryFunction<ItemDropEntity>(self, lua)
|
||||||
callbacks["playerQuery"] = createQueryFunction<PlayerEntity>(self)
|
callbacks["playerQuery"] = createQueryFunction<PlayerEntity>(self, lua)
|
||||||
|
|
||||||
callbacks["entityLineQuery"] = createLineQueryFunction<AbstractEntity>(self)
|
callbacks["entityLineQuery"] = createLineQueryFunction<AbstractEntity>(self, lua)
|
||||||
callbacks["monsterLineQuery"] = createLineQueryFunction<AbstractEntity>(self) // TODO
|
callbacks["monsterLineQuery"] = createLineQueryFunction<AbstractEntity>(self, lua) // TODO
|
||||||
callbacks["npcLineQuery"] = createLineQueryFunction<AbstractEntity>(self) // TODO
|
callbacks["npcLineQuery"] = createLineQueryFunction<AbstractEntity>(self, lua) // TODO
|
||||||
callbacks["itemDropLineQuery"] = createLineQueryFunction<ItemDropEntity>(self)
|
callbacks["itemDropLineQuery"] = createLineQueryFunction<ItemDropEntity>(self, lua)
|
||||||
callbacks["playerLineQuery"] = createLineQueryFunction<PlayerEntity>(self)
|
callbacks["playerLineQuery"] = createLineQueryFunction<PlayerEntity>(self, lua)
|
||||||
|
|
||||||
callbacks["objectQuery"] = luaFunction { pos1: Table, pos2OrRadius: Any, options: Table? ->
|
callbacks["objectQuery"] = luaFunction { pos1: Table, pos2OrRadius: Any, options: Table? ->
|
||||||
var objectName: String? = null
|
var objectName: String? = null
|
||||||
@ -260,7 +260,7 @@ fun provideWorldEntitiesBindings(self: World<*, *>, callbacks: Table, lua: LuaEn
|
|||||||
if (options != null)
|
if (options != null)
|
||||||
objectName = (indexNoYield(options, "name") as ByteString?)?.decode()
|
objectName = (indexNoYield(options, "name") as ByteString?)?.decode()
|
||||||
|
|
||||||
intermediateQueryFunction(self, pos1, pos2OrRadius, options, Predicate {
|
intermediateQueryFunction(self, lua, pos1, pos2OrRadius, options, Predicate {
|
||||||
it is WorldObject && (objectName == null || it.config.key == objectName)
|
it is WorldObject && (objectName == null || it.config.key == objectName)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -271,7 +271,7 @@ fun provideWorldEntitiesBindings(self: World<*, *>, callbacks: Table, lua: LuaEn
|
|||||||
if (options != null)
|
if (options != null)
|
||||||
objectName = (indexNoYield(options, "name") as ByteString?)?.decode()
|
objectName = (indexNoYield(options, "name") as ByteString?)?.decode()
|
||||||
|
|
||||||
intermediateLineQueryFunction(self, pos1, pos2, options, Predicate {
|
intermediateLineQueryFunction(self, lua, pos1, pos2, options, Predicate {
|
||||||
it is WorldObject && (objectName == null || it.config.key == objectName)
|
it is WorldObject && (objectName == null || it.config.key == objectName)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -287,7 +287,7 @@ fun provideWorldEntitiesBindings(self: World<*, *>, callbacks: Table, lua: LuaEn
|
|||||||
else -> LoungeOrientation.entries.valueOf(orientationName)
|
else -> LoungeOrientation.entries.valueOf(orientationName)
|
||||||
}
|
}
|
||||||
|
|
||||||
intermediateQueryFunction(self, pos1, pos2OrRadius, options, Predicate {
|
intermediateQueryFunction(self, lua, pos1, pos2OrRadius, options, Predicate {
|
||||||
it is LoungeableObject && (orientation == LoungeOrientation.NONE || it.sitOrientation == orientation)
|
it is LoungeableObject && (orientation == LoungeOrientation.NONE || it.sitOrientation == orientation)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -303,7 +303,7 @@ fun provideWorldEntitiesBindings(self: World<*, *>, callbacks: Table, lua: LuaEn
|
|||||||
else -> LoungeOrientation.entries.valueOf(orientationName!!)
|
else -> LoungeOrientation.entries.valueOf(orientationName!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
intermediateLineQueryFunction(self, pos1, pos2, options, Predicate {
|
intermediateLineQueryFunction(self, lua, pos1, pos2, options, Predicate {
|
||||||
it is LoungeableObject && (orientation == LoungeOrientation.NONE || it.sitOrientation == orientation)
|
it is LoungeableObject && (orientation == LoungeOrientation.NONE || it.sitOrientation == orientation)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -121,8 +121,8 @@ abstract class Connection(val side: ConnectionSide, val type: ConnectionType) :
|
|||||||
|
|
||||||
open fun bind(channel: Channel) {
|
open fun bind(channel: Channel) {
|
||||||
scope = CoroutineScope(channel.eventLoop().asCoroutineDispatcher() + SupervisorJob() + CoroutineExceptionHandler { coroutineContext, throwable ->
|
scope = CoroutineScope(channel.eventLoop().asCoroutineDispatcher() + SupervisorJob() + CoroutineExceptionHandler { coroutineContext, throwable ->
|
||||||
disconnect("Uncaught exception in one of connection' coroutines: $throwable")
|
|
||||||
LOGGER.fatal("Uncaught exception in one of $this coroutines", throwable)
|
LOGGER.fatal("Uncaught exception in one of $this coroutines", throwable)
|
||||||
|
disconnect("Uncaught exception in one of connection' coroutines: $throwable")
|
||||||
})
|
})
|
||||||
|
|
||||||
channel.config().setOption(ChannelOption.TCP_NODELAY, true)
|
channel.config().setOption(ChannelOption.TCP_NODELAY, true)
|
||||||
|
@ -28,7 +28,7 @@ class EntityInteractPacket(val request: InteractRequest, val id: UUID) : IServer
|
|||||||
|
|
||||||
override suspend fun play(connection: ServerConnection) {
|
override suspend fun play(connection: ServerConnection) {
|
||||||
if (request.target >= 0) {
|
if (request.target >= 0) {
|
||||||
connection.enqueue {
|
connection.enqueueAndSuspend {
|
||||||
connection.send(EntityInteractResultPacket((entities[request.target] as? InteractiveEntity)?.interact(request) ?: InteractAction.NONE, id, request.source))
|
connection.send(EntityInteractResultPacket((entities[request.target] as? InteractiveEntity)?.interact(request) ?: InteractAction.NONE, id, request.source))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -742,11 +742,11 @@ class ServerConnection(val server: StarboundServer, type: ConnectionType) : Conn
|
|||||||
val startingLocation = findStartingSystem() ?: return
|
val startingLocation = findStartingSystem() ?: return
|
||||||
scope.launch { shipFlightEventLoop(startingLocation.location, SystemWorldLocation.Celestial(startingLocation)) }
|
scope.launch { shipFlightEventLoop(startingLocation.location, SystemWorldLocation.Celestial(startingLocation)) }
|
||||||
} else {
|
} else {
|
||||||
/*if (context.returnWarp != null) {
|
if (context.returnWarp != null) {
|
||||||
enqueueWarp(context.returnWarp, ifFailed = WarpAlias.OwnShip)
|
enqueueWarp(context.returnWarp, ifFailed = WarpAlias.OwnShip)
|
||||||
} else {
|
} else {
|
||||||
enqueueWarp(WarpAlias.OwnShip)
|
enqueueWarp(WarpAlias.OwnShip)
|
||||||
}*/
|
}
|
||||||
|
|
||||||
enqueueWarp(WarpAction.World(WorldID.Instance("outpost")))
|
enqueueWarp(WarpAction.World(WorldID.Instance("outpost")))
|
||||||
|
|
||||||
|
@ -25,10 +25,14 @@ import ru.dbotthepony.kstarbound.defs.world.WorldTemplate
|
|||||||
import ru.dbotthepony.kstarbound.fromJson
|
import ru.dbotthepony.kstarbound.fromJson
|
||||||
import ru.dbotthepony.kstarbound.json.readJsonElement
|
import ru.dbotthepony.kstarbound.json.readJsonElement
|
||||||
import ru.dbotthepony.kstarbound.json.readJsonElementInflated
|
import ru.dbotthepony.kstarbound.json.readJsonElementInflated
|
||||||
|
import ru.dbotthepony.kstarbound.json.readJsonElementZstd
|
||||||
import ru.dbotthepony.kstarbound.json.readJsonObject
|
import ru.dbotthepony.kstarbound.json.readJsonObject
|
||||||
|
import ru.dbotthepony.kstarbound.json.readJsonObjectZstd
|
||||||
import ru.dbotthepony.kstarbound.json.writeJsonElement
|
import ru.dbotthepony.kstarbound.json.writeJsonElement
|
||||||
import ru.dbotthepony.kstarbound.json.writeJsonElementDeflated
|
import ru.dbotthepony.kstarbound.json.writeJsonElementDeflated
|
||||||
|
import ru.dbotthepony.kstarbound.json.writeJsonElementZstd
|
||||||
import ru.dbotthepony.kstarbound.json.writeJsonObject
|
import ru.dbotthepony.kstarbound.json.writeJsonObject
|
||||||
|
import ru.dbotthepony.kstarbound.json.writeJsonObjectZstd
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2i
|
import ru.dbotthepony.kstarbound.math.vector.Vector2i
|
||||||
import ru.dbotthepony.kstarbound.server.world.LegacyWorldStorage
|
import ru.dbotthepony.kstarbound.server.world.LegacyWorldStorage
|
||||||
import ru.dbotthepony.kstarbound.server.world.NativeLocalWorldStorage
|
import ru.dbotthepony.kstarbound.server.world.NativeLocalWorldStorage
|
||||||
@ -159,7 +163,7 @@ sealed class StarboundServer(val root: File) : BlockableEventLoop("Server thread
|
|||||||
|
|
||||||
lookupClientContext.executeQuery().use {
|
lookupClientContext.executeQuery().use {
|
||||||
if (it.next()) {
|
if (it.next()) {
|
||||||
it.getBytes(1).readJsonObject()
|
it.getBytes(1).readJsonObjectZstd()
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@ -168,9 +172,10 @@ sealed class StarboundServer(val root: File) : BlockableEventLoop("Server thread
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun writeClientContext(uuid: UUID, context: JsonObject): CompletableFuture<*> {
|
fun writeClientContext(uuid: UUID, context: JsonObject): CompletableFuture<*> {
|
||||||
|
val compressed = context.writeJsonObjectZstd()
|
||||||
return supplyAsync {
|
return supplyAsync {
|
||||||
writeClientContext.setString(1, uuid.toStarboundString())
|
writeClientContext.setString(1, uuid.toStarboundString())
|
||||||
writeClientContext.setBytes(2, context.writeJsonObject())
|
writeClientContext.setBytes(2, compressed)
|
||||||
writeClientContext.execute()
|
writeClientContext.execute()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -241,7 +246,7 @@ sealed class StarboundServer(val root: File) : BlockableEventLoop("Server thread
|
|||||||
|
|
||||||
lookupSystemWorld.executeQuery().use {
|
lookupSystemWorld.executeQuery().use {
|
||||||
if (it.next()) {
|
if (it.next()) {
|
||||||
it.getBytes(1).readJsonElementInflated()
|
it.getBytes(1).readJsonElementZstd()
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@ -250,11 +255,13 @@ sealed class StarboundServer(val root: File) : BlockableEventLoop("Server thread
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun writeServerWorldData(pos: Vector3i, data: JsonElement) {
|
fun writeServerWorldData(pos: Vector3i, data: JsonElement) {
|
||||||
|
val compressed = data.writeJsonElementZstd()
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
writeSystemWorld.setInt(1, pos.x)
|
writeSystemWorld.setInt(1, pos.x)
|
||||||
writeSystemWorld.setInt(2, pos.y)
|
writeSystemWorld.setInt(2, pos.y)
|
||||||
writeSystemWorld.setInt(3, pos.z)
|
writeSystemWorld.setInt(3, pos.z)
|
||||||
writeSystemWorld.setBytes(4, data.writeJsonElementDeflated())
|
writeSystemWorld.setBytes(4, compressed)
|
||||||
|
|
||||||
writeSystemWorld.execute()
|
writeSystemWorld.execute()
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package ru.dbotthepony.kstarbound.server.world
|
package ru.dbotthepony.kstarbound.server.world
|
||||||
|
|
||||||
|
import com.github.luben.zstd.ZstdInputStreamNoFinalizer
|
||||||
|
import com.github.luben.zstd.ZstdOutputStreamNoFinalizer
|
||||||
import com.google.gson.JsonArray
|
import com.google.gson.JsonArray
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap
|
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap
|
||||||
@ -19,9 +21,11 @@ import ru.dbotthepony.kstarbound.defs.EntityType
|
|||||||
import ru.dbotthepony.kstarbound.io.SQLSavepoint
|
import ru.dbotthepony.kstarbound.io.SQLSavepoint
|
||||||
import ru.dbotthepony.kstarbound.json.VersionedJson
|
import ru.dbotthepony.kstarbound.json.VersionedJson
|
||||||
import ru.dbotthepony.kstarbound.json.readJsonArrayInflated
|
import ru.dbotthepony.kstarbound.json.readJsonArrayInflated
|
||||||
|
import ru.dbotthepony.kstarbound.json.readJsonArrayZstd
|
||||||
import ru.dbotthepony.kstarbound.json.readJsonElement
|
import ru.dbotthepony.kstarbound.json.readJsonElement
|
||||||
import ru.dbotthepony.kstarbound.json.readJsonObjectInflated
|
import ru.dbotthepony.kstarbound.json.readJsonObjectInflated
|
||||||
import ru.dbotthepony.kstarbound.json.writeJsonArrayDeflated
|
import ru.dbotthepony.kstarbound.json.writeJsonArrayDeflated
|
||||||
|
import ru.dbotthepony.kstarbound.json.writeJsonArrayZstd
|
||||||
import ru.dbotthepony.kstarbound.json.writeJsonElement
|
import ru.dbotthepony.kstarbound.json.writeJsonElement
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2i
|
import ru.dbotthepony.kstarbound.math.vector.Vector2i
|
||||||
@ -140,8 +144,7 @@ class NativeLocalWorldStorage(file: File?) : WorldStorage() {
|
|||||||
val version = it.getInt(2)
|
val version = it.getInt(2)
|
||||||
val data = it.getBytes(3)
|
val data = it.getBytes(3)
|
||||||
|
|
||||||
val inflater = Inflater()
|
val stream = DataInputStream(BufferedInputStream(ZstdInputStreamNoFinalizer(FastByteArrayInputStream(data)), 0x40000))
|
||||||
val stream = DataInputStream(BufferedInputStream(InflaterInputStream(FastByteArrayInputStream(data), inflater, 0x10000), 0x40000))
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val palette = PaletteSet()
|
val palette = PaletteSet()
|
||||||
@ -159,7 +162,7 @@ class NativeLocalWorldStorage(file: File?) : WorldStorage() {
|
|||||||
|
|
||||||
return@supplyAsync ChunkCells(array as Object2DArray<out AbstractCell>, stage)
|
return@supplyAsync ChunkCells(array as Object2DArray<out AbstractCell>, stage)
|
||||||
} finally {
|
} finally {
|
||||||
inflater.end()
|
stream.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,7 +181,7 @@ class NativeLocalWorldStorage(file: File?) : WorldStorage() {
|
|||||||
|
|
||||||
readEntities.executeQuery().use {
|
readEntities.executeQuery().use {
|
||||||
if (it.next()) {
|
if (it.next()) {
|
||||||
val data = it.getBytes(1).readJsonArrayInflated()
|
val data = it.getBytes(1).readJsonArrayZstd()
|
||||||
|
|
||||||
for (entry in data) {
|
for (entry in data) {
|
||||||
val versioned = VersionedJson.fromJson(entry)
|
val versioned = VersionedJson.fromJson(entry)
|
||||||
@ -228,16 +231,19 @@ class NativeLocalWorldStorage(file: File?) : WorldStorage() {
|
|||||||
return executor.supplyAsync {
|
return executor.supplyAsync {
|
||||||
val (version, metadata) = readMetadata("metadata") ?: return@supplyAsync null
|
val (version, metadata) = readMetadata("metadata") ?: return@supplyAsync null
|
||||||
|
|
||||||
val stream = DataInputStream(BufferedInputStream(InflaterInputStream(FastByteArrayInputStream(metadata))))
|
val stream = DataInputStream(BufferedInputStream(ZstdInputStreamNoFinalizer(FastByteArrayInputStream(metadata))))
|
||||||
|
|
||||||
val width = stream.readInt()
|
try {
|
||||||
val height = stream.readInt()
|
val width = stream.readInt()
|
||||||
val loopX = stream.readBoolean()
|
val height = stream.readInt()
|
||||||
val loopY = stream.readBoolean()
|
val loopX = stream.readBoolean()
|
||||||
val json = VersionedJson("WorldMetadata", version, stream.readJsonElement())
|
val loopY = stream.readBoolean()
|
||||||
|
val json = VersionedJson("WorldMetadata", version, stream.readJsonElement())
|
||||||
|
|
||||||
stream.close()
|
Metadata(WorldGeometry(Vector2i(width, height), loopX, loopY), json)
|
||||||
Metadata(WorldGeometry(Vector2i(width, height), loopX, loopY), json)
|
} finally {
|
||||||
|
stream.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,7 +292,7 @@ class NativeLocalWorldStorage(file: File?) : WorldStorage() {
|
|||||||
|
|
||||||
writeEntities.setInt(1, pos.x)
|
writeEntities.setInt(1, pos.x)
|
||||||
writeEntities.setInt(2, pos.y)
|
writeEntities.setInt(2, pos.y)
|
||||||
writeEntities.setBytes(3, storeData.writeJsonArrayDeflated())
|
writeEntities.setBytes(3, storeData.writeJsonArrayZstd())
|
||||||
writeEntities.execute()
|
writeEntities.execute()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -380,9 +386,10 @@ class NativeLocalWorldStorage(file: File?) : WorldStorage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val deflater = Deflater()
|
|
||||||
val buff = FastByteArrayOutputStream()
|
val buff = FastByteArrayOutputStream()
|
||||||
val stream = DataOutputStream(BufferedOutputStream(DeflaterOutputStream(buff, deflater, 0x10000), 0x40000))
|
val z = ZstdOutputStreamNoFinalizer(buff)
|
||||||
|
z.setLevel(6)
|
||||||
|
val stream = DataOutputStream(BufferedOutputStream(z, 0x40000))
|
||||||
|
|
||||||
try {
|
try {
|
||||||
palette.write(stream)
|
palette.write(stream)
|
||||||
@ -405,7 +412,7 @@ class NativeLocalWorldStorage(file: File?) : WorldStorage() {
|
|||||||
writeCells.setBytes(5, buff.array.copyOf(buff.length))
|
writeCells.setBytes(5, buff.array.copyOf(buff.length))
|
||||||
writeCells.execute()
|
writeCells.execute()
|
||||||
} finally {
|
} finally {
|
||||||
deflater.end()
|
z.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -413,16 +420,23 @@ class NativeLocalWorldStorage(file: File?) : WorldStorage() {
|
|||||||
override fun saveMetadata(data: Metadata) {
|
override fun saveMetadata(data: Metadata) {
|
||||||
executor.execute {
|
executor.execute {
|
||||||
val buff = FastByteArrayOutputStream()
|
val buff = FastByteArrayOutputStream()
|
||||||
val stream = DataOutputStream(BufferedOutputStream(DeflaterOutputStream(buff, Deflater(), 0x10000), 0x40000))
|
val z = ZstdOutputStreamNoFinalizer(buff)
|
||||||
|
z.setLevel(6)
|
||||||
|
|
||||||
stream.writeInt(data.geometry.size.x)
|
try {
|
||||||
stream.writeInt(data.geometry.size.y)
|
val stream = DataOutputStream(BufferedOutputStream(z, 0x40000))
|
||||||
stream.writeBoolean(data.geometry.loopX)
|
|
||||||
stream.writeBoolean(data.geometry.loopY)
|
|
||||||
stream.writeJsonElement(data.data.content)
|
|
||||||
|
|
||||||
stream.close()
|
stream.writeInt(data.geometry.size.x)
|
||||||
writeMetadata("metadata", data.data.version ?: 0, buff.array.copyOf(buff.length))
|
stream.writeInt(data.geometry.size.y)
|
||||||
|
stream.writeBoolean(data.geometry.loopX)
|
||||||
|
stream.writeBoolean(data.geometry.loopY)
|
||||||
|
stream.writeJsonElement(data.data.content)
|
||||||
|
|
||||||
|
stream.close()
|
||||||
|
writeMetadata("metadata", data.data.version ?: 0, buff.array.copyOf(buff.length))
|
||||||
|
} finally {
|
||||||
|
z.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,19 +8,15 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
|||||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
|
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.asCoroutineDispatcher
|
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.cancel
|
import kotlinx.coroutines.cancel
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.future.asCompletableFuture
|
import kotlinx.coroutines.future.asCompletableFuture
|
||||||
import kotlinx.coroutines.future.await
|
import kotlinx.coroutines.future.await
|
||||||
import ru.dbotthepony.kommons.gson.JsonArrayCollector
|
import ru.dbotthepony.kommons.gson.JsonArrayCollector
|
||||||
import ru.dbotthepony.kommons.gson.contains
|
import ru.dbotthepony.kommons.gson.contains
|
||||||
import ru.dbotthepony.kommons.gson.get
|
import ru.dbotthepony.kommons.gson.get
|
||||||
import ru.dbotthepony.kommons.gson.set
|
import ru.dbotthepony.kommons.gson.set
|
||||||
import ru.dbotthepony.kstarbound.math.AABBi
|
|
||||||
import ru.dbotthepony.kommons.util.KOptional
|
import ru.dbotthepony.kommons.util.KOptional
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2i
|
|
||||||
import ru.dbotthepony.kstarbound.Globals
|
import ru.dbotthepony.kstarbound.Globals
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
import ru.dbotthepony.kstarbound.defs.world.CelestialBaseInformation
|
import ru.dbotthepony.kstarbound.defs.world.CelestialBaseInformation
|
||||||
@ -30,11 +26,13 @@ import ru.dbotthepony.kstarbound.fromJson
|
|||||||
import ru.dbotthepony.kstarbound.io.BTreeDB5
|
import ru.dbotthepony.kstarbound.io.BTreeDB5
|
||||||
import ru.dbotthepony.kstarbound.json.jsonArrayOf
|
import ru.dbotthepony.kstarbound.json.jsonArrayOf
|
||||||
import ru.dbotthepony.kstarbound.json.mergeJson
|
import ru.dbotthepony.kstarbound.json.mergeJson
|
||||||
import ru.dbotthepony.kstarbound.json.readJsonArrayInflated
|
import ru.dbotthepony.kstarbound.json.readJsonArrayZstd
|
||||||
import ru.dbotthepony.kstarbound.json.readJsonElementInflated
|
import ru.dbotthepony.kstarbound.json.readJsonElementZstd
|
||||||
import ru.dbotthepony.kstarbound.json.writeJsonArrayDeflated
|
import ru.dbotthepony.kstarbound.json.writeJsonArrayZstd
|
||||||
import ru.dbotthepony.kstarbound.json.writeJsonElementDeflated
|
import ru.dbotthepony.kstarbound.json.writeJsonElementZstd
|
||||||
|
import ru.dbotthepony.kstarbound.math.AABBi
|
||||||
import ru.dbotthepony.kstarbound.math.Line2d
|
import ru.dbotthepony.kstarbound.math.Line2d
|
||||||
|
import ru.dbotthepony.kstarbound.math.vector.Vector2i
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector3i
|
import ru.dbotthepony.kstarbound.math.vector.Vector3i
|
||||||
import ru.dbotthepony.kstarbound.util.CarriedExecutor
|
import ru.dbotthepony.kstarbound.util.CarriedExecutor
|
||||||
import ru.dbotthepony.kstarbound.util.ScheduledCoroutineExecutor
|
import ru.dbotthepony.kstarbound.util.ScheduledCoroutineExecutor
|
||||||
@ -57,11 +55,9 @@ import java.sql.ResultSet
|
|||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import java.util.function.Consumer
|
|
||||||
import java.util.function.Function
|
import java.util.function.Function
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
import java.util.random.RandomGenerator
|
import java.util.random.RandomGenerator
|
||||||
import kotlin.collections.ArrayList
|
|
||||||
|
|
||||||
class ServerUniverse(folder: File? = null) : Universe(), Closeable {
|
class ServerUniverse(folder: File? = null) : Universe(), Closeable {
|
||||||
override val baseInformation: CelestialBaseInformation
|
override val baseInformation: CelestialBaseInformation
|
||||||
@ -148,8 +144,8 @@ class ServerUniverse(folder: File? = null) : Universe(), Closeable {
|
|||||||
private data class Chunk(val x: Int, val y: Int, val systems: Set<Vector3i>, val constellations: Set<Pair<Vector2i, Vector2i>>) {
|
private data class Chunk(val x: Int, val y: Int, val systems: Set<Vector3i>, val constellations: Set<Pair<Vector2i, Vector2i>>) {
|
||||||
constructor(x: Int, y: Int, data: ResultSet) : this(
|
constructor(x: Int, y: Int, data: ResultSet) : this(
|
||||||
x, y,
|
x, y,
|
||||||
data.getBytes(1).readJsonArrayInflated().map { Vector3i(it.asJsonArray[0].asInt, it.asJsonArray[1].asInt, it.asJsonArray[2].asInt) }.toSet(),
|
data.getBytes(1).readJsonArrayZstd().map { Vector3i(it.asJsonArray[0].asInt, it.asJsonArray[1].asInt, it.asJsonArray[2].asInt) }.toSet(),
|
||||||
data.getBytes(2).readJsonArrayInflated().map {
|
data.getBytes(2).readJsonArrayZstd().map {
|
||||||
val a = it.asJsonArray[0].asJsonArray
|
val a = it.asJsonArray[0].asJsonArray
|
||||||
val b = it.asJsonArray[1].asJsonArray
|
val b = it.asJsonArray[1].asJsonArray
|
||||||
Vector2i(a[0].asInt, a[1].asInt) to Vector2i(b[0].asInt, b[1].asInt)
|
Vector2i(a[0].asInt, a[1].asInt) to Vector2i(b[0].asInt, b[1].asInt)
|
||||||
@ -162,10 +158,10 @@ class ServerUniverse(folder: File? = null) : Universe(), Closeable {
|
|||||||
systems.stream()
|
systems.stream()
|
||||||
.map { jsonArrayOf(it.x, it.y, it.z) }
|
.map { jsonArrayOf(it.x, it.y, it.z) }
|
||||||
.collect(JsonArrayCollector)
|
.collect(JsonArrayCollector)
|
||||||
.writeJsonArrayDeflated(),
|
.writeJsonArrayZstd(4),
|
||||||
constellations.stream().map {
|
constellations.stream().map {
|
||||||
jsonArrayOf(jsonArrayOf(it.first.x, it.first.y), jsonArrayOf(it.second.x, it.second.y))
|
jsonArrayOf(jsonArrayOf(it.first.x, it.first.y), jsonArrayOf(it.second.x, it.second.y))
|
||||||
}.collect(JsonArrayCollector).writeJsonArrayDeflated()
|
}.collect(JsonArrayCollector).writeJsonArrayZstd(4)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,10 +194,10 @@ class ServerUniverse(folder: File? = null) : Universe(), Closeable {
|
|||||||
fun serialize(): SerializedSystem {
|
fun serialize(): SerializedSystem {
|
||||||
return SerializedSystem(
|
return SerializedSystem(
|
||||||
x, y, z,
|
x, y, z,
|
||||||
Starbound.gson.toJsonTree(parameters).writeJsonElementDeflated(),
|
Starbound.gson.toJsonTree(parameters).writeJsonElementZstd(8),
|
||||||
planets.entries.stream()
|
planets.entries.stream()
|
||||||
.map { jsonArrayOf(it.key.first, it.key.second, it.value) }
|
.map { jsonArrayOf(it.key.first, it.key.second, it.value) }
|
||||||
.collect(JsonArrayCollector).writeJsonArrayDeflated()
|
.collect(JsonArrayCollector).writeJsonArrayZstd(8)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,9 +266,9 @@ class ServerUniverse(folder: File? = null) : Universe(), Closeable {
|
|||||||
|
|
||||||
// deserialize in off-thread since it involves big json structures
|
// deserialize in off-thread since it involves big json structures
|
||||||
Starbound.EXECUTOR.supplyAsync {
|
Starbound.EXECUTOR.supplyAsync {
|
||||||
val parameters: CelestialParameters = Starbound.gson.fromJson(parametersBytes.readJsonElementInflated())!!
|
val parameters: CelestialParameters = Starbound.gson.fromJson(parametersBytes.readJsonElementZstd())!!
|
||||||
|
|
||||||
val planets: Map<Pair<Int, Int>, CelestialParameters> = planetsBytes.readJsonArrayInflated().associate {
|
val planets: Map<Pair<Int, Int>, CelestialParameters> = planetsBytes.readJsonArrayZstd().associate {
|
||||||
it as JsonArray
|
it as JsonArray
|
||||||
(it[0].asInt to it[1].asInt) to Starbound.gson.fromJson(it[2])!!
|
(it[0].asInt to it[1].asInt) to Starbound.gson.fromJson(it[2])!!
|
||||||
}
|
}
|
||||||
|
@ -474,9 +474,9 @@ class ServerWorld private constructor(
|
|||||||
|
|
||||||
if (updated) {
|
if (updated) {
|
||||||
if (breathable != null) {
|
if (breathable != null) {
|
||||||
LOGGER.info("Dungeon ID $id breathable set to: $breathable")
|
LOGGER.info("Dungeon ID $id 'breathable' set to: $breathable")
|
||||||
} else {
|
} else {
|
||||||
LOGGER.info("Dungeon ID $id no longer has special breathable flag")
|
LOGGER.info("Dungeon ID $id no longer has special 'breathable' flag")
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast(UpdateDungeonBreathablePacket(id, breathable))
|
broadcast(UpdateDungeonBreathablePacket(id, breathable))
|
||||||
@ -716,7 +716,7 @@ class ServerWorld private constructor(
|
|||||||
var currentDungeonID = 0
|
var currentDungeonID = 0
|
||||||
|
|
||||||
// primary dungeons must be generated sequentially since they can get very large
|
// primary dungeons must be generated sequentially since they can get very large
|
||||||
// and to avoid dungeons colliding with each other, we must generate them first, one by one
|
// and to avoid dungeons colliding with each other, we must generate them one after another
|
||||||
for (dungeon in template.gatherDungeons()) {
|
for (dungeon in template.gatherDungeons()) {
|
||||||
var spawnDungeonRetries = Globals.worldServer.spawnDungeonRetries
|
var spawnDungeonRetries = Globals.worldServer.spawnDungeonRetries
|
||||||
|
|
||||||
@ -751,18 +751,12 @@ class ServerWorld private constructor(
|
|||||||
protectedDungeonIDsInternal.add(currentDungeonID)
|
protectedDungeonIDsInternal.add(currentDungeonID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dungeon.dungeon.value.metadata.gravity != null) {
|
dungeon.dungeon.value.metadata.gravity?.map({ setDungeonGravity(currentDungeonID, it) }, { setDungeonGravity(currentDungeonID, it) })
|
||||||
// TODO: set gravity here
|
setDungeonBreathable(currentDungeonID, dungeon.dungeon.value.metadata.breathable)
|
||||||
}
|
|
||||||
|
|
||||||
if (dungeon.dungeon.value.metadata.breathable != null) {
|
|
||||||
// TODO: set "breathable" here
|
|
||||||
}
|
|
||||||
|
|
||||||
currentDungeonID++
|
currentDungeonID++
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} while (--spawnDungeonRetries > 0)
|
} while (spawnDungeonRetries-- > 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -670,6 +670,8 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
|
|||||||
ticks++
|
ticks++
|
||||||
simulationTime += delta
|
simulationTime += delta
|
||||||
|
|
||||||
|
// TODO: Zombie entity handling (don't tick entities ending up in partially loaded chunks,
|
||||||
|
// or chunks which border with unloaded/not sufficiently loaded regions)
|
||||||
if (entityList.size < 128) {
|
if (entityList.size < 128) {
|
||||||
entityList.forEach { it.tickParallel(delta) }
|
entityList.forEach { it.tickParallel(delta) }
|
||||||
} else {
|
} else {
|
||||||
|
@ -40,10 +40,40 @@ data class WorldGeometry(val size: Vector2i, val loopX: Boolean = true, val loop
|
|||||||
return Vector2i(x.cell(pos.component1()), y.cell(pos.component2()))
|
return Vector2i(x.cell(pos.component1()), y.cell(pos.component2()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun wrap(pos: Vector2i): Vector2i {
|
||||||
|
val x = this.x.cell(pos.x)
|
||||||
|
val y = this.y.cell(pos.y)
|
||||||
|
|
||||||
|
// avoid allocating heap garbage
|
||||||
|
if (x == pos.x && y == pos.y)
|
||||||
|
return pos
|
||||||
|
|
||||||
|
return Vector2i(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun wrap(x: Int, y: Int): Vector2i {
|
||||||
|
return Vector2i(this.x.cell(x), this.y.cell(y))
|
||||||
|
}
|
||||||
|
|
||||||
fun wrap(pos: IStruct2d): Vector2d {
|
fun wrap(pos: IStruct2d): Vector2d {
|
||||||
return Vector2d(x.cell(pos.component1()), y.cell(pos.component2()))
|
return Vector2d(x.cell(pos.component1()), y.cell(pos.component2()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun wrap(pos: Vector2d): Vector2d {
|
||||||
|
val x = this.x.cell(pos.x)
|
||||||
|
val y = this.y.cell(pos.y)
|
||||||
|
|
||||||
|
// avoid allocating heap garbage
|
||||||
|
if (x == pos.x && y == pos.y)
|
||||||
|
return pos
|
||||||
|
|
||||||
|
return Vector2d(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun wrap(x: Double, y: Double): Vector2d {
|
||||||
|
return Vector2d(this.x.cell(x), this.y.cell(y))
|
||||||
|
}
|
||||||
|
|
||||||
fun chunkFromCell(pos: IStruct2i): ChunkPos {
|
fun chunkFromCell(pos: IStruct2i): ChunkPos {
|
||||||
return ChunkPos(x.chunkFromCell(pos.component1()), y.chunkFromCell(pos.component2()))
|
return ChunkPos(x.chunkFromCell(pos.component1()), y.chunkFromCell(pos.component2()))
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ class ActorMovementController() : MovementController() {
|
|||||||
|
|
||||||
if (networkState != null) {
|
if (networkState != null) {
|
||||||
val entity = world.entities[networkState.entityID] as? LoungeableEntity ?: throw InvalidArgumentException("${networkState.entityID} is not a valid entity or not a loungeable one")
|
val entity = world.entities[networkState.entityID] as? LoungeableEntity ?: throw InvalidArgumentException("${networkState.entityID} is not a valid entity or not a loungeable one")
|
||||||
state = entity.anchor(networkState.positionIndex) ?: throw IllegalArgumentException("Anchor with index ${networkState.positionIndex} of $entity is either invalid or disabled")
|
state = entity.anchors.getOrNull(networkState.positionIndex) ?: throw IllegalArgumentException("Anchor with index ${networkState.positionIndex} of $entity is either invalid or disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == null && this.anchorState?.exitBottomPosition != null) {
|
if (state == null && this.anchorState?.exitBottomPosition != null) {
|
||||||
@ -344,7 +344,7 @@ class ActorMovementController() : MovementController() {
|
|||||||
if (anchorNetworkState == null)
|
if (anchorNetworkState == null)
|
||||||
this.anchorState = null
|
this.anchorState = null
|
||||||
else
|
else
|
||||||
this.anchorState = (world.entities[anchorNetworkState.entityID] as? LoungeableEntity)?.anchor(anchorNetworkState.positionIndex)
|
this.anchorState = (world.entities[anchorNetworkState.entityID] as? LoungeableEntity)?.anchors?.getOrNull(anchorNetworkState.positionIndex)
|
||||||
|
|
||||||
super.tickRemote(delta)
|
super.tickRemote(delta)
|
||||||
}
|
}
|
||||||
@ -355,7 +355,7 @@ class ActorMovementController() : MovementController() {
|
|||||||
val anchorNetworkState = anchorNetworkState
|
val anchorNetworkState = anchorNetworkState
|
||||||
|
|
||||||
if (anchorNetworkState != null) {
|
if (anchorNetworkState != null) {
|
||||||
state = (world.entities[anchorNetworkState.entityID] as? LoungeableEntity)?.anchor(anchorNetworkState.positionIndex)
|
state = (world.entities[anchorNetworkState.entityID] as? LoungeableEntity)?.anchors?.getOrNull(anchorNetworkState.positionIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == null) {
|
if (state == null) {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package ru.dbotthepony.kstarbound.world.entities
|
package ru.dbotthepony.kstarbound.world.entities
|
||||||
|
|
||||||
import com.google.gson.JsonElement
|
import com.google.gson.JsonElement
|
||||||
import com.google.gson.JsonObject
|
|
||||||
import ru.dbotthepony.kstarbound.client.render.RenderLayer
|
import ru.dbotthepony.kstarbound.client.render.RenderLayer
|
||||||
import ru.dbotthepony.kstarbound.defs.actor.PersistentStatusEffect
|
import ru.dbotthepony.kstarbound.defs.actor.PersistentStatusEffect
|
||||||
|
import ru.dbotthepony.kstarbound.defs.`object`.LoungeOrientation
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||||
import ru.dbotthepony.kstarbound.world.Direction
|
import ru.dbotthepony.kstarbound.world.Direction
|
||||||
|
|
||||||
@ -21,23 +21,15 @@ data class AnchorState(
|
|||||||
override val angle: Double
|
override val angle: Double
|
||||||
) : IAnchorState
|
) : IAnchorState
|
||||||
|
|
||||||
// int32_t
|
|
||||||
enum class LoungeOrientation(val humanoidState: HumanoidActorEntity.HumanoidState) {
|
|
||||||
NONE(HumanoidActorEntity.HumanoidState.IDLE),
|
|
||||||
SIT(HumanoidActorEntity.HumanoidState.SIT),
|
|
||||||
LAY(HumanoidActorEntity.HumanoidState.LAY),
|
|
||||||
STAND(HumanoidActorEntity.HumanoidState.IDLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
data class LoungeAnchorState(
|
data class LoungeAnchorState(
|
||||||
override val position: Vector2d,
|
override val position: Vector2d,
|
||||||
override val exitBottomPosition: Vector2d?,
|
override val exitBottomPosition: Vector2d?,
|
||||||
override val direction: Direction,
|
override val direction: Direction,
|
||||||
override val angle: Double,
|
override val angle: Double,
|
||||||
val orientation: LoungeOrientation = LoungeOrientation.NONE,
|
val orientation: LoungeOrientation = LoungeOrientation.NONE,
|
||||||
val loungeRenderLayer: RenderLayer,
|
val loungeRenderLayer: RenderLayer.Point,
|
||||||
val controllable: Boolean = false,
|
val controllable: Boolean = false,
|
||||||
val statusEffects: List<PersistentStatusEffect> = listOf(),
|
val statusEffects: Set<PersistentStatusEffect> = setOf(),
|
||||||
val effectEmitters: Set<String> = setOf(),
|
val effectEmitters: Set<String> = setOf(),
|
||||||
val emote: String? = null,
|
val emote: String? = null,
|
||||||
val dance: String? = null,
|
val dance: String? = null,
|
||||||
|
@ -30,6 +30,7 @@ import ru.dbotthepony.kstarbound.world.physics.Poly
|
|||||||
import java.io.DataInputStream
|
import java.io.DataInputStream
|
||||||
import java.io.DataOutputStream
|
import java.io.DataOutputStream
|
||||||
import java.util.function.Predicate
|
import java.util.function.Predicate
|
||||||
|
import java.util.random.RandomGenerator
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
class ItemDropEntity() : DynamicEntity() {
|
class ItemDropEntity() : DynamicEntity() {
|
||||||
@ -70,8 +71,8 @@ class ItemDropEntity() : DynamicEntity() {
|
|||||||
movement.applyParameters(MovementParameters(collisionPoly = Either.left(Poly(AABB(Vector2d(-0.5, -0.5), Vector2d(0.5, 0.5))))))
|
movement.applyParameters(MovementParameters(collisionPoly = Either.left(Poly(AABB(Vector2d(-0.5, -0.5), Vector2d(0.5, 0.5))))))
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(item: ItemDescriptor) : this() {
|
constructor(item: ItemDescriptor, random: RandomGenerator? = null) : this() {
|
||||||
this.item = item.build()
|
this.item = item.build(random = random)
|
||||||
this.owningEntity = 0
|
this.owningEntity = 0
|
||||||
this.state = State.AVAILABLE
|
this.state = State.AVAILABLE
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,152 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.world.entities
|
||||||
|
|
||||||
|
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 ru.dbotthepony.kommons.gson.contains
|
||||||
|
import ru.dbotthepony.kommons.gson.get
|
||||||
|
import ru.dbotthepony.kommons.gson.set
|
||||||
|
import ru.dbotthepony.kommons.util.getValue
|
||||||
|
import ru.dbotthepony.kommons.util.setValue
|
||||||
|
import ru.dbotthepony.kstarbound.Registries
|
||||||
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
|
import ru.dbotthepony.kstarbound.defs.AssetPath
|
||||||
|
import ru.dbotthepony.kstarbound.defs.EntityType
|
||||||
|
import ru.dbotthepony.kstarbound.fromJsonFast
|
||||||
|
import ru.dbotthepony.kstarbound.json.jsonArrayOf
|
||||||
|
import ru.dbotthepony.kstarbound.json.putAll
|
||||||
|
import ru.dbotthepony.kstarbound.json.readJsonElement
|
||||||
|
import ru.dbotthepony.kstarbound.json.writeJsonElement
|
||||||
|
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
||||||
|
import ru.dbotthepony.kstarbound.lua.LuaUpdateComponent
|
||||||
|
import ru.dbotthepony.kstarbound.lua.from
|
||||||
|
import ru.dbotthepony.kstarbound.lua.get
|
||||||
|
import ru.dbotthepony.kstarbound.lua.set
|
||||||
|
import ru.dbotthepony.kstarbound.lua.tableOf
|
||||||
|
import ru.dbotthepony.kstarbound.lua.toJsonFromLua
|
||||||
|
import ru.dbotthepony.kstarbound.math.AABB
|
||||||
|
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||||
|
import ru.dbotthepony.kstarbound.network.syncher.networkedFloat
|
||||||
|
import ru.dbotthepony.kstarbound.world.World
|
||||||
|
import ru.dbotthepony.kstarbound.world.entities.api.ScriptedEntity
|
||||||
|
import java.io.DataInputStream
|
||||||
|
import java.io.DataOutputStream
|
||||||
|
|
||||||
|
class StagehandEntity(isRemote: Boolean = false) : AbstractEntity(), ScriptedEntity {
|
||||||
|
constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readJsonElement().asJsonObject, true)
|
||||||
|
|
||||||
|
constructor(data: JsonObject, isRemote: Boolean = false) : this(isRemote) {
|
||||||
|
deserialize(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(type: String, overrides: JsonElement? = JsonNull.INSTANCE) : this(Registries.makeStagehandConfig(type, overrides))
|
||||||
|
|
||||||
|
var xPosition by networkedFloat().also { networkGroup.upstream.add(it) }
|
||||||
|
var yPosition by networkedFloat().also { networkGroup.upstream.add(it) }
|
||||||
|
|
||||||
|
init {
|
||||||
|
networkGroup.upstream.add(uniqueID)
|
||||||
|
this.isRemote = isRemote
|
||||||
|
}
|
||||||
|
|
||||||
|
override var position: Vector2d
|
||||||
|
get() = Vector2d(xPosition, yPosition)
|
||||||
|
set(value) {
|
||||||
|
xPosition = value.x
|
||||||
|
yPosition = value.y
|
||||||
|
}
|
||||||
|
|
||||||
|
override val type: EntityType
|
||||||
|
get() = EntityType.STAGEHAND
|
||||||
|
|
||||||
|
private var config: JsonObject = JsonObject()
|
||||||
|
|
||||||
|
var isScripted = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
val lua = LuaEnvironment()
|
||||||
|
val luaUpdate = LuaUpdateComponent(lua)
|
||||||
|
|
||||||
|
init {
|
||||||
|
lua.globals["storage"] = lua.tableOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserialize(data: JsonObject) {
|
||||||
|
super.deserialize(data)
|
||||||
|
|
||||||
|
config = data.deepCopy()
|
||||||
|
isScripted = config["scripts"] is JsonArray
|
||||||
|
|
||||||
|
if ("position" in config) {
|
||||||
|
val pos = config["position"].asJsonArray
|
||||||
|
xPosition = pos[0].asDouble
|
||||||
|
yPosition = pos[1].asDouble
|
||||||
|
}
|
||||||
|
|
||||||
|
var broadcastArea: AABB? = null
|
||||||
|
|
||||||
|
if ("broadcastArea" in config) {
|
||||||
|
broadcastArea = Starbound.gson.fromJsonFast(config["broadcastArena"], AABB::class.java)
|
||||||
|
|
||||||
|
if (broadcastArea.width < 0.0 || broadcastArea.height > 0.0)
|
||||||
|
broadcastArea = null
|
||||||
|
}
|
||||||
|
|
||||||
|
boundingBox = broadcastArea ?: AABB.withSide(Vector2d.ZERO, 5.0)
|
||||||
|
|
||||||
|
if (isScripted && isLocal) {
|
||||||
|
lua.attach(config["scripts"].asJsonArray.map { AssetPath(it.asString) })
|
||||||
|
luaUpdate.stepCount = config.get("scriptDelta", 5.0)
|
||||||
|
|
||||||
|
if ("scriptStorage" in config) {
|
||||||
|
lua.globals["storage"] = lua.from(config["scriptStorage"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onJoinWorld(world: World<*, *>) {
|
||||||
|
super.onJoinWorld(world)
|
||||||
|
|
||||||
|
if (isLocal && isScripted) {
|
||||||
|
lua.init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(data: JsonObject) {
|
||||||
|
super.serialize(data)
|
||||||
|
|
||||||
|
data.putAll(config, true)
|
||||||
|
data["position"] = jsonArrayOf(JsonPrimitive(xPosition), JsonPrimitive(yPosition))
|
||||||
|
|
||||||
|
if (uniqueID.get() != null)
|
||||||
|
data["uniqueId"] = uniqueID.get()!!
|
||||||
|
else
|
||||||
|
data.remove("uniqueId")
|
||||||
|
|
||||||
|
if (isScripted)
|
||||||
|
data["scriptStorage"] = toJsonFromLua(lua.globals["storage"])
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeNetwork(stream: DataOutputStream, isLegacy: Boolean) {
|
||||||
|
stream.writeJsonElement(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var boundingBox = AABB.withSide(Vector2d.ZERO, 5.0)
|
||||||
|
|
||||||
|
override val metaBoundingBox: AABB
|
||||||
|
get() = boundingBox + position
|
||||||
|
override val name: String
|
||||||
|
get() = "Stagehand"
|
||||||
|
override val description: String
|
||||||
|
get() = "Technical entity responsible for doing cool stuff"
|
||||||
|
|
||||||
|
override fun callScript(fnName: String, vararg arguments: Any?): Array<Any?> {
|
||||||
|
return lua.call(fnName, *arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun evalScript(code: String): Array<Any?> {
|
||||||
|
return lua.eval(code)
|
||||||
|
}
|
||||||
|
}
|
@ -318,7 +318,7 @@ class StatusController(val entity: ActorEntity, val config: StatusControllerConf
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun deserialize(data: SerializedData) {
|
fun deserialize(data: SerializedData) {
|
||||||
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----- Persistent status effects
|
// ----- Persistent status effects
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
package ru.dbotthepony.kstarbound.world.entities.api
|
package ru.dbotthepony.kstarbound.world.entities.api
|
||||||
|
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
|
||||||
import ru.dbotthepony.kstarbound.world.entities.ActorEntity
|
import ru.dbotthepony.kstarbound.world.entities.ActorEntity
|
||||||
import ru.dbotthepony.kstarbound.world.entities.IAnchorState
|
import ru.dbotthepony.kstarbound.world.entities.IAnchorState
|
||||||
|
|
||||||
interface LoungeableEntity {
|
interface LoungeableEntity {
|
||||||
val sitPositions: List<Vector2d>
|
val anchors: List<IAnchorState>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines entities currently lounging in this entity
|
||||||
|
*/
|
||||||
|
fun entitiesLoungingIn(): List<ActorEntity>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines entities currently lounging at specified anchor index
|
* Determines entities currently lounging at specified anchor index
|
||||||
*/
|
*/
|
||||||
fun entitiesLoungingIn(index: Int): List<ActorEntity>
|
fun entitiesLoungingIn(index: Int): List<ActorEntity>
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns anchor information with given index, or null if index is invalid
|
|
||||||
*/
|
|
||||||
fun anchor(index: Int): IAnchorState?
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package ru.dbotthepony.kstarbound.world.entities.tile
|
|||||||
import com.google.common.collect.ImmutableList
|
import com.google.common.collect.ImmutableList
|
||||||
import com.google.common.collect.ImmutableSet
|
import com.google.common.collect.ImmutableSet
|
||||||
import com.google.gson.JsonArray
|
import com.google.gson.JsonArray
|
||||||
|
import com.google.gson.JsonElement
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import com.google.gson.JsonPrimitive
|
import com.google.gson.JsonPrimitive
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
@ -11,6 +12,7 @@ import ru.dbotthepony.kommons.util.Either
|
|||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||||
import ru.dbotthepony.kstarbound.Registry
|
import ru.dbotthepony.kstarbound.Registry
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
|
import ru.dbotthepony.kstarbound.client.render.RenderLayer
|
||||||
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.actor.PersistentStatusEffect
|
import ru.dbotthepony.kstarbound.defs.actor.PersistentStatusEffect
|
||||||
@ -23,6 +25,7 @@ import ru.dbotthepony.kstarbound.world.Direction
|
|||||||
import ru.dbotthepony.kstarbound.world.PIXELS_IN_STARBOUND_UNIT
|
import ru.dbotthepony.kstarbound.world.PIXELS_IN_STARBOUND_UNIT
|
||||||
import ru.dbotthepony.kstarbound.world.entities.ActorEntity
|
import ru.dbotthepony.kstarbound.world.entities.ActorEntity
|
||||||
import ru.dbotthepony.kstarbound.world.entities.IAnchorState
|
import ru.dbotthepony.kstarbound.world.entities.IAnchorState
|
||||||
|
import ru.dbotthepony.kstarbound.world.entities.LoungeAnchorState
|
||||||
import ru.dbotthepony.kstarbound.world.entities.api.LoungeableEntity
|
import ru.dbotthepony.kstarbound.world.entities.api.LoungeableEntity
|
||||||
import java.lang.Math.toRadians
|
import java.lang.Math.toRadians
|
||||||
|
|
||||||
@ -31,67 +34,102 @@ class LoungeableObject(config: Registry.Entry<ObjectDefinition>) : WorldObject(c
|
|||||||
isInteractive = true
|
isInteractive = true
|
||||||
}
|
}
|
||||||
|
|
||||||
override val sitPositions = ArrayList<Vector2d>()
|
override val anchors = ArrayList<LoungeAnchorState>()
|
||||||
|
|
||||||
var sitFlipDirection = false
|
|
||||||
private set
|
|
||||||
var sitOrientation = LoungeOrientation.NONE
|
|
||||||
private set
|
|
||||||
var sitAngle = 0.0
|
|
||||||
private set
|
|
||||||
var sitCoverImage = ""
|
var sitCoverImage = ""
|
||||||
private set
|
private set
|
||||||
var sitFlipImages = false
|
var sitFlipImages = false
|
||||||
private set
|
private set
|
||||||
|
var sitOrientation: LoungeOrientation = LoungeOrientation.NONE
|
||||||
val sitStatusEffects = ObjectArraySet<PersistentStatusEffect>()
|
|
||||||
val sitEffectEmitters = ObjectArraySet<String>()
|
|
||||||
|
|
||||||
var sitEmote: String? = null
|
|
||||||
private set
|
|
||||||
var sitDance: String? = null
|
|
||||||
private set
|
|
||||||
var sitArmorCosmeticOverrides: JsonObject = JsonObject()
|
|
||||||
private set
|
|
||||||
var sitCursorOverride: String? = null
|
|
||||||
private set
|
private set
|
||||||
|
|
||||||
private fun updateSitParams() {
|
private fun updateSitParams() {
|
||||||
orientation ?: return
|
anchors.clear()
|
||||||
|
val orientation = orientation ?: return
|
||||||
|
|
||||||
|
val sitPositionsList = ArrayList<Vector2d>()
|
||||||
|
|
||||||
|
val sitDir = orientation.directionAffinity ?: Direction.LEFT
|
||||||
|
|
||||||
sitPositions.clear()
|
|
||||||
val sitPosition = lookupProperty("sitPosition")
|
val sitPosition = lookupProperty("sitPosition")
|
||||||
val sitPositions = lookupProperty("sitPositions")
|
val sitPositions = lookupProperty("sitPositions")
|
||||||
|
|
||||||
if (!sitPosition.isJsonNull) {
|
if (!sitPosition.isJsonNull) {
|
||||||
this.sitPositions.add(vectors.fromJsonTree(sitPosition) / PIXELS_IN_STARBOUND_UNIT)
|
sitPositionsList.add(vectors.fromJsonTree(sitPosition) / PIXELS_IN_STARBOUND_UNIT * sitDir.normal)
|
||||||
} else if (!sitPositions.isJsonNull) {
|
} else if (!sitPositions.isJsonNull) {
|
||||||
vectorsList.fromJsonTree(sitPositions).forEach { this.sitPositions.add(it / PIXELS_IN_STARBOUND_UNIT) }
|
vectorsList.fromJsonTree(sitPositions).forEach { sitPositionsList.add(it / PIXELS_IN_STARBOUND_UNIT * sitDir.normal) }
|
||||||
}
|
}
|
||||||
|
|
||||||
sitFlipDirection = lookupProperty("sitFlipDirection") { JsonPrimitive(false) }.asBoolean
|
val sitFlipDirection = lookupProperty("sitFlipDirection") { JsonPrimitive(false) }.asBoolean
|
||||||
sitOrientation = LoungeOrientation.entries.valueOf(lookupProperty("sitOrientation") { JsonPrimitive("sit") }.asString)
|
sitOrientation = LoungeOrientation.entries.valueOf(lookupProperty("sitOrientation") { JsonPrimitive("sit") }.asString)
|
||||||
sitAngle = toRadians(lookupProperty("sitAngle") { JsonPrimitive(0.0) }.asDouble)
|
var sitAngle = toRadians(lookupProperty("sitAngle") { JsonPrimitive(0.0) }.asDouble)
|
||||||
sitCoverImage = lookupProperty("sitCoverImage") { JsonPrimitive("") }.asString
|
sitCoverImage = lookupProperty("sitCoverImage") { JsonPrimitive("") }.asString
|
||||||
sitFlipImages = lookupProperty("flipImages") { JsonPrimitive(false) }.asBoolean
|
sitFlipImages = lookupProperty("flipImages") { JsonPrimitive(false) }.asBoolean
|
||||||
|
|
||||||
sitStatusEffects.clear()
|
if (sitDir == Direction.LEFT) {
|
||||||
|
sitAngle *= -1f
|
||||||
|
}
|
||||||
|
|
||||||
|
val sitStatusEffects = ObjectArraySet<PersistentStatusEffect>()
|
||||||
sitStatusEffects.addAll(statusEffects.fromJsonTree(lookupProperty("sitStatusEffects") { JsonArray() }))
|
sitStatusEffects.addAll(statusEffects.fromJsonTree(lookupProperty("sitStatusEffects") { JsonArray() }))
|
||||||
|
|
||||||
sitEffectEmitters.clear()
|
val sitEffectEmitters = ObjectArraySet<String>()
|
||||||
lookupProperty("sitEffectEmitters") { JsonArray() }.asJsonArray.forEach { sitEffectEmitters.add(it.asString) }
|
lookupProperty("sitEffectEmitters") { JsonArray() }.asJsonArray.forEach { sitEffectEmitters.add(it.asString) }
|
||||||
sitEmote = lookupProperty("sitEmote").coalesceNull?.asString
|
val sitEmote = lookupProperty("sitEmote").coalesceNull?.asString
|
||||||
sitDance = lookupProperty("sitDance").coalesceNull?.asString
|
val sitDance = lookupProperty("sitDance").coalesceNull?.asString
|
||||||
sitArmorCosmeticOverrides = lookupProperty("sitArmorCosmeticOverrides") { JsonObject() } as JsonObject
|
val sitArmorCosmeticOverrides = lookupProperty("sitArmorCosmeticOverrides") { JsonObject() } as JsonObject
|
||||||
sitCursorOverride = lookupProperty("sitCursorOverride").coalesceNull?.asString
|
val sitCursorOverride = lookupProperty("sitCursorOverride").coalesceNull?.asString
|
||||||
|
|
||||||
|
for ((i, pos) in sitPositionsList.withIndex()) {
|
||||||
|
val anchor = LoungeAnchorState(
|
||||||
|
position = pos,
|
||||||
|
exitBottomPosition = pos.copy(y = yTilePosition.toDouble() + volumeBoundingBox.mins.y),
|
||||||
|
direction = if (sitFlipDirection) sitDir.opposite else sitDir,
|
||||||
|
angle = sitAngle,
|
||||||
|
orientation = sitOrientation,
|
||||||
|
// Layer all anchored entities one above the object layer, in top to bottom
|
||||||
|
// order based on the anchor index.
|
||||||
|
loungeRenderLayer = RenderLayer.Object.point(i.toLong()),
|
||||||
|
controllable = false,
|
||||||
|
emote = sitEmote,
|
||||||
|
dance = sitDance,
|
||||||
|
armorCosmeticOverrides = LinkedHashMap<String, JsonElement>().also { for ((k, v) in sitArmorCosmeticOverrides.entrySet()) { it[k] = v } },
|
||||||
|
cursorOverride = sitCursorOverride,
|
||||||
|
statusEffects = sitStatusEffects,
|
||||||
|
effectEmitters = sitEffectEmitters
|
||||||
|
)
|
||||||
|
|
||||||
|
anchors.add(anchor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun entitiesLoungingIn(index: Int): List<ActorEntity> {
|
override fun entitiesLoungingIn(index: Int): List<ActorEntity> {
|
||||||
TODO("Not yet implemented")
|
if (index !in anchors.indices) return listOf()
|
||||||
|
val result = ArrayList<ActorEntity>()
|
||||||
|
|
||||||
|
world.entityIndex.iterate(metaBoundingBox, {
|
||||||
|
if (it is ActorEntity) {
|
||||||
|
val state = it.movement.anchorNetworkState
|
||||||
|
|
||||||
|
if (state != null && state.entityID == entityID && state.positionIndex == index) {
|
||||||
|
result.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun anchor(index: Int): IAnchorState? {
|
override fun entitiesLoungingIn(): List<ActorEntity> {
|
||||||
TODO("Not yet implemented")
|
val result = ArrayList<ActorEntity>()
|
||||||
|
|
||||||
|
world.entityIndex.iterate(metaBoundingBox, {
|
||||||
|
if (it is ActorEntity && it.movement.anchorNetworkState?.entityID == entityID) {
|
||||||
|
result.add(it)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun parametersUpdated() {
|
override fun parametersUpdated() {
|
||||||
@ -107,12 +145,12 @@ class LoungeableObject(config: Registry.Entry<ObjectDefinition>) : WorldObject(c
|
|||||||
override fun interact(request: InteractRequest): InteractAction {
|
override fun interact(request: InteractRequest): InteractAction {
|
||||||
val upstream = super.interact(request)
|
val upstream = super.interact(request)
|
||||||
|
|
||||||
if (upstream.type == InteractAction.Type.NONE && sitPositions.isNotEmpty()) {
|
if (upstream.type == InteractAction.Type.NONE && anchors.isNotEmpty()) {
|
||||||
val interactOffset = if (direction.isRight) position - request.targetPos else request.targetPos - position
|
val interactOffset = if (direction.isRight) position - request.targetPos else request.targetPos - position
|
||||||
var index = 0
|
var index = 0
|
||||||
|
|
||||||
for (i in 1 until sitPositions.size) {
|
for (i in 1 until anchors.size) {
|
||||||
if ((sitPositions[i] + interactOffset).length < (sitPositions[index] + interactOffset).length) {
|
if ((anchors[i].position + interactOffset).length < (anchors[index].position + interactOffset).length) {
|
||||||
index = i
|
index = i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -549,7 +549,6 @@ 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)
|
||||||
lua.random = world.random
|
|
||||||
luaUpdate.stepCount = lookupProperty(JsonPath("scriptDelta")) { JsonPrimitive(5) }.asDouble
|
luaUpdate.stepCount = lookupProperty(JsonPath("scriptDelta")) { JsonPrimitive(5) }.asDouble
|
||||||
lua.init()
|
lua.init()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user