Compare commits
No commits in common. "918b6ff95f6e1d44186607e89d78e3ba2f7f02e1" and "09f7e8ac7040df12ca10a94aa79f6d1d3da1f983" have entirely different histories.
918b6ff95f
...
09f7e8ac70
@ -154,11 +154,6 @@ In addition to `add`, `multiply`, `merge` and `override` new merge methods are a
|
|||||||
* `monster.setPhysicsForces(forces: Table?)` now accepts nil as equivalent of empty table (consistency fix)
|
* `monster.setPhysicsForces(forces: Table?)` now accepts nil as equivalent of empty table (consistency fix)
|
||||||
* `mosnter.setName(name: String?)` now accepts nil to reset custom name
|
* `mosnter.setName(name: String?)` now accepts nil to reset custom name
|
||||||
|
|
||||||
## npc
|
|
||||||
* `npc.setDropPools(dropPools: Table?)` now accepts `nil`
|
|
||||||
* Added `npc.beginSecondaryFire()` which is alias for `npc.beginAltFire()`
|
|
||||||
* Added `npc.endSecondaryFire()` which is alias for `npc.endAltFire()`
|
|
||||||
|
|
||||||
## status
|
## status
|
||||||
|
|
||||||
* Implemented `status.appliesEnvironmentStatusEffects(): Boolean`, which exists in original engine's code but was never hooked up to Lua bindings
|
* Implemented `status.appliesEnvironmentStatusEffects(): Boolean`, which exists in original engine's code but was never hooked up to Lua bindings
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
|
||||||
|
import org.gradle.internal.jvm.Jvm
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "1.9.10"
|
kotlin("jvm") version "1.9.10"
|
||||||
id("me.champeau.jmh") version "0.7.1"
|
id("me.champeau.jmh") version "0.7.1"
|
||||||
|
@ -37,11 +37,11 @@ inline fun <reified T> GsonBuilder.registerTypeAdapter(noinline factory: (Gson)
|
|||||||
|
|
||||||
fun <T> Array<T>.stream(): Stream<T> = Arrays.stream(this)
|
fun <T> Array<T>.stream(): Stream<T> = Arrays.stream(this)
|
||||||
|
|
||||||
operator fun <T> ThreadLocal<T>.getValue(thisRef: Any?, property: KProperty<*>): T {
|
operator fun <T> ThreadLocal<T>.getValue(thisRef: Any, property: KProperty<*>): T {
|
||||||
return get()
|
return get()
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun <T> ThreadLocal<T>.setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
operator fun <T> ThreadLocal<T>.setValue(thisRef: Any, property: KProperty<*>, value: T) {
|
||||||
set(value)
|
set(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ import ru.dbotthepony.kstarbound.defs.ElementalDamageType
|
|||||||
import ru.dbotthepony.kstarbound.defs.MovementParameters
|
import ru.dbotthepony.kstarbound.defs.MovementParameters
|
||||||
import ru.dbotthepony.kstarbound.defs.world.SpawnerConfig
|
import ru.dbotthepony.kstarbound.defs.world.SpawnerConfig
|
||||||
import ru.dbotthepony.kstarbound.defs.UniverseServerConfig
|
import ru.dbotthepony.kstarbound.defs.UniverseServerConfig
|
||||||
import ru.dbotthepony.kstarbound.defs.actor.GlobalNPCConfig
|
|
||||||
import ru.dbotthepony.kstarbound.defs.world.WorldServerConfig
|
import ru.dbotthepony.kstarbound.defs.world.WorldServerConfig
|
||||||
import ru.dbotthepony.kstarbound.defs.actor.player.PlayerConfig
|
import ru.dbotthepony.kstarbound.defs.actor.player.PlayerConfig
|
||||||
import ru.dbotthepony.kstarbound.defs.actor.player.ShipUpgrades
|
import ru.dbotthepony.kstarbound.defs.actor.player.ShipUpgrades
|
||||||
@ -130,9 +129,6 @@ object Globals {
|
|||||||
var elementalTypes by Delegates.notNull<ImmutableMap<String, ElementalDamageType>>()
|
var elementalTypes by Delegates.notNull<ImmutableMap<String, ElementalDamageType>>()
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var npcs by Delegates.notNull<GlobalNPCConfig>()
|
|
||||||
private set
|
|
||||||
|
|
||||||
private var profanityFilterInternal by Delegates.notNull<ImmutableList<String>>()
|
private var profanityFilterInternal by Delegates.notNull<ImmutableList<String>>()
|
||||||
|
|
||||||
val profanityFilter: ImmutableSet<String> by lazy {
|
val profanityFilter: ImmutableSet<String> by lazy {
|
||||||
@ -241,7 +237,6 @@ object Globals {
|
|||||||
tasks.add(load("/ships/shipupgrades.config", ::shipUpgrades))
|
tasks.add(load("/ships/shipupgrades.config", ::shipUpgrades))
|
||||||
tasks.add(load("/quests/quests.config", ::quests))
|
tasks.add(load("/quests/quests.config", ::quests))
|
||||||
tasks.add(load("/spawning.config", ::spawner))
|
tasks.add(load("/spawning.config", ::spawner))
|
||||||
tasks.add(load("/npcs/npc.config", ::npcs))
|
|
||||||
|
|
||||||
tasks.add(Starbound.GLOBAL_SCOPE.launch { load("/dungeon_worlds.config", ::dungeonWorlds, mapAdapter("/dungeon_worlds.config")) }.asCompletableFuture())
|
tasks.add(Starbound.GLOBAL_SCOPE.launch { load("/dungeon_worlds.config", ::dungeonWorlds, mapAdapter("/dungeon_worlds.config")) }.asCompletableFuture())
|
||||||
tasks.add(Starbound.GLOBAL_SCOPE.launch { load("/currencies.config", ::currencies, mapAdapter("/currencies.config")) }.asCompletableFuture())
|
tasks.add(Starbound.GLOBAL_SCOPE.launch { load("/currencies.config", ::currencies, mapAdapter("/currencies.config")) }.asCompletableFuture())
|
||||||
|
@ -38,7 +38,6 @@ import ru.dbotthepony.kstarbound.defs.animation.ParticleConfig
|
|||||||
import ru.dbotthepony.kstarbound.defs.dungeon.DungeonDefinition
|
import ru.dbotthepony.kstarbound.defs.dungeon.DungeonDefinition
|
||||||
import ru.dbotthepony.kstarbound.defs.item.TreasureChestDefinition
|
import ru.dbotthepony.kstarbound.defs.item.TreasureChestDefinition
|
||||||
import ru.dbotthepony.kstarbound.defs.ProjectileDefinition
|
import ru.dbotthepony.kstarbound.defs.ProjectileDefinition
|
||||||
import ru.dbotthepony.kstarbound.defs.actor.DanceDefinition
|
|
||||||
import ru.dbotthepony.kstarbound.defs.actor.behavior.BehaviorDefinition
|
import ru.dbotthepony.kstarbound.defs.actor.behavior.BehaviorDefinition
|
||||||
import ru.dbotthepony.kstarbound.defs.actor.behavior.BehaviorNodeDefinition
|
import ru.dbotthepony.kstarbound.defs.actor.behavior.BehaviorNodeDefinition
|
||||||
import ru.dbotthepony.kstarbound.defs.monster.MonsterPaletteSwap
|
import ru.dbotthepony.kstarbound.defs.monster.MonsterPaletteSwap
|
||||||
@ -113,7 +112,6 @@ object Registries {
|
|||||||
val dungeons = Registry<DungeonDefinition>("dungeon").also(registriesInternal::add).also { adapters.add(it.adapter()) }
|
val dungeons = Registry<DungeonDefinition>("dungeon").also(registriesInternal::add).also { adapters.add(it.adapter()) }
|
||||||
val markovGenerators = Registry<MarkovTextGenerator>("markov text generator").also(registriesInternal::add).also { adapters.add(it.adapter()) }
|
val markovGenerators = Registry<MarkovTextGenerator>("markov text generator").also(registriesInternal::add).also { adapters.add(it.adapter()) }
|
||||||
val damageKinds = Registry<DamageKind>("damage kind").also(registriesInternal::add).also { adapters.add(it.adapter()) }
|
val damageKinds = Registry<DamageKind>("damage kind").also(registriesInternal::add).also { adapters.add(it.adapter()) }
|
||||||
val dance = Registry<DanceDefinition>("dance").also(registriesInternal::add).also { adapters.add(it.adapter()) }
|
|
||||||
|
|
||||||
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>>())
|
||||||
@ -294,7 +292,6 @@ object Registries {
|
|||||||
tasks.addAll(loadRegistry(projectiles, patchTree, fileTree["projectile"] ?: listOf(), key(ProjectileDefinition::projectileName)))
|
tasks.addAll(loadRegistry(projectiles, patchTree, fileTree["projectile"] ?: listOf(), key(ProjectileDefinition::projectileName)))
|
||||||
tasks.addAll(loadRegistry(behavior, patchTree, fileTree["behavior"] ?: listOf(), key(BehaviorDefinition::name)))
|
tasks.addAll(loadRegistry(behavior, patchTree, fileTree["behavior"] ?: listOf(), key(BehaviorDefinition::name)))
|
||||||
tasks.addAll(loadRegistry(damageKinds, patchTree, fileTree["damage"] ?: listOf(), key(DamageKind::kind)))
|
tasks.addAll(loadRegistry(damageKinds, patchTree, fileTree["damage"] ?: listOf(), key(DamageKind::kind)))
|
||||||
tasks.addAll(loadRegistry(dance, patchTree, fileTree["dance"] ?: listOf(), key(DanceDefinition::name)))
|
|
||||||
|
|
||||||
tasks.addAll(loadCombined(behaviorNodes, fileTree["nodes"] ?: listOf(), patchTree))
|
tasks.addAll(loadCombined(behaviorNodes, fileTree["nodes"] ?: listOf(), patchTree))
|
||||||
|
|
||||||
|
@ -10,15 +10,10 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
|||||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet
|
import it.unimi.dsi.fastutil.longs.LongOpenHashSet
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import ru.dbotthepony.kommons.io.writeBinaryString
|
|
||||||
import ru.dbotthepony.kommons.util.Either
|
import ru.dbotthepony.kommons.util.Either
|
||||||
import ru.dbotthepony.kommons.util.KOptional
|
import ru.dbotthepony.kommons.util.KOptional
|
||||||
import ru.dbotthepony.kommons.util.XXHash64
|
import ru.dbotthepony.kommons.util.XXHash64
|
||||||
import ru.dbotthepony.kstarbound.io.StreamCodec
|
|
||||||
import ru.dbotthepony.kstarbound.io.readInternedString
|
|
||||||
import ru.dbotthepony.kstarbound.util.limit
|
import ru.dbotthepony.kstarbound.util.limit
|
||||||
import java.io.DataInputStream
|
|
||||||
import java.io.DataOutputStream
|
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
@ -38,7 +33,7 @@ class Registry<T : Any>(val name: String, val storeJson: Boolean = true) {
|
|||||||
|
|
||||||
// idiot-proof miss lookup. Surely, it will cause some entries to be never logged
|
// idiot-proof miss lookup. Surely, it will cause some entries to be never logged
|
||||||
// if they are missing, but at least if malicious actor spams with long-ass invalid data
|
// if they are missing, but at least if malicious actor spams with long-ass invalid data
|
||||||
// it won't easily explode memory usage of server
|
// it won't explode memory usage of server
|
||||||
private val loggedMisses = LongOpenHashSet()
|
private val loggedMisses = LongOpenHashSet()
|
||||||
|
|
||||||
val keys: Map<String, Entry<T>> = Collections.unmodifiableMap(keysInternal)
|
val keys: Map<String, Entry<T>> = Collections.unmodifiableMap(keysInternal)
|
||||||
@ -49,9 +44,6 @@ class Registry<T : Any>(val name: String, val storeJson: Boolean = true) {
|
|||||||
abstract val entry: Entry<T>?
|
abstract val entry: Entry<T>?
|
||||||
abstract val registry: Registry<T>
|
abstract val registry: Registry<T>
|
||||||
|
|
||||||
val entryOrThrow: Entry<T>
|
|
||||||
get() = entry ?: throw NoSuchElementException("No such ${registry.name}: ${key.map({ it }, { it.toString() }).limit()}")
|
|
||||||
|
|
||||||
val isPresent: Boolean
|
val isPresent: Boolean
|
||||||
get() = value != null
|
get() = value != null
|
||||||
|
|
||||||
@ -61,18 +53,6 @@ class Registry<T : Any>(val name: String, val storeJson: Boolean = true) {
|
|||||||
val value: T?
|
val value: T?
|
||||||
get() = entry?.value
|
get() = entry?.value
|
||||||
|
|
||||||
inline fun ifPresent(block: (Entry<T>) -> Unit) {
|
|
||||||
entry?.let(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <R> map(block: (Entry<T>) -> R): KOptional<R> {
|
|
||||||
return entry?.let { KOptional(block(it)) } ?: KOptional()
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <R> mapOrThrow(block: (Entry<T>) -> R): R {
|
|
||||||
return block(entryOrThrow)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun get(): Entry<T>? {
|
final override fun get(): Entry<T>? {
|
||||||
return entry
|
return entry
|
||||||
}
|
}
|
||||||
@ -126,7 +106,7 @@ class Registry<T : Any>(val name: String, val storeJson: Boolean = true) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "E/$name/$key/${id ?: "-"}"
|
return "Entry of $name at $key/${id ?: "-"}"
|
||||||
}
|
}
|
||||||
|
|
||||||
override val registry: Registry<T>
|
override val registry: Registry<T>
|
||||||
@ -159,11 +139,7 @@ class Registry<T : Any>(val name: String, val storeJson: Boolean = true) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
if (entry == null) {
|
return "Ref of $name at $key/${if (entry != null) "bound" else "missing"}"
|
||||||
return "R/$name/${key.map({ "'$it'" }, { it.toString() })}/!"
|
|
||||||
} else {
|
|
||||||
return "R/$name/${entry!!.key}/${entry?.id ?: "-"}"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override val registry: Registry<T>
|
override val registry: Registry<T>
|
||||||
@ -353,34 +329,6 @@ class Registry<T : Any>(val name: String, val storeJson: Boolean = true) {
|
|||||||
|
|
||||||
val emptyRef = ref("")
|
val emptyRef = ref("")
|
||||||
|
|
||||||
val nameRefCodec = object : StreamCodec<Ref<T>> {
|
|
||||||
override fun read(stream: DataInputStream): Ref<T> {
|
|
||||||
return ref(stream.readInternedString())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun write(stream: DataOutputStream, value: Ref<T>) {
|
|
||||||
stream.writeBinaryString(value.key.left())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun copy(value: Ref<T>): Ref<T> {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val nameEntryCodec = object : StreamCodec<Entry<T>> {
|
|
||||||
override fun read(stream: DataInputStream): Entry<T> {
|
|
||||||
return getOrThrow(stream.readInternedString())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun write(stream: DataOutputStream, value: Entry<T>) {
|
|
||||||
stream.writeBinaryString(value.key)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun copy(value: Entry<T>): Entry<T> {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val LOGGER = LogManager.getLogger()
|
private val LOGGER = LogManager.getLogger()
|
||||||
}
|
}
|
||||||
|
@ -4,18 +4,19 @@ import com.github.benmanes.caffeine.cache.AsyncCacheLoader
|
|||||||
import com.github.benmanes.caffeine.cache.Caffeine
|
import com.github.benmanes.caffeine.cache.Caffeine
|
||||||
import com.github.benmanes.caffeine.cache.Interner
|
import com.github.benmanes.caffeine.cache.Interner
|
||||||
import com.github.benmanes.caffeine.cache.Scheduler
|
import com.github.benmanes.caffeine.cache.Scheduler
|
||||||
|
import com.google.common.base.Predicate
|
||||||
import com.google.gson.*
|
import com.google.gson.*
|
||||||
import com.google.gson.stream.JsonReader
|
import com.google.gson.stream.JsonReader
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Runnable
|
import kotlinx.coroutines.Runnable
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.asCoroutineDispatcher
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.delay
|
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 kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import org.classdump.luna.compiler.CompilerChunkLoader
|
import org.classdump.luna.compiler.CompilerChunkLoader
|
||||||
import org.classdump.luna.compiler.CompilerSettings
|
import org.classdump.luna.compiler.CompilerSettings
|
||||||
@ -33,7 +34,6 @@ import ru.dbotthepony.kstarbound.defs.image.Image
|
|||||||
import ru.dbotthepony.kstarbound.defs.image.SpriteReference
|
import ru.dbotthepony.kstarbound.defs.image.SpriteReference
|
||||||
import ru.dbotthepony.kstarbound.defs.actor.player.BlueprintLearnList
|
import ru.dbotthepony.kstarbound.defs.actor.player.BlueprintLearnList
|
||||||
import ru.dbotthepony.kstarbound.defs.animation.Particle
|
import ru.dbotthepony.kstarbound.defs.animation.Particle
|
||||||
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor
|
|
||||||
import ru.dbotthepony.kstarbound.defs.quest.QuestParameter
|
import ru.dbotthepony.kstarbound.defs.quest.QuestParameter
|
||||||
import ru.dbotthepony.kstarbound.defs.world.CelestialParameters
|
import ru.dbotthepony.kstarbound.defs.world.CelestialParameters
|
||||||
import ru.dbotthepony.kstarbound.defs.world.VisitableWorldParametersType
|
import ru.dbotthepony.kstarbound.defs.world.VisitableWorldParametersType
|
||||||
@ -97,11 +97,17 @@ import java.util.concurrent.ForkJoinPool
|
|||||||
import java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory
|
import java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory
|
||||||
import java.util.concurrent.ForkJoinWorkerThread
|
import java.util.concurrent.ForkJoinWorkerThread
|
||||||
import java.util.concurrent.Future
|
import java.util.concurrent.Future
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue
|
||||||
|
import java.util.concurrent.ThreadFactory
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
import java.util.concurrent.locks.LockSupport
|
||||||
import java.util.random.RandomGenerator
|
import java.util.random.RandomGenerator
|
||||||
import kotlin.NoSuchElementException
|
import kotlin.NoSuchElementException
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
import kotlin.math.max
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
object Starbound : BlockableEventLoop("Universe Thread"), Scheduler, ISBFileLocator {
|
object Starbound : BlockableEventLoop("Universe Thread"), Scheduler, ISBFileLocator {
|
||||||
const val ENGINE_VERSION = "0.0.1"
|
const val ENGINE_VERSION = "0.0.1"
|
||||||
@ -116,11 +122,6 @@ object Starbound : BlockableEventLoop("Universe Thread"), Scheduler, ISBFileLoca
|
|||||||
const val DEDUP_CELL_STATES = true
|
const val DEDUP_CELL_STATES = true
|
||||||
const val USE_CAFFEINE_INTERNER = false
|
const val USE_CAFFEINE_INTERNER = false
|
||||||
const val USE_INTERNER = true
|
const val USE_INTERNER = true
|
||||||
// enables a fuckton of runtime checks for data which doesn't make much sense
|
|
||||||
// especially for data which will explode legacy client
|
|
||||||
// also changes some constants
|
|
||||||
const val DEBUG_BUILD = true
|
|
||||||
// ----
|
|
||||||
|
|
||||||
fun <E : Any> interner(): Interner<E> {
|
fun <E : Any> interner(): Interner<E> {
|
||||||
if (!USE_INTERNER)
|
if (!USE_INTERNER)
|
||||||
@ -416,8 +417,6 @@ object Starbound : BlockableEventLoop("Universe Thread"), Scheduler, ISBFileLoca
|
|||||||
|
|
||||||
registerTypeAdapter(LongRangeAdapter)
|
registerTypeAdapter(LongRangeAdapter)
|
||||||
|
|
||||||
registerTypeAdapter(ItemDescriptor.Adapter)
|
|
||||||
|
|
||||||
create()
|
create()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -783,22 +782,10 @@ object Starbound : BlockableEventLoop("Universe Thread"), Scheduler, ISBFileLoca
|
|||||||
return result.toString()
|
return result.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated("Blocks thread it is running in, consider generateNameAsync instead", replaceWith = ReplaceWith("this.generateNameAsync"))
|
fun generateName(asset: String, random: RandomGenerator): String {
|
||||||
fun generateName(asset: String, random: RandomGenerator, maxTries: Int = 500): String {
|
val load = loadJsonAsset(asset).get() as? JsonArray ?: return "missingasset"
|
||||||
return runBlocking {
|
|
||||||
generateNameAsync(asset, random, maxTries)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated("Blocks thread it is running in, consider generateNameAsync instead", replaceWith = ReplaceWith("this.generateNameAsync"))
|
var tries = 500
|
||||||
fun generateName(asset: String, seed: Long, maxTries: Int = 500) = generateName(asset, random(seed), maxTries)
|
|
||||||
@Deprecated("Blocks thread it is running in, consider generateNameAsync instead", replaceWith = ReplaceWith("this.generateNameAsync"))
|
|
||||||
fun generateName(asset: String, maxTries: Int = 500) = generateName(asset, System.nanoTime(), maxTries)
|
|
||||||
|
|
||||||
suspend fun generateNameAsync(asset: String, random: RandomGenerator, maxTries: Int = 500): String {
|
|
||||||
val load = loadJsonAsset(asset).await() as? JsonArray ?: return "missingasset:$asset"
|
|
||||||
|
|
||||||
var tries = maxTries
|
|
||||||
var result = ""
|
var result = ""
|
||||||
|
|
||||||
while (tries-- > 0 && (result.isEmpty() || result.lowercase() in Globals.profanityFilter))
|
while (tries-- > 0 && (result.isEmpty() || result.lowercase() in Globals.profanityFilter))
|
||||||
@ -807,7 +794,7 @@ object Starbound : BlockableEventLoop("Universe Thread"), Scheduler, ISBFileLoca
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun generateNameAsync(asset: String, seed: Long, maxTries: Int = 500) = generateNameAsync(asset, random(seed), maxTries)
|
fun generateName(asset: String, seed: Long) = generateName(asset, random(seed))
|
||||||
suspend fun generateNameAsync(asset: String, maxTries: Int = 500) = generateNameAsync(asset, System.nanoTime(), maxTries)
|
fun generateName(asset: String) = generateName(asset, System.nanoTime())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ import io.netty.channel.local.LocalAddress
|
|||||||
import io.netty.channel.local.LocalChannel
|
import io.netty.channel.local.LocalChannel
|
||||||
import io.netty.channel.socket.nio.NioSocketChannel
|
import io.netty.channel.socket.nio.NioSocketChannel
|
||||||
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
|
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import ru.dbotthepony.kommons.util.KOptional
|
import ru.dbotthepony.kommons.util.KOptional
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
@ -79,14 +78,12 @@ class ClientConnection(val client: StarboundClient, type: ConnectionType) : Conn
|
|||||||
|
|
||||||
override fun channelRead(ctx: ChannelHandlerContext, msg: Any) {
|
override fun channelRead(ctx: ChannelHandlerContext, msg: Any) {
|
||||||
if (msg is IClientPacket) {
|
if (msg is IClientPacket) {
|
||||||
runBlocking {
|
|
||||||
try {
|
try {
|
||||||
msg.play(this@ClientConnection)
|
msg.play(this)
|
||||||
} catch (err: Throwable) {
|
} catch (err: Throwable) {
|
||||||
LOGGER.error("Failed to read incoming packet $msg", err)
|
LOGGER.error("Failed to read incoming packet $msg", err)
|
||||||
disconnect(err.toString())
|
disconnect(err.toString())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
LOGGER.error("Unknown incoming packet type $msg")
|
LOGGER.error("Unknown incoming packet type $msg")
|
||||||
disconnect("Unknown incoming packet type $msg")
|
disconnect("Unknown incoming packet type $msg")
|
||||||
|
@ -29,7 +29,7 @@ class ChunkCellsPacket(val pos: ChunkPos, val data: List<ImmutableCell>) : IClie
|
|||||||
stream.writeCollection(data) { it.writeLegacy(stream) }
|
stream.writeCollection(data) { it.writeLegacy(stream) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
connection.client.mailbox.execute {
|
connection.client.mailbox.execute {
|
||||||
val chunk = connection.client.world?.chunkMap?.compute(pos.x, pos.y) ?: return@execute
|
val chunk = connection.client.world?.chunkMap?.compute(pos.x, pos.y) ?: return@execute
|
||||||
val itr = data.iterator()
|
val itr = data.iterator()
|
||||||
|
@ -15,7 +15,7 @@ class ForgetChunkPacket(val pos: ChunkPos) : IClientPacket {
|
|||||||
stream.writeStruct2i(pos)
|
stream.writeStruct2i(pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
connection.client.mailbox.execute {
|
connection.client.mailbox.execute {
|
||||||
val world = connection.client.world ?: return@execute
|
val world = connection.client.world ?: return@execute
|
||||||
world.chunkMap.remove(pos)
|
world.chunkMap.remove(pos)
|
||||||
|
@ -15,7 +15,7 @@ class ForgetEntityPacket(val uuid: UUID) : IClientPacket {
|
|||||||
stream.writeUUID(uuid)
|
stream.writeUUID(uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
val world = connection.client.world ?: return
|
val world = connection.client.world ?: return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ data class JoinWorldPacket(val uuid: UUID, val geometry: WorldGeometry) : IClien
|
|||||||
geometry.write(stream)
|
geometry.write(stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
connection.client.mailbox.execute {
|
connection.client.mailbox.execute {
|
||||||
connection.client.world = ClientWorld(connection.client, WorldTemplate(geometry))
|
connection.client.world = ClientWorld(connection.client, WorldTemplate(geometry))
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ object LeaveWorldPacket : IClientPacket {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
connection.client.mailbox.execute {
|
connection.client.mailbox.execute {
|
||||||
connection.client.world = null
|
connection.client.world = null
|
||||||
}
|
}
|
||||||
|
@ -19,12 +19,8 @@ class ColorReplacements private constructor(private val mapping: Int2IntOpenHash
|
|||||||
return mapping.getOrDefault(color, color)
|
return mapping.getOrDefault(color, color)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val asImageOperator by lazy {
|
|
||||||
"?replace;${mapping.int2IntEntrySet().joinToString(";") { "${RGBAColor.rgb(it.intKey).toHexStringRGB().substring(1)}=${RGBAColor.rgb(it.intValue).toHexStringRGB().substring(1)}" }}"
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toImageOperator(): String {
|
fun toImageOperator(): String {
|
||||||
return asImageOperator
|
return "replace;${mapping.int2IntEntrySet().joinToString(";") { "${RGBAColor.rgb(it.intKey).toHexStringRGB().substring(1)}=${RGBAColor.rgb(it.intValue).toHexStringRGB().substring(1)}" }}"
|
||||||
}
|
}
|
||||||
|
|
||||||
class Adapter(gson: Gson) : TypeAdapter<ColorReplacements>() {
|
class Adapter(gson: Gson) : TypeAdapter<ColorReplacements>() {
|
||||||
|
@ -15,7 +15,6 @@ import ru.dbotthepony.kommons.io.readSignedVarInt
|
|||||||
import ru.dbotthepony.kommons.io.writeBinaryString
|
import ru.dbotthepony.kommons.io.writeBinaryString
|
||||||
import ru.dbotthepony.kommons.io.writeCollection
|
import ru.dbotthepony.kommons.io.writeCollection
|
||||||
import ru.dbotthepony.kommons.io.writeSignedVarInt
|
import ru.dbotthepony.kommons.io.writeSignedVarInt
|
||||||
import ru.dbotthepony.kommons.io.writeStruct2d
|
|
||||||
import ru.dbotthepony.kommons.util.Either
|
import ru.dbotthepony.kommons.util.Either
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||||
import ru.dbotthepony.kstarbound.io.readDouble
|
import ru.dbotthepony.kstarbound.io.readDouble
|
||||||
@ -154,36 +153,8 @@ data class TouchDamage(
|
|||||||
val damageSourceKind: String = "",
|
val damageSourceKind: String = "",
|
||||||
val knockback: Double = 0.0,
|
val knockback: Double = 0.0,
|
||||||
val statusEffects: ImmutableSet<String> = ImmutableSet.of(),
|
val statusEffects: ImmutableSet<String> = ImmutableSet.of(),
|
||||||
) {
|
|
||||||
/**
|
|
||||||
* new protocol only
|
|
||||||
*/
|
|
||||||
constructor(stream: DataInputStream) : this(
|
|
||||||
ImmutableList.copyOf(stream.readCollection { readVector2d() }),
|
|
||||||
TeamType.entries[stream.readUnsignedByte()],
|
|
||||||
stream.readDouble(),
|
|
||||||
stream.readInternedString(),
|
|
||||||
stream.readDouble(),
|
|
||||||
ImmutableSet.copyOf(stream.readCollection { readInternedString() })
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
|
||||||
* new protocol only
|
|
||||||
*/
|
|
||||||
fun write(stream: DataOutputStream) {
|
|
||||||
stream.writeCollection(poly) { writeStruct2d(it) }
|
|
||||||
stream.writeByte(teamType.ordinal)
|
|
||||||
stream.writeDouble(damage)
|
|
||||||
stream.writeBinaryString(damageSourceKind)
|
|
||||||
stream.writeDouble(knockback)
|
|
||||||
stream.writeCollection(statusEffects) { writeBinaryString(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val EMPTY = TouchDamage()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonFactory
|
@JsonFactory
|
||||||
data class DamageNotification(
|
data class DamageNotification(
|
||||||
val sourceEntityId: Int,
|
val sourceEntityId: Int,
|
||||||
|
@ -4,7 +4,6 @@ import com.google.gson.JsonObject
|
|||||||
import ru.dbotthepony.kommons.io.readBinaryString
|
import ru.dbotthepony.kommons.io.readBinaryString
|
||||||
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.actor.NPCVariant
|
|
||||||
import ru.dbotthepony.kstarbound.defs.monster.MonsterVariant
|
import ru.dbotthepony.kstarbound.defs.monster.MonsterVariant
|
||||||
import ru.dbotthepony.kstarbound.defs.`object`.ObjectType
|
import ru.dbotthepony.kstarbound.defs.`object`.ObjectType
|
||||||
import ru.dbotthepony.kstarbound.fromJsonFast
|
import ru.dbotthepony.kstarbound.fromJsonFast
|
||||||
@ -14,7 +13,6 @@ import ru.dbotthepony.kstarbound.json.readJsonElement
|
|||||||
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.MonsterEntity
|
||||||
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.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
|
||||||
@ -26,7 +24,7 @@ import java.io.DataInputStream
|
|||||||
|
|
||||||
enum class EntityType(override val jsonName: String, val storeName: String, val canBeCreatedByClient: Boolean, val canBeSpawnedByClient: Boolean, val ephemeralIfSpawnedByClient: Boolean = true) : IStringSerializable {
|
enum class EntityType(override val jsonName: String, val storeName: String, val canBeCreatedByClient: Boolean, val canBeSpawnedByClient: Boolean, val ephemeralIfSpawnedByClient: Boolean = true) : IStringSerializable {
|
||||||
PLANT("plant", "PlantEntity", false, false) {
|
PLANT("plant", "PlantEntity", false, false) {
|
||||||
override suspend fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity {
|
override fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity {
|
||||||
return PlantEntity(stream, isLegacy)
|
return PlantEntity(stream, isLegacy)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +34,7 @@ enum class EntityType(override val jsonName: String, val storeName: String, val
|
|||||||
},
|
},
|
||||||
|
|
||||||
OBJECT("object", "ObjectEntity", false, true) {
|
OBJECT("object", "ObjectEntity", false, true) {
|
||||||
override suspend fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity {
|
override fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity {
|
||||||
val name = stream.readInternedString()
|
val name = stream.readInternedString()
|
||||||
val parameters = stream.readJsonElement()
|
val parameters = stream.readJsonElement()
|
||||||
val config = Registries.worldObjects.getOrThrow(name)
|
val config = Registries.worldObjects.getOrThrow(name)
|
||||||
@ -61,7 +59,7 @@ enum class EntityType(override val jsonName: String, val storeName: String, val
|
|||||||
},
|
},
|
||||||
|
|
||||||
VEHICLE("vehicle", "VehicleEntity", false, true, false) {
|
VEHICLE("vehicle", "VehicleEntity", false, true, false) {
|
||||||
override suspend fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity {
|
override fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity {
|
||||||
TODO("VEHICLE")
|
TODO("VEHICLE")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +69,7 @@ enum class EntityType(override val jsonName: String, val storeName: String, val
|
|||||||
},
|
},
|
||||||
|
|
||||||
ITEM_DROP("itemDrop", "ItemDropEntity", false, true, false) {
|
ITEM_DROP("itemDrop", "ItemDropEntity", false, true, false) {
|
||||||
override suspend fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity {
|
override fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity {
|
||||||
return ItemDropEntity(stream, isLegacy)
|
return ItemDropEntity(stream, isLegacy)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +79,7 @@ enum class EntityType(override val jsonName: String, val storeName: String, val
|
|||||||
},
|
},
|
||||||
|
|
||||||
PLANT_DROP("plantDrop", "PlantDropEntity", false, false) {
|
PLANT_DROP("plantDrop", "PlantDropEntity", false, false) {
|
||||||
override suspend fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity {
|
override fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity {
|
||||||
return PlantPieceEntity(stream, isLegacy)
|
return PlantPieceEntity(stream, isLegacy)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +89,7 @@ enum class EntityType(override val jsonName: String, val storeName: String, val
|
|||||||
},
|
},
|
||||||
|
|
||||||
PROJECTILE("projectile", "ProjectileEntity", true, true) {
|
PROJECTILE("projectile", "ProjectileEntity", true, true) {
|
||||||
override suspend fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity {
|
override fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity {
|
||||||
return ProjectileEntity(Registries.projectiles.getOrThrow(stream.readBinaryString()), stream, isLegacy)
|
return ProjectileEntity(Registries.projectiles.getOrThrow(stream.readBinaryString()), stream, isLegacy)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +99,7 @@ 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 fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity {
|
||||||
TODO("STAGEHAND")
|
TODO("STAGEHAND")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +109,7 @@ enum class EntityType(override val jsonName: String, val storeName: String, val
|
|||||||
},
|
},
|
||||||
|
|
||||||
MONSTER("monster", "MonsterEntity", false, false) {
|
MONSTER("monster", "MonsterEntity", false, false) {
|
||||||
override suspend fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity {
|
override fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity {
|
||||||
return MonsterEntity(MonsterVariant.read(stream, isLegacy))
|
return MonsterEntity(MonsterVariant.read(stream, isLegacy))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,19 +132,17 @@ enum class EntityType(override val jsonName: String, val storeName: String, val
|
|||||||
},
|
},
|
||||||
|
|
||||||
NPC("npc", "NpcEntity", false, false) {
|
NPC("npc", "NpcEntity", false, false) {
|
||||||
override suspend fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity {
|
override fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity {
|
||||||
return NPCEntity(NPCVariant.read(stream, isLegacy))
|
TODO("NPC")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun fromStorage(data: JsonObject): AbstractEntity {
|
override fun fromStorage(data: JsonObject): AbstractEntity {
|
||||||
val entity = NPCEntity(Starbound.gson.fromJsonFast(data["npcVariant"], NPCVariant::class.java))
|
TODO("NPC")
|
||||||
entity.deserialize(data)
|
|
||||||
return entity
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
PLAYER("player", "PlayerEntity", true, false) {
|
PLAYER("player", "PlayerEntity", true, false) {
|
||||||
override suspend fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity {
|
override fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity {
|
||||||
return PlayerEntity(stream, isLegacy)
|
return PlayerEntity(stream, isLegacy)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,6 +151,6 @@ enum class EntityType(override val jsonName: String, val storeName: String, val
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
abstract suspend fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity
|
abstract fun fromNetwork(stream: DataInputStream, isLegacy: Boolean): AbstractEntity
|
||||||
abstract fun fromStorage(data: JsonObject): AbstractEntity
|
abstract fun fromStorage(data: JsonObject): AbstractEntity
|
||||||
}
|
}
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.defs.actor
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList
|
|
||||||
import com.google.common.collect.ImmutableSet
|
|
||||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
|
||||||
|
|
||||||
@JsonFactory
|
|
||||||
data class DanceDefinition(
|
|
||||||
val name: String,
|
|
||||||
val states: ImmutableSet<String>,
|
|
||||||
val cycle: Double,
|
|
||||||
val cyclic: Boolean,
|
|
||||||
val duration: Double,
|
|
||||||
val steps: ImmutableList<Step>
|
|
||||||
) {
|
|
||||||
@JsonFactory(asList = 0)
|
|
||||||
data class Step(
|
|
||||||
val bodyFrame: String? = null,
|
|
||||||
val frontArmFrame: String? = null,
|
|
||||||
val backArmFrame: String? = null,
|
|
||||||
val headOffset: Vector2d = Vector2d.ZERO,
|
|
||||||
val frontArmOffset: Vector2d = Vector2d.ZERO,
|
|
||||||
val backArmOffset: Vector2d = Vector2d.ZERO,
|
|
||||||
val frontArmRotation: Double = 0.0,
|
|
||||||
val backArmRotation: Double = 0.0,
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.defs.actor
|
|
||||||
|
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
|
||||||
|
|
||||||
data class GlobalNPCConfig(
|
|
||||||
val hitDamageNotificationLimit: Int,
|
|
||||||
val emoteCooldown: Double,
|
|
||||||
val danceCooldown: Double,
|
|
||||||
val shieldHitSoundLimit: Double,
|
|
||||||
val blinkInterval: Vector2d,
|
|
||||||
) {
|
|
||||||
init {
|
|
||||||
require(hitDamageNotificationLimit >= 1) { "Pointless hitDamageNotificationLimit: $hitDamageNotificationLimit" }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.defs.actor
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList
|
|
||||||
import com.google.gson.JsonObject
|
|
||||||
import ru.dbotthepony.kstarbound.defs.ActorMovementParameters
|
|
||||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
|
||||||
|
|
||||||
@JsonFactory
|
|
||||||
data class HumanoidConfig(
|
|
||||||
val globalOffset: Vector2d, // in pixels
|
|
||||||
val headRunOffset: Vector2d, // in pixels
|
|
||||||
val headSwimOffset: Vector2d, // in pixels
|
|
||||||
val runFallOffset: Double, // in pixels
|
|
||||||
val duckOffset: Double, // in pixels
|
|
||||||
val headDuckOffset: Vector2d, // in pixels
|
|
||||||
val sitOffset: Double, // in pixels
|
|
||||||
val layOffset: Double, // in pixels
|
|
||||||
val headSitOffset: Vector2d, // in pixels
|
|
||||||
val headLayOffset: Vector2d, // in pixels
|
|
||||||
val recoilOffset: Vector2d, // in pixels
|
|
||||||
val mouthOffset: Vector2d, // in pixels
|
|
||||||
val feetOffset: Vector2d, // in pixels
|
|
||||||
|
|
||||||
val bodyFullbright: Boolean = false,
|
|
||||||
|
|
||||||
val headArmorOffset: Vector2d, // in pixels
|
|
||||||
val chestArmorOffset: Vector2d, // in pixels
|
|
||||||
val legsArmorOffset: Vector2d, // in pixels
|
|
||||||
val backArmorOffset: Vector2d, // in pixels
|
|
||||||
|
|
||||||
val bodyHidden: Boolean = false,
|
|
||||||
|
|
||||||
val armWalkSeq: ImmutableList<Int>,
|
|
||||||
val armRunSeq: ImmutableList<Int>,
|
|
||||||
|
|
||||||
val walkBob: ImmutableList<Double>, // in pixels
|
|
||||||
val runBob: ImmutableList<Double>, // in pixels
|
|
||||||
val swimBob: ImmutableList<Double>, // in pixels
|
|
||||||
|
|
||||||
val jumpBob: Double,
|
|
||||||
val frontArmRotationCenter: Vector2d, // in pixels
|
|
||||||
val backArmRotationCenter: Vector2d, // in pixels
|
|
||||||
val frontHandPosition: Vector2d, // in pixels
|
|
||||||
val backArmOffset: Vector2d, // in pixels
|
|
||||||
val vaporTrailFrames: Int,
|
|
||||||
val vaporTrailCycle: Double,
|
|
||||||
|
|
||||||
val deathParticles: String,
|
|
||||||
val particleEmitters: JsonObject,
|
|
||||||
|
|
||||||
val movementParameters: ActorMovementParameters,
|
|
||||||
|
|
||||||
val personalities: ImmutableList<Personality>,
|
|
||||||
) {
|
|
||||||
data class Timing(
|
|
||||||
val stateCycle: ImmutableList<Double>? = null,
|
|
||||||
val emoteCycle: ImmutableList<Double>? = null,
|
|
||||||
val stateFrames: ImmutableList<Int>? = null,
|
|
||||||
val emoteFrames: ImmutableList<Int>? = null,
|
|
||||||
)
|
|
||||||
}
|
|
@ -6,9 +6,6 @@ import ru.dbotthepony.kommons.io.writeBinaryString
|
|||||||
import ru.dbotthepony.kommons.io.writeStruct2d
|
import ru.dbotthepony.kommons.io.writeStruct2d
|
||||||
import ru.dbotthepony.kommons.io.writeStruct2f
|
import ru.dbotthepony.kommons.io.writeStruct2f
|
||||||
import ru.dbotthepony.kommons.math.RGBAColor
|
import ru.dbotthepony.kommons.math.RGBAColor
|
||||||
import ru.dbotthepony.kstarbound.Registries
|
|
||||||
import ru.dbotthepony.kstarbound.Registry
|
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||||
import ru.dbotthepony.kstarbound.io.readColor
|
import ru.dbotthepony.kstarbound.io.readColor
|
||||||
import ru.dbotthepony.kstarbound.io.readInternedString
|
import ru.dbotthepony.kstarbound.io.readInternedString
|
||||||
@ -20,9 +17,9 @@ import java.io.DataInputStream
|
|||||||
import java.io.DataOutputStream
|
import java.io.DataOutputStream
|
||||||
|
|
||||||
@JsonFactory
|
@JsonFactory
|
||||||
data class HumanoidIdentity(
|
data class HumanoidData(
|
||||||
val name: String = "Humanoid",
|
val name: String = "Humanoid",
|
||||||
val species: Registry.Ref<Species> = Registries.species.ref("human"),
|
val species: String = "human",
|
||||||
val gender: Gender = Gender.MALE,
|
val gender: Gender = Gender.MALE,
|
||||||
|
|
||||||
val hairGroup: String = "hair",
|
val hairGroup: String = "hair",
|
||||||
@ -40,8 +37,8 @@ data class HumanoidIdentity(
|
|||||||
|
|
||||||
val color: RGBAColor = RGBAColor.BLACK,
|
val color: RGBAColor = RGBAColor.BLACK,
|
||||||
|
|
||||||
val personalityIdle: String = "idle.1",
|
val personalityIdle: String = "",
|
||||||
val personalityArmIdle: String = "idle.1",
|
val personalityArmIdle: String = "",
|
||||||
val personalityHeadOffset: Vector2d = Vector2d.ZERO,
|
val personalityHeadOffset: Vector2d = Vector2d.ZERO,
|
||||||
val personalityArmOffset: Vector2d = Vector2d.ZERO,
|
val personalityArmOffset: Vector2d = Vector2d.ZERO,
|
||||||
|
|
||||||
@ -51,28 +48,14 @@ data class HumanoidIdentity(
|
|||||||
get() = personalityIdle
|
get() = personalityIdle
|
||||||
override val armIdle: String
|
override val armIdle: String
|
||||||
get() = personalityArmIdle
|
get() = personalityArmIdle
|
||||||
override val headOffset: Vector2d
|
override val handOffset: Vector2d
|
||||||
get() = personalityHeadOffset
|
get() = personalityHeadOffset
|
||||||
override val armOffset: Vector2d
|
override val armOffset: Vector2d
|
||||||
get() = personalityArmOffset
|
get() = personalityArmOffset
|
||||||
|
|
||||||
fun check() {
|
|
||||||
if (Starbound.DEBUG_BUILD) {
|
|
||||||
check(hairType.isNotEmpty()) { "'hairType' is an empty string" }
|
|
||||||
check(hairGroup.isNotEmpty()) { "'hairGroup' is an empty string" }
|
|
||||||
// check(facialHairType.isNotEmpty()) { "'facialHairType' is an empty string" }
|
|
||||||
// check(facialMaskGroup.isNotEmpty()) { "'facialMaskGroup' is an empty string" }
|
|
||||||
// check(facialMaskType.isNotEmpty()) { "'facialMaskType' is an empty string" }
|
|
||||||
check(personalityIdle.isNotEmpty()) { "'personalityIdle' is an empty string" }
|
|
||||||
check(personalityArmIdle.isNotEmpty()) { "'personalityArmIdle' is an empty string" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun write(stream: DataOutputStream, isLegacy: Boolean) {
|
fun write(stream: DataOutputStream, isLegacy: Boolean) {
|
||||||
check()
|
|
||||||
|
|
||||||
stream.writeBinaryString(name)
|
stream.writeBinaryString(name)
|
||||||
stream.writeBinaryString(species.key.left())
|
stream.writeBinaryString(species)
|
||||||
stream.writeByte(gender.ordinal)
|
stream.writeByte(gender.ordinal)
|
||||||
stream.writeBinaryString(hairGroup)
|
stream.writeBinaryString(hairGroup)
|
||||||
stream.writeBinaryString(hairType)
|
stream.writeBinaryString(hairType)
|
||||||
@ -108,12 +91,12 @@ data class HumanoidIdentity(
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val CODEC = nativeCodec(::read, HumanoidIdentity::write)
|
val CODEC = nativeCodec(::read, HumanoidData::write)
|
||||||
val LEGACY_CODEC = legacyCodec(::read, HumanoidIdentity::write)
|
val LEGACY_CODEC = legacyCodec(::read, HumanoidData::write)
|
||||||
|
|
||||||
fun read(stream: DataInputStream, isLegacy: Boolean): HumanoidIdentity {
|
fun read(stream: DataInputStream, isLegacy: Boolean): HumanoidData {
|
||||||
val name: String = stream.readInternedString()
|
val name: String = stream.readInternedString()
|
||||||
val species = Registries.species.ref(stream.readInternedString())
|
val species: String = stream.readInternedString()
|
||||||
val gender: Gender = Gender.entries[stream.readUnsignedByte()]
|
val gender: Gender = Gender.entries[stream.readUnsignedByte()]
|
||||||
|
|
||||||
val hairGroup = stream.readInternedString()
|
val hairGroup = stream.readInternedString()
|
||||||
@ -136,7 +119,7 @@ data class HumanoidIdentity(
|
|||||||
val color: RGBAColor = if (isLegacy) RGBAColor(stream.readUnsignedByte(), stream.readUnsignedByte(), stream.readUnsignedByte(), stream.readUnsignedByte()) else stream.readColor()
|
val color: RGBAColor = if (isLegacy) RGBAColor(stream.readUnsignedByte(), stream.readUnsignedByte(), stream.readUnsignedByte(), stream.readUnsignedByte()) else stream.readColor()
|
||||||
val imagePath: String? = if (stream.readBoolean()) stream.readInternedString() else null
|
val imagePath: String? = if (stream.readBoolean()) stream.readInternedString() else null
|
||||||
|
|
||||||
return HumanoidIdentity(
|
return HumanoidData(
|
||||||
name, species, gender, hairGroup, hairType, hairDirectives, bodyDirectives,
|
name, species, gender, hairGroup, hairType, hairDirectives, bodyDirectives,
|
||||||
emoteDirectives, facialHairGroup, facialHairType, facialHairDirectives,
|
emoteDirectives, facialHairGroup, facialHairType, facialHairDirectives,
|
||||||
facialMaskGroup, facialMaskType, facialMaskDirectives, color, personalityIdle,
|
facialMaskGroup, facialMaskType, facialMaskDirectives, color, personalityIdle,
|
@ -1,11 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.defs.actor
|
|
||||||
|
|
||||||
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
|
|
||||||
|
|
||||||
enum class MoveControlType(override val jsonName: String) : IStringSerializable {
|
|
||||||
LEFT("left"),
|
|
||||||
RIGHT("right"),
|
|
||||||
DOWN("down"),
|
|
||||||
UP("up"),
|
|
||||||
JUMP("jump");
|
|
||||||
}
|
|
@ -1,415 +1,44 @@
|
|||||||
package ru.dbotthepony.kstarbound.defs.actor
|
package ru.dbotthepony.kstarbound.defs.actor
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList
|
import com.google.common.collect.ImmutableList
|
||||||
import com.google.common.collect.ImmutableMap
|
|
||||||
import com.google.gson.Gson
|
|
||||||
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
|
||||||
import com.google.gson.JsonObject
|
|
||||||
import com.google.gson.JsonPrimitive
|
|
||||||
import com.google.gson.TypeAdapter
|
|
||||||
import com.google.gson.annotations.JsonAdapter
|
|
||||||
import com.google.gson.stream.JsonReader
|
|
||||||
import com.google.gson.stream.JsonWriter
|
|
||||||
import kotlinx.coroutines.future.await
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import ru.dbotthepony.kommons.gson.contains
|
|
||||||
import ru.dbotthepony.kommons.gson.set
|
|
||||||
import ru.dbotthepony.kommons.gson.stream
|
|
||||||
import ru.dbotthepony.kommons.io.readBinaryString
|
|
||||||
import ru.dbotthepony.kommons.io.readMap
|
|
||||||
import ru.dbotthepony.kommons.io.writeBinaryString
|
|
||||||
import ru.dbotthepony.kommons.io.writeMap
|
|
||||||
import ru.dbotthepony.kommons.math.RGBAColor
|
|
||||||
import ru.dbotthepony.kommons.util.Either
|
|
||||||
import ru.dbotthepony.kstarbound.Registries
|
import ru.dbotthepony.kstarbound.Registries
|
||||||
import ru.dbotthepony.kstarbound.Registry
|
import ru.dbotthepony.kstarbound.Registry
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
import ru.dbotthepony.kstarbound.defs.ActorMovementParameters
|
|
||||||
import ru.dbotthepony.kstarbound.defs.AssetPath
|
import ru.dbotthepony.kstarbound.defs.AssetPath
|
||||||
import ru.dbotthepony.kstarbound.defs.EntityDamageTeam
|
|
||||||
import ru.dbotthepony.kstarbound.defs.TeamType
|
|
||||||
import ru.dbotthepony.kstarbound.defs.TouchDamage
|
|
||||||
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor
|
|
||||||
import ru.dbotthepony.kstarbound.defs.item.TreasurePoolDefinition
|
|
||||||
import ru.dbotthepony.kstarbound.io.readColor
|
|
||||||
import ru.dbotthepony.kstarbound.io.readDouble
|
|
||||||
import ru.dbotthepony.kstarbound.io.readInternedString
|
|
||||||
import ru.dbotthepony.kstarbound.io.writeColor
|
|
||||||
import ru.dbotthepony.kstarbound.io.writeDouble
|
|
||||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||||
import ru.dbotthepony.kstarbound.json.mergeJson
|
|
||||||
import ru.dbotthepony.kstarbound.json.readJsonElement
|
|
||||||
import ru.dbotthepony.kstarbound.json.writeJsonElement
|
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||||
import ru.dbotthepony.kstarbound.util.coalesceNull
|
|
||||||
import ru.dbotthepony.kstarbound.util.random.nextRange
|
import ru.dbotthepony.kstarbound.util.random.nextRange
|
||||||
import ru.dbotthepony.kstarbound.util.random.random
|
import ru.dbotthepony.kstarbound.util.random.random
|
||||||
import java.io.DataInputStream
|
import java.util.random.RandomGenerator
|
||||||
import java.io.DataOutputStream
|
|
||||||
import kotlin.jvm.optionals.getOrNull
|
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
@JsonAdapter(NPCVariant.Adapter::class)
|
|
||||||
data class NPCVariant(
|
data class NPCVariant(
|
||||||
val species: Registry.Entry<Species>,
|
val species: Registry.Entry<Species>,
|
||||||
val seed: Long,
|
|
||||||
val typeName: String,
|
|
||||||
val level: Double,
|
|
||||||
val overrides: JsonElement,
|
|
||||||
val humanoidConfig: HumanoidConfig,
|
|
||||||
val humanoidIdentity: HumanoidIdentity,
|
|
||||||
val scripts: ImmutableList<AssetPath>,
|
val scripts: ImmutableList<AssetPath>,
|
||||||
val initialScriptDelta: Double = 5.0,
|
val initialScriptDelta: Int = 5,
|
||||||
val scriptConfig: JsonElement = JsonNull.INSTANCE,
|
|
||||||
val movementParameters: ActorMovementParameters = ActorMovementParameters.EMPTY,
|
|
||||||
val statusControllerSettings: StatusControllerConfig = StatusControllerConfig.EMPTY,
|
|
||||||
val innateStatusEffects: ImmutableList<PersistentStatusEffect> = ImmutableList.of(),
|
|
||||||
val touchDamage: TouchDamage = TouchDamage.EMPTY,
|
|
||||||
val disableWornArmor: Boolean = true,
|
|
||||||
val dropPools: ImmutableList<Registry.Ref<TreasurePoolDefinition>> = ImmutableList.of(),
|
|
||||||
val persistent: Boolean = false,
|
|
||||||
val keepAlive: Boolean = false,
|
|
||||||
val damageTeam: Int = 0,
|
|
||||||
val damageTeamType: TeamType = TeamType.ENEMY,
|
|
||||||
val nametagColor: RGBAColor = RGBAColor.WHITE,
|
|
||||||
val items: ImmutableMap<String, ItemDescriptor> = ImmutableMap.of(),
|
|
||||||
) {
|
) {
|
||||||
@JsonFactory
|
@JsonFactory
|
||||||
data class SerializedData(
|
data class SerializedData(
|
||||||
val levelVariance: Vector2d = Vector2d.ZERO,
|
val levelVariance: Vector2d = Vector2d.ZERO,
|
||||||
val scripts: ImmutableList<AssetPath>,
|
val scripts: ImmutableList<AssetPath>,
|
||||||
val initialScriptDelta: Double = 5.0,
|
val initialScriptDelta: Int = 5,
|
||||||
val scriptConfig: JsonElement = JsonNull.INSTANCE,
|
val scriptConfig: JsonElement = JsonNull.INSTANCE,
|
||||||
val humanoidConfig: String? = null,
|
val humanoidConfig: String? = null,
|
||||||
val npcname: String? = null,
|
|
||||||
val nameGen: ImmutableList<String>? = null,
|
|
||||||
val movementParameters: ActorMovementParameters = ActorMovementParameters.EMPTY,
|
|
||||||
val statusControllerSettings: StatusControllerConfig = StatusControllerConfig.EMPTY,
|
|
||||||
val innateStatusEffects: ImmutableList<PersistentStatusEffect> = ImmutableList.of(),
|
|
||||||
val touchDamage: TouchDamage = TouchDamage.EMPTY,
|
|
||||||
val disableWornArmor: Boolean = true,
|
|
||||||
val dropPools: ImmutableList<Registry.Ref<TreasurePoolDefinition>> = ImmutableList.of(),
|
|
||||||
val persistent: Boolean = false,
|
|
||||||
val keepAlive: Boolean = false,
|
|
||||||
val damageTeam: Int = 0,
|
|
||||||
val damageTeamType: TeamType = TeamType.ENEMY,
|
|
||||||
val nametagColor: RGBAColor = RGBAColor.WHITE,
|
|
||||||
val matchColorIndices: Boolean = false,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val team: EntityDamageTeam
|
|
||||||
get() = EntityDamageTeam(damageTeamType, damageTeam)
|
|
||||||
|
|
||||||
fun check() {
|
|
||||||
if (Starbound.DEBUG_BUILD) {
|
|
||||||
humanoidIdentity.check()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun write(stream: DataOutputStream, isLegacy: Boolean) {
|
|
||||||
stream.writeBinaryString(species.key)
|
|
||||||
stream.writeBinaryString(typeName)
|
|
||||||
stream.writeDouble(level, isLegacy)
|
|
||||||
stream.writeLong(seed)
|
|
||||||
stream.writeJsonElement(overrides)
|
|
||||||
|
|
||||||
if (isLegacy)
|
|
||||||
stream.writeInt(initialScriptDelta.toInt())
|
|
||||||
else
|
|
||||||
stream.writeDouble(initialScriptDelta)
|
|
||||||
|
|
||||||
humanoidIdentity.write(stream, isLegacy)
|
|
||||||
stream.writeMap(items, { writeBinaryString(it) }, { it.write(this) })
|
|
||||||
stream.writeBoolean(persistent)
|
|
||||||
stream.writeBoolean(keepAlive)
|
|
||||||
stream.writeByte(damageTeam)
|
|
||||||
stream.writeByte(damageTeamType.ordinal)
|
|
||||||
|
|
||||||
if (!isLegacy) {
|
|
||||||
stream.writeBoolean(disableWornArmor)
|
|
||||||
touchDamage.write(stream)
|
|
||||||
stream.writeColor(nametagColor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonFactory
|
|
||||||
data class DiskSerializedData(
|
|
||||||
val species: Registry.Entry<Species>,
|
|
||||||
val typeName: String,
|
|
||||||
val level: Double,
|
|
||||||
val seed: Long,
|
|
||||||
val overrides: JsonElement,
|
|
||||||
val initialScriptDelta: Double = 5.0,
|
|
||||||
val humanoidIdentity: HumanoidIdentity,
|
|
||||||
val items: ImmutableMap<String, ItemDescriptor>,
|
|
||||||
val persistent: Boolean,
|
|
||||||
val keepAlive: Boolean,
|
|
||||||
val damageTeam: Int,
|
|
||||||
val damageTeamType: TeamType,
|
|
||||||
val touchDamage: TouchDamage? = null,
|
|
||||||
val disableWornArmor: Boolean? = null,
|
|
||||||
val nametagColor: RGBAColor? = null,
|
|
||||||
)
|
|
||||||
|
|
||||||
class Adapter(gson: Gson) : TypeAdapter<NPCVariant>() {
|
|
||||||
private val parent = gson.getAdapter(DiskSerializedData::class.java)
|
|
||||||
|
|
||||||
override fun write(out: JsonWriter, value: NPCVariant) {
|
|
||||||
parent.write(out, DiskSerializedData(
|
|
||||||
species = value.species,
|
|
||||||
typeName = value.typeName,
|
|
||||||
level = value.level,
|
|
||||||
seed = value.seed,
|
|
||||||
overrides = value.overrides,
|
|
||||||
initialScriptDelta = value.initialScriptDelta,
|
|
||||||
humanoidIdentity = value.humanoidIdentity,
|
|
||||||
items = value.items,
|
|
||||||
persistent = value.persistent,
|
|
||||||
keepAlive = value.keepAlive,
|
|
||||||
damageTeam = value.damageTeam,
|
|
||||||
damageTeamType = value.damageTeamType,
|
|
||||||
touchDamage = value.touchDamage,
|
|
||||||
disableWornArmor = value.disableWornArmor,
|
|
||||||
nametagColor = value.nametagColor,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun read(`in`: JsonReader): NPCVariant {
|
|
||||||
val (
|
|
||||||
species,
|
|
||||||
typeName,
|
|
||||||
level,
|
|
||||||
seed,
|
|
||||||
overrides,
|
|
||||||
initialScriptDelta,
|
|
||||||
humanoidIdentity,
|
|
||||||
items,
|
|
||||||
persistent,
|
|
||||||
keepAlive,
|
|
||||||
damageTeam,
|
|
||||||
damageTeamType,
|
|
||||||
touchDamage,
|
|
||||||
disableWornArmor,
|
|
||||||
nametagColor,
|
|
||||||
) = parent.read(`in`)
|
|
||||||
|
|
||||||
val config = Registries.buildNPCConfig(typeName, overrides)
|
|
||||||
val serialized = Starbound.gson.fromJson(config, SerializedData::class.java)
|
|
||||||
|
|
||||||
val humanoidConfig = runBlocking { humanoidConfig(serialized, species) }
|
|
||||||
|
|
||||||
val movementParameters = humanoidConfig
|
|
||||||
.movementParameters
|
|
||||||
.merge(serialized.movementParameters)
|
|
||||||
|
|
||||||
return NPCVariant(
|
|
||||||
species = species,
|
|
||||||
seed = seed,
|
|
||||||
typeName = typeName,
|
|
||||||
level = level,
|
|
||||||
humanoidConfig = humanoidConfig,
|
|
||||||
humanoidIdentity = humanoidIdentity,
|
|
||||||
overrides = overrides.deepCopy(),
|
|
||||||
scripts = serialized.scripts,
|
|
||||||
initialScriptDelta = initialScriptDelta,
|
|
||||||
scriptConfig = serialized.scriptConfig,
|
|
||||||
movementParameters = movementParameters,
|
|
||||||
statusControllerSettings = serialized.statusControllerSettings,
|
|
||||||
innateStatusEffects = ImmutableList.copyOf(innates(serialized, level)),
|
|
||||||
touchDamage = touchDamage ?: serialized.touchDamage,
|
|
||||||
disableWornArmor = disableWornArmor ?: serialized.disableWornArmor,
|
|
||||||
dropPools = serialized.dropPools,
|
|
||||||
persistent = persistent,
|
|
||||||
keepAlive = keepAlive,
|
|
||||||
damageTeam = damageTeam,
|
|
||||||
damageTeamType = damageTeamType,
|
|
||||||
nametagColor = nametagColor ?: serialized.nametagColor,
|
|
||||||
items = ImmutableMap.copyOf(items)
|
|
||||||
).also { it.check() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
suspend fun humanoidConfig(serialized: SerializedData, species: Registry.Entry<Species>): HumanoidConfig {
|
suspend fun create(species: Registry.Entry<Species>, type: String, level: Double, random: RandomGenerator, overrides: JsonElement = JsonNull.INSTANCE): NPCVariant {
|
||||||
if (serialized.humanoidConfig != null)
|
|
||||||
return Starbound.gson.fromJson(Starbound.loadJsonAsset(serialized.humanoidConfig).await() as JsonObject, HumanoidConfig::class.java)
|
|
||||||
else
|
|
||||||
return species.value.humanoidConfig()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun innates(serialized: SerializedData, level: Double): ArrayList<PersistentStatusEffect> {
|
|
||||||
val innateStatusEffects = ArrayList(serialized.innateStatusEffects)
|
|
||||||
|
|
||||||
innateStatusEffects.add(
|
|
||||||
Either.left(StatModifier.value("powerMultiplier", Registries.jsonFunctions.getOrThrow("npcLevelPowerMultiplierModifier").value.evaluate(level)))
|
|
||||||
)
|
|
||||||
|
|
||||||
innateStatusEffects.add(
|
|
||||||
Either.left(StatModifier.multBase("protection", Registries.jsonFunctions.getOrThrow("npcLevelProtectionMultiplier").value.evaluate(level)))
|
|
||||||
)
|
|
||||||
|
|
||||||
innateStatusEffects.add(
|
|
||||||
Either.left(StatModifier.multBase("maxHealth", Registries.jsonFunctions.getOrThrow("npcLevelHealthMultiplier").value.evaluate(level)))
|
|
||||||
)
|
|
||||||
|
|
||||||
innateStatusEffects.add(
|
|
||||||
Either.left(StatModifier.multBase("maxEnergy", Registries.jsonFunctions.getOrThrow("npcLevelEnergyMultiplier").value.evaluate(level)))
|
|
||||||
)
|
|
||||||
|
|
||||||
return innateStatusEffects
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
|
||||||
suspend fun read(stream: DataInputStream, isLegacy: Boolean): NPCVariant {
|
|
||||||
val species = Registries.species.getOrThrow(stream.readBinaryString())
|
|
||||||
val type = stream.readInternedString()
|
|
||||||
val level = stream.readDouble(isLegacy)
|
|
||||||
val seed = stream.readLong()
|
|
||||||
val overrides = stream.readJsonElement()
|
|
||||||
|
|
||||||
val initialScriptDelta = if (isLegacy) stream.readInt().toDouble() else stream.readDouble()
|
|
||||||
val identity = HumanoidIdentity.read(stream, isLegacy)
|
|
||||||
val items = stream.readMap({ readInternedString() }, { ItemDescriptor(this) })
|
|
||||||
val persistent = stream.readBoolean()
|
|
||||||
val keepAlive = stream.readBoolean()
|
|
||||||
val damageTeam = stream.readUnsignedByte()
|
|
||||||
val damageTeamType = TeamType.entries[stream.readUnsignedByte()]
|
|
||||||
|
|
||||||
val config = Registries.buildNPCConfig(type, overrides)
|
|
||||||
val serialized = Starbound.gson.fromJson(config, SerializedData::class.java)
|
|
||||||
|
|
||||||
val humanoidConfig = humanoidConfig(serialized, species)
|
|
||||||
|
|
||||||
val movementParameters = humanoidConfig
|
|
||||||
.movementParameters
|
|
||||||
.merge(serialized.movementParameters)
|
|
||||||
|
|
||||||
val disableWornArmor = if (isLegacy) serialized.disableWornArmor else stream.readBoolean()
|
|
||||||
val touchDamage = if (isLegacy) serialized.touchDamage else TouchDamage(stream)
|
|
||||||
val nametagColor = if (isLegacy) serialized.nametagColor else stream.readColor()
|
|
||||||
|
|
||||||
return NPCVariant(
|
|
||||||
species = species,
|
|
||||||
seed = seed,
|
|
||||||
typeName = type,
|
|
||||||
level = level,
|
|
||||||
humanoidConfig = humanoidConfig,
|
|
||||||
humanoidIdentity = identity,
|
|
||||||
overrides = overrides.deepCopy(),
|
|
||||||
scripts = serialized.scripts,
|
|
||||||
initialScriptDelta = initialScriptDelta,
|
|
||||||
scriptConfig = serialized.scriptConfig,
|
|
||||||
movementParameters = movementParameters,
|
|
||||||
statusControllerSettings = serialized.statusControllerSettings,
|
|
||||||
innateStatusEffects = ImmutableList.copyOf(innates(serialized, level)),
|
|
||||||
touchDamage = touchDamage,
|
|
||||||
disableWornArmor = disableWornArmor,
|
|
||||||
dropPools = serialized.dropPools,
|
|
||||||
persistent = persistent,
|
|
||||||
keepAlive = keepAlive,
|
|
||||||
damageTeam = damageTeam,
|
|
||||||
damageTeamType = damageTeamType,
|
|
||||||
nametagColor = nametagColor,
|
|
||||||
items = ImmutableMap.copyOf(items)
|
|
||||||
).also { it.check() }
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun create(species: Registry.Entry<Species>, type: String, level: Double, seed: Long, overrides: JsonElement = JsonNull.INSTANCE): NPCVariant {
|
|
||||||
val random = random(seed)
|
|
||||||
val config = Registries.buildNPCConfig(type, overrides)
|
val config = Registries.buildNPCConfig(type, overrides)
|
||||||
|
|
||||||
val serialized = Starbound.gson.fromJson(config, SerializedData::class.java)
|
val serialized = Starbound.gson.fromJson(config, SerializedData::class.java)
|
||||||
val finalLevel = max(0.0, random.nextRange(serialized.levelVariance) + level)
|
val finalLevel = max(0.0, random.nextRange(serialized.levelVariance) + level)
|
||||||
|
TODO()
|
||||||
val humanoidConfig = humanoidConfig(serialized, species)
|
|
||||||
|
|
||||||
var identity = species.value.generateHumanoid(random)
|
|
||||||
|
|
||||||
val name = serialized.npcname ?: serialized.nameGen?.let { Starbound.generateNameAsync(it[identity.gender.ordinal], random) } ?: ""
|
|
||||||
val personality = humanoidConfig.personalities.random(random)
|
|
||||||
|
|
||||||
identity = identity.copy(
|
|
||||||
name = name,
|
|
||||||
personalityIdle = personality.idle,
|
|
||||||
personalityArmIdle = personality.armIdle,
|
|
||||||
personalityHeadOffset = personality.headOffset,
|
|
||||||
personalityArmOffset = personality.armOffset
|
|
||||||
)
|
|
||||||
|
|
||||||
if ("identity" in config)
|
|
||||||
identity = Starbound.gson.fromJson(mergeJson(Starbound.gson.toJsonTree(identity), config["identity"]!!), HumanoidIdentity::class.java)
|
|
||||||
|
|
||||||
val movementParameters = humanoidConfig
|
|
||||||
.movementParameters
|
|
||||||
.merge(serialized.movementParameters)
|
|
||||||
|
|
||||||
val items = ImmutableMap.Builder<String, ItemDescriptor>()
|
|
||||||
|
|
||||||
if ("items" in config) {
|
|
||||||
val itemsConfig = config.getAsJsonObject("items")
|
|
||||||
|
|
||||||
val speciesItemsConfig = itemsConfig["override"]?.coalesceNull?.asJsonArray
|
|
||||||
?: itemsConfig[species.key]?.coalesceNull?.asJsonArray
|
|
||||||
?: itemsConfig["default"]?.coalesceNull?.asJsonArray
|
|
||||||
|
|
||||||
if (speciesItemsConfig != null) {
|
|
||||||
val highestLevelItemsConfig = speciesItemsConfig.stream()
|
|
||||||
.map { it.asJsonArray.let { it[0].asDouble to it[1] } }
|
|
||||||
.filter { it.first <= finalLevel }
|
|
||||||
.sorted { o1, o2 -> o2.first.compareTo(o1.first) }
|
|
||||||
.findFirst()
|
|
||||||
.getOrNull()?.second as JsonArray?
|
|
||||||
|
|
||||||
if (highestLevelItemsConfig != null) {
|
|
||||||
var randomColorIndex = -1
|
|
||||||
|
|
||||||
for (itemSlotConfig in highestLevelItemsConfig.random(random).asJsonObject.entrySet()) {
|
|
||||||
var item = ItemDescriptor(itemSlotConfig.value.asJsonArray.random(random))
|
|
||||||
val colorIndex = item.parameters["colorIndex"]
|
|
||||||
|
|
||||||
// Randomize color index if colorIndex is an array
|
|
||||||
if (colorIndex is JsonArray) {
|
|
||||||
if (!serialized.matchColorIndices || randomColorIndex == -1) {
|
|
||||||
randomColorIndex = colorIndex.random(random).asInt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
item = item.applyParameters(JsonObject().also {
|
suspend fun create(species: Registry.Entry<Species>, type: String, level: Double, seed: Long, overrides: JsonElement = JsonNull.INSTANCE): NPCVariant {
|
||||||
it["colorIndex"] = JsonPrimitive(randomColorIndex)
|
return create(species, type, level, random(seed), overrides)
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
items.put(itemSlotConfig.key, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NPCVariant(
|
|
||||||
species = species,
|
|
||||||
seed = seed,
|
|
||||||
typeName = type,
|
|
||||||
level = finalLevel,
|
|
||||||
humanoidConfig = humanoidConfig,
|
|
||||||
humanoidIdentity = identity,
|
|
||||||
overrides = overrides.deepCopy(),
|
|
||||||
scripts = serialized.scripts,
|
|
||||||
initialScriptDelta = serialized.initialScriptDelta,
|
|
||||||
scriptConfig = serialized.scriptConfig,
|
|
||||||
movementParameters = movementParameters,
|
|
||||||
statusControllerSettings = serialized.statusControllerSettings,
|
|
||||||
innateStatusEffects = ImmutableList.copyOf(innates(serialized, finalLevel)),
|
|
||||||
touchDamage = serialized.touchDamage,
|
|
||||||
disableWornArmor = serialized.disableWornArmor,
|
|
||||||
dropPools = serialized.dropPools,
|
|
||||||
persistent = serialized.persistent,
|
|
||||||
keepAlive = serialized.keepAlive,
|
|
||||||
damageTeam = serialized.damageTeam,
|
|
||||||
damageTeamType = serialized.damageTeamType,
|
|
||||||
nametagColor = serialized.nametagColor,
|
|
||||||
items = items.buildOrThrow()
|
|
||||||
).also { it.check() }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,50 +1,20 @@
|
|||||||
package ru.dbotthepony.kstarbound.defs.actor
|
package ru.dbotthepony.kstarbound.defs.actor
|
||||||
|
|
||||||
import ru.dbotthepony.kommons.io.writeBinaryString
|
|
||||||
import ru.dbotthepony.kommons.io.writeStruct2d
|
|
||||||
import ru.dbotthepony.kstarbound.io.readInternedString
|
|
||||||
import ru.dbotthepony.kstarbound.io.readVector2d
|
|
||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
||||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||||
import java.io.DataInputStream
|
|
||||||
import java.io.DataOutputStream
|
|
||||||
|
|
||||||
// this is because personality data structure is used inconsistently across original source code
|
// this is because personality data structure is used inconsistently across original source code
|
||||||
interface IPersonality {
|
interface IPersonality {
|
||||||
val idle: String
|
val idle: String
|
||||||
val armIdle: String
|
val armIdle: String
|
||||||
val headOffset: Vector2d
|
val handOffset: Vector2d
|
||||||
val armOffset: Vector2d
|
val armOffset: Vector2d
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonFactory(asList = 1)
|
@JsonFactory(asList = true)
|
||||||
data class Personality(
|
data class Personality(
|
||||||
override val idle: String,
|
override val idle: String,
|
||||||
override val armIdle: String,
|
override val armIdle: String,
|
||||||
override val headOffset: Vector2d,
|
override val handOffset: Vector2d,
|
||||||
override val armOffset: Vector2d
|
override val armOffset: Vector2d
|
||||||
) : IPersonality {
|
) : IPersonality
|
||||||
/**
|
|
||||||
* new protocol only
|
|
||||||
*/
|
|
||||||
constructor(stream: DataInputStream) : this(
|
|
||||||
stream.readInternedString(),
|
|
||||||
stream.readInternedString(),
|
|
||||||
stream.readVector2d(),
|
|
||||||
stream.readVector2d(),
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* new protocol only
|
|
||||||
*/
|
|
||||||
fun write(stream: DataOutputStream) {
|
|
||||||
stream.writeBinaryString(idle)
|
|
||||||
stream.writeBinaryString(armIdle)
|
|
||||||
stream.writeStruct2d(headOffset)
|
|
||||||
stream.writeStruct2d(armOffset)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val ORIGINAL_ENGINE_VERY_COMPATIBLE_COMPATIBILITY = Personality("", "", Vector2d.ZERO, Vector2d.ZERO)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -2,33 +2,18 @@ package ru.dbotthepony.kstarbound.defs.actor
|
|||||||
|
|
||||||
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.JsonObject
|
|
||||||
import kotlinx.coroutines.future.await
|
|
||||||
import ru.dbotthepony.kommons.collect.filterNotNull
|
|
||||||
import ru.dbotthepony.kommons.util.Either
|
|
||||||
import ru.dbotthepony.kstarbound.Registries
|
|
||||||
import ru.dbotthepony.kstarbound.Registry
|
import ru.dbotthepony.kstarbound.Registry
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
|
||||||
import ru.dbotthepony.kstarbound.defs.AssetPath
|
import ru.dbotthepony.kstarbound.defs.AssetPath
|
||||||
import ru.dbotthepony.kstarbound.defs.ColorReplacements
|
import ru.dbotthepony.kstarbound.defs.ColorReplacements
|
||||||
import ru.dbotthepony.kstarbound.defs.StatusEffectDefinition
|
import ru.dbotthepony.kstarbound.defs.StatusEffectDefinition
|
||||||
import ru.dbotthepony.kstarbound.defs.image.SpriteReference
|
import ru.dbotthepony.kstarbound.defs.image.SpriteReference
|
||||||
import ru.dbotthepony.kstarbound.defs.actor.player.BlueprintLearnList
|
import ru.dbotthepony.kstarbound.defs.actor.player.BlueprintLearnList
|
||||||
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor
|
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor
|
||||||
import ru.dbotthepony.kstarbound.fromJson
|
|
||||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||||
import ru.dbotthepony.kstarbound.json.mergeJson
|
|
||||||
import ru.dbotthepony.kstarbound.util.getWrapAround
|
|
||||||
import ru.dbotthepony.kstarbound.util.random.random
|
|
||||||
import java.util.random.RandomGenerator
|
|
||||||
|
|
||||||
@JsonFactory
|
@JsonFactory
|
||||||
data class Species(
|
data class Species(
|
||||||
val kind: String,
|
val kind: String,
|
||||||
@Deprecated("Use humanoidConfig() function", replaceWith = ReplaceWith("this.humanoidConfig()"))
|
|
||||||
val humanoidConfig: AssetPath = AssetPath("/humanoid.config"),
|
|
||||||
@Deprecated("Use humanoidConfig() function", replaceWith = ReplaceWith("this.humanoidConfig()"))
|
|
||||||
val humanoidOverrides: JsonObject = JsonObject(),
|
|
||||||
val charCreationTooltip: Tooltip = Tooltip(),
|
val charCreationTooltip: Tooltip = Tooltip(),
|
||||||
val nameGen: ImmutableList<String>,
|
val nameGen: ImmutableList<String>,
|
||||||
val ouchNoises: ImmutableList<AssetPath>,
|
val ouchNoises: ImmutableList<AssetPath>,
|
||||||
@ -36,8 +21,6 @@ data class Species(
|
|||||||
val skull: AssetPath = AssetPath("/humanoid/any/dead.png"), // ваш свет угасает
|
val skull: AssetPath = AssetPath("/humanoid/any/dead.png"), // ваш свет угасает
|
||||||
val defaultBlueprints: BlueprintLearnList,
|
val defaultBlueprints: BlueprintLearnList,
|
||||||
|
|
||||||
val effectDirectives: String = "",
|
|
||||||
|
|
||||||
val headOptionAsHairColor: Boolean = false,
|
val headOptionAsHairColor: Boolean = false,
|
||||||
val headOptionAsFacialhair: Boolean = false,
|
val headOptionAsFacialhair: Boolean = false,
|
||||||
val altOptionAsUndyColor: Boolean = false,
|
val altOptionAsUndyColor: Boolean = false,
|
||||||
@ -61,92 +44,13 @@ data class Species(
|
|||||||
val name: String = "",
|
val name: String = "",
|
||||||
val image: SpriteReference = SpriteReference.NEVER,
|
val image: SpriteReference = SpriteReference.NEVER,
|
||||||
val characterImage: SpriteReference = SpriteReference.NEVER,
|
val characterImage: SpriteReference = SpriteReference.NEVER,
|
||||||
val hairGroup: String = "hair",
|
val hairGroup: String? = null,
|
||||||
val hair: ImmutableSet<String>,
|
val hair: ImmutableSet<String>,
|
||||||
val shirt: ImmutableSet<ItemDescriptor>,
|
val shirt: ImmutableSet<ItemDescriptor>,
|
||||||
val pants: ImmutableSet<ItemDescriptor>,
|
val pants: ImmutableSet<ItemDescriptor>,
|
||||||
val facialHairGroup: String = "",
|
val facialHairGroup: String? = null,
|
||||||
val facialHair: ImmutableSet<String> = ImmutableSet.of(),
|
val facialHair: ImmutableSet<String> = ImmutableSet.of(),
|
||||||
val facialMaskGroup: String = "",
|
val facialMaskGroup: String? = null,
|
||||||
val facialMask: ImmutableList<String> = ImmutableList.of(),
|
val facialMask: ImmutableList<String> = ImmutableList.of(),
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun humanoidConfig(): HumanoidConfig {
|
|
||||||
return Starbound.gson.fromJson(mergeJson(Starbound.loadJsonAsset(humanoidConfig.fullPath).thenApply { it ?: JsonObject() }.await().deepCopy(), humanoidOverrides), HumanoidConfig::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun buildStatusEffects(): List<PersistentStatusEffect> {
|
|
||||||
return statusEffects.stream().filter { it.isPresent }.map { Either.right<StatModifier, Registry.Ref<StatusEffectDefinition>>(it) }.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun generateHumanoid(random: RandomGenerator): HumanoidIdentity {
|
|
||||||
val gender = Gender.valueOf(random.nextBoolean())
|
|
||||||
val name = Starbound.generateNameAsync(nameGen[gender.ordinal], random)
|
|
||||||
|
|
||||||
val genderSettings = genders[gender.ordinal]
|
|
||||||
var bodyColor = bodyColor.random(random).toImageOperator()
|
|
||||||
|
|
||||||
var altColor = ""
|
|
||||||
|
|
||||||
val altOpt = random.nextInt()
|
|
||||||
val headOpt = random.nextInt()
|
|
||||||
val hairOpt = random.nextInt()
|
|
||||||
|
|
||||||
if (altOptionAsUndyColor) {
|
|
||||||
altColor = undyColor.getWrapAround(altOpt).toImageOperator()
|
|
||||||
}
|
|
||||||
|
|
||||||
val hair = genderSettings.hair.getWrapAround(hairOpt)
|
|
||||||
var hairColor = bodyColor
|
|
||||||
|
|
||||||
if (headOptionAsHairColor) {
|
|
||||||
hairColor = this.hairColor.getWrapAround(headOpt).toImageOperator()
|
|
||||||
if (altOptionAsHairColor) hairColor += this.undyColor.getWrapAround(altOpt).toImageOperator()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hairColorAsBodySubColor)
|
|
||||||
bodyColor += hairColor
|
|
||||||
|
|
||||||
var facialHair = ""
|
|
||||||
var facialHairGroup = ""
|
|
||||||
var facialHairDirective = ""
|
|
||||||
|
|
||||||
if (headOptionAsFacialhair) {
|
|
||||||
facialHair = genderSettings.facialHair.getWrapAround(headOpt)
|
|
||||||
facialHairGroup = genderSettings.facialHairGroup
|
|
||||||
facialHairDirective = hairColor
|
|
||||||
}
|
|
||||||
|
|
||||||
var facialMask = ""
|
|
||||||
var facialMaskGroup = ""
|
|
||||||
var facialMaskDirectives = ""
|
|
||||||
|
|
||||||
if (altOptionAsFacialMask) {
|
|
||||||
facialMask = genderSettings.facialMask.getWrapAround(altOpt)
|
|
||||||
facialMaskGroup = genderSettings.facialMaskGroup
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bodyColorAsFacialMaskSubColor)
|
|
||||||
facialMaskDirectives += bodyColor
|
|
||||||
|
|
||||||
if (altColorAsFacialMaskSubColor)
|
|
||||||
facialMaskDirectives += altColor
|
|
||||||
|
|
||||||
return HumanoidIdentity(
|
|
||||||
name = name,
|
|
||||||
species = Registries.species.getOrThrow(kind).ref,
|
|
||||||
gender = gender,
|
|
||||||
hairGroup = genderSettings.hairGroup,
|
|
||||||
hairType = hair,
|
|
||||||
hairDirectives = hairColor,
|
|
||||||
bodyDirectives = bodyColor + altColor,
|
|
||||||
emoteDirectives = bodyColor + altColor,
|
|
||||||
facialHairGroup = facialHairGroup,
|
|
||||||
facialHairType = facialHair,
|
|
||||||
facialHairDirectives = facialHairDirective,
|
|
||||||
facialMaskGroup = facialMaskGroup,
|
|
||||||
facialMaskType = facialMask,
|
|
||||||
facialMaskDirectives = facialMaskDirectives
|
|
||||||
).also { it.check() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -11,12 +11,6 @@ typealias PersistentStatusEffect = Either<StatModifier, Registry.Ref<StatusEffec
|
|||||||
// uint8_t
|
// uint8_t
|
||||||
enum class Gender(override val jsonName: String) : IStringSerializable {
|
enum class Gender(override val jsonName: String) : IStringSerializable {
|
||||||
MALE("Male"), FEMALE("Female");
|
MALE("Male"), FEMALE("Female");
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun valueOf(value: Boolean): Gender {
|
|
||||||
return if (value) MALE else FEMALE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// int32_t
|
// int32_t
|
||||||
|
@ -31,8 +31,6 @@ data class PlayerConfig(
|
|||||||
|
|
||||||
val defaultBlueprints: BlueprintLearnList,
|
val defaultBlueprints: BlueprintLearnList,
|
||||||
|
|
||||||
val blinkInterval: Vector2d,
|
|
||||||
|
|
||||||
val defaultCodexes: ImmutableMap<String, ImmutableList<ItemDescriptor>>,
|
val defaultCodexes: ImmutableMap<String, ImmutableList<ItemDescriptor>>,
|
||||||
val metaBoundBox: AABB,
|
val metaBoundBox: AABB,
|
||||||
val movementParameters: ActorMovementParameters,
|
val movementParameters: ActorMovementParameters,
|
||||||
|
@ -1,29 +1,18 @@
|
|||||||
package ru.dbotthepony.kstarbound.defs.dungeon
|
package ru.dbotthepony.kstarbound.defs.dungeon
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList
|
import com.google.common.collect.ImmutableList
|
||||||
import com.google.common.collect.ImmutableSet
|
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import com.google.gson.JsonPrimitive
|
|
||||||
import com.google.gson.JsonSyntaxException
|
import com.google.gson.JsonSyntaxException
|
||||||
import com.google.gson.TypeAdapter
|
import com.google.gson.TypeAdapter
|
||||||
import com.google.gson.annotations.JsonAdapter
|
import com.google.gson.annotations.JsonAdapter
|
||||||
import com.google.gson.stream.JsonReader
|
import com.google.gson.stream.JsonReader
|
||||||
import com.google.gson.stream.JsonWriter
|
import com.google.gson.stream.JsonWriter
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import ru.dbotthepony.kommons.collect.collect
|
|
||||||
import ru.dbotthepony.kommons.collect.filterNotNull
|
|
||||||
import ru.dbotthepony.kommons.collect.map
|
|
||||||
import ru.dbotthepony.kommons.gson.contains
|
|
||||||
import ru.dbotthepony.kommons.gson.set
|
|
||||||
import ru.dbotthepony.kommons.util.Either
|
|
||||||
import ru.dbotthepony.kstarbound.Registries
|
|
||||||
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.defs.actor.Species
|
|
||||||
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor
|
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor
|
||||||
import ru.dbotthepony.kstarbound.defs.monster.MonsterTypeDefinition
|
|
||||||
import ru.dbotthepony.kstarbound.defs.`object`.ObjectDefinition
|
import ru.dbotthepony.kstarbound.defs.`object`.ObjectDefinition
|
||||||
import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials
|
import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials
|
||||||
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
||||||
@ -34,10 +23,8 @@ import ru.dbotthepony.kstarbound.defs.tile.isRealModifier
|
|||||||
import ru.dbotthepony.kstarbound.defs.tile.orEmptyLiquid
|
import ru.dbotthepony.kstarbound.defs.tile.orEmptyLiquid
|
||||||
import ru.dbotthepony.kstarbound.defs.tile.orEmptyModifier
|
import ru.dbotthepony.kstarbound.defs.tile.orEmptyModifier
|
||||||
import ru.dbotthepony.kstarbound.defs.tile.orEmptyTile
|
import ru.dbotthepony.kstarbound.defs.tile.orEmptyTile
|
||||||
import ru.dbotthepony.kstarbound.fromJsonFast
|
|
||||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||||
import ru.dbotthepony.kstarbound.json.builder.JsonIgnore
|
import ru.dbotthepony.kstarbound.json.builder.JsonIgnore
|
||||||
import ru.dbotthepony.kstarbound.util.AssetPathStack
|
|
||||||
import ru.dbotthepony.kstarbound.util.random.random
|
import ru.dbotthepony.kstarbound.util.random.random
|
||||||
import ru.dbotthepony.kstarbound.world.Direction
|
import ru.dbotthepony.kstarbound.world.Direction
|
||||||
import ru.dbotthepony.kstarbound.world.api.AbstractLiquidState
|
import ru.dbotthepony.kstarbound.world.api.AbstractLiquidState
|
||||||
@ -231,83 +218,10 @@ abstract class DungeonBrush {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NPC(data: JsonObject) : DungeonBrush() {
|
data class NPC(val data: JsonObject) : DungeonBrush() {
|
||||||
@JsonFactory
|
|
||||||
data class NPCConfig(
|
|
||||||
val seed: Either<Long, String>? = null,
|
|
||||||
val species: String,
|
|
||||||
val typeName: String = "default",
|
|
||||||
val parameters: JsonObject = JsonObject(),
|
|
||||||
) {
|
|
||||||
private val assetCommentary = AssetPathStack.assetCommentary()
|
|
||||||
// interpret species as a comma separated list of unquoted strings
|
|
||||||
val speciesList: ImmutableSet<Registry.Ref<Species>> = ImmutableSet.copyOf(species.trim().replace(" ", "").split(',').map { it.trim() }.filter { it.isNotEmpty() }.map { Registries.species.ref(it) })
|
|
||||||
private var triggeredWarning = false
|
|
||||||
|
|
||||||
val speciesListResolved: ImmutableList<Registry.Entry<Species>> get() {
|
|
||||||
val result = speciesList.iterator().map { it.entry }.filterNotNull().collect(ImmutableList.toImmutableList())
|
|
||||||
|
|
||||||
if (result.isEmpty() && !triggeredWarning) {
|
|
||||||
triggeredWarning = true
|
|
||||||
LOGGER.error("No valid species specified or species string is empty: $species$assetCommentary")
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
if ("persistent" !in parameters) {
|
|
||||||
parameters["persistent"] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonFactory
|
|
||||||
data class MonsterConfig(
|
|
||||||
val seed: Either<Long, String>? = null,
|
|
||||||
val typeName: Registry.Ref<MonsterTypeDefinition>,
|
|
||||||
val parameters: JsonObject = JsonObject(),
|
|
||||||
) {
|
|
||||||
init {
|
|
||||||
if (seed != null && seed.isRight && seed.right() != "stable")
|
|
||||||
|
|
||||||
if ("persistent" !in parameters) {
|
|
||||||
parameters["persistent"] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val data: Either<NPCConfig, MonsterConfig>
|
|
||||||
|
|
||||||
init {
|
|
||||||
val kind = data["kind"]?.asString?.lowercase() ?: throw JsonSyntaxException("Missing NPC 'kind' in NPC brush data")
|
|
||||||
|
|
||||||
if (kind == "npc") {
|
|
||||||
this.data = Either.left(Starbound.gson.fromJsonFast(data, NPCConfig::class.java))
|
|
||||||
} else if (kind == "monster") {
|
|
||||||
this.data = Either.right(Starbound.gson.fromJsonFast(data, MonsterConfig::class.java))
|
|
||||||
} else {
|
|
||||||
throw JsonSyntaxException("Unknown NPC type $kind!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun execute(x: Int, y: Int, phase: Phase, world: DungeonWorld) {
|
override fun execute(x: Int, y: Int, phase: Phase, world: DungeonWorld) {
|
||||||
if (phase === Phase.PLACE_NPCS) {
|
if (phase === Phase.PLACE_NPCS) {
|
||||||
data.map({
|
LOGGER.warn("NYI: NPC at $x, $y")
|
||||||
val speciesListResolved = it.speciesListResolved
|
|
||||||
|
|
||||||
if (speciesListResolved.isNotEmpty()) {
|
|
||||||
world.placeNPC(x, y, speciesListResolved.random(world.random), it.typeName, it.seed?.map({ it }, { world.random.nextLong() }) ?: world.random.nextLong(), it.parameters)
|
|
||||||
} else {
|
|
||||||
LOGGER.error("Unable to place NPC ${it.typeName}, because species list is empty or contain no valid species")
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
if (it.typeName.isPresent) {
|
|
||||||
world.placeMonster(x, y, it.typeName.entryOrThrow, it.seed?.map({ it }, { world.random.nextLong() }) ?: world.random.nextLong(), it.parameters)
|
|
||||||
} else {
|
|
||||||
LOGGER.error("Unable to place Monster ${it.typeName} at $x $y, because no such monster exists")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,8 @@
|
|||||||
package ru.dbotthepony.kstarbound.defs.dungeon
|
package ru.dbotthepony.kstarbound.defs.dungeon
|
||||||
|
|
||||||
import com.google.gson.JsonElement
|
|
||||||
import com.google.gson.JsonNull
|
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectFunction
|
import it.unimi.dsi.fastutil.longs.Long2ObjectFunction
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
|
||||||
import kotlinx.coroutines.Deferred
|
|
||||||
import kotlinx.coroutines.async
|
|
||||||
import kotlinx.coroutines.coroutineScope
|
|
||||||
import kotlinx.coroutines.future.await
|
import kotlinx.coroutines.future.await
|
||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import ru.dbotthepony.kstarbound.math.AABBi
|
import ru.dbotthepony.kstarbound.math.AABBi
|
||||||
@ -15,11 +10,7 @@ import ru.dbotthepony.kstarbound.math.vector.Vector2d
|
|||||||
import ru.dbotthepony.kstarbound.math.vector.Vector2i
|
import ru.dbotthepony.kstarbound.math.vector.Vector2i
|
||||||
import ru.dbotthepony.kstarbound.Registry
|
import ru.dbotthepony.kstarbound.Registry
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
import ru.dbotthepony.kstarbound.defs.actor.NPCVariant
|
|
||||||
import ru.dbotthepony.kstarbound.defs.actor.Species
|
|
||||||
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor
|
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor
|
||||||
import ru.dbotthepony.kstarbound.defs.monster.MonsterTypeDefinition
|
|
||||||
import ru.dbotthepony.kstarbound.defs.monster.MonsterVariant
|
|
||||||
import ru.dbotthepony.kstarbound.defs.`object`.ObjectDefinition
|
import ru.dbotthepony.kstarbound.defs.`object`.ObjectDefinition
|
||||||
import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials
|
import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials
|
||||||
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
||||||
@ -36,16 +27,16 @@ import ru.dbotthepony.kstarbound.util.random.random
|
|||||||
import ru.dbotthepony.kstarbound.world.ChunkPos
|
import ru.dbotthepony.kstarbound.world.ChunkPos
|
||||||
import ru.dbotthepony.kstarbound.world.ChunkState
|
import ru.dbotthepony.kstarbound.world.ChunkState
|
||||||
import ru.dbotthepony.kstarbound.world.Direction
|
import ru.dbotthepony.kstarbound.world.Direction
|
||||||
|
import ru.dbotthepony.kstarbound.world.api.AbstractCell
|
||||||
import ru.dbotthepony.kstarbound.world.api.AbstractLiquidState
|
import ru.dbotthepony.kstarbound.world.api.AbstractLiquidState
|
||||||
import ru.dbotthepony.kstarbound.world.api.AbstractTileState
|
import ru.dbotthepony.kstarbound.world.api.AbstractTileState
|
||||||
import ru.dbotthepony.kstarbound.world.api.MutableTileState
|
import ru.dbotthepony.kstarbound.world.api.MutableTileState
|
||||||
import ru.dbotthepony.kstarbound.world.api.TileColor
|
import ru.dbotthepony.kstarbound.world.api.TileColor
|
||||||
import ru.dbotthepony.kstarbound.world.entities.MonsterEntity
|
|
||||||
import ru.dbotthepony.kstarbound.world.entities.NPCEntity
|
|
||||||
import ru.dbotthepony.kstarbound.world.entities.tile.TileEntity
|
import ru.dbotthepony.kstarbound.world.entities.tile.TileEntity
|
||||||
import ru.dbotthepony.kstarbound.world.entities.tile.WireConnection
|
import ru.dbotthepony.kstarbound.world.entities.tile.WireConnection
|
||||||
import ru.dbotthepony.kstarbound.world.entities.tile.WorldObject
|
import ru.dbotthepony.kstarbound.world.entities.tile.WorldObject
|
||||||
import ru.dbotthepony.kstarbound.world.physics.Poly
|
import ru.dbotthepony.kstarbound.world.physics.Poly
|
||||||
|
import java.util.Collections
|
||||||
import java.util.LinkedHashSet
|
import java.util.LinkedHashSet
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
@ -93,19 +84,6 @@ class DungeonWorld(
|
|||||||
val parameters: JsonObject = JsonObject()
|
val parameters: JsonObject = JsonObject()
|
||||||
)
|
)
|
||||||
|
|
||||||
private data class NPCData(
|
|
||||||
val species: Registry.Entry<Species>,
|
|
||||||
val type: String,
|
|
||||||
val seed: Long,
|
|
||||||
val overrides: JsonElement = JsonNull.INSTANCE
|
|
||||||
)
|
|
||||||
|
|
||||||
private data class MonsterData(
|
|
||||||
val type: Registry.Entry<MonsterTypeDefinition>,
|
|
||||||
val seed: Long,
|
|
||||||
val overrides: JsonObject = JsonObject()
|
|
||||||
)
|
|
||||||
|
|
||||||
var hasGenerated = false
|
var hasGenerated = false
|
||||||
private set
|
private set
|
||||||
|
|
||||||
@ -190,9 +168,6 @@ class DungeonWorld(
|
|||||||
|
|
||||||
private val placedObjects = LinkedHashMap<Vector2i, PlacedObject>()
|
private val placedObjects = LinkedHashMap<Vector2i, PlacedObject>()
|
||||||
|
|
||||||
private val placedNPCs = LinkedHashMap<Vector2i, ArrayList<NPCData>>()
|
|
||||||
private val placedMonsters = LinkedHashMap<Vector2i, ArrayList<MonsterData>>()
|
|
||||||
|
|
||||||
var playerStart: Vector2d? = null
|
var playerStart: Vector2d? = null
|
||||||
|
|
||||||
fun finishPart() {
|
fun finishPart() {
|
||||||
@ -214,14 +189,6 @@ class DungeonWorld(
|
|||||||
table.computeIfAbsent(group) { LinkedHashSet() }.add(geometry.wrap(Vector2i(x, y)))
|
table.computeIfAbsent(group) { LinkedHashSet() }.add(geometry.wrap(Vector2i(x, y)))
|
||||||
}
|
}
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
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(Vector2i(x, y))] = liquid
|
||||||
}
|
}
|
||||||
@ -528,28 +495,6 @@ class DungeonWorld(
|
|||||||
val tickets = ArrayList<ServerChunk.ITicket>()
|
val tickets = ArrayList<ServerChunk.ITicket>()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// construct npc variants as early as possible because they potentially involve disk I/O
|
|
||||||
val npcVariantsJob = Starbound.GLOBAL_SCOPE.async {
|
|
||||||
val variants = ArrayList<Pair<Vector2i, Deferred<NPCVariant?>>>(placedNPCs.values.sumOf { it.size })
|
|
||||||
|
|
||||||
for ((pos, npcs) in placedNPCs) {
|
|
||||||
for (npc in npcs) {
|
|
||||||
val variant = async {
|
|
||||||
try {
|
|
||||||
NPCVariant.create(npc.species, npc.type, parent.template.threatLevel, npc.seed, npc.overrides)
|
|
||||||
} catch (err: Throwable) {
|
|
||||||
LOGGER.error("Unable to place NPC '${npc.type}' with species '${npc.species}' at $pos", err)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
variants.add(pos to variant)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
variants.map { it.first to it.second.await() }
|
|
||||||
}
|
|
||||||
|
|
||||||
val terrainBlendingVertexes = ArrayList<Vector2d>()
|
val terrainBlendingVertexes = ArrayList<Vector2d>()
|
||||||
val spaceBlendingVertexes = ArrayList<Vector2d>()
|
val spaceBlendingVertexes = ArrayList<Vector2d>()
|
||||||
|
|
||||||
@ -655,15 +600,6 @@ class DungeonWorld(
|
|||||||
}, Starbound.EXECUTOR)
|
}, Starbound.EXECUTOR)
|
||||||
}
|
}
|
||||||
|
|
||||||
val monsterVariants = ArrayList<Pair<Vector2i, MonsterVariant>>(placedMonsters.values.sumOf { it.size })
|
|
||||||
|
|
||||||
for ((pos, monsters) in placedMonsters) {
|
|
||||||
for (monster in monsters) {
|
|
||||||
val variant = monster.type.value.create(monster.seed, monster.overrides)
|
|
||||||
monsterVariants.add(pos to variant)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val biomeItems = ArrayList<() -> Unit>()
|
val biomeItems = ArrayList<() -> Unit>()
|
||||||
|
|
||||||
for (biomeTree in biomeTreesFutures) {
|
for (biomeTree in biomeTreesFutures) {
|
||||||
@ -691,8 +627,6 @@ class DungeonWorld(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val npcVariants = npcVariantsJob.await()
|
|
||||||
|
|
||||||
// wait for all chunks to be loaded (and cell changes to be applied)
|
// wait for all chunks to be loaded (and cell changes to be applied)
|
||||||
// if any of cell change operation fails, entire generation fails... leaving world in inconsistent state,
|
// if any of cell change operation fails, entire generation fails... leaving world in inconsistent state,
|
||||||
// but also limiting damage by exiting early.
|
// but also limiting damage by exiting early.
|
||||||
@ -746,20 +680,6 @@ class DungeonWorld(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for ((pos, variant) in npcVariants) {
|
|
||||||
val ent = NPCEntity(variant ?: continue)
|
|
||||||
ent.movement.xPosition = pos.x.toDouble()
|
|
||||||
ent.movement.yPosition = pos.y.toDouble()
|
|
||||||
ent.joinWorld(parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
for ((pos, variant) in monsterVariants) {
|
|
||||||
val ent = MonsterEntity(variant)
|
|
||||||
ent.movement.xPosition = pos.x.toDouble()
|
|
||||||
ent.movement.yPosition = pos.y.toDouble()
|
|
||||||
ent.joinWorld(parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
parent.enableDungeonTileProtection = true
|
parent.enableDungeonTileProtection = true
|
||||||
}.await()
|
}.await()
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -31,7 +31,6 @@ import ru.dbotthepony.kstarbound.VersionRegistry
|
|||||||
import ru.dbotthepony.kstarbound.io.readInternedString
|
import ru.dbotthepony.kstarbound.io.readInternedString
|
||||||
import ru.dbotthepony.kstarbound.item.ItemRegistry
|
import ru.dbotthepony.kstarbound.item.ItemRegistry
|
||||||
import ru.dbotthepony.kstarbound.item.ItemStack
|
import ru.dbotthepony.kstarbound.item.ItemStack
|
||||||
import ru.dbotthepony.kstarbound.json.mergeJson
|
|
||||||
import ru.dbotthepony.kstarbound.json.readJsonElement
|
import ru.dbotthepony.kstarbound.json.readJsonElement
|
||||||
import ru.dbotthepony.kstarbound.json.writeJsonElement
|
import ru.dbotthepony.kstarbound.json.writeJsonElement
|
||||||
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
||||||
@ -163,6 +162,7 @@ fun ItemDescriptor(stream: DataInputStream): ItemDescriptor {
|
|||||||
* [parameters] is considered to be immutable and should not be modified
|
* [parameters] is considered to be immutable and should not be modified
|
||||||
* directly (must be copied for mutable context)
|
* directly (must be copied for mutable context)
|
||||||
*/
|
*/
|
||||||
|
@JsonAdapter(ItemDescriptor.Adapter::class)
|
||||||
data class ItemDescriptor(
|
data class ItemDescriptor(
|
||||||
val name: String,
|
val name: String,
|
||||||
val count: Long,
|
val count: Long,
|
||||||
@ -172,10 +172,6 @@ data class ItemDescriptor(
|
|||||||
val isNotEmpty get() = !isEmpty
|
val isNotEmpty get() = !isEmpty
|
||||||
val ref by lazy { if (name == "") ItemRegistry.AIR else ItemRegistry[name] }
|
val ref by lazy { if (name == "") ItemRegistry.AIR else ItemRegistry[name] }
|
||||||
|
|
||||||
fun applyParameters(parameters: JsonObject): ItemDescriptor {
|
|
||||||
return copy(parameters = mergeJson(this.parameters.deepCopy(), parameters))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "ItemDescriptor[$name, $count, $parameters]"
|
return "ItemDescriptor[$name, $count, $parameters]"
|
||||||
}
|
}
|
||||||
@ -244,7 +240,7 @@ data class ItemDescriptor(
|
|||||||
stream.writeJsonElement(parameters)
|
stream.writeJsonElement(parameters)
|
||||||
}
|
}
|
||||||
|
|
||||||
object Adapter : TypeAdapter<ItemDescriptor>() {
|
class Adapter(gson: Gson) : TypeAdapter<ItemDescriptor>() {
|
||||||
override fun write(out: JsonWriter, value: ItemDescriptor) {
|
override fun write(out: JsonWriter, value: ItemDescriptor) {
|
||||||
if (value.isEmpty)
|
if (value.isEmpty)
|
||||||
out.nullValue()
|
out.nullValue()
|
||||||
|
@ -5,7 +5,6 @@ import com.google.gson.Gson
|
|||||||
import com.google.gson.TypeAdapter
|
import com.google.gson.TypeAdapter
|
||||||
import com.google.gson.annotations.JsonAdapter
|
import com.google.gson.annotations.JsonAdapter
|
||||||
import com.google.gson.stream.JsonReader
|
import com.google.gson.stream.JsonReader
|
||||||
import com.google.gson.stream.JsonToken
|
|
||||||
import com.google.gson.stream.JsonWriter
|
import com.google.gson.stream.JsonWriter
|
||||||
import ru.dbotthepony.kommons.io.readCollection
|
import ru.dbotthepony.kommons.io.readCollection
|
||||||
import ru.dbotthepony.kommons.io.writeBinaryString
|
import ru.dbotthepony.kommons.io.writeBinaryString
|
||||||
@ -39,21 +38,7 @@ data class QuestArcDescriptor(
|
|||||||
if (stagehandUniqueId != null) stream.writeBinaryString(stagehandUniqueId)
|
if (stagehandUniqueId != null) stream.writeBinaryString(stagehandUniqueId)
|
||||||
}
|
}
|
||||||
|
|
||||||
class Adapter(gson: Gson) : TypeAdapter<QuestArcDescriptor>() {
|
class Adapter(gson: Gson) : VersionedAdapter<QuestArcDescriptor>("QuestArcDescriptor", FactoryAdapter.createFor(QuestArcDescriptor::class, gson))
|
||||||
private val parent = VersionedAdapter("QuestArcDescriptor", FactoryAdapter.createFor(QuestArcDescriptor::class, gson))
|
|
||||||
|
|
||||||
override fun write(out: JsonWriter, value: QuestArcDescriptor) {
|
|
||||||
parent.write(out, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun read(`in`: JsonReader): QuestArcDescriptor {
|
|
||||||
if (`in`.peek() == JsonToken.STRING) {
|
|
||||||
return QuestArcDescriptor(ImmutableList.of(QuestDescriptor(`in`.nextString())))
|
|
||||||
} else {
|
|
||||||
return parent.read(`in`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val CODEC = nativeCodec(::QuestArcDescriptor, QuestArcDescriptor::write)
|
val CODEC = nativeCodec(::QuestArcDescriptor, QuestArcDescriptor::write)
|
||||||
|
@ -14,7 +14,6 @@ import ru.dbotthepony.kstarbound.json.builder.FactoryAdapter
|
|||||||
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
|
||||||
import ru.dbotthepony.kstarbound.network.syncher.legacyCodec
|
import ru.dbotthepony.kstarbound.network.syncher.legacyCodec
|
||||||
import ru.dbotthepony.kstarbound.network.syncher.nativeCodec
|
import ru.dbotthepony.kstarbound.network.syncher.nativeCodec
|
||||||
import ru.dbotthepony.kstarbound.util.random.threadLocalRandom
|
|
||||||
import java.io.DataInputStream
|
import java.io.DataInputStream
|
||||||
import java.io.DataOutputStream
|
import java.io.DataOutputStream
|
||||||
|
|
||||||
@ -23,8 +22,7 @@ data class QuestDescriptor(
|
|||||||
val questId: String,
|
val questId: String,
|
||||||
val templateId: String = questId,
|
val templateId: String = questId,
|
||||||
val parameters: ImmutableMap<String, QuestParameter> = ImmutableMap.of(),
|
val parameters: ImmutableMap<String, QuestParameter> = ImmutableMap.of(),
|
||||||
// TODO: this is original engine behavior, and this is stupid
|
val seed: Long = makeSeed(),
|
||||||
val seed: Long = threadLocalRandom.nextLong(),
|
|
||||||
) {
|
) {
|
||||||
constructor(stream: DataInputStream, isLegacy: Boolean) : this(
|
constructor(stream: DataInputStream, isLegacy: Boolean) : this(
|
||||||
stream.readInternedString(),
|
stream.readInternedString(),
|
||||||
@ -45,5 +43,9 @@ data class QuestDescriptor(
|
|||||||
companion object {
|
companion object {
|
||||||
val CODEC = nativeCodec(::QuestDescriptor, QuestDescriptor::write)
|
val CODEC = nativeCodec(::QuestDescriptor, QuestDescriptor::write)
|
||||||
val LEGACY_CODEC = legacyCodec(::QuestDescriptor, QuestDescriptor::write)
|
val LEGACY_CODEC = legacyCodec(::QuestDescriptor, QuestDescriptor::write)
|
||||||
|
|
||||||
|
fun makeSeed(): Long {
|
||||||
|
return System.nanoTime().rotateLeft(27).xor(System.currentTimeMillis())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,7 @@ data class RenderMatch(
|
|||||||
val haltOnMatch: Boolean = false,
|
val haltOnMatch: Boolean = false,
|
||||||
val haltOnSubMatch: Boolean = false,
|
val haltOnSubMatch: Boolean = false,
|
||||||
) {
|
) {
|
||||||
@JsonFactory(asList = 1)
|
@JsonFactory(asList = true)
|
||||||
data class Piece(
|
data class Piece(
|
||||||
val name: String,
|
val name: String,
|
||||||
val offset: Vector2i
|
val offset: Vector2i
|
||||||
@ -140,7 +140,7 @@ data class RenderMatch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonFactory(asList = 1)
|
@JsonFactory(asList = true)
|
||||||
data class Matcher(
|
data class Matcher(
|
||||||
val offset: Vector2i,
|
val offset: Vector2i,
|
||||||
val ruleName: String
|
val ruleName: String
|
||||||
@ -204,7 +204,7 @@ data class RenderMatch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonFactory(asList = 1)
|
@JsonFactory(asList = true)
|
||||||
data class RenderMatchList(
|
data class RenderMatchList(
|
||||||
val name: String,
|
val name: String,
|
||||||
val list: ImmutableList<RenderMatch>
|
val list: ImmutableList<RenderMatch>
|
||||||
|
@ -31,7 +31,7 @@ class BushVariant(
|
|||||||
val ephemeral: Boolean,
|
val ephemeral: Boolean,
|
||||||
val tileDamageParameters: TileDamageParameters,
|
val tileDamageParameters: TileDamageParameters,
|
||||||
) {
|
) {
|
||||||
@JsonFactory(asList = 1)
|
@JsonFactory(asList = true)
|
||||||
data class Shape(val image: String, val mods: ImmutableList<String>)
|
data class Shape(val image: String, val mods: ImmutableList<String>)
|
||||||
|
|
||||||
@JsonFactory
|
@JsonFactory
|
||||||
|
@ -287,7 +287,7 @@ val BigDecimalValueCodec = StreamCodec.Impl(DataInputStream::readBigDecimal, Dat
|
|||||||
val UUIDValueCodec = StreamCodec.Impl({ s -> UUID(s.readLong(), s.readLong()) }, { s, v -> s.writeLong(v.mostSignificantBits); s.writeLong(v.leastSignificantBits) })
|
val UUIDValueCodec = StreamCodec.Impl({ s -> UUID(s.readLong(), s.readLong()) }, { s, v -> s.writeLong(v.mostSignificantBits); s.writeLong(v.leastSignificantBits) })
|
||||||
val VarIntValueCodec = StreamCodec.Impl(DataInputStream::readSignedVarInt, DataOutputStream::writeSignedVarInt)
|
val VarIntValueCodec = StreamCodec.Impl(DataInputStream::readSignedVarInt, DataOutputStream::writeSignedVarInt)
|
||||||
val VarLongValueCodec = StreamCodec.Impl(DataInputStream::readSignedVarLong, DataOutputStream::writeSignedVarLong)
|
val VarLongValueCodec = StreamCodec.Impl(DataInputStream::readSignedVarLong, DataOutputStream::writeSignedVarLong)
|
||||||
val BinaryStringCodec = StreamCodec.Impl(DataInputStream::readInternedString, DataOutputStream::writeBinaryString)
|
val BinaryStringCodec = StreamCodec.Impl(DataInputStream::readBinaryString, DataOutputStream::writeBinaryString)
|
||||||
|
|
||||||
val UnsignedVarLongCodec = StreamCodec.Impl(DataInputStream::readVarLong, DataOutputStream::writeVarLong)
|
val UnsignedVarLongCodec = StreamCodec.Impl(DataInputStream::readVarLong, DataOutputStream::writeVarLong)
|
||||||
val UnsignedVarIntCodec = StreamCodec.Impl(DataInputStream::readVarInt, DataOutputStream::writeVarInt)
|
val UnsignedVarIntCodec = StreamCodec.Impl(DataInputStream::readVarInt, DataOutputStream::writeVarInt)
|
||||||
@ -334,21 +334,5 @@ val KOptionalBinaryStringCodec = StreamCodec.KOptional(BinaryStringCodec)
|
|||||||
val KOptionalRGBCodec = StreamCodec.KOptional(RGBCodec)
|
val KOptionalRGBCodec = StreamCodec.KOptional(RGBCodec)
|
||||||
val KOptionalRGBACodec = StreamCodec.KOptional(RGBACodec)
|
val KOptionalRGBACodec = StreamCodec.KOptional(RGBACodec)
|
||||||
|
|
||||||
val NullableBooleanValueCodec = StreamCodec.Nullable(BooleanValueCodec)
|
|
||||||
val NullableByteValueCodec = StreamCodec.Nullable(ByteValueCodec)
|
|
||||||
val NullableShortValueCodec = StreamCodec.Nullable(ShortValueCodec)
|
|
||||||
val NullableCharValueCodec = StreamCodec.Nullable(CharValueCodec)
|
|
||||||
val NullableIntValueCodec = StreamCodec.Nullable(IntValueCodec)
|
|
||||||
val NullableLongValueCodec = StreamCodec.Nullable(LongValueCodec)
|
|
||||||
val NullableFloatValueCodec = StreamCodec.Nullable(FloatValueCodec)
|
|
||||||
val NullableDoubleValueCodec = StreamCodec.Nullable(DoubleValueCodec)
|
|
||||||
val NullableBigDecimalValueCodec = StreamCodec.Nullable(BigDecimalValueCodec)
|
|
||||||
val NullableUUIDValueCodec = StreamCodec.Nullable(UUIDValueCodec)
|
|
||||||
val NullableVarIntValueCodec = StreamCodec.Nullable(VarIntValueCodec)
|
|
||||||
val NullableVarLongValueCodec = StreamCodec.Nullable(VarLongValueCodec)
|
|
||||||
val NullableBinaryStringCodec = StreamCodec.Nullable(BinaryStringCodec)
|
|
||||||
val NullableRGBCodec = StreamCodec.Nullable(RGBCodec)
|
|
||||||
val NullableRGBACodec = StreamCodec.Nullable(RGBACodec)
|
|
||||||
|
|
||||||
fun <E : Enum<E>> Class<E>.codec() = StreamCodec.Enum(this)
|
fun <E : Enum<E>> Class<E>.codec() = StreamCodec.Enum(this)
|
||||||
fun <E : Enum<E>> KClass<E>.codec() = StreamCodec.Enum(this.java)
|
fun <E : Enum<E>> KClass<E>.codec() = StreamCodec.Enum(this.java)
|
||||||
|
@ -31,7 +31,6 @@ import ru.dbotthepony.kstarbound.Globals
|
|||||||
import ru.dbotthepony.kstarbound.Registry
|
import ru.dbotthepony.kstarbound.Registry
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
import ru.dbotthepony.kstarbound.defs.Drawable
|
import ru.dbotthepony.kstarbound.defs.Drawable
|
||||||
import ru.dbotthepony.kstarbound.defs.actor.PersistentStatusEffect
|
|
||||||
import ru.dbotthepony.kstarbound.defs.image.SpriteReference
|
import ru.dbotthepony.kstarbound.defs.image.SpriteReference
|
||||||
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor
|
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor
|
||||||
import ru.dbotthepony.kstarbound.defs.item.ItemRarity
|
import ru.dbotthepony.kstarbound.defs.item.ItemRarity
|
||||||
@ -263,12 +262,6 @@ open class ItemStack(val entry: ItemRegistry.Entry, val config: JsonObject, para
|
|||||||
return AgingResult(null, false)
|
return AgingResult(null, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
open val effects: Collection<String>
|
|
||||||
get() = listOf()
|
|
||||||
|
|
||||||
open val statusEffects: Collection<PersistentStatusEffect>
|
|
||||||
get() = listOf()
|
|
||||||
|
|
||||||
fun createDescriptor(): ItemDescriptor {
|
fun createDescriptor(): ItemDescriptor {
|
||||||
if (isEmpty)
|
if (isEmpty)
|
||||||
return ItemDescriptor.EMPTY
|
return ItemDescriptor.EMPTY
|
||||||
|
@ -153,16 +153,15 @@ enum class JsonPatch(val key: String) {
|
|||||||
@Suppress("NAME_SHADOWING")
|
@Suppress("NAME_SHADOWING")
|
||||||
suspend fun applyAsync(base: JsonElement, source: Collection<IStarboundFile>?): JsonElement {
|
suspend fun applyAsync(base: JsonElement, source: Collection<IStarboundFile>?): JsonElement {
|
||||||
source ?: return base
|
source ?: return base
|
||||||
val loaded = source.map { it.asyncJsonReader() to it }
|
|
||||||
var base = base
|
var base = base
|
||||||
|
|
||||||
for ((patch, f) in loaded) {
|
for (patch in source) {
|
||||||
val read = Starbound.ELEMENTS_ADAPTER.read(patch.await())
|
val read = Starbound.ELEMENTS_ADAPTER.read(patch.asyncJsonReader().await())
|
||||||
|
|
||||||
if (read !is JsonArray) {
|
if (read !is JsonArray) {
|
||||||
LOGGER.error("$patch root element is not an array")
|
LOGGER.error("$patch root element is not an array")
|
||||||
} else {
|
} else {
|
||||||
base = apply(base, read, f)
|
base = apply(base, read, patch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import com.google.gson.stream.JsonWriter
|
|||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
import ru.dbotthepony.kstarbound.VersionRegistry
|
import ru.dbotthepony.kstarbound.VersionRegistry
|
||||||
|
|
||||||
open class VersionedAdapter<T>(val name: String, val parent: TypeAdapter<T>) : TypeAdapter<T>() {
|
abstract class VersionedAdapter<T>(val name: String, val parent: TypeAdapter<T>) : TypeAdapter<T>() {
|
||||||
override fun write(out: JsonWriter, value: T) {
|
override fun write(out: JsonWriter, value: T) {
|
||||||
if (Starbound.IS_STORE_JSON) {
|
if (Starbound.IS_STORE_JSON) {
|
||||||
VersionRegistry.make(name, parent.toJsonTree(value)).toJson(out)
|
VersionRegistry.make(name, parent.toJsonTree(value)).toJson(out)
|
||||||
|
@ -61,12 +61,7 @@ annotation class JsonBuilder
|
|||||||
@Target(AnnotationTarget.CLASS)
|
@Target(AnnotationTarget.CLASS)
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
annotation class JsonFactory(
|
annotation class JsonFactory(
|
||||||
/**
|
val asList: Boolean = false,
|
||||||
* * -1 - input can be only as object
|
|
||||||
* * 0 - input may be either an object or a list, output is constructed as object
|
|
||||||
* * 1 - input can be only as list
|
|
||||||
*/
|
|
||||||
val asList: Int = -1,
|
|
||||||
val logMisses: Boolean = false,
|
val logMisses: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -47,19 +47,15 @@ class FactoryAdapter<T : Any> private constructor(
|
|||||||
val clazz: KClass<T>,
|
val clazz: KClass<T>,
|
||||||
val types: ImmutableList<ReferencedProperty<T, *>>,
|
val types: ImmutableList<ReferencedProperty<T, *>>,
|
||||||
aliases: Map<String, List<String>>,
|
aliases: Map<String, List<String>>,
|
||||||
val asJsonArray: AsJsonArray,
|
val asJsonArray: Boolean,
|
||||||
val stringInterner: Interner<String>,
|
val stringInterner: Interner<String>,
|
||||||
val logMisses: Boolean,
|
val logMisses: Boolean,
|
||||||
) : TypeAdapter<T>() {
|
) : TypeAdapter<T>() {
|
||||||
enum class AsJsonArray {
|
|
||||||
NO, EITHER, YES
|
|
||||||
}
|
|
||||||
|
|
||||||
private val name2index = Object2ObjectArrayMap<String, IntArrayList>()
|
private val name2index = Object2ObjectArrayMap<String, IntArrayList>()
|
||||||
private val loggedMisses = Collections.synchronizedSet(ObjectArraySet<String>())
|
private val loggedMisses = Collections.synchronizedSet(ObjectArraySet<String>())
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (asJsonArray != AsJsonArray.NO && types.any { it.isFlat }) {
|
if (asJsonArray && types.any { it.isFlat }) {
|
||||||
throw IllegalArgumentException("Can't have both flat properties and input be as json array")
|
throw IllegalArgumentException("Can't have both flat properties and input be as json array")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +159,7 @@ class FactoryAdapter<T : Any> private constructor(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (asJsonArray == AsJsonArray.YES)
|
if (asJsonArray)
|
||||||
out.beginArray()
|
out.beginArray()
|
||||||
else
|
else
|
||||||
out.beginObject()
|
out.beginObject()
|
||||||
@ -173,7 +169,7 @@ class FactoryAdapter<T : Any> private constructor(
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if (type.isFlat) {
|
if (type.isFlat) {
|
||||||
check(asJsonArray != AsJsonArray.YES)
|
check(!asJsonArray)
|
||||||
|
|
||||||
val (field, adapter) = type
|
val (field, adapter) = type
|
||||||
val result = (adapter as TypeAdapter<Any>).toJsonTree((field as KProperty1<T, Any>).get(value))
|
val result = (adapter as TypeAdapter<Any>).toJsonTree((field as KProperty1<T, Any>).get(value))
|
||||||
@ -193,10 +189,10 @@ class FactoryAdapter<T : Any> private constructor(
|
|||||||
val getValue = field.get(value)
|
val getValue = field.get(value)
|
||||||
|
|
||||||
// god fucking damn it
|
// god fucking damn it
|
||||||
if (asJsonArray != AsJsonArray.YES && getValue === null)
|
if (!asJsonArray && getValue === null)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if (asJsonArray != AsJsonArray.YES)
|
if (!asJsonArray)
|
||||||
out.name(field.name)
|
out.name(field.name)
|
||||||
|
|
||||||
@Suppress("unchecked_cast")
|
@Suppress("unchecked_cast")
|
||||||
@ -204,7 +200,7 @@ class FactoryAdapter<T : Any> private constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (asJsonArray == AsJsonArray.YES)
|
if (asJsonArray)
|
||||||
out.endArray()
|
out.endArray()
|
||||||
else
|
else
|
||||||
out.endObject()
|
out.endObject()
|
||||||
@ -220,10 +216,9 @@ class FactoryAdapter<T : Any> private constructor(
|
|||||||
|
|
||||||
@Suppress("name_shadowing")
|
@Suppress("name_shadowing")
|
||||||
var reader = reader
|
var reader = reader
|
||||||
val readingAsArray = asJsonArray == AsJsonArray.YES || asJsonArray == AsJsonArray.EITHER && reader.peek() == JsonToken.BEGIN_ARRAY
|
|
||||||
|
|
||||||
// Если нам необходимо читать объект как набор данных массива, то давай
|
// Если нам необходимо читать объект как набор данных массива, то давай
|
||||||
if (readingAsArray) {
|
if (asJsonArray) {
|
||||||
reader.beginArray()
|
reader.beginArray()
|
||||||
val iterator = types.iterator()
|
val iterator = types.iterator()
|
||||||
var fieldId = 0
|
var fieldId = 0
|
||||||
@ -338,7 +333,7 @@ class FactoryAdapter<T : Any> private constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (readingAsArray) {
|
if (asJsonArray) {
|
||||||
reader.endArray()
|
reader.endArray()
|
||||||
} else {
|
} else {
|
||||||
reader.endObject()
|
reader.endObject()
|
||||||
@ -420,7 +415,8 @@ class FactoryAdapter<T : Any> private constructor(
|
|||||||
* Позволяет построить класс [FactoryAdapter] на основе заданных параметров
|
* Позволяет построить класс [FactoryAdapter] на основе заданных параметров
|
||||||
*/
|
*/
|
||||||
class Builder<T : Any>(val clazz: KClass<T>, vararg fields: KProperty1<T, *>) : TypeAdapterFactory {
|
class Builder<T : Any>(val clazz: KClass<T>, vararg fields: KProperty1<T, *>) : TypeAdapterFactory {
|
||||||
private var asList = AsJsonArray.NO
|
private var asList = false
|
||||||
|
private var storesJson = false
|
||||||
private val types = ArrayList<ReferencedProperty<T, *>>()
|
private val types = ArrayList<ReferencedProperty<T, *>>()
|
||||||
private val aliases = Object2ObjectArrayMap<String, ArrayList<String>>()
|
private val aliases = Object2ObjectArrayMap<String, ArrayList<String>>()
|
||||||
var stringInterner: Interner<String> = Interner { it }
|
var stringInterner: Interner<String> = Interner { it }
|
||||||
@ -444,7 +440,7 @@ class FactoryAdapter<T : Any> private constructor(
|
|||||||
* Собирает этот [FactoryAdapter] с указанным GSON объектом
|
* Собирает этот [FactoryAdapter] с указанным GSON объектом
|
||||||
*/
|
*/
|
||||||
fun build(gson: Gson): TypeAdapter<T> {
|
fun build(gson: Gson): TypeAdapter<T> {
|
||||||
check(asList == AsJsonArray.NO || types.none { it.isFlat }) { "Can't have both flat properties and json data array layout" }
|
check(!asList || types.none { it.isFlat }) { "Can't have both flat properties and json data array layout" }
|
||||||
|
|
||||||
return FactoryAdapter(
|
return FactoryAdapter(
|
||||||
clazz = clazz,
|
clazz = clazz,
|
||||||
@ -482,12 +478,7 @@ class FactoryAdapter<T : Any> private constructor(
|
|||||||
* @see inputAsList
|
* @see inputAsList
|
||||||
*/
|
*/
|
||||||
fun inputAsMap(): Builder<T> {
|
fun inputAsMap(): Builder<T> {
|
||||||
asList = AsJsonArray.NO
|
asList = false
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun inputAsMapOrList(): Builder<T> {
|
|
||||||
asList = AsJsonArray.EITHER
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,7 +492,7 @@ class FactoryAdapter<T : Any> private constructor(
|
|||||||
* @see inputAsMap
|
* @see inputAsMap
|
||||||
*/
|
*/
|
||||||
fun inputAsList(): Builder<T> {
|
fun inputAsList(): Builder<T> {
|
||||||
asList = AsJsonArray.YES
|
asList = true
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -513,11 +504,7 @@ class FactoryAdapter<T : Any> private constructor(
|
|||||||
val builder = Builder(kclass)
|
val builder = Builder(kclass)
|
||||||
val properties = kclass.declaredMembers.filterIsInstance<KProperty1<T, *>>()
|
val properties = kclass.declaredMembers.filterIsInstance<KProperty1<T, *>>()
|
||||||
|
|
||||||
if (config.asList < 0) {
|
if (config.asList) {
|
||||||
builder.inputAsMap()
|
|
||||||
} else if (config.asList == 0) {
|
|
||||||
builder.inputAsMapOrList()
|
|
||||||
} else {
|
|
||||||
builder.inputAsList()
|
builder.inputAsList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ import ru.dbotthepony.kstarbound.math.Line2d
|
|||||||
import ru.dbotthepony.kstarbound.world.entities.AbstractEntity
|
import ru.dbotthepony.kstarbound.world.entities.AbstractEntity
|
||||||
import ru.dbotthepony.kstarbound.world.entities.ActorEntity
|
import ru.dbotthepony.kstarbound.world.entities.ActorEntity
|
||||||
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.player.PlayerEntity
|
import ru.dbotthepony.kstarbound.world.entities.player.PlayerEntity
|
||||||
import ru.dbotthepony.kstarbound.world.entities.tile.WorldObject
|
import ru.dbotthepony.kstarbound.world.entities.tile.WorldObject
|
||||||
|
|
||||||
@ -25,9 +24,6 @@ fun provideEntityBindings(self: AbstractEntity, lua: LuaEnvironment) {
|
|||||||
if (self is ActorEntity)
|
if (self is ActorEntity)
|
||||||
provideStatusControllerBindings(self.statusController, lua)
|
provideStatusControllerBindings(self.statusController, lua)
|
||||||
|
|
||||||
if (self is NPCEntity)
|
|
||||||
provideNPCBindings(self, lua)
|
|
||||||
|
|
||||||
provideWorldBindings(self.world, lua)
|
provideWorldBindings(self.world, lua)
|
||||||
|
|
||||||
val table = lua.newTable()
|
val table = lua.newTable()
|
||||||
|
@ -27,6 +27,13 @@ import ru.dbotthepony.kstarbound.world.entities.ActorEntity
|
|||||||
import ru.dbotthepony.kstarbound.world.entities.MonsterEntity
|
import ru.dbotthepony.kstarbound.world.entities.MonsterEntity
|
||||||
|
|
||||||
fun provideMonsterBindings(self: MonsterEntity, lua: LuaEnvironment) {
|
fun provideMonsterBindings(self: MonsterEntity, lua: LuaEnvironment) {
|
||||||
|
val config = lua.newTable()
|
||||||
|
lua.globals["config"] = config
|
||||||
|
|
||||||
|
config["getParameter"] = createConfigBinding { key, default ->
|
||||||
|
key.find(self.variant.parameters) ?: default
|
||||||
|
}
|
||||||
|
|
||||||
val callbacks = lua.newTable()
|
val callbacks = lua.newTable()
|
||||||
lua.globals["monster"] = callbacks
|
lua.globals["monster"] = callbacks
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ import ru.dbotthepony.kstarbound.lua.toVector2d
|
|||||||
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
|
||||||
import ru.dbotthepony.kstarbound.world.entities.ActorMovementController
|
import ru.dbotthepony.kstarbound.world.entities.ActorMovementController
|
||||||
import ru.dbotthepony.kstarbound.world.entities.AnchorNetworkState
|
import ru.dbotthepony.kstarbound.world.entities.AnchorState
|
||||||
import ru.dbotthepony.kstarbound.world.physics.Poly
|
import ru.dbotthepony.kstarbound.world.physics.Poly
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
|
|
||||||
@ -82,15 +82,15 @@ class MovementControllerBindings(val self: ActorMovementController) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
callbacks["setAnchorState"] = luaFunction { anchor: Number, index: Number ->
|
callbacks["setAnchorState"] = luaFunction { anchor: Number, index: Number ->
|
||||||
self.anchorNetworkState = AnchorNetworkState(anchor.toInt(), index.toInt())
|
self.anchorState = AnchorState(anchor.toInt(), index.toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["resetAnchorState"] = luaFunction {
|
callbacks["resetAnchorState"] = luaFunction {
|
||||||
self.anchorNetworkState = null
|
self.anchorState = null
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["anchorState"] = luaFunction {
|
callbacks["anchorState"] = luaFunction {
|
||||||
val anchorState = self.anchorNetworkState
|
val anchorState = self.anchorState
|
||||||
|
|
||||||
if (anchorState != null) {
|
if (anchorState != null) {
|
||||||
returnBuffer.setTo(anchorState.entityID, anchorState.positionIndex)
|
returnBuffer.setTo(anchorState.entityID, anchorState.positionIndex)
|
||||||
@ -146,8 +146,8 @@ class MovementControllerBindings(val self: ActorMovementController) {
|
|||||||
callbacks["baseParameters"] = luaFunction { returnBuffer.setTo(from(Starbound.gson.toJsonTree(self.actorMovementParameters))) }
|
callbacks["baseParameters"] = luaFunction { returnBuffer.setTo(from(Starbound.gson.toJsonTree(self.actorMovementParameters))) }
|
||||||
callbacks["walking"] = luaFunction { returnBuffer.setTo(self.isWalking) }
|
callbacks["walking"] = luaFunction { returnBuffer.setTo(self.isWalking) }
|
||||||
callbacks["running"] = luaFunction { returnBuffer.setTo(self.isRunning) }
|
callbacks["running"] = luaFunction { returnBuffer.setTo(self.isRunning) }
|
||||||
callbacks["movingDirection"] = luaFunction { returnBuffer.setTo(self.movingDirection.numericalValue) }
|
callbacks["movingDirection"] = luaFunction { returnBuffer.setTo(self.movingDirection.luaValue) }
|
||||||
callbacks["facingDirection"] = luaFunction { returnBuffer.setTo(self.facingDirection.numericalValue) }
|
callbacks["facingDirection"] = luaFunction { returnBuffer.setTo(self.facingDirection.luaValue) }
|
||||||
callbacks["crouching"] = luaFunction { returnBuffer.setTo(self.isCrouching) }
|
callbacks["crouching"] = luaFunction { returnBuffer.setTo(self.isCrouching) }
|
||||||
callbacks["flying"] = luaFunction { returnBuffer.setTo(self.isFlying) }
|
callbacks["flying"] = luaFunction { returnBuffer.setTo(self.isFlying) }
|
||||||
callbacks["falling"] = luaFunction { returnBuffer.setTo(self.isFalling) }
|
callbacks["falling"] = luaFunction { returnBuffer.setTo(self.isFalling) }
|
||||||
|
@ -1,222 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.lua.bindings
|
|
||||||
|
|
||||||
import org.classdump.luna.ByteString
|
|
||||||
import org.classdump.luna.Table
|
|
||||||
import ru.dbotthepony.kstarbound.Registries
|
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
|
||||||
import ru.dbotthepony.kstarbound.defs.EntityDamageTeam
|
|
||||||
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor
|
|
||||||
import ru.dbotthepony.kstarbound.defs.quest.QuestArcDescriptor
|
|
||||||
import ru.dbotthepony.kstarbound.fromJsonFast
|
|
||||||
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
|
||||||
import ru.dbotthepony.kstarbound.lua.from
|
|
||||||
import ru.dbotthepony.kstarbound.lua.get
|
|
||||||
import ru.dbotthepony.kstarbound.lua.iterator
|
|
||||||
import ru.dbotthepony.kstarbound.lua.luaFunction
|
|
||||||
import ru.dbotthepony.kstarbound.lua.set
|
|
||||||
import ru.dbotthepony.kstarbound.lua.tableOf
|
|
||||||
import ru.dbotthepony.kstarbound.lua.toByteString
|
|
||||||
import ru.dbotthepony.kstarbound.lua.toJsonFromLua
|
|
||||||
import ru.dbotthepony.kstarbound.lua.toVector2d
|
|
||||||
import ru.dbotthepony.kstarbound.util.SBPattern
|
|
||||||
import ru.dbotthepony.kstarbound.util.sbIntern
|
|
||||||
import ru.dbotthepony.kstarbound.util.valueOfOrNull
|
|
||||||
import ru.dbotthepony.kstarbound.world.entities.AnchorNetworkState
|
|
||||||
import ru.dbotthepony.kstarbound.world.entities.HumanoidActorEntity
|
|
||||||
import ru.dbotthepony.kstarbound.world.entities.NPCEntity
|
|
||||||
import ru.dbotthepony.kstarbound.world.entities.api.LoungeableEntity
|
|
||||||
|
|
||||||
fun provideNPCBindings(self: NPCEntity, lua: LuaEnvironment) {
|
|
||||||
val callbacks = lua.newTable()
|
|
||||||
lua.globals["npc"] = callbacks
|
|
||||||
|
|
||||||
callbacks["toAbsolutePosition"] = luaFunction { pos: Table ->
|
|
||||||
returnBuffer.setTo(from(self.toAbsolutePosition(toVector2d(pos))))
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["species"] = luaFunction { returnBuffer.setTo(self.variant.species.key.toByteString()) }
|
|
||||||
callbacks["gender"] = luaFunction { returnBuffer.setTo(self.variant.humanoidIdentity.gender.jsonName.toByteString()) }
|
|
||||||
callbacks["humanoidIdentity"] = luaFunction { returnBuffer.setTo(from(Starbound.gson.toJsonTree(self.variant.humanoidIdentity))) }
|
|
||||||
callbacks["npcType"] = luaFunction { returnBuffer.setTo(self.variant.typeName.toByteString()) }
|
|
||||||
callbacks["seed"] = luaFunction { returnBuffer.setTo(self.variant.seed) }
|
|
||||||
callbacks["level"] = luaFunction { returnBuffer.setTo(self.variant.level) }
|
|
||||||
callbacks["dropPools"] = luaFunction { returnBuffer.setTo(tableOf(*self.dropPools.map { it.key.left().toByteString() }.toTypedArray())) }
|
|
||||||
|
|
||||||
callbacks["setDropPools"] = luaFunction { dropPools: Table? ->
|
|
||||||
self.dropPools.clear()
|
|
||||||
|
|
||||||
if (dropPools != null) {
|
|
||||||
for ((_, pool) in dropPools) {
|
|
||||||
self.dropPools.add(Registries.treasurePools.ref(pool.toString()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lol why
|
|
||||||
callbacks["energy"] = luaFunction { returnBuffer.setTo(self.energy) }
|
|
||||||
callbacks["maxEnergy"] = luaFunction { returnBuffer.setTo(self.maxEnergy) }
|
|
||||||
|
|
||||||
callbacks["say"] = luaFunction { line: ByteString, tags: Table?, config: Any? ->
|
|
||||||
val actualLine = if (tags != null) {
|
|
||||||
SBPattern.of(line.decode()).resolveOrSkip({ tags[it]?.toString() })
|
|
||||||
} else {
|
|
||||||
line.decode()
|
|
||||||
}
|
|
||||||
|
|
||||||
val isNotEmpty = actualLine.isNotEmpty()
|
|
||||||
|
|
||||||
if (isNotEmpty)
|
|
||||||
self.addChatMessage(actualLine, toJsonFromLua(config))
|
|
||||||
|
|
||||||
returnBuffer.setTo(isNotEmpty)
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["sayPortrait"] = luaFunction { line: ByteString, portrait: ByteString, tags: Table?, config: Any? ->
|
|
||||||
val actualLine = if (tags != null) {
|
|
||||||
SBPattern.of(line.decode()).resolveOrSkip({ tags[it]?.toString() })
|
|
||||||
} else {
|
|
||||||
line.decode()
|
|
||||||
}
|
|
||||||
|
|
||||||
val isNotEmpty = actualLine.isNotEmpty()
|
|
||||||
|
|
||||||
if (isNotEmpty)
|
|
||||||
self.addChatMessage(actualLine, toJsonFromLua(config), portrait.decode())
|
|
||||||
|
|
||||||
returnBuffer.setTo(isNotEmpty)
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["emote"] = luaFunction { emote: ByteString ->
|
|
||||||
self.addEmote(emote.decode())
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["dance"] = luaFunction { dance: ByteString ->
|
|
||||||
self.setDance(dance.decode())
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["setInteractive"] = luaFunction { isInteractive: Boolean ->
|
|
||||||
self.isInteractive = isInteractive
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["setLounging"] = luaFunction { loungeable: Number, oAnchorIndex: Number? ->
|
|
||||||
val anchorIndex = oAnchorIndex?.toInt() ?: 0
|
|
||||||
val entity = self.world.entities[loungeable.toInt()] as? LoungeableEntity
|
|
||||||
|
|
||||||
if (entity == null || anchorIndex !in 0 until entity.sitPositions.size || entity.entitiesLoungingIn(anchorIndex).isNotEmpty()) {
|
|
||||||
returnBuffer.setTo(false)
|
|
||||||
} else {
|
|
||||||
self.movement.anchorNetworkState = AnchorNetworkState(loungeable.toInt(), anchorIndex)
|
|
||||||
returnBuffer.setTo(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["resetLounging"] = luaFunction {
|
|
||||||
self.movement.anchorNetworkState = null
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["isLounging"] = luaFunction {
|
|
||||||
returnBuffer.setTo(self.movement.anchorNetworkState != null)
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["loungingIn"] = luaFunction {
|
|
||||||
returnBuffer.setTo(self.movement.anchorNetworkState?.entityID)
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["setOfferedQuests"] = luaFunction { values: Table? ->
|
|
||||||
self.offeredQuests.clear()
|
|
||||||
|
|
||||||
if (values != null) {
|
|
||||||
for ((_, quest) in values) {
|
|
||||||
self.offeredQuests.add(Starbound.gson.fromJsonFast(toJsonFromLua(quest), QuestArcDescriptor::class.java))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["setTurnInQuests"] = luaFunction { values: Table? ->
|
|
||||||
self.turnInQuests.clear()
|
|
||||||
|
|
||||||
if (values != null) {
|
|
||||||
for ((_, value) in values) {
|
|
||||||
self.turnInQuests.add(value.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["setItemSlot"] = luaFunction { slot: ByteString, descriptor: Any ->
|
|
||||||
returnBuffer.setTo(self.setItem(slot.decode(), ItemDescriptor(descriptor)))
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["getItemSlot"] = luaFunction { slot: ByteString ->
|
|
||||||
val decoded = slot.decode()
|
|
||||||
val slotType = HumanoidActorEntity.ItemSlot.entries.valueOfOrNull(decoded.lowercase())
|
|
||||||
|
|
||||||
if (slotType == null) {
|
|
||||||
if (decoded in self.variant.items) {
|
|
||||||
returnBuffer.setTo(self.variant.items[decoded]!!.toTable(this))
|
|
||||||
} else {
|
|
||||||
returnBuffer.setTo()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
returnBuffer.setTo(self.getItem(slotType).toTable(this))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["disableWornArmor"] = luaFunction { disable: Boolean ->
|
|
||||||
self.disableWornArmor = disable
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["beginPrimaryFire"] = luaFunction { self.beginPrimaryFire() }
|
|
||||||
callbacks["beginAltFire"] = luaFunction { self.beginSecondaryFire() }
|
|
||||||
callbacks["beginSecondaryFire"] = luaFunction { self.beginSecondaryFire() }
|
|
||||||
callbacks["endPrimaryFire"] = luaFunction { self.endPrimaryFire() }
|
|
||||||
callbacks["endAltFire"] = luaFunction { self.endSecondaryFire() }
|
|
||||||
callbacks["endSecondaryFire"] = luaFunction { self.endSecondaryFire() }
|
|
||||||
|
|
||||||
callbacks["setShifting"] = luaFunction { value: Boolean ->
|
|
||||||
self.isShifting = value
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["setDamageOnTouch"] = luaFunction { value: Boolean ->
|
|
||||||
self.damageOnTouch = value
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["aimPosition"] = luaFunction {
|
|
||||||
returnBuffer.setTo(from(self.aimPosition))
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["setAimPosition"] = luaFunction { pos: Table ->
|
|
||||||
self.aimPosition = self.world.geometry.diff(toVector2d(pos), self.position)
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["setDeathParticleBurst"] = luaFunction { value: ByteString? ->
|
|
||||||
self.deathParticleBurst = value?.decode()?.sbIntern()
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["setStatusText"] = luaFunction { value: ByteString? ->
|
|
||||||
self.statusText = value?.decode()?.sbIntern()
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["setDisplayNametag"] = luaFunction { value: Boolean ->
|
|
||||||
self.displayNametag = value
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["setPersistent"] = luaFunction { value: Boolean ->
|
|
||||||
self.isPersistent = value
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["setKeepAlive"] = luaFunction { value: Boolean ->
|
|
||||||
self.keepAlive = value
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["setAggressive"] = luaFunction { value: Boolean ->
|
|
||||||
self.isAggressive = value
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["setDamageTeam"] = luaFunction { value: Table ->
|
|
||||||
self.team.accept(Starbound.gson.fromJsonFast(toJsonFromLua(value), EntityDamageTeam::class.java))
|
|
||||||
}
|
|
||||||
|
|
||||||
callbacks["setUniqueId"] = luaFunction { value: ByteString? ->
|
|
||||||
self.uniqueID.accept(value?.decode()?.sbIntern())
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,6 +14,7 @@ import ru.dbotthepony.kstarbound.defs.actor.PersistentStatusEffect
|
|||||||
import ru.dbotthepony.kstarbound.fromJsonFast
|
import ru.dbotthepony.kstarbound.fromJsonFast
|
||||||
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
|
||||||
import ru.dbotthepony.kstarbound.lua.from
|
import ru.dbotthepony.kstarbound.lua.from
|
||||||
|
import ru.dbotthepony.kstarbound.lua.get
|
||||||
import ru.dbotthepony.kstarbound.lua.iterator
|
import ru.dbotthepony.kstarbound.lua.iterator
|
||||||
import ru.dbotthepony.kstarbound.lua.luaFunction
|
import ru.dbotthepony.kstarbound.lua.luaFunction
|
||||||
import ru.dbotthepony.kstarbound.lua.set
|
import ru.dbotthepony.kstarbound.lua.set
|
||||||
@ -22,6 +23,7 @@ import ru.dbotthepony.kstarbound.lua.toByteString
|
|||||||
import ru.dbotthepony.kstarbound.lua.toJsonFromLua
|
import ru.dbotthepony.kstarbound.lua.toJsonFromLua
|
||||||
import ru.dbotthepony.kstarbound.util.sbIntern
|
import ru.dbotthepony.kstarbound.util.sbIntern
|
||||||
import ru.dbotthepony.kstarbound.world.entities.StatusController
|
import ru.dbotthepony.kstarbound.world.entities.StatusController
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
private object PersistentStatusEffectToken : TypeToken<PersistentStatusEffect>()
|
private object PersistentStatusEffectToken : TypeToken<PersistentStatusEffect>()
|
||||||
private object CPersistentStatusEffectToken : TypeToken<ArrayList<PersistentStatusEffect>>()
|
private object CPersistentStatusEffectToken : TypeToken<ArrayList<PersistentStatusEffect>>()
|
||||||
@ -45,7 +47,7 @@ fun provideStatusControllerBindings(self: StatusController, lua: LuaEnvironment)
|
|||||||
}
|
}
|
||||||
|
|
||||||
callbacks["stat"] = luaFunction { name: ByteString ->
|
callbacks["stat"] = luaFunction { name: ByteString ->
|
||||||
returnBuffer.setTo(self.effectiveStats[name.decode()]?.effectiveModifiedValue ?: 0.0)
|
returnBuffer.setTo(self.liveStats[name.decode()]?.effectiveModifiedValue ?: 0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["statPositive"] = luaFunction { name: ByteString ->
|
callbacks["statPositive"] = luaFunction { name: ByteString ->
|
||||||
|
@ -346,12 +346,12 @@ fun provideWorldEntitiesBindings(self: World<*, *>, callbacks: Table, lua: LuaEn
|
|||||||
|
|
||||||
callbacks["entitySpecies"] = luaFunction { id: Number ->
|
callbacks["entitySpecies"] = luaFunction { id: Number ->
|
||||||
val entity = self.entities[id.toInt()] as? HumanoidActorEntity ?: return@luaFunction returnBuffer.setTo()
|
val entity = self.entities[id.toInt()] as? HumanoidActorEntity ?: return@luaFunction returnBuffer.setTo()
|
||||||
returnBuffer.setTo(entity.humanoidIdentity.species.key.left().toByteString())
|
returnBuffer.setTo(entity.species.toByteString())
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["entityGender"] = luaFunction { id: Number ->
|
callbacks["entityGender"] = luaFunction { id: Number ->
|
||||||
val entity = self.entities[id.toInt()] as? HumanoidActorEntity ?: return@luaFunction returnBuffer.setTo()
|
val entity = self.entities[id.toInt()] as? HumanoidActorEntity ?: return@luaFunction returnBuffer.setTo()
|
||||||
returnBuffer.setTo(entity.humanoidIdentity.gender.jsonName.toByteString())
|
returnBuffer.setTo(entity.gender.jsonName.toByteString())
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["entityName"] = luaFunction { id: Number ->
|
callbacks["entityName"] = luaFunction { id: Number ->
|
||||||
|
@ -6,6 +6,7 @@ import com.google.gson.JsonPrimitive
|
|||||||
import org.classdump.luna.ByteString
|
import org.classdump.luna.ByteString
|
||||||
import org.classdump.luna.LuaRuntimeException
|
import org.classdump.luna.LuaRuntimeException
|
||||||
import org.classdump.luna.Table
|
import org.classdump.luna.Table
|
||||||
|
import ru.dbotthepony.kommons.util.KOptional
|
||||||
import ru.dbotthepony.kstarbound.Registries
|
import ru.dbotthepony.kstarbound.Registries
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
import ru.dbotthepony.kstarbound.defs.DamageSource
|
import ru.dbotthepony.kstarbound.defs.DamageSource
|
||||||
@ -50,7 +51,7 @@ fun provideWorldObjectBindings(self: WorldObject, lua: LuaEnvironment) {
|
|||||||
lua.globals["object"] = table
|
lua.globals["object"] = table
|
||||||
|
|
||||||
table["name"] = luaFunction { returnBuffer.setTo(self.config.key.toByteString()) }
|
table["name"] = luaFunction { returnBuffer.setTo(self.config.key.toByteString()) }
|
||||||
table["direction"] = luaFunction { returnBuffer.setTo(self.direction.numericalValue) }
|
table["direction"] = luaFunction { returnBuffer.setTo(self.direction.luaValue) }
|
||||||
table["position"] = luaFunction { returnBuffer.setTo(from(self.tilePosition)) }
|
table["position"] = luaFunction { returnBuffer.setTo(from(self.tilePosition)) }
|
||||||
table["setInteractive"] = luaFunction { interactive: Boolean -> self.isInteractive = interactive }
|
table["setInteractive"] = luaFunction { interactive: Boolean -> self.isInteractive = interactive }
|
||||||
table["uniqueId"] = luaFunction { returnBuffer.setTo(self.uniqueID.get().toByteString()) }
|
table["uniqueId"] = luaFunction { returnBuffer.setTo(self.uniqueID.get().toByteString()) }
|
||||||
|
@ -92,13 +92,9 @@ interface IPacket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface IServerPacket : IPacket {
|
interface IServerPacket : IPacket {
|
||||||
suspend fun play(connection: ServerConnection)
|
fun play(connection: ServerConnection)
|
||||||
val handleImmediatelyOnServer: Boolean
|
|
||||||
get() = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IClientPacket : IPacket {
|
interface IClientPacket : IPacket {
|
||||||
suspend fun play(connection: ClientConnection)
|
fun play(connection: ClientConnection)
|
||||||
val handleImmediatelyOnClient: Boolean
|
|
||||||
get() = false
|
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,7 @@ abstract class Connection(val side: ConnectionSide, val type: ConnectionType) :
|
|||||||
inGame()
|
inGame()
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun bind(channel: Channel) {
|
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")
|
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)
|
||||||
|
@ -219,10 +219,6 @@ class PacketRegistry(val isLegacy: Boolean) {
|
|||||||
i++
|
i++
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// TODO: separate "immediate" and "sequential" packets, so immediate packets are decompressed/parsed right away,
|
|
||||||
// and sequential packets are decompressed/parsed only when they need to be handled
|
|
||||||
// This way, malicious clients won't be able to fill up server memory by repeatedly sending huge packets
|
|
||||||
// which can take up long time to process
|
|
||||||
ctx.fireChannelRead(readingType!!.factory.read(DataInputStream(stream), isLegacy, side))
|
ctx.fireChannelRead(readingType!!.factory.read(DataInputStream(stream), isLegacy, side))
|
||||||
} catch (err: Throwable) {
|
} catch (err: Throwable) {
|
||||||
LOGGER.error("Error while reading incoming packet from network (type ${readingType!!.id}; ${readingType!!.type}; packet No. $i in stream)", err)
|
LOGGER.error("Error while reading incoming packet from network (type ${readingType!!.id}; ${readingType!!.type}; packet No. $i in stream)", err)
|
||||||
@ -293,13 +289,6 @@ class PacketRegistry(val isLegacy: Boolean) {
|
|||||||
networkReadBuffer.size(index + msg.readableBytes())
|
networkReadBuffer.size(index + msg.readableBytes())
|
||||||
msg.readBytes(networkReadBuffer.elements(), index, msg.readableBytes())
|
msg.readBytes(networkReadBuffer.elements(), index, msg.readableBytes())
|
||||||
drainNetworkBuffer(ctx)
|
drainNetworkBuffer(ctx)
|
||||||
|
|
||||||
if (networkReadBuffer.size >= MAX_BUFFER_SIZE) {
|
|
||||||
// if client sends billions of data, tell them to fuck off (the rude way)
|
|
||||||
networkReadBuffer.clear()
|
|
||||||
networkReadBuffer.trim()
|
|
||||||
ctx.channel().close()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
msg.release()
|
msg.release()
|
||||||
@ -394,7 +383,6 @@ class PacketRegistry(val isLegacy: Boolean) {
|
|||||||
companion object {
|
companion object {
|
||||||
const val LOG_PACKETS = false
|
const val LOG_PACKETS = false
|
||||||
const val MAX_PACKET_SIZE = 64L * 1024L * 1024L // 64 MiB
|
const val MAX_PACKET_SIZE = 64L * 1024L * 1024L // 64 MiB
|
||||||
const val MAX_BUFFER_SIZE = 128L * 1024L * 1024L // 128 MiB
|
|
||||||
// this includes both compressed and uncompressed
|
// this includes both compressed and uncompressed
|
||||||
// Original game allows 16 mebibyte packets
|
// Original game allows 16 mebibyte packets
|
||||||
// but it doesn't account for compression bomb (packets are fully uncompressed
|
// but it doesn't account for compression bomb (packets are fully uncompressed
|
||||||
|
@ -76,11 +76,11 @@ class ClientContextUpdatePacket(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ServerConnection) {
|
override fun play(connection: ServerConnection) {
|
||||||
connection.rpc.read(rpcEntries)
|
connection.rpc.read(rpcEntries)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
connection.rpc.read(rpcEntries)
|
connection.rpc.read(rpcEntries)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ class DamageNotificationPacket(val source: Int, val notification: DamageNotifica
|
|||||||
client.isTracking(notification.position)
|
client.isTracking(notification.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ServerConnection) {
|
override fun play(connection: ServerConnection) {
|
||||||
connection.enqueue {
|
connection.enqueue {
|
||||||
pushRemoteDamageNotification(this@DamageNotificationPacket)
|
pushRemoteDamageNotification(this@DamageNotificationPacket)
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ class DamageNotificationPacket(val source: Int, val notification: DamageNotifica
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
connection.enqueue {
|
connection.enqueue {
|
||||||
world?.pushRemoteDamageNotification(this@DamageNotificationPacket)
|
world?.pushRemoteDamageNotification(this@DamageNotificationPacket)
|
||||||
}
|
}
|
||||||
|
@ -19,13 +19,13 @@ class DamageRequestPacket(val inflictor: Int, val target: Int, val request: Dama
|
|||||||
val destinationConnection: Int
|
val destinationConnection: Int
|
||||||
get() = Connection.connectionForEntityID(target)
|
get() = Connection.connectionForEntityID(target)
|
||||||
|
|
||||||
override suspend fun play(connection: ServerConnection) {
|
override fun play(connection: ServerConnection) {
|
||||||
connection.enqueueAndSuspend {
|
connection.enqueue {
|
||||||
pushRemoteDamageRequest(this@DamageRequestPacket)
|
pushRemoteDamageRequest(this@DamageRequestPacket)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
connection.enqueue {
|
connection.enqueue {
|
||||||
world?.pushRemoteDamageRequest(this@DamageRequestPacket)
|
world?.pushRemoteDamageRequest(this@DamageRequestPacket)
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ class EntityCreatePacket(val entityType: EntityType, val storeData: ByteArrayLis
|
|||||||
stream.writeSignedVarInt(entityID)
|
stream.writeSignedVarInt(entityID)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ServerConnection) {
|
override fun play(connection: ServerConnection) {
|
||||||
if (entityID !in connection.entityIDRange) {
|
if (entityID !in connection.entityIDRange) {
|
||||||
LOGGER.error("Player $connection tried to create entity $entityType with ID $entityID, but that's outside of allowed range ${connection.entityIDRange}!")
|
LOGGER.error("Player $connection tried to create entity $entityType with ID $entityID, but that's outside of allowed range ${connection.entityIDRange}!")
|
||||||
connection.disconnect("Creating entity with ID $entityID outside of allowed range ${connection.entityIDRange}")
|
connection.disconnect("Creating entity with ID $entityID outside of allowed range ${connection.entityIDRange}")
|
||||||
@ -46,13 +46,13 @@ class EntityCreatePacket(val entityType: EntityType, val storeData: ByteArrayLis
|
|||||||
entity.isRemote = true
|
entity.isRemote = true
|
||||||
entity.networkGroup.upstream.enableInterpolation(0.0)
|
entity.networkGroup.upstream.enableInterpolation(0.0)
|
||||||
|
|
||||||
connection.enqueueAndSuspend {
|
connection.enqueue {
|
||||||
entity.joinWorld(this)
|
entity.joinWorld(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ class EntityDestroyPacket(val entityID: Int, val finalNetState: ByteArrayList, v
|
|||||||
stream.writeBoolean(isDeath)
|
stream.writeBoolean(isDeath)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ServerConnection) {
|
override fun play(connection: ServerConnection) {
|
||||||
if (entityID !in connection.entityIDRange) {
|
if (entityID !in connection.entityIDRange) {
|
||||||
LOGGER.error("Client $connection tried to remove entity with ID $entityID, but that's outside of allowed range ${connection.entityIDRange}!")
|
LOGGER.error("Client $connection tried to remove entity with ID $entityID, but that's outside of allowed range ${connection.entityIDRange}!")
|
||||||
connection.disconnect("Removing entity with ID $entityID outside of allowed range ${connection.entityIDRange}")
|
connection.disconnect("Removing entity with ID $entityID outside of allowed range ${connection.entityIDRange}")
|
||||||
@ -34,7 +34,7 @@ class EntityDestroyPacket(val entityID: Int, val finalNetState: ByteArrayList, v
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,18 +44,17 @@ class EntityMessagePacket(val entity: Either<Int, String>, val message: String,
|
|||||||
stream.writeShort(sourceConnection)
|
stream.writeShort(sourceConnection)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ServerConnection) {
|
override fun play(connection: ServerConnection) {
|
||||||
val tracker = connection.tracker
|
val tracker = connection.tracker
|
||||||
|
|
||||||
if (tracker == null) {
|
if (tracker == null) {
|
||||||
connection.send(EntityMessageResponsePacket(Either.left("Not in world"), this@EntityMessagePacket.id))
|
connection.send(EntityMessageResponsePacket(Either.left("Not in world"), this@EntityMessagePacket.id))
|
||||||
} else {
|
} else {
|
||||||
// Don't suspend here, entity messages are generally not reliable
|
|
||||||
tracker.handleEntityMessage(this)
|
tracker.handleEntityMessage(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
connection.enqueue {
|
connection.enqueue {
|
||||||
val world = world
|
val world = world
|
||||||
|
|
||||||
|
@ -31,8 +31,7 @@ class EntityMessageResponsePacket(val response: Either<String, JsonElement>, val
|
|||||||
stream.writeUUID(id)
|
stream.writeUUID(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ServerConnection) {
|
override fun play(connection: ServerConnection) {
|
||||||
// Don't suspend here, entity messages are generally not reliable
|
|
||||||
connection.enqueue {
|
connection.enqueue {
|
||||||
val message = pendingEntityMessages.asMap().remove(id)
|
val message = pendingEntityMessages.asMap().remove(id)
|
||||||
|
|
||||||
@ -46,7 +45,7 @@ class EntityMessageResponsePacket(val response: Either<String, JsonElement>, val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
connection.enqueue {
|
connection.enqueue {
|
||||||
val message = world?.pendingEntityMessages?.asMap()?.remove(this@EntityMessageResponsePacket.id)
|
val message = world?.pendingEntityMessages?.asMap()?.remove(this@EntityMessageResponsePacket.id)
|
||||||
|
|
||||||
|
@ -29,8 +29,8 @@ class EntityUpdateSetPacket(val forConnection: Int, val deltas: Int2ObjectMap<By
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ServerConnection) {
|
override fun play(connection: ServerConnection) {
|
||||||
connection.enqueueAndSuspend {
|
connection.enqueue {
|
||||||
for ((id, delta) in deltas) {
|
for ((id, delta) in deltas) {
|
||||||
if (id !in connection.entityIDRange) {
|
if (id !in connection.entityIDRange) {
|
||||||
LOGGER.error("Player $connection tried to update entity with ID $id, but that's outside of allowed range ${connection.entityIDRange}!")
|
LOGGER.error("Player $connection tried to update entity with ID $id, but that's outside of allowed range ${connection.entityIDRange}!")
|
||||||
@ -45,7 +45,7 @@ class EntityUpdateSetPacket(val forConnection: Int, val deltas: Int2ObjectMap<By
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,13 +19,13 @@ class HitRequestPacket(val inflictor: Int, val target: Int, val request: DamageD
|
|||||||
val destinationConnection: Int
|
val destinationConnection: Int
|
||||||
get() = Connection.connectionForEntityID(inflictor)
|
get() = Connection.connectionForEntityID(inflictor)
|
||||||
|
|
||||||
override suspend fun play(connection: ServerConnection) {
|
override fun play(connection: ServerConnection) {
|
||||||
connection.enqueueAndSuspend {
|
connection.enqueue {
|
||||||
pushRemoteHitRequest(this@HitRequestPacket)
|
pushRemoteHitRequest(this@HitRequestPacket)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
connection.enqueue {
|
connection.enqueue {
|
||||||
world?.pushRemoteHitRequest(this@HitRequestPacket)
|
world?.pushRemoteHitRequest(this@HitRequestPacket)
|
||||||
}
|
}
|
||||||
|
@ -8,14 +8,11 @@ import java.io.DataInputStream
|
|||||||
import java.io.DataOutputStream
|
import java.io.DataOutputStream
|
||||||
|
|
||||||
object PongPacket : IClientPacket {
|
object PongPacket : IClientPacket {
|
||||||
override val handleImmediatelyOnClient: Boolean
|
|
||||||
get() = true
|
|
||||||
|
|
||||||
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
|
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
|
||||||
if (isLegacy) stream.write(0)
|
if (isLegacy) stream.write(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,14 +23,11 @@ object PongPacket : IClientPacket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
object PingPacket : IServerPacket {
|
object PingPacket : IServerPacket {
|
||||||
override val handleImmediatelyOnServer: Boolean
|
|
||||||
get() = true
|
|
||||||
|
|
||||||
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
|
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
|
||||||
if (isLegacy) stream.write(0)
|
if (isLegacy) stream.write(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ServerConnection) {
|
override fun play(connection: ServerConnection) {
|
||||||
connection.send(PongPacket)
|
connection.send(PongPacket)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,14 +10,11 @@ import java.io.DataOutputStream
|
|||||||
data class ProtocolRequestPacket(val version: Int) : IServerPacket {
|
data class ProtocolRequestPacket(val version: Int) : IServerPacket {
|
||||||
constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readInt())
|
constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readInt())
|
||||||
|
|
||||||
override val handleImmediatelyOnServer: Boolean
|
|
||||||
get() = true
|
|
||||||
|
|
||||||
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
|
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
|
||||||
stream.writeInt(version)
|
stream.writeInt(version)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ServerConnection) {
|
override fun play(connection: ServerConnection) {
|
||||||
if (version == Starbound.NATIVE_PROTOCOL_VERSION) {
|
if (version == Starbound.NATIVE_PROTOCOL_VERSION) {
|
||||||
connection.channel.write(ProtocolResponsePacket(true))
|
connection.channel.write(ProtocolResponsePacket(true))
|
||||||
connection.channel.flush()
|
connection.channel.flush()
|
||||||
|
@ -16,7 +16,7 @@ data class ProtocolResponsePacket(val allowed: Boolean) : IClientPacket {
|
|||||||
stream.writeBoolean(allowed)
|
stream.writeBoolean(allowed)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
if (allowed) {
|
if (allowed) {
|
||||||
if (connection.isLegacy) {
|
if (connection.isLegacy) {
|
||||||
connection.setupLegacy()
|
connection.setupLegacy()
|
||||||
|
@ -12,18 +12,15 @@ import java.io.DataOutputStream
|
|||||||
class StepUpdatePacket(val remoteStep: Long) : IServerPacket, IClientPacket {
|
class StepUpdatePacket(val remoteStep: Long) : IServerPacket, IClientPacket {
|
||||||
constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readVarLong())
|
constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readVarLong())
|
||||||
|
|
||||||
override val handleImmediatelyOnServer: Boolean
|
|
||||||
get() = true
|
|
||||||
|
|
||||||
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
|
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
|
||||||
stream.writeVarLong(remoteStep)
|
stream.writeVarLong(remoteStep)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ServerConnection) {
|
override fun play(connection: ServerConnection) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ class CelestialResponsePacket(val responses: Collection<Either<ChunkData, System
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,7 +18,7 @@ class CentralStructureUpdatePacket(val data: JsonElement) : IClientPacket {
|
|||||||
stream.writeJsonElement(data)
|
stream.writeJsonElement(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
val read = Starbound.gson.fromJson(data, WorldStructure::class.java)
|
val read = Starbound.gson.fromJson(data, WorldStructure::class.java)
|
||||||
|
|
||||||
connection.enqueue {
|
connection.enqueue {
|
||||||
|
@ -26,7 +26,7 @@ class ChatReceivePacket(val data: ChatMessage) : IClientPacket {
|
|||||||
stream.writeBinaryString(data.text)
|
stream.writeBinaryString(data.text)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ class ConnectFailurePacket(val reason: String = "") : IClientPacket {
|
|||||||
stream.writeBinaryString(reason)
|
stream.writeBinaryString(reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
connection.disconnectNow()
|
connection.disconnectNow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ class ConnectSuccessPacket(val connectionID: Int, val serverUUID: UUID, val cele
|
|||||||
celestialInformation.write(stream, isLegacy)
|
celestialInformation.write(stream, isLegacy)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
connection.connectionID = connectionID
|
connection.connectionID = connectionID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,11 +24,11 @@ class EntityInteractResultPacket(val action: InteractAction, val id: UUID, val s
|
|||||||
stream.writeInt(source)
|
stream.writeInt(source)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ServerConnection) {
|
override fun play(connection: ServerConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ class EnvironmentUpdatePacket(val sky: ByteArrayList, val weather: ByteArrayList
|
|||||||
stream.writeByteArray(weather.elements(), 0, weather.size)
|
stream.writeByteArray(weather.elements(), 0, weather.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ class FindUniqueEntityResponsePacket(val name: String, val position: Vector2d?)
|
|||||||
stream.writeStruct2d(position, isLegacy)
|
stream.writeStruct2d(position, isLegacy)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
connection.enqueue {
|
connection.enqueue {
|
||||||
world?.pendingUniqueEntityRequests?.asMap()?.remove(name)?.complete(position)
|
world?.pendingUniqueEntityRequests?.asMap()?.remove(name)?.complete(position)
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ class GiveItemPacket(val descriptor: ItemDescriptor) : IClientPacket {
|
|||||||
descriptor.write(stream)
|
descriptor.write(stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ class HandshakeChallengePacket(val passwordSalt: ByteArray) : IClientPacket {
|
|||||||
stream.writeByteArray(passwordSalt)
|
stream.writeByteArray(passwordSalt)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ class LegacyTileUpdatePacket(val position: Vector2i, val tile: LegacyNetworkCell
|
|||||||
tile.write(stream)
|
tile.write(stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ class LegacyTileArrayUpdatePacket(val origin: Vector2i, val data: Object2DArray<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ class PlayerWarpResultPacket(val success: Boolean, val target: WarpAction, val w
|
|||||||
stream.writeBoolean(warpActionInvalid)
|
stream.writeBoolean(warpActionInvalid)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ class ServerDisconnectPacket(val reason: String = "") : IClientPacket {
|
|||||||
stream.writeBinaryString(reason)
|
stream.writeBinaryString(reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
connection.disconnectNow()
|
connection.disconnectNow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ class ServerInfoPacket(val players: Int, val maxPlayers: Int) : IClientPacket {
|
|||||||
stream.writeShort(maxPlayers)
|
stream.writeShort(maxPlayers)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ class SetPlayerStartPacket(val position: Vector2d, val respawnInWorld: Boolean)
|
|||||||
stream.writeBoolean(respawnInWorld)
|
stream.writeBoolean(respawnInWorld)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
connection.enqueue {
|
connection.enqueue {
|
||||||
world?.setPlayerSpawn(position, respawnInWorld)
|
world?.setPlayerSpawn(position, respawnInWorld)
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ class SystemObjectCreatePacket(val data: ByteArrayList) : IClientPacket {
|
|||||||
stream.writeByteArray(data.elements(), 0, data.size)
|
stream.writeByteArray(data.elements(), 0, data.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ class SystemObjectDestroyPacket(val uuid: UUID) : IClientPacket {
|
|||||||
stream.writeUUID(uuid)
|
stream.writeUUID(uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ class SystemShipCreatePacket(val data: ByteArrayList) : IClientPacket {
|
|||||||
stream.writeByteArray(data.elements(), 0, data.size)
|
stream.writeByteArray(data.elements(), 0, data.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ class SystemShipDestroyPacket(val uuid: UUID) : IClientPacket {
|
|||||||
stream.writeUUID(uuid)
|
stream.writeUUID(uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ class SystemWorldStartPacket(val location: Vector3i, val objects: Collection<Byt
|
|||||||
shipLocation.write(stream, isLegacy)
|
shipLocation.write(stream, isLegacy)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -24,7 +24,7 @@ class SystemWorldUpdatePacket(val objects: Map<UUID, ByteArrayList>, val ships:
|
|||||||
stream.writeMap(ships, { writeUUID(it) }, { writeByteArray(it.elements(), 0, it.size) })
|
stream.writeMap(ships, { writeUUID(it) }, { writeByteArray(it.elements(), 0, it.size) })
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,7 +16,7 @@ class TileDamageUpdatePacket(val x: Int, val y: Int, val isBackground: Boolean,
|
|||||||
health.write(stream, isLegacy)
|
health.write(stream, isLegacy)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ class TileModificationFailurePacket(val modifications: Collection<Pair<Vector2i,
|
|||||||
stream.writeCollection(modifications) { stream.writeStruct2i(it.first); it.second.write(stream, isLegacy) }
|
stream.writeCollection(modifications) { stream.writeStruct2i(it.first); it.second.write(stream, isLegacy) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ class UniverseTimeUpdatePacket(val time: Double) : IClientPacket {
|
|||||||
stream.writeDouble(time)
|
stream.writeDouble(time)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ class UpdateDungeonBreathablePacket(val id: Int, val breathable: Boolean?) : ICl
|
|||||||
stream.writeNullable(breathable) { writeBoolean(it) }
|
stream.writeNullable(breathable) { writeBoolean(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
connection.enqueue {
|
connection.enqueue {
|
||||||
world?.setDungeonBreathable(this@UpdateDungeonBreathablePacket.id, breathable)
|
world?.setDungeonBreathable(this@UpdateDungeonBreathablePacket.id, breathable)
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ class UpdateDungeonGravityPacket(val id: Int, val gravity: Vector2d?) : IClientP
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
connection.enqueue {
|
connection.enqueue {
|
||||||
world?.setDungeonGravity(this@UpdateDungeonGravityPacket.id, gravity)
|
world?.setDungeonGravity(this@UpdateDungeonGravityPacket.id, gravity)
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ class UpdateDungeonProtectionPacket(val id: Int, val isProtected: Boolean) : ICl
|
|||||||
stream.writeBoolean(isProtected)
|
stream.writeBoolean(isProtected)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
connection.enqueue {
|
connection.enqueue {
|
||||||
world?.switchDungeonIDProtection(this@UpdateDungeonProtectionPacket.id, isProtected)
|
world?.switchDungeonIDProtection(this@UpdateDungeonProtectionPacket.id, isProtected)
|
||||||
}
|
}
|
||||||
|
@ -19,13 +19,13 @@ class UpdateWorldPropertiesPacket(val update: JsonObject) : IClientPacket, IServ
|
|||||||
stream.writeJsonObject(update)
|
stream.writeJsonObject(update)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
connection.enqueue {
|
connection.enqueue {
|
||||||
world?.updateProperties(update)
|
world?.updateProperties(update)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ServerConnection) {
|
override fun play(connection: ServerConnection) {
|
||||||
connection.enqueue {
|
connection.enqueue {
|
||||||
updateProperties(update)
|
updateProperties(update)
|
||||||
broadcast(this@UpdateWorldPropertiesPacket)
|
broadcast(this@UpdateWorldPropertiesPacket)
|
||||||
|
@ -78,7 +78,7 @@ class WorldStartPacket(
|
|||||||
stream.writeBoolean(localInterpolationMode)
|
stream.writeBoolean(localInterpolationMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ class WorldStopPacket(val reason: String = "") : IClientPacket {
|
|||||||
stream.writeBinaryString(reason)
|
stream.writeBinaryString(reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
connection.resetOccupiedEntityIDs()
|
connection.resetOccupiedEntityIDs()
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
@ -29,10 +29,7 @@ class CelestialRequestPacket(val requests: Collection<Either<Vector2i, Vector3i>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override val handleImmediatelyOnServer: Boolean
|
override fun play(connection: ServerConnection) {
|
||||||
get() = true
|
|
||||||
|
|
||||||
override suspend fun play(connection: ServerConnection) {
|
|
||||||
connection.pushCelestialRequests(requests)
|
connection.pushCelestialRequests(requests)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,15 +11,12 @@ import java.io.DataOutputStream
|
|||||||
class ChatSendPacket(val text: String, val mode: ChatSendMode) : IServerPacket {
|
class ChatSendPacket(val text: String, val mode: ChatSendMode) : IServerPacket {
|
||||||
constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readBinaryString(), ChatSendMode.entries[stream.readUnsignedByte()])
|
constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readBinaryString(), ChatSendMode.entries[stream.readUnsignedByte()])
|
||||||
|
|
||||||
override val handleImmediatelyOnServer: Boolean
|
|
||||||
get() = true
|
|
||||||
|
|
||||||
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
|
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
|
||||||
stream.writeBinaryString(text)
|
stream.writeBinaryString(text)
|
||||||
stream.writeByte(mode.ordinal)
|
stream.writeByte(mode.ordinal)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ServerConnection) {
|
override fun play(connection: ServerConnection) {
|
||||||
connection.server.chat.handle(connection, this)
|
connection.server.chat.handle(connection, this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ data class ClientConnectPacket(
|
|||||||
stream.writeBinaryString(account)
|
stream.writeBinaryString(account)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ServerConnection) {
|
override fun play(connection: ServerConnection) {
|
||||||
if (connection.server.clientByUUID(playerUuid) != null) {
|
if (connection.server.clientByUUID(playerUuid) != null) {
|
||||||
connection.send(ConnectFailurePacket("Duplicate player UUID $playerUuid"))
|
connection.send(ConnectFailurePacket("Duplicate player UUID $playerUuid"))
|
||||||
LOGGER.warn("Unable to accept player $playerName/$playerUuid because such UUID is already taken")
|
LOGGER.warn("Unable to accept player $playerName/$playerUuid because such UUID is already taken")
|
||||||
|
@ -10,7 +10,7 @@ object ClientDisconnectRequestPacket : IServerPacket {
|
|||||||
if (isLegacy) stream.writeBoolean(false)
|
if (isLegacy) stream.writeBoolean(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ServerConnection) {
|
override fun play(connection: ServerConnection) {
|
||||||
connection.disconnect("Disconnect by user.")
|
connection.disconnect("Disconnect by user.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,13 +15,13 @@ class ConnectWirePacket(val target: WireConnection, val source: WireConnection)
|
|||||||
source.write(stream)
|
source.write(stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ServerConnection) {
|
override fun play(connection: ServerConnection) {
|
||||||
connection.enqueueAndSuspend {
|
connection.enqueue {
|
||||||
val target = entityIndex.tileEntityAt(target.entityLocation, WorldObject::class) ?: return@enqueueAndSuspend
|
val target = entityIndex.tileEntityAt(target.entityLocation, WorldObject::class) ?: return@enqueue
|
||||||
val source = entityIndex.tileEntityAt(source.entityLocation, WorldObject::class) ?: return@enqueueAndSuspend
|
val source = entityIndex.tileEntityAt(source.entityLocation, WorldObject::class) ?: return@enqueue
|
||||||
|
|
||||||
val targetNode = target.outputNodes.getOrNull(this@ConnectWirePacket.target.index) ?: return@enqueueAndSuspend
|
val targetNode = target.outputNodes.getOrNull(this@ConnectWirePacket.target.index) ?: return@enqueue
|
||||||
val sourceNode = source.inputNodes.getOrNull(this@ConnectWirePacket.source.index) ?: return@enqueueAndSuspend
|
val sourceNode = source.inputNodes.getOrNull(this@ConnectWirePacket.source.index) ?: return@enqueue
|
||||||
|
|
||||||
if (this@ConnectWirePacket.source in targetNode.connections && this@ConnectWirePacket.target in sourceNode.connections) {
|
if (this@ConnectWirePacket.source in targetNode.connections && this@ConnectWirePacket.target in sourceNode.connections) {
|
||||||
// disconnect
|
// disconnect
|
||||||
|
@ -41,7 +41,7 @@ class DamageTileGroupPacket(val tiles: Collection<Vector2i>, val isBackground: B
|
|||||||
stream.writeInt(source)
|
stream.writeInt(source)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ServerConnection) {
|
override fun play(connection: ServerConnection) {
|
||||||
connection.tracker?.damageTiles(tiles, isBackground, sourcePosition, damage, source)
|
connection.tracker?.damageTiles(tiles, isBackground, sourcePosition, damage, source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ class DisconnectAllWiresPacket(val pos: Vector2i, val node: WireNode) : IServerP
|
|||||||
node.write(stream, isLegacy)
|
node.write(stream, isLegacy)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ServerConnection) {
|
override fun play(connection: ServerConnection) {
|
||||||
connection.enqueue {
|
connection.enqueue {
|
||||||
val target = entityIndex.tileEntityAt(pos, WorldObject::class) ?: return@enqueue
|
val target = entityIndex.tileEntityAt(pos, WorldObject::class) ?: return@enqueue
|
||||||
val node = if (node.isInput) target.inputNodes.getOrNull(node.index) else target.outputNodes.getOrNull(node.index)
|
val node = if (node.isInput) target.inputNodes.getOrNull(node.index) else target.outputNodes.getOrNull(node.index)
|
||||||
|
@ -26,7 +26,7 @@ class EntityInteractPacket(val request: InteractRequest, val id: UUID) : IServer
|
|||||||
stream.writeUUID(id)
|
stream.writeUUID(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ServerConnection) {
|
override fun play(connection: ServerConnection) {
|
||||||
if (request.target >= 0) {
|
if (request.target >= 0) {
|
||||||
connection.enqueue {
|
connection.enqueue {
|
||||||
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))
|
||||||
@ -41,7 +41,7 @@ class EntityInteractPacket(val request: InteractRequest, val id: UUID) : IServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun play(connection: ClientConnection) {
|
override fun play(connection: ClientConnection) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user