Redesigned registry

This commit is contained in:
DBotThePony 2023-10-22 00:45:22 +07:00
parent 8cd84a4501
commit 949ed802ad
Signed by: DBot
GPG Key ID: DCC23B5715498507
40 changed files with 641 additions and 513 deletions

View File

@ -50,11 +50,14 @@ fun String.sintern(): String = Starbound.STRINGS.intern(this)
inline fun <reified T> Gson.fromJson(reader: JsonReader): T? = fromJson<T>(reader, T::class.java) inline fun <reified T> Gson.fromJson(reader: JsonReader): T? = fromJson<T>(reader, T::class.java)
fun <T : Any> Collection<IStarboundFile>.batch(executor: ForkJoinPool, batchSize: Int = 16, mapper: (IStarboundFile) -> KOptional<RegistryObject<T>>): Stream<RegistryObject<T>> { /**
* guarantees even distribution of tasks while also preserving encountered order of elements
*/
fun <T> Collection<IStarboundFile>.batch(executor: ForkJoinPool, batchSize: Int = 16, mapper: (IStarboundFile) -> KOptional<T>): Stream<T> {
require(batchSize >= 1) { "Invalid batch size: $batchSize" } require(batchSize >= 1) { "Invalid batch size: $batchSize" }
if (batchSize == 1 || size <= batchSize) { if (batchSize == 1 || size <= batchSize) {
val tasks = ArrayList<ForkJoinTask<KOptional<RegistryObject<T>>>>() val tasks = ArrayList<ForkJoinTask<KOptional<T>>>()
for (listedFile in this) { for (listedFile in this) {
tasks.add(executor.submit(Callable { mapper.invoke(listedFile) })) tasks.add(executor.submit(Callable { mapper.invoke(listedFile) }))
@ -63,7 +66,7 @@ fun <T : Any> Collection<IStarboundFile>.batch(executor: ForkJoinPool, batchSize
return tasks.stream().map { it.join() }.filter { it.isPresent }.map { it.value } return tasks.stream().map { it.join() }.filter { it.isPresent }.map { it.value }
} }
val batches = ArrayList<ForkJoinTask<List<KOptional<RegistryObject<T>>>>>() val batches = ArrayList<ForkJoinTask<List<KOptional<T>>>>()
var batch = ArrayList<IStarboundFile>(batchSize) var batch = ArrayList<IStarboundFile>(batchSize)
for (listedFile in this) { for (listedFile in this) {

View File

@ -11,7 +11,6 @@ import ru.dbotthepony.kstarbound.player.Avatar
import ru.dbotthepony.kstarbound.player.QuestDescriptor import ru.dbotthepony.kstarbound.player.QuestDescriptor
import ru.dbotthepony.kstarbound.player.QuestInstance import ru.dbotthepony.kstarbound.player.QuestInstance
import ru.dbotthepony.kstarbound.util.JVMTimeSource import ru.dbotthepony.kstarbound.util.JVMTimeSource
import ru.dbotthepony.kstarbound.world.api.AbstractCell
import ru.dbotthepony.kstarbound.world.entities.ItemEntity import ru.dbotthepony.kstarbound.world.entities.ItemEntity
import ru.dbotthepony.kstarbound.io.json.VersionedJson import ru.dbotthepony.kstarbound.io.json.VersionedJson
import ru.dbotthepony.kstarbound.io.readVarInt import ru.dbotthepony.kstarbound.io.readVarInt
@ -118,7 +117,7 @@ fun main() {
val rand = Random() val rand = Random()
for (i in 0 .. 128) { for (i in 0 .. 128) {
val item = ItemEntity(client.world!!, Registries.items.values.random().value) val item = ItemEntity(client.world!!, Registries.items.keys.values.random().value)
item.position = Vector2d(225.0 - i, 785.0) item.position = Vector2d(225.0 - i, 785.0)
item.spawn() item.spawn()

View File

@ -1,185 +0,0 @@
package ru.dbotthepony.kstarbound
import com.google.gson.JsonArray
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import com.google.gson.internal.bind.JsonTreeReader
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kstarbound.api.IStarboundFile
import ru.dbotthepony.kstarbound.lua.LuaState
import ru.dbotthepony.kstarbound.util.AssetPathStack
import ru.dbotthepony.kstarbound.util.set
import ru.dbotthepony.kstarbound.util.traverseJsonPath
import java.util.*
import kotlin.reflect.KClass
inline fun <reified T : Any> ObjectRegistry(name: String, noinline key: ((T) -> String)? = null, noinline intKey: ((T) -> Int)? = null): ObjectRegistry<T> {
return ObjectRegistry(T::class, name, key, intKey)
}
fun mergeJsonElements(source: JsonObject, destination: JsonObject): JsonObject {
for ((k, v) in source.entrySet()) {
if (!destination.has(k)) {
destination[k] = v.deepCopy()
} else {
mergeJsonElements(v, destination[k])
}
}
return destination
}
fun mergeJsonElements(source: JsonArray, destination: JsonArray): JsonArray {
for ((i, v) in source.withIndex()) {
if (i >= destination.size()) {
destination.add(v.deepCopy())
} else {
destination[i] = mergeJsonElements(v, destination[i])
}
}
return destination
}
fun mergeJsonElements(source: JsonElement, destination: JsonElement): JsonElement {
if (destination is JsonPrimitive) {
return destination
}
if (destination is JsonObject && source is JsonObject) {
return mergeJsonElements(source, destination)
}
if (destination is JsonArray && source is JsonArray) {
return mergeJsonElements(source, destination)
}
return destination
}
class RegistryObject<T : Any>(
/**
* Объект реестра
*/
val value: T,
/**
* Оригинальный JSON объекта без каких либо изменений
*/
val json: JsonElement,
/**
* Файл, откуда данный объект был загружен
*/
val file: IStarboundFile,
) {
val jsonObject get() = json as JsonObject
fun push(lua: LuaState) {
lua.push(toJson())
}
fun push(lua: LuaState.ArgStack) {
lua.push(toJson())
}
/**
* Возвращает полную (обработанную) структуру [JsonObject] объекта [value]
*
* Полнота определяется тем, что [value] может иметь свойства по умолчанию, которые не указаны
* в оригинальной JSON структуре. [copy] не вернёт данные свойства по умолчанию, а [toJson] вернёт.
*/
fun toJson(): JsonElement {
return mergeJsonElements(json, Starbound.gson.toJsonTree(value))
}
fun traverseJsonPath(path: String): JsonElement? {
return traverseJsonPath(path, mergeJsonElements(json, Starbound.gson.toJsonTree(value)))
}
override fun equals(other: Any?): Boolean {
return other === this || other is RegistryObject<*> && other.value == value && other.json == json
}
private var computedHash = false
private var hash = 0
override fun hashCode(): Int {
if (!computedHash) {
hash = value.hashCode().rotateRight(13) xor json.hashCode()
computedHash = true
}
return hash
}
override fun toString(): String {
return "RegistryObject[$value from $file]"
}
}
class ObjectRegistry<T : Any>(val clazz: KClass<T>, val name: String, val key: ((T) -> String)? = null, val intKey: ((T) -> Int)? = null) {
val objects = Object2ObjectOpenHashMap<String, RegistryObject<T>>()
val intObjects = Int2ObjectOpenHashMap<RegistryObject<T>>()
val values get() = objects.values
operator fun get(index: String) = objects[index]
operator fun get(index: Int): RegistryObject<T>? = intObjects[index]
operator fun contains(index: String) = index in objects
operator fun contains(index: Int) = index in intObjects
fun clear() {
objects.clear()
intObjects.clear()
}
fun add(file: IStarboundFile): Boolean {
return AssetPathStack(file.computeDirectory()) {
val elem = Starbound.gson.fromJson(file.reader(), JsonElement::class.java)
val value = Starbound.gson.fromJson<T>(JsonTreeReader(elem), clazz.java)
add(RegistryObject(value, elem, file), this.key?.invoke(value) ?: throw UnsupportedOperationException("No key mapper"))
}
}
fun add(value: RegistryObject<T>): Boolean {
return add(value, this.key?.invoke(value.value) ?: throw UnsupportedOperationException("No key mapper"))
}
fun add(value: T, json: JsonElement, file: IStarboundFile): Boolean {
return add(RegistryObject(value, json, file), this.key?.invoke(value) ?: throw UnsupportedOperationException("No key mapper"))
}
fun add(value: T, json: JsonElement, file: IStarboundFile, key: String): Boolean {
return add(RegistryObject(value, json, file), key)
}
private val lock = Any()
private fun add(value: RegistryObject<T>, key: String): Boolean {
synchronized(lock) {
val existing = objects.put(key, value)
if (existing != null) {
LOGGER.warn("Registry $name already has object with key $key! Overwriting. (old originated from ${existing.file}, new originate from ${value.file}).")
}
if (this.intKey == null)
return existing != null
val intKey = this.intKey.invoke(value.value)
val intExisting = intObjects.put(intKey, value)
if (intExisting != null) {
LOGGER.warn("Registry $name already has object with ID $intKey (new $key, old ${ objects.entries.firstOrNull { it.value === intExisting }?.key })! Overwriting. (old originated from ${intExisting.file}, new originate from ${value.file}).")
}
return existing != null || intExisting != null
}
}
companion object {
private val LOGGER = LogManager.getLogger()
}
}

View File

@ -1,63 +1,66 @@
package ru.dbotthepony.kstarbound package ru.dbotthepony.kstarbound
import com.google.gson.Gson
import com.google.gson.JsonElement import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.internal.bind.JsonTreeReader
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kstarbound.api.IStarboundFile import ru.dbotthepony.kstarbound.api.IStarboundFile
import ru.dbotthepony.kstarbound.defs.player.RecipeDefinition import ru.dbotthepony.kstarbound.defs.player.RecipeDefinition
import ru.dbotthepony.kstarbound.util.KOptional import ru.dbotthepony.kstarbound.util.KOptional
import java.util.Collections import ru.dbotthepony.kstarbound.util.ParallelPerform
import java.util.LinkedList import java.util.*
import java.util.concurrent.Callable
import java.util.concurrent.ExecutorService
import java.util.concurrent.ForkJoinPool import java.util.concurrent.ForkJoinPool
import java.util.concurrent.ForkJoinTask import java.util.concurrent.ForkJoinTask
import java.util.concurrent.Future import java.util.concurrent.locks.ReentrantLock
import kotlin.collections.ArrayList
import kotlin.concurrent.withLock
object RecipeRegistry { object RecipeRegistry {
private val LOGGER = LogManager.getLogger() private val LOGGER = LogManager.getLogger()
private val recipesInternal = ArrayList<RegistryObject<RecipeDefinition>>() data class Entry(val value: RecipeDefinition, val json: JsonElement, val file: IStarboundFile)
private val group2recipesInternal = Object2ObjectOpenHashMap<String, LinkedList<RegistryObject<RecipeDefinition>>>()
private val group2recipesBacking = Object2ObjectOpenHashMap<String, List<RegistryObject<RecipeDefinition>>>()
private val output2recipesInternal = Object2ObjectOpenHashMap<String, LinkedList<RegistryObject<RecipeDefinition>>>()
private val output2recipesBacking = Object2ObjectOpenHashMap<String, List<RegistryObject<RecipeDefinition>>>()
private val input2recipesInternal = Object2ObjectOpenHashMap<String, LinkedList<RegistryObject<RecipeDefinition>>>()
private val input2recipesBacking = Object2ObjectOpenHashMap<String, List<RegistryObject<RecipeDefinition>>>()
val recipes: List<RegistryObject<RecipeDefinition>> = Collections.unmodifiableList(recipesInternal) private val recipesInternal = ArrayList<Entry>()
val group2recipes: Map<String, List<RegistryObject<RecipeDefinition>>> = Collections.unmodifiableMap(group2recipesBacking) private val group2recipesInternal = Object2ObjectOpenHashMap<String, ArrayList<Entry>>()
val output2recipes: Map<String, List<RegistryObject<RecipeDefinition>>> = Collections.unmodifiableMap(output2recipesBacking) private val group2recipesBacking = Object2ObjectOpenHashMap<String, List<Entry>>()
val input2recipes: Map<String, List<RegistryObject<RecipeDefinition>>> = Collections.unmodifiableMap(input2recipesBacking) private val output2recipesInternal = Object2ObjectOpenHashMap<String, ArrayList<Entry>>()
private val output2recipesBacking = Object2ObjectOpenHashMap<String, List<Entry>>()
private val input2recipesInternal = Object2ObjectOpenHashMap<String, ArrayList<Entry>>()
private val input2recipesBacking = Object2ObjectOpenHashMap<String, List<Entry>>()
fun add(recipe: RegistryObject<RecipeDefinition>) { val recipes: List<Entry> = Collections.unmodifiableList(recipesInternal)
val value = recipe.value val group2recipes: Map<String, List<Entry>> = Collections.unmodifiableMap(group2recipesBacking)
recipesInternal.add(recipe) val output2recipes: Map<String, List<Entry>> = Collections.unmodifiableMap(output2recipesBacking)
val input2recipes: Map<String, List<Entry>> = Collections.unmodifiableMap(input2recipesBacking)
for (group in value.groups) { private val lock = ReentrantLock()
group2recipesInternal.computeIfAbsent(group, Object2ObjectFunction { p ->
LinkedList<RegistryObject<RecipeDefinition>>().also {
group2recipesBacking[p as String] = Collections.unmodifiableList(it)
}
}).add(recipe)
}
output2recipesInternal.computeIfAbsent(value.output.item.name, Object2ObjectFunction { p -> fun add(recipe: Entry) {
LinkedList<RegistryObject<RecipeDefinition>>().also { lock.withLock {
output2recipesBacking[p as String] = Collections.unmodifiableList(it) val value = recipe.value
recipesInternal.add(recipe)
for (group in value.groups) {
group2recipesInternal.computeIfAbsent(group, Object2ObjectFunction { p ->
ArrayList<Entry>(1).also {
group2recipesBacking[p as String] = Collections.unmodifiableList(it)
}
}).add(recipe)
} }
}).add(recipe)
for (input in value.input) { output2recipesInternal.computeIfAbsent(value.output.item.key.left(), Object2ObjectFunction { p ->
input2recipesInternal.computeIfAbsent(input.item.name, Object2ObjectFunction { p -> ArrayList<Entry>(1).also {
LinkedList<RegistryObject<RecipeDefinition>>().also { output2recipesBacking[p as String] = Collections.unmodifiableList(it)
input2recipesBacking[p as String] = Collections.unmodifiableList(it)
} }
}).add(recipe) }).add(recipe)
for (input in value.input) {
input2recipesInternal.computeIfAbsent(input.item.key.left(), Object2ObjectFunction { p ->
ArrayList<Entry>(1).also {
input2recipesBacking[p as String] = Collections.unmodifiableList(it)
}
}).add(recipe)
}
} }
} }
@ -77,12 +80,12 @@ object RecipeRegistry {
line.text = ("Loading $listedFile") line.text = ("Loading $listedFile")
val json = elements.read(listedFile.jsonReader()) val json = elements.read(listedFile.jsonReader())
val value = recipes.fromJsonTree(json) val value = recipes.fromJsonTree(json)
line.elements.incrementAndGet() KOptional.of(Entry(value, json, listedFile))
KOptional(RegistryObject(value, json, listedFile))
} catch (err: Throwable) { } catch (err: Throwable) {
LOGGER.error("Loading recipe definition file $listedFile", err) LOGGER.error("Loading recipe definition file $listedFile", err)
line.elements.incrementAndGet()
KOptional.empty() KOptional.empty()
} finally {
line.elements.incrementAndGet()
} }
}.forEach { add(it) } }.forEach { add(it) }

View File

@ -36,33 +36,64 @@ import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
import ru.dbotthepony.kstarbound.defs.tile.TileDefinition import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
import ru.dbotthepony.kstarbound.util.AssetPathStack import ru.dbotthepony.kstarbound.util.AssetPathStack
import ru.dbotthepony.kstarbound.util.KOptional import ru.dbotthepony.kstarbound.util.KOptional
import java.util.concurrent.Callable import ru.dbotthepony.kstarbound.util.ParallelPerform
import java.util.concurrent.ExecutorService
import java.util.concurrent.ForkJoinPool import java.util.concurrent.ForkJoinPool
import java.util.concurrent.ForkJoinTask import java.util.concurrent.ForkJoinTask
import java.util.concurrent.Future
object Registries { object Registries {
private val LOGGER = LogManager.getLogger() private val LOGGER = LogManager.getLogger()
val tiles = ObjectRegistry("tiles", TileDefinition::materialName, TileDefinition::materialId) val tiles = Registry<TileDefinition>("tiles")
val tileModifiers = ObjectRegistry("tile modifiers", MaterialModifier::modName, MaterialModifier::modId) val tileModifiers = Registry<MaterialModifier>("tile modifiers")
val liquid = ObjectRegistry("liquid", LiquidDefinition::name, LiquidDefinition::liquidId) val liquid = Registry<LiquidDefinition>("liquid")
val species = ObjectRegistry("species", Species::kind) val species = Registry<Species>("species")
val statusEffects = ObjectRegistry("status effects", StatusEffectDefinition::name) val statusEffects = Registry<StatusEffectDefinition>("status effects")
val particles = ObjectRegistry("particles", ParticleDefinition::kind) val particles = Registry<ParticleDefinition>("particles")
val items = ObjectRegistry("items", IItemDefinition::itemName) val items = Registry<IItemDefinition>("items")
val questTemplates = ObjectRegistry("quest templates", QuestTemplate::id) val questTemplates = Registry<QuestTemplate>("quest templates")
val techs = ObjectRegistry("techs", TechDefinition::name) val techs = Registry<TechDefinition>("techs")
val jsonFunctions = ObjectRegistry<JsonFunction>("json functions") val jsonFunctions = Registry<JsonFunction>("json functions")
val json2Functions = ObjectRegistry<Json2Function>("json 2functions") val json2Functions = Registry<Json2Function>("json 2functions")
val npcTypes = ObjectRegistry("npc types", NpcTypeDefinition::type) val npcTypes = Registry<NpcTypeDefinition>("npc types")
val projectiles = ObjectRegistry("projectiles", ProjectileDefinition::projectileName) val projectiles = Registry<ProjectileDefinition>("projectiles")
val tenants = ObjectRegistry("tenants", TenantDefinition::name) val tenants = Registry<TenantDefinition>("tenants")
val treasurePools = ObjectRegistry("treasure pools", TreasurePoolDefinition::name) val treasurePools = Registry<TreasurePoolDefinition>("treasure pools")
val monsterSkills = ObjectRegistry("monster skills", MonsterSkillDefinition::name) val monsterSkills = Registry<MonsterSkillDefinition>("monster skills")
val monsterTypes = ObjectRegistry("monster types", MonsterTypeDefinition::type) val monsterTypes = Registry<MonsterTypeDefinition>("monster types")
val worldObjects = ObjectRegistry("objects", ObjectDefinition::objectName) val worldObjects = Registry<ObjectDefinition>("objects")
private fun <T> key(mapper: (T) -> String): (T) -> Pair<String, Int?> {
return { mapper.invoke(it) to null }
}
private fun <T> key(mapper: (T) -> String, mapperInt: (T) -> Int): (T) -> Pair<String, Int> {
return { mapper.invoke(it) to mapperInt.invoke(it) }
}
fun validate(): Boolean {
var any = false
any = !tiles.validate() || any
any = !tileModifiers.validate() || any
any = !liquid.validate() || any
any = !species.validate() || any
any = !statusEffects.validate() || any
any = !particles.validate() || any
any = !items.validate() || any
any = !questTemplates.validate() || any
any = !techs.validate() || any
any = !jsonFunctions.validate() || any
any = !json2Functions.validate() || any
any = !npcTypes.validate() || any
any = !projectiles.validate() || any
any = !tenants.validate() || any
any = !treasurePools.validate() || any
any = !monsterSkills.validate() || any
any = !monsterTypes.validate() || any
any = !worldObjects.validate() || any
return !any
}
private fun loadStage( private fun loadStage(
log: ILoadingLog, log: ILoadingLog,
@ -81,8 +112,9 @@ object Registries {
private inline fun <reified T : Any> loadStage( private inline fun <reified T : Any> loadStage(
log: ILoadingLog, log: ILoadingLog,
executor: ForkJoinPool, executor: ForkJoinPool,
registry: ObjectRegistry<T>, registry: Registry<T>,
files: List<IStarboundFile>, files: List<IStarboundFile>,
noinline keyProvider: (T) -> Pair<String, Int?>,
name: String = registry.name name: String = registry.name
) { ) {
val adapter = Starbound.gson.getAdapter(T::class.java) val adapter = Starbound.gson.getAdapter(T::class.java)
@ -95,19 +127,29 @@ object Registries {
try { try {
it.text = "Loading $listedFile" it.text = "Loading $listedFile"
val result = AssetPathStack(listedFile.computeDirectory()) { AssetPathStack(listedFile.computeDirectory()) {
val elem = elementAdapter.read(listedFile.jsonReader()) val elem = elementAdapter.read(listedFile.jsonReader())
RegistryObject(adapter.fromJsonTree(elem), elem, listedFile) val read = adapter.fromJsonTree(elem)
} val keys = keyProvider(read);
it.elements.incrementAndGet() KOptional.of {
KOptional(result) try {
if (keys.second != null)
registry.add(keys.first, keys.second!!, read, elem, listedFile)
else
registry.add(keys.first, read, elem, listedFile)
} catch (err: Throwable) {
LOGGER.error("Loading ${registry.name} definition file $listedFile", err);
}
}
}
} catch (err: Throwable) { } catch (err: Throwable) {
LOGGER.error("Loading ${registry.name} definition file $listedFile", err) LOGGER.error("Loading ${registry.name} definition file $listedFile", err);
it.elements.incrementAndGet()
KOptional.empty() KOptional.empty()
} finally {
it.elements.incrementAndGet()
} }
}.forEach { registry.add(it) } }.forEach { it.invoke() }
}, name) }, name)
} }
@ -120,20 +162,20 @@ object Registries {
tasks.add(executor.submit { loadStage(log, { loadJson2Functions(it, fileTree["2functions"] ?: listOf()) }, "json 2functions") }) tasks.add(executor.submit { loadStage(log, { loadJson2Functions(it, fileTree["2functions"] ?: listOf()) }, "json 2functions") })
tasks.add(executor.submit { loadStage(log, { loadTreasurePools(it, fileTree["treasurepools"] ?: listOf()) }, "treasure pools") }) tasks.add(executor.submit { loadStage(log, { loadTreasurePools(it, fileTree["treasurepools"] ?: listOf()) }, "treasure pools") })
tasks.add(executor.submit { loadStage(log, executor, tiles, fileTree["material"] ?: listOf()) }) tasks.add(executor.submit { loadStage(log, executor, tiles, fileTree["material"] ?: listOf(), key(TileDefinition::materialName, TileDefinition::materialId)) })
tasks.add(executor.submit { loadStage(log, executor, tileModifiers, fileTree["matmod"] ?: listOf()) }) tasks.add(executor.submit { loadStage(log, executor, tileModifiers, fileTree["matmod"] ?: listOf(), key(MaterialModifier::modName, MaterialModifier::modId)) })
tasks.add(executor.submit { loadStage(log, executor, liquid, fileTree["liquid"] ?: listOf()) }) tasks.add(executor.submit { loadStage(log, executor, liquid, fileTree["liquid"] ?: listOf(), key(LiquidDefinition::name, LiquidDefinition::liquidId)) })
tasks.add(executor.submit { loadStage(log, executor, worldObjects, fileTree["object"] ?: listOf()) })
tasks.add(executor.submit { loadStage(log, executor, statusEffects, fileTree["statuseffect"] ?: listOf()) }) tasks.add(executor.submit { loadStage(log, executor, worldObjects, fileTree["object"] ?: listOf(), key(ObjectDefinition::objectName)) })
tasks.add(executor.submit { loadStage(log, executor, species, fileTree["species"] ?: listOf()) }) tasks.add(executor.submit { loadStage(log, executor, statusEffects, fileTree["statuseffect"] ?: listOf(), key(StatusEffectDefinition::name)) })
tasks.add(executor.submit { loadStage(log, executor, particles, fileTree["particle"] ?: listOf()) }) tasks.add(executor.submit { loadStage(log, executor, species, fileTree["species"] ?: listOf(), key(Species::kind)) })
tasks.add(executor.submit { loadStage(log, executor, questTemplates, fileTree["questtemplate"] ?: listOf()) }) tasks.add(executor.submit { loadStage(log, executor, particles, fileTree["particle"] ?: listOf(), key(ParticleDefinition::kind)) })
tasks.add(executor.submit { loadStage(log, executor, techs, fileTree["tech"] ?: listOf()) }) tasks.add(executor.submit { loadStage(log, executor, questTemplates, fileTree["questtemplate"] ?: listOf(), key(QuestTemplate::id)) })
tasks.add(executor.submit { loadStage(log, executor, npcTypes, fileTree["npctype"] ?: listOf()) }) tasks.add(executor.submit { loadStage(log, executor, techs, fileTree["tech"] ?: listOf(), key(TechDefinition::name)) })
// tasks.add(executor.submit { loadStage(log, executor, projectiles, ext2files["projectile"] ?: listOf()) }) tasks.add(executor.submit { loadStage(log, executor, npcTypes, fileTree["npctype"] ?: listOf(), key(NpcTypeDefinition::type)) })
// tasks.add(executor.submit { loadStage(log, executor, tenants, ext2files["tenant"] ?: listOf()) }) // tasks.add(executor.submit { loadStage(log, executor, projectiles, ext2files["projectile"] ?: listOf(), key(ProjectileDefinition::projectileName)) })
tasks.add(executor.submit { loadStage(log, executor, monsterSkills, fileTree["monsterskill"] ?: listOf()) }) // tasks.add(executor.submit { loadStage(log, executor, tenants, ext2files["tenant"] ?: listOf(), key(TenantDefinition::name)) })
// tasks.add(executor.submit { loadStage(log, _monsterTypes, ext2files["monstertype"] ?: listOf()) }) tasks.add(executor.submit { loadStage(log, executor, monsterSkills, fileTree["monsterskill"] ?: listOf(), key(MonsterSkillDefinition::name)) })
return tasks return tasks
} }
@ -164,19 +206,18 @@ object Registries {
line.maxElements = fileList.size line.maxElements = fileList.size
val time = System.nanoTime() val time = System.nanoTime()
fileList.batch(executor) { listedFile -> ParallelPerform(fileList.spliterator(), { listedFile ->
try { try {
line.text = "Loading $listedFile" line.text = "Loading $listedFile"
val json = objects.read(listedFile.jsonReader()) val json = objects.read(listedFile.jsonReader())
val def = AssetPathStack(listedFile.computeDirectory()) { adapter.fromJsonTree(json) } val def = AssetPathStack(listedFile.computeDirectory()) { adapter.fromJsonTree(json) }
line.elements.incrementAndGet() items.add(def.itemName, def, json, listedFile)
KOptional(RegistryObject(def, json, listedFile))
} catch (err: Throwable) { } catch (err: Throwable) {
LOGGER.error("Loading item definition file $listedFile", err) LOGGER.error("Loading item definition file $listedFile", err)
} finally {
line.elements.incrementAndGet() line.elements.incrementAndGet()
KOptional.empty()
} }
}.forEach { items.add(it) } }).fork().join()
line.text = "Loaded items '$ext' in ${((System.nanoTime() - time) / 1_000_000.0).toLong()}ms" line.text = "Loaded items '$ext' in ${((System.nanoTime() - time) / 1_000_000.0).toLong()}ms"
}) })
@ -196,7 +237,7 @@ object Registries {
try { try {
line.text = ("Loading $k from $listedFile") line.text = ("Loading $k from $listedFile")
val fn = Starbound.gson.fromJson<JsonFunction>(JsonTreeReader(v), JsonFunction::class.java) val fn = Starbound.gson.fromJson<JsonFunction>(JsonTreeReader(v), JsonFunction::class.java)
jsonFunctions.add(fn, v, listedFile, k) jsonFunctions.add(k, fn, v, listedFile)
} catch (err: Exception) { } catch (err: Exception) {
LOGGER.error("Loading json function definition $k from file $listedFile", err) LOGGER.error("Loading json function definition $k from file $listedFile", err)
} }
@ -222,7 +263,7 @@ object Registries {
try { try {
line.text = ("Loading $k from $listedFile") line.text = ("Loading $k from $listedFile")
val fn = Starbound.gson.fromJson<Json2Function>(JsonTreeReader(v), Json2Function::class.java) val fn = Starbound.gson.fromJson<Json2Function>(JsonTreeReader(v), Json2Function::class.java)
json2Functions.add(fn, v, listedFile, k) json2Functions.add(k, fn, v, listedFile)
} catch (err: Throwable) { } catch (err: Throwable) {
LOGGER.error("Loading json 2function definition $k from file $listedFile", err) LOGGER.error("Loading json 2function definition $k from file $listedFile", err)
} }
@ -249,7 +290,7 @@ object Registries {
line.text = ("Loading $k from $listedFile") line.text = ("Loading $k from $listedFile")
val result = Starbound.gson.fromJson<TreasurePoolDefinition>(JsonTreeReader(v), TreasurePoolDefinition::class.java) val result = Starbound.gson.fromJson<TreasurePoolDefinition>(JsonTreeReader(v), TreasurePoolDefinition::class.java)
result.name = k result.name = k
treasurePools.add(result, v, listedFile) treasurePools.add(result.name, result, v, listedFile)
} catch (err: Throwable) { } catch (err: Throwable) {
LOGGER.error("Loading treasure pool definition $k from file $listedFile", err) LOGGER.error("Loading treasure pool definition $k from file $listedFile", err)
} }

View File

@ -0,0 +1,350 @@
package ru.dbotthepony.kstarbound
import com.google.gson.Gson
import com.google.gson.JsonElement
import com.google.gson.JsonNull
import com.google.gson.JsonObject
import com.google.gson.JsonSyntaxException
import com.google.gson.TypeAdapter
import com.google.gson.TypeAdapterFactory
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
import it.unimi.dsi.fastutil.ints.Int2ObjectFunction
import it.unimi.dsi.fastutil.ints.Int2ObjectMap
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
import it.unimi.dsi.fastutil.objects.Object2ObjectMap
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kstarbound.api.IStarboundFile
import ru.dbotthepony.kstarbound.io.json.consumeNull
import ru.dbotthepony.kstarbound.util.Either
import java.lang.reflect.ParameterizedType
import java.util.concurrent.locks.ReentrantLock
import java.util.function.Supplier
import kotlin.collections.contains
import kotlin.collections.set
import kotlin.concurrent.withLock
import ru.dbotthepony.kstarbound.util.traverseJsonPath
inline fun <reified S : Any> Registry<S>.adapter(): TypeAdapterFactory {
return object : TypeAdapterFactory {
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
val subtype = type.type as? ParameterizedType ?: return null
if (subtype.actualTypeArguments.size != 1 || subtype.actualTypeArguments[0] != S::class.java) return null
return when (type.rawType) {
Registry.Entry::class.java -> {
object : TypeAdapter<Registry.Entry<S>>() {
override fun write(out: JsonWriter, value: Registry.Entry<S>?) {
if (value != null) {
out.value(value.key)
} else {
out.nullValue()
}
}
override fun read(`in`: JsonReader): Registry.Entry<S>? {
if (`in`.consumeNull()) {
return null
} else if (`in`.peek() == JsonToken.STRING) {
return this@adapter[`in`.nextString()]
} else if (`in`.peek() == JsonToken.NUMBER) {
return this@adapter[`in`.nextInt()]
} else {
throw JsonSyntaxException("Expected registry key or registry id, got ${`in`.peek()}")
}
}
}
}
Registry.Ref::class.java -> {
object : TypeAdapter<Registry.Ref<S>>() {
override fun write(out: JsonWriter, value: Registry.Ref<S>?) {
if (value != null) {
value.key.map(out::value, out::value)
} else {
out.nullValue()
}
}
override fun read(`in`: JsonReader): Registry.Ref<S>? {
if (`in`.consumeNull()) {
return null
} else if (`in`.peek() == JsonToken.STRING) {
return this@adapter.ref(`in`.nextString())
} else if (`in`.peek() == JsonToken.NUMBER) {
return this@adapter.ref(`in`.nextInt())
} else {
throw JsonSyntaxException("Expected registry key or registry id, got ${`in`.peek()}")
}
}
}
}
else -> null
} as TypeAdapter<T>?
}
}
}
class Registry<T : Any>(val name: String) {
private val keysInternal = Object2ObjectOpenHashMap<String, Impl>()
private val idsInternal = Int2ObjectOpenHashMap<Impl>()
private val keyRefs = Object2ObjectOpenHashMap<String, RefImpl>()
private val idRefs = Int2ObjectOpenHashMap<RefImpl>()
private val lock = ReentrantLock()
val keys: Object2ObjectMap<String, out Entry<T>> = Object2ObjectMaps.unmodifiable(keysInternal)
val ids: Int2ObjectMap<out Entry<T>> = Int2ObjectMaps.unmodifiable(idsInternal)
sealed class Ref<T : Any> : Supplier<Entry<T>?> {
abstract val key: Either<String, Int>
abstract val entry: Entry<T>?
abstract val registry: Registry<T>
val isPresent: Boolean
get() = value != null
val value: T?
get() = entry?.value
fun traverseJsonPath(path: String): JsonElement? {
return traverseJsonPath(path, entry?.json ?: return null)
}
final override fun get(): Entry<T>? {
return entry
}
}
sealed class Entry<T : Any> : Supplier<T> {
abstract val key: String
abstract val id: Int?
abstract val value: T
abstract val json: JsonElement
abstract val file: IStarboundFile?
abstract val registry: Registry<T>
abstract val isBuiltin: Boolean
fun traverseJsonPath(path: String): JsonElement? {
return traverseJsonPath(path, json)
}
final override fun get(): T {
return value
}
val jsonObject: JsonObject
get() = json as JsonObject
}
private inner class Impl(override val key: String, override var value: T, override var id: Int? = null) : Entry<T>() {
override var json: JsonElement = JsonNull.INSTANCE
override var file: IStarboundFile? = null
override var isBuiltin: Boolean = false
override fun equals(other: Any?): Boolean {
return this === other
}
override fun hashCode(): Int {
return key.hashCode()
}
override fun toString(): String {
return "Registry.Entry[key=$key, id=$id, registry=$name]"
}
override val registry: Registry<T>
get() = this@Registry
}
private inner class RefImpl(override val key: Either<String, Int>) : Ref<T>() {
override var entry: Entry<T>? = null
override fun equals(other: Any?): Boolean {
return this === other || other is Registry<*>.RefImpl && other.key == key && other.registry == registry
}
override fun hashCode(): Int {
return key.hashCode()
}
override fun toString(): String {
return "Registry.Ref[key=$key, bound=${entry != null}, registry=$name]"
}
override val registry: Registry<T>
get() = this@Registry
}
operator fun get(index: String): Entry<T>? = lock.withLock { keysInternal[index] }
operator fun get(index: Int): Entry<T>? = lock.withLock { idsInternal[index] }
fun ref(index: String): Ref<T> = lock.withLock {
keyRefs.computeIfAbsent(index, Object2ObjectFunction {
val ref = RefImpl(Either.left(it as String))
ref.entry = keysInternal[it]
ref
})
}
fun ref(index: Int): Ref<T> = lock.withLock {
idRefs.computeIfAbsent(index, Int2ObjectFunction {
val ref = RefImpl(Either.right(it))
ref.entry = idsInternal[it]
ref
})
}
operator fun contains(index: String) = lock.withLock { index in keysInternal }
operator fun contains(index: Int) = lock.withLock { index in idsInternal }
fun validate(): Boolean {
var any = true
keyRefs.values.forEach {
if (!it.isPresent) {
LOGGER.warn("Registry '$name' reference at '${it.key.left()}' is not bound to value, expect problems")
any = false
}
}
idRefs.values.forEach {
if (!it.isPresent) {
LOGGER.warn("Registry '$name' reference with ID '${it.key.right()}' is not bound to value, expect problems")
any = false
}
}
return any
}
fun add(key: String, value: T, json: JsonElement, file: IStarboundFile): Entry<T> {
lock.withLock {
if (key in keysInternal) {
LOGGER.warn("Overwriting Registry entry at '$key' (new def originate from $file; old def originate from ${keysInternal[key]?.file ?: "<code>"})")
}
val entry = keysInternal.computeIfAbsent(key, Object2ObjectFunction { Impl(key, value) })
check(!entry.isBuiltin) { "Trying to redefine builtin entry" }
entry.id?.let {
idsInternal.remove(it)
idRefs[it]?.entry = null
}
entry.id = null
entry.value = value
entry.json = json
entry.file = file
keyRefs[key]?.entry = entry
return entry
}
}
fun add(key: String, id: Int, value: T, json: JsonElement, file: IStarboundFile): Entry<T> {
lock.withLock {
if (key in keysInternal) {
LOGGER.warn("Overwriting Registry entry at '$key' (new def originate from $file; old def originate from ${keysInternal[key]?.file ?: "<code>"})")
}
if (id in idsInternal) {
LOGGER.warn("Overwriting Registry entry with ID '$id' (new def originate from $file; old def originate from ${idsInternal[id]?.file ?: "<code>"})")
}
val entry = keysInternal.computeIfAbsent(key, Object2ObjectFunction { Impl(key, value) })
check(!entry.isBuiltin) { "Trying to redefine builtin entry" }
entry.id?.let {
idsInternal.remove(it)
idRefs[it]?.entry = null
}
entry.id = id
entry.value = value
entry.json = json
entry.file = file
keyRefs[key]?.entry = entry
idRefs[id]?.entry = entry
idsInternal[id] = entry
return entry
}
}
fun add(key: String, value: T, isBuiltin: Boolean = false): Entry<T> {
lock.withLock {
if (key in keysInternal) {
LOGGER.warn("Overwriting Registry entry at '$key' (new def originate from <code>; old def originate from ${keysInternal[key]?.file ?: "<code>"})")
}
val entry = keysInternal.computeIfAbsent(key, Object2ObjectFunction { Impl(key, value) })
check(!entry.isBuiltin || isBuiltin) { "Trying to redefine builtin entry" }
entry.id?.let {
idsInternal.remove(it)
idRefs[it]?.entry = null
}
entry.id = null
entry.value = value
entry.json = JsonNull.INSTANCE
entry.file = null
entry.isBuiltin = isBuiltin
keyRefs[key]?.entry = entry
return entry
}
}
fun add(key: String, id: Int, value: T, isBuiltin: Boolean = false): Entry<T> {
lock.withLock {
if (key in keysInternal) {
LOGGER.warn("Overwriting Registry entry at '$key' (new def originate from <code>; old def originate from ${keysInternal[key]?.file ?: "<code>"})")
}
if (id in idsInternal) {
LOGGER.warn("Overwriting Registry entry with ID '$id' (new def originate from <code>; old def originate from ${idsInternal[id]?.file ?: "<code>"})")
}
val entry = keysInternal.computeIfAbsent(key, Object2ObjectFunction { Impl(key, value) })
check(!entry.isBuiltin || isBuiltin) { "Trying to redefine builtin entry" }
entry.id?.let {
idsInternal.remove(it)
idRefs[it]?.entry = null
}
entry.id = id
entry.value = value
entry.json = JsonNull.INSTANCE
entry.file = null
entry.isBuiltin = isBuiltin
keyRefs[key]?.entry = entry
idRefs[id]?.entry = entry
idsInternal[id] = entry
return entry
}
}
companion object {
private val LOGGER = LogManager.getLogger()
}
}

View File

@ -194,40 +194,30 @@ object Starbound : ISBFileLocator {
registerTypeAdapterFactory(Poly.Companion) registerTypeAdapterFactory(Poly.Companion)
registerTypeAdapterFactory(with(RegistryReferenceFactory()) { registerTypeAdapterFactory(Registries.tiles.adapter())
add(Registries.tiles) registerTypeAdapterFactory(Registries.tileModifiers.adapter())
add(Registries.tileModifiers) registerTypeAdapterFactory(Registries.liquid.adapter())
add(Registries.liquid) registerTypeAdapterFactory(Registries.items.adapter())
add(Registries.items) registerTypeAdapterFactory(Registries.species.adapter())
add(Registries.species) registerTypeAdapterFactory(Registries.statusEffects.adapter())
add(Registries.statusEffects) registerTypeAdapterFactory(Registries.particles.adapter())
add(Registries.particles) registerTypeAdapterFactory(Registries.questTemplates.adapter())
add(Registries.questTemplates) registerTypeAdapterFactory(Registries.techs.adapter())
add(Registries.techs) registerTypeAdapterFactory(Registries.jsonFunctions.adapter())
add(Registries.jsonFunctions) registerTypeAdapterFactory(Registries.json2Functions.adapter())
add(Registries.json2Functions) registerTypeAdapterFactory(Registries.npcTypes.adapter())
add(Registries.npcTypes) registerTypeAdapterFactory(Registries.projectiles.adapter())
add(Registries.projectiles) registerTypeAdapterFactory(Registries.tenants.adapter())
add(Registries.tenants) registerTypeAdapterFactory(Registries.treasurePools.adapter())
add(Registries.treasurePools) registerTypeAdapterFactory(Registries.monsterSkills.adapter())
add(Registries.monsterSkills) registerTypeAdapterFactory(Registries.monsterTypes.adapter())
add(Registries.monsterTypes) registerTypeAdapterFactory(Registries.worldObjects.adapter())
add(Registries.worldObjects)
})
registerTypeAdapter(LongRangeAdapter) registerTypeAdapter(LongRangeAdapter)
create() create()
} }
init {
val f = NonExistingFile("/metamaterials.config")
for (material in BuiltinMetaMaterials.MATERIALS) {
Registries.tiles.add(material, JsonNull.INSTANCE, f)
}
}
fun item(name: String): ItemStack { fun item(name: String): ItemStack {
return ItemStack(Registries.items[name] ?: return ItemStack.EMPTY) return ItemStack(Registries.items[name] ?: return ItemStack.EMPTY)
} }
@ -411,7 +401,7 @@ object Starbound : ISBFileLocator {
state.setTableFunction("recipesForItem", this) { args -> state.setTableFunction("recipesForItem", this) { args ->
args.lua.push(JsonArray().also { a -> args.lua.push(JsonArray().also { a ->
RecipeRegistry.output2recipes[args.getString()]?.stream()?.map { it.toJson() }?.forEach { RecipeRegistry.output2recipes[args.getString()]?.stream()?.map { it.json }?.forEach {
a.add(it) a.add(it)
} }
}) })
@ -449,7 +439,7 @@ object Starbound : ISBFileLocator {
args.push() args.push()
} else { } else {
args.push(JsonObject().also { args.push(JsonObject().also {
it["directory"] = item.item!!.file.computeDirectory() it["directory"] = item.item?.file?.computeDirectory()?.let(::JsonPrimitive) ?: JsonNull.INSTANCE
it["config"] = item.item!!.json it["config"] = item.item!!.json
it["parameters"] = item.parameters it["parameters"] = item.parameters
}) })
@ -481,7 +471,7 @@ object Starbound : ISBFileLocator {
state.setTableFunction("tenantConfig", this) { args -> state.setTableFunction("tenantConfig", this) { args ->
// Json root.tenantConfig(String tenantName) // Json root.tenantConfig(String tenantName)
val name = args.getString() val name = args.getString()
Registries.tenants[name]?.push(args) ?: throw NoSuchElementException("No such tenant $name") args.push(Registries.tenants[name] ?: throw NoSuchElementException("No such tenant $name"))
1 1
} }
@ -496,11 +486,11 @@ object Starbound : ISBFileLocator {
} }
} }
args.push(Registries.tenants.values args.push(Registries.tenants.keys.values
.stream() .stream()
.filter { it.value.test(actualTags) } .filter { it.value.test(actualTags) }
.sorted { a, b -> b.value.compareTo(a.value) } .sorted { a, b -> b.value.compareTo(a.value) }
.map { it.toJson() } .map { it.json }
.collect(JsonArrayCollector)) .collect(JsonArrayCollector))
1 1
@ -517,7 +507,7 @@ object Starbound : ISBFileLocator {
liquid = Registries.liquid[id]?.value ?: throw NoSuchElementException("No such liquid with ID $id") liquid = Registries.liquid[id]?.value ?: throw NoSuchElementException("No such liquid with ID $id")
} }
args.lua.pushStrings(liquid.statusEffects.stream().map { it.value?.value?.name }.filterNotNull().toList()) args.lua.pushStrings(liquid.statusEffects.stream().map { it.value?.name }.filterNotNull().toList())
1 1
} }
@ -711,7 +701,7 @@ object Starbound : ISBFileLocator {
state.setTableFunction("techConfig", this) { args -> state.setTableFunction("techConfig", this) { args ->
val name = args.getString() val name = args.getString()
val tech = Registries.techs[name] ?: throw NoSuchElementException("No such tech $name") val tech = Registries.techs[name] ?: throw NoSuchElementException("No such tech $name")
tech.push(args) args.push(tech)
1 1
} }
@ -898,6 +888,8 @@ object Starbound : ISBFileLocator {
if (!parallel) if (!parallel)
pool.shutdown() pool.shutdown()
Registries.validate()
initializing = false initializing = false
initialized = true initialized = true
log.line("Finished loading in ${System.currentTimeMillis() - time}ms") log.line("Finished loading in ${System.currentTimeMillis() - time}ms")

View File

@ -71,9 +71,9 @@ enum class RenderLayer {
fun tileLayer(isBackground: Boolean, isModifier: Boolean, tile: AbstractTileState): Point { fun tileLayer(isBackground: Boolean, isModifier: Boolean, tile: AbstractTileState): Point {
if (isModifier) { if (isModifier) {
return tileLayer(isBackground, true, tile.modifier?.renderParameters?.zLevel ?: 0L, tile.modifier?.modId?.toLong() ?: 0L, tile.modifierHueShift) return tileLayer(isBackground, true, tile.modifier?.value?.renderParameters?.zLevel ?: 0L, tile.modifier?.value?.modId?.toLong() ?: 0L, tile.modifierHueShift)
} else { } else {
return tileLayer(isBackground, false, tile.material.renderParameters.zLevel, tile.material.materialId.toLong(), tile.hueShift) return tileLayer(isBackground, false, tile.material.value.renderParameters.zLevel, tile.material.value.materialId.toLong(), tile.hueShift)
} }
} }

View File

@ -95,13 +95,13 @@ class TileRenderers(val client: StarboundClient) {
private class TileEqualityTester(val definition: TileDefinition) : EqualityRuleTester { private class TileEqualityTester(val definition: TileDefinition) : EqualityRuleTester {
override fun test(thisTile: AbstractTileState?, otherTile: AbstractTileState?): Boolean { override fun test(thisTile: AbstractTileState?, otherTile: AbstractTileState?): Boolean {
return otherTile?.material == definition && thisTile?.hueShift == otherTile.hueShift return otherTile?.material?.value == definition && thisTile?.hueShift == otherTile.hueShift
} }
} }
private class ModifierEqualityTester(val definition: MaterialModifier) : EqualityRuleTester { private class ModifierEqualityTester(val definition: MaterialModifier) : EqualityRuleTester {
override fun test(thisTile: AbstractTileState?, otherTile: AbstractTileState?): Boolean { override fun test(thisTile: AbstractTileState?, otherTile: AbstractTileState?): Boolean {
return otherTile?.modifier == definition return otherTile?.modifier?.value == definition
} }
} }

View File

@ -5,6 +5,7 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
import it.unimi.dsi.fastutil.longs.LongArraySet import it.unimi.dsi.fastutil.longs.LongArraySet
import it.unimi.dsi.fastutil.objects.ObjectArrayList import it.unimi.dsi.fastutil.objects.ObjectArrayList
import it.unimi.dsi.fastutil.objects.ReferenceArraySet import it.unimi.dsi.fastutil.objects.ReferenceArraySet
import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.world.PIXELS_IN_STARBOUND_UNITf import ru.dbotthepony.kstarbound.world.PIXELS_IN_STARBOUND_UNITf
import ru.dbotthepony.kstarbound.client.StarboundClient import ru.dbotthepony.kstarbound.client.StarboundClient
import ru.dbotthepony.kstarbound.client.render.ConfiguredMesh import ru.dbotthepony.kstarbound.client.render.ConfiguredMesh
@ -97,9 +98,9 @@ class ClientWorld(
val tile = view.getTile(x, y) ?: continue val tile = view.getTile(x, y) ?: continue
val material = tile.material val material = tile.material
if (!material.isMeta) { if (!material.value.isMeta) {
client.tileRenderers client.tileRenderers
.getMaterialRenderer(material.materialName) .getMaterialRenderer(material.key)
.tesselate(tile, view, meshes, Vector2i(x, y), isBackground = isBackground) .tesselate(tile, view, meshes, Vector2i(x, y), isBackground = isBackground)
} }
@ -107,7 +108,7 @@ class ClientWorld(
if (modifier != null) { if (modifier != null) {
client.tileRenderers client.tileRenderers
.getModifierRenderer(modifier.modName) .getModifierRenderer(modifier.key)
.tesselate(tile, view, meshes, Vector2i(x, y), isBackground = isBackground, isModifier = true) .tesselate(tile, view, meshes, Vector2i(x, y), isBackground = isBackground, isModifier = true)
} }
} }
@ -134,11 +135,11 @@ class ClientWorld(
liquidIsDirty = false liquidIsDirty = false
liquidMesh.clear() liquidMesh.clear()
val liquidTypes = ReferenceArraySet<LiquidDefinition>() val liquidTypes = ReferenceArraySet<Registry.Entry<LiquidDefinition>>()
for (x in 0 until renderRegionWidth) { for (x in 0 until renderRegionWidth) {
for (y in 0 until renderRegionHeight) { for (y in 0 until renderRegionHeight) {
view.getCell(x, y)?.liquid?.def?.let { liquidTypes.add(it) } view.getCell(x, y).liquid.def?.let { liquidTypes.add(it) }
} }
} }
@ -151,7 +152,7 @@ class ClientWorld(
for (y in 0 until renderRegionHeight) { for (y in 0 until renderRegionHeight) {
val state = view.getCell(x, y) val state = view.getCell(x, y)
if (state?.liquid?.def === type) { if (state.liquid.def == type) {
builder.vertex(x.toFloat(), y.toFloat()) builder.vertex(x.toFloat(), y.toFloat())
builder.vertex(x.toFloat() + 1f, y.toFloat()) builder.vertex(x.toFloat() + 1f, y.toFloat())
builder.vertex(x.toFloat() + 1f, y.toFloat() + 1f) builder.vertex(x.toFloat() + 1f, y.toFloat() + 1f)
@ -160,7 +161,7 @@ class ClientWorld(
} }
} }
liquidMesh.add(Mesh(builder) to type.color) liquidMesh.add(Mesh(builder) to type.value.color)
} }
} }

View File

@ -9,6 +9,7 @@ import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter import com.google.gson.stream.JsonWriter
import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.defs.item.api.IItemDefinition import ru.dbotthepony.kstarbound.defs.item.api.IItemDefinition
import ru.dbotthepony.kstarbound.io.json.builder.FactoryAdapter import ru.dbotthepony.kstarbound.io.json.builder.FactoryAdapter
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
@ -19,12 +20,16 @@ import ru.dbotthepony.kstarbound.util.ItemStack
* Прототип [ItemStack] в JSON файлах * Прототип [ItemStack] в JSON файлах
*/ */
data class ItemReference( data class ItemReference(
val item: RegistryReference<IItemDefinition>, val item: Registry.Ref<IItemDefinition>,
val count: Long = 1, val count: Long = 1,
val parameters: JsonObject = JsonObject() val parameters: JsonObject = JsonObject()
) { ) {
init {
require(item.key.isLeft) { "Can't reference item by ID" }
}
fun makeStack(): ItemStack { fun makeStack(): ItemStack {
return ItemStack(item.value ?: return ItemStack.EMPTY, count, parameters) return ItemStack(item.entry ?: return ItemStack.EMPTY, count, parameters)
} }
class Factory(val stringInterner: Interner<String> = Interner { it }) : TypeAdapterFactory { class Factory(val stringInterner: Interner<String> = Interner { it }) : TypeAdapterFactory {
@ -33,7 +38,7 @@ data class ItemReference(
return object : TypeAdapter<ItemReference>() { return object : TypeAdapter<ItemReference>() {
private val regularObject = FactoryAdapter.createFor(ItemReference::class, JsonFactory(storesJson = false, asList = false), gson, stringInterner) private val regularObject = FactoryAdapter.createFor(ItemReference::class, JsonFactory(storesJson = false, asList = false), gson, stringInterner)
private val regularList = FactoryAdapter.createFor(ItemReference::class, JsonFactory(storesJson = false, asList = true), gson, stringInterner) private val regularList = FactoryAdapter.createFor(ItemReference::class, JsonFactory(storesJson = false, asList = true), gson, stringInterner)
private val references = gson.getAdapter(TypeToken.getParameterized(RegistryReference::class.java, IItemDefinition::class.java)) as TypeAdapter<RegistryReference<IItemDefinition>> private val references = gson.getAdapter(TypeToken.getParameterized(Registry.Ref::class.java, IItemDefinition::class.java)) as TypeAdapter<Registry.Ref<IItemDefinition>>
override fun write(out: JsonWriter, value: ItemReference?) { override fun write(out: JsonWriter, value: ItemReference?) {
if (value == null) if (value == null)

View File

@ -1,100 +0,0 @@
package ru.dbotthepony.kstarbound.defs
import com.google.gson.Gson
import com.google.gson.JsonSyntaxException
import com.google.gson.TypeAdapter
import com.google.gson.TypeAdapterFactory
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kstarbound.ObjectRegistry
import ru.dbotthepony.kstarbound.RegistryObject
import ru.dbotthepony.kstarbound.io.json.consumeNull
import java.lang.reflect.ParameterizedType
import java.util.function.Supplier
class RegistryReferenceFactory : TypeAdapterFactory {
private val types = Reference2ObjectArrayMap<Class<*>, Pair<(String) -> RegistryObject<Nothing>?, String>>()
private var isLenient = false
fun lenient(): RegistryReferenceFactory {
isLenient = true
return this
}
fun <T : Any> add(clazz: Class<T>, resolver: (String) -> RegistryObject<T>?, name: String): RegistryReferenceFactory {
check(types.put(clazz, (resolver as (String) -> RegistryObject<Nothing>?) to name) == null) { "Already has resolver for class $clazz!" }
return this
}
fun <T : Any> add(registry: ObjectRegistry<T>): RegistryReferenceFactory {
return add(registry.clazz.java, registry::get, registry.name)
}
inline fun <reified T: Any> add(noinline resolver: (String) -> RegistryObject<T>?, name: String) = add(T::class.java, resolver, name)
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
if (type.rawType == RegistryReference::class.java) {
val ptype = type.type as? ParameterizedType ?: return null
val registryType = ptype.actualTypeArguments[0]
val resolver = types[registryType] ?: return if (isLenient) null else throw NoSuchElementException("Can't deserialize registry reference with type $registryType!")
return RegistryReferenceTypeAdapter(resolver.first, gson.getAdapter(String::class.java), resolver.second) as TypeAdapter<T>
}
return null
}
}
class RegistryReferenceTypeAdapter<T : Any>(val resolver: (String) -> RegistryObject<T>?, val strings: TypeAdapter<String>, val name: String) : TypeAdapter<RegistryReference<T>>() {
override fun write(out: JsonWriter, value: RegistryReference<T>?) {
if (value == null)
out.nullValue()
else
strings.write(out, value.name)
}
override fun read(`in`: JsonReader): RegistryReference<T>? {
if (`in`.consumeNull())
return null
if (`in`.peek() == JsonToken.STRING) {
return RegistryReference(strings.read(`in`)!!, resolver, name)
}
throw JsonSyntaxException("Expecting string for registry reference, ${`in`.peek()} given, near ${`in`.path}")
}
}
data class RegistryReference<T : Any>(val name: String, val resolver: (String) -> RegistryObject<T>?, val registryName: String) : Supplier<RegistryObject<T>?>, () -> RegistryObject<T>?, Lazy<RegistryObject<T>?> {
private val lazy = lazy {
val result = resolver.invoke(name)
if (result == null) {
LOGGER.error("No such object '$name' in registry '$registryName'! Expect stuff being broken!")
}
result
}
override fun get(): RegistryObject<T>? {
return lazy.value
}
override val value: RegistryObject<T>?
get() = lazy.value
override fun isInitialized(): Boolean {
return lazy.isInitialized()
}
override fun invoke(): RegistryObject<T>? {
return lazy.value
}
companion object {
private val LOGGER = LogManager.getLogger()
}
}

View File

@ -2,6 +2,7 @@ package ru.dbotthepony.kstarbound.defs
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 ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.defs.image.SpriteReference import ru.dbotthepony.kstarbound.defs.image.SpriteReference
import ru.dbotthepony.kstarbound.defs.item.api.IItemDefinition import ru.dbotthepony.kstarbound.defs.item.api.IItemDefinition
import ru.dbotthepony.kstarbound.defs.player.BlueprintLearnList import ru.dbotthepony.kstarbound.defs.player.BlueprintLearnList
@ -25,7 +26,7 @@ data class Species(
val undyColor: ImmutableList<ColorReplacements>, val undyColor: ImmutableList<ColorReplacements>,
val hairColor: ImmutableList<ColorReplacements>, val hairColor: ImmutableList<ColorReplacements>,
val genders: ImmutableList<Gender>, val genders: ImmutableList<Gender>,
val statusEffects: ImmutableSet<RegistryReference<StatusEffectDefinition>> = ImmutableSet.of(), val statusEffects: ImmutableSet<Registry.Ref<StatusEffectDefinition>> = ImmutableSet.of(),
) { ) {
@JsonFactory @JsonFactory
data class Tooltip(val title: String, val subTitle: String, val description: String) data class Tooltip(val title: String, val subTitle: String, val description: String)
@ -37,8 +38,8 @@ data class Species(
val characterImage: SpriteReference, val characterImage: SpriteReference,
val hairGroup: String? = null, val hairGroup: String? = null,
val hair: ImmutableSet<String>, val hair: ImmutableSet<String>,
val shirt: ImmutableSet<RegistryReference<IItemDefinition>>, val shirt: ImmutableSet<Registry.Ref<IItemDefinition>>,
val pants: ImmutableSet<RegistryReference<IItemDefinition>>, val pants: ImmutableSet<Registry.Ref<IItemDefinition>>,
val facialHairGroup: String? = null, val facialHairGroup: String? = null,
val facialHair: ImmutableSet<String> = ImmutableSet.of(), val facialHair: ImmutableSet<String> = ImmutableSet.of(),
val facialMaskGroup: String? = null, val facialMaskGroup: String? = null,

View File

@ -12,8 +12,8 @@ import com.google.gson.internal.bind.JsonTreeReader
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter import com.google.gson.stream.JsonWriter
import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.defs.ItemReference import ru.dbotthepony.kstarbound.defs.ItemReference
import ru.dbotthepony.kstarbound.defs.RegistryReference
import ru.dbotthepony.kstarbound.io.json.consumeNull import ru.dbotthepony.kstarbound.io.json.consumeNull
import ru.dbotthepony.kstarbound.io.json.stream import ru.dbotthepony.kstarbound.io.json.stream
import ru.dbotthepony.kstarbound.util.Either import ru.dbotthepony.kstarbound.util.Either
@ -49,7 +49,7 @@ class TreasurePoolDefinition(pieces: List<Piece>) {
data class Piece( data class Piece(
val level: Double, val level: Double,
val pool: ImmutableList<PoolEntry> = ImmutableList.of(), val pool: ImmutableList<PoolEntry> = ImmutableList.of(),
val fill: ImmutableList<Either<ItemReference, RegistryReference<TreasurePoolDefinition>>> = ImmutableList.of(), val fill: ImmutableList<Either<ItemReference, Registry.Ref<TreasurePoolDefinition>>> = ImmutableList.of(),
val poolRounds: IPoolRounds = OneRound, val poolRounds: IPoolRounds = OneRound,
// TODO: что оно делает? // TODO: что оно делает?
// оно точно не запрещает ему появляться несколько раз за одну генерацию treasure pool // оно точно не запрещает ему появляться несколько раз за одну генерацию treasure pool
@ -69,7 +69,7 @@ class TreasurePoolDefinition(pieces: List<Piece>) {
val stack = it.makeStack() val stack = it.makeStack()
if (stack.isNotEmpty) result.add(stack) if (stack.isNotEmpty) result.add(stack)
}, { }, {
it.value?.value?.evaluate(random, actualLevel) it.value?.evaluate(random, actualLevel)
}) })
} }
@ -82,7 +82,7 @@ class TreasurePoolDefinition(pieces: List<Piece>) {
val stack = it.makeStack() val stack = it.makeStack()
if (stack.isNotEmpty) result.add(stack) if (stack.isNotEmpty) result.add(stack)
}, { }, {
it.value?.value?.evaluate(random, actualLevel) it.value?.evaluate(random, actualLevel)
}) })
break break
@ -145,7 +145,7 @@ class TreasurePoolDefinition(pieces: List<Piece>) {
data class PoolEntry( data class PoolEntry(
val weight: Double, val weight: Double,
val treasure: Either<ItemReference, RegistryReference<TreasurePoolDefinition>> val treasure: Either<ItemReference, Registry.Ref<TreasurePoolDefinition>>
) { ) {
init { init {
require(weight > 0.0) { "Invalid pool entry weight: $weight" } require(weight > 0.0) { "Invalid pool entry weight: $weight" }
@ -157,7 +157,7 @@ class TreasurePoolDefinition(pieces: List<Piece>) {
if (type.rawType === TreasurePoolDefinition::class.java) { if (type.rawType === TreasurePoolDefinition::class.java) {
return object : TypeAdapter<TreasurePoolDefinition>() { return object : TypeAdapter<TreasurePoolDefinition>() {
private val itemAdapter = gson.getAdapter(ItemReference::class.java) private val itemAdapter = gson.getAdapter(ItemReference::class.java)
private val poolAdapter = gson.getAdapter(TypeToken.getParameterized(RegistryReference::class.java, TreasurePoolDefinition::class.java)) as TypeAdapter<RegistryReference<TreasurePoolDefinition>> private val poolAdapter = gson.getAdapter(TypeToken.getParameterized(Registry.Ref::class.java, TreasurePoolDefinition::class.java)) as TypeAdapter<Registry.Ref<TreasurePoolDefinition>>
private val objReader = gson.getAdapter(JsonObject::class.java) private val objReader = gson.getAdapter(JsonObject::class.java)
override fun write(out: JsonWriter, value: TreasurePoolDefinition?) { override fun write(out: JsonWriter, value: TreasurePoolDefinition?) {
@ -184,7 +184,7 @@ class TreasurePoolDefinition(pieces: List<Piece>) {
val things = objReader.read(`in`) val things = objReader.read(`in`)
val pool = ImmutableList.Builder<PoolEntry>() val pool = ImmutableList.Builder<PoolEntry>()
val fill = ImmutableList.Builder<Either<ItemReference, RegistryReference<TreasurePoolDefinition>>>() val fill = ImmutableList.Builder<Either<ItemReference, Registry.Ref<TreasurePoolDefinition>>>()
var poolRounds: IPoolRounds = OneRound var poolRounds: IPoolRounds = OneRound
val allowDuplication = things["allowDuplication"]?.asBoolean ?: false val allowDuplication = things["allowDuplication"]?.asBoolean ?: false

View File

@ -1,7 +1,7 @@
package ru.dbotthepony.kstarbound.defs.item.api package ru.dbotthepony.kstarbound.defs.item.api
import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.defs.IThingWithDescription import ru.dbotthepony.kstarbound.defs.IThingWithDescription
import ru.dbotthepony.kstarbound.defs.RegistryReference
import ru.dbotthepony.kstarbound.defs.item.IInventoryIcon import ru.dbotthepony.kstarbound.defs.item.IInventoryIcon
import ru.dbotthepony.kstarbound.defs.item.ItemRarity import ru.dbotthepony.kstarbound.defs.item.ItemRarity
import ru.dbotthepony.kstarbound.defs.item.impl.ItemDefinition import ru.dbotthepony.kstarbound.defs.item.impl.ItemDefinition
@ -46,7 +46,7 @@ interface IItemDefinition : IThingWithDescription {
/** /**
* При подборе предмета мгновенно заставляет игрока изучить эти рецепты крафта * При подборе предмета мгновенно заставляет игрока изучить эти рецепты крафта
*/ */
val learnBlueprintsOnPickup: List<RegistryReference<IItemDefinition>> val learnBlueprintsOnPickup: List<Registry.Ref<IItemDefinition>>
/** /**
* Максимальное количество предмета в стопке * Максимальное количество предмета в стопке

View File

@ -1,12 +1,11 @@
package ru.dbotthepony.kstarbound.defs.item.impl package ru.dbotthepony.kstarbound.defs.item.impl
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableList
import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.defs.IThingWithDescription import ru.dbotthepony.kstarbound.defs.IThingWithDescription
import ru.dbotthepony.kstarbound.defs.RegistryReference
import ru.dbotthepony.kstarbound.defs.ThingDescription import ru.dbotthepony.kstarbound.defs.ThingDescription
import ru.dbotthepony.kstarbound.defs.item.IInventoryIcon import ru.dbotthepony.kstarbound.defs.item.IInventoryIcon
import ru.dbotthepony.kstarbound.defs.item.api.IItemDefinition import ru.dbotthepony.kstarbound.defs.item.api.IItemDefinition
import ru.dbotthepony.kstarbound.defs.item.InventoryIcon
import ru.dbotthepony.kstarbound.defs.item.ItemRarity import ru.dbotthepony.kstarbound.defs.item.ItemRarity
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
import ru.dbotthepony.kstarbound.io.json.builder.JsonFlat import ru.dbotthepony.kstarbound.io.json.builder.JsonFlat
@ -22,7 +21,7 @@ data class ItemDefinition(
override val category: String? = null, override val category: String? = null,
override val inventoryIcon: ImmutableList<IInventoryIcon>? = null, override val inventoryIcon: ImmutableList<IInventoryIcon>? = null,
override val itemTags: ImmutableList<String> = ImmutableList.of(), override val itemTags: ImmutableList<String> = ImmutableList.of(),
override val learnBlueprintsOnPickup: ImmutableList<RegistryReference<IItemDefinition>> = ImmutableList.of(), override val learnBlueprintsOnPickup: ImmutableList<Registry.Ref<IItemDefinition>> = ImmutableList.of(),
override val maxStack: Long = 9999L, override val maxStack: Long = 9999L,
override val eventCategory: String? = null, override val eventCategory: String? = null,
override val consumeOnPickup: Boolean = false, override val consumeOnPickup: Boolean = false,

View File

@ -3,11 +3,11 @@ package ru.dbotthepony.kstarbound.defs.monster
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap import com.google.common.collect.ImmutableMap
import com.google.common.collect.ImmutableSet import com.google.common.collect.ImmutableSet
import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.defs.AssetReference import ru.dbotthepony.kstarbound.defs.AssetReference
import ru.dbotthepony.kstarbound.defs.IScriptable import ru.dbotthepony.kstarbound.defs.IScriptable
import ru.dbotthepony.kstarbound.defs.IThingWithDescription import ru.dbotthepony.kstarbound.defs.IThingWithDescription
import ru.dbotthepony.kstarbound.defs.player.PlayerMovementParameters import ru.dbotthepony.kstarbound.defs.player.PlayerMovementParameters
import ru.dbotthepony.kstarbound.defs.RegistryReference
import ru.dbotthepony.kstarbound.defs.animation.AnimationDefinition import ru.dbotthepony.kstarbound.defs.animation.AnimationDefinition
import ru.dbotthepony.kstarbound.defs.item.TreasurePoolDefinition import ru.dbotthepony.kstarbound.defs.item.TreasurePoolDefinition
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
@ -24,7 +24,7 @@ data class MonsterTypeDefinition(
val animation: AssetReference<AnimationDefinition>, val animation: AssetReference<AnimationDefinition>,
// [ { "default" : "poptopTreasure", "bow" : "poptopHunting" } ], // [ { "default" : "poptopTreasure", "bow" : "poptopHunting" } ],
// "dropPools" : [ "smallRobotTreasure" ], // "dropPools" : [ "smallRobotTreasure" ],
val dropPools: Either<ImmutableList<ImmutableMap<String, RegistryReference<TreasurePoolDefinition>>>, ImmutableList<RegistryReference<TreasurePoolDefinition>>>, val dropPools: Either<ImmutableList<ImmutableMap<String, Registry.Ref<TreasurePoolDefinition>>>, ImmutableList<Registry.Ref<TreasurePoolDefinition>>>,
val baseParameters: BaseParameters val baseParameters: BaseParameters
) : IThingWithDescription by desc { ) : IThingWithDescription by desc {
@JsonFactory @JsonFactory

View File

@ -11,14 +11,11 @@ import com.google.gson.JsonSyntaxException
import com.google.gson.TypeAdapter import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter import com.google.gson.stream.JsonWriter
import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.defs.AssetPath import ru.dbotthepony.kstarbound.defs.AssetPath
import ru.dbotthepony.kstarbound.defs.AssetReference
import ru.dbotthepony.kstarbound.defs.ItemReference import ru.dbotthepony.kstarbound.defs.ItemReference
import ru.dbotthepony.kstarbound.defs.JsonReference import ru.dbotthepony.kstarbound.defs.JsonReference
import ru.dbotthepony.kstarbound.defs.RegistryReference
import ru.dbotthepony.kstarbound.defs.StatModifier import ru.dbotthepony.kstarbound.defs.StatModifier
import ru.dbotthepony.kstarbound.defs.TouchDamage
import ru.dbotthepony.kstarbound.defs.animation.AnimationDefinition
import ru.dbotthepony.kstarbound.defs.item.TreasurePoolDefinition import ru.dbotthepony.kstarbound.defs.item.TreasurePoolDefinition
import ru.dbotthepony.kstarbound.defs.tile.TileDamageConfig import ru.dbotthepony.kstarbound.defs.tile.TileDamageConfig
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
@ -44,10 +41,10 @@ data class ObjectDefinition(
val hasObjectItem: Boolean = true, val hasObjectItem: Boolean = true,
val scannable: Boolean = true, val scannable: Boolean = true,
val retainObjectParametersInItem: Boolean = false, val retainObjectParametersInItem: Boolean = false,
val breakDropPool: RegistryReference<TreasurePoolDefinition>? = null, val breakDropPool: Registry.Ref<TreasurePoolDefinition>? = null,
// null - not specified, empty list - always drop nothing // null - not specified, empty list - always drop nothing
val breakDropOptions: ImmutableList<ImmutableList<ItemReference>>? = null, val breakDropOptions: ImmutableList<ImmutableList<ItemReference>>? = null,
val smashDropPool: RegistryReference<TreasurePoolDefinition>? = null, val smashDropPool: Registry.Ref<TreasurePoolDefinition>? = null,
val smashDropOptions: ImmutableList<ImmutableList<ItemReference>> = ImmutableList.of(), val smashDropOptions: ImmutableList<ImmutableList<ItemReference>> = ImmutableList.of(),
//val animation: AssetReference<AnimationDefinition>? = null, //val animation: AssetReference<AnimationDefinition>? = null,
val animation: AssetPath? = null, val animation: AssetPath? = null,
@ -97,10 +94,10 @@ data class ObjectDefinition(
val hasObjectItem: Boolean = true, val hasObjectItem: Boolean = true,
val scannable: Boolean = true, val scannable: Boolean = true,
val retainObjectParametersInItem: Boolean = false, val retainObjectParametersInItem: Boolean = false,
val breakDropPool: RegistryReference<TreasurePoolDefinition>? = null, val breakDropPool: Registry.Ref<TreasurePoolDefinition>? = null,
// null - not specified, empty list - always drop nothing // null - not specified, empty list - always drop nothing
val breakDropOptions: ImmutableList<ImmutableList<ItemReference>>? = null, val breakDropOptions: ImmutableList<ImmutableList<ItemReference>>? = null,
val smashDropPool: RegistryReference<TreasurePoolDefinition>? = null, val smashDropPool: Registry.Ref<TreasurePoolDefinition>? = null,
val smashDropOptions: ImmutableList<ImmutableList<ItemReference>> = ImmutableList.of(), val smashDropOptions: ImmutableList<ImmutableList<ItemReference>> = ImmutableList.of(),
//val animation: AssetReference<AnimationDefinition>? = null, //val animation: AssetReference<AnimationDefinition>? = null,
val animation: AssetPath? = null, val animation: AssetPath? = null,

View File

@ -210,8 +210,8 @@ data class ObjectOrientation(
val builder = ImmutableList.Builder<Pair<Vector2i, String>>() val builder = ImmutableList.Builder<Pair<Vector2i, String>>()
when (val collisionType = obj.get("collisionType", "none").lowercase()) { when (val collisionType = obj.get("collisionType", "none").lowercase()) {
"solid" -> collisionSpaces.forEach { builder.add(it to BuiltinMetaMaterials.OBJECT_SOLID.materialName) } "solid" -> collisionSpaces.forEach { builder.add(it to BuiltinMetaMaterials.OBJECT_SOLID.key) }
"platform" -> collisionSpaces.forEach { if (it.y == boundingBox.maxs.y) builder.add(it to BuiltinMetaMaterials.OBJECT_PLATFORM.materialName) } "platform" -> collisionSpaces.forEach { if (it.y == boundingBox.maxs.y) builder.add(it to BuiltinMetaMaterials.OBJECT_PLATFORM.key) }
"none" -> {} "none" -> {}
else -> throw JsonSyntaxException("Unknown collision type $collisionType") else -> throw JsonSyntaxException("Unknown collision type $collisionType")
} }

View File

@ -1,13 +1,13 @@
package ru.dbotthepony.kstarbound.defs.particle package ru.dbotthepony.kstarbound.defs.particle
import ru.dbotthepony.kstarbound.defs.RegistryReference import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
import ru.dbotthepony.kstarbound.util.Either import ru.dbotthepony.kstarbound.util.Either
@JsonFactory @JsonFactory
data class ParticleCreator( data class ParticleCreator(
val count: Int = 1, val count: Int = 1,
val particle: Either<RegistryReference<ParticleDefinition>, IParticleConfig>, val particle: Either<Registry.Ref<ParticleDefinition>, IParticleConfig>,
//override val offset: Vector2d? = null, //override val offset: Vector2d? = null,
//override val position: Vector2d? = null, //override val position: Vector2d? = null,

View File

@ -10,7 +10,7 @@ import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter import com.google.gson.stream.JsonWriter
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap
import ru.dbotthepony.kstarbound.defs.RegistryReference import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.defs.item.api.IItemDefinition import ru.dbotthepony.kstarbound.defs.item.api.IItemDefinition
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
@ -18,7 +18,7 @@ class BlueprintLearnList private constructor(private val tiers: Int2ObjectArrayM
constructor(tiers: Map<Int, List<Entry>>) : this(Int2ObjectArrayMap<ImmutableList<Entry>>().also { for ((k, v) in tiers.entries) it.put(k, ImmutableList.copyOf(v)) }) constructor(tiers: Map<Int, List<Entry>>) : this(Int2ObjectArrayMap<ImmutableList<Entry>>().also { for ((k, v) in tiers.entries) it.put(k, ImmutableList.copyOf(v)) })
@JsonFactory @JsonFactory
data class Entry(val item: RegistryReference<IItemDefinition>) data class Entry(val item: Registry.Ref<IItemDefinition>)
operator fun get(tier: Int): List<Entry> { operator fun get(tier: Int): List<Entry> {
return tiers.getOrDefault(tier, ImmutableList.of()) return tiers.getOrDefault(tier, ImmutableList.of())

View File

@ -2,9 +2,9 @@ package ru.dbotthepony.kstarbound.defs.player
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap import com.google.common.collect.ImmutableMap
import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.defs.AssetPath import ru.dbotthepony.kstarbound.defs.AssetPath
import ru.dbotthepony.kstarbound.defs.IScriptable import ru.dbotthepony.kstarbound.defs.IScriptable
import ru.dbotthepony.kstarbound.defs.RegistryReference
import ru.dbotthepony.kstarbound.defs.item.api.IItemDefinition import ru.dbotthepony.kstarbound.defs.item.api.IItemDefinition
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
@ -13,8 +13,8 @@ data class DeploymentConfig(
override val scripts: ImmutableList<AssetPath>, override val scripts: ImmutableList<AssetPath>,
override val scriptDelta: Int, override val scriptDelta: Int,
val starterMechSet: ImmutableMap<String, RegistryReference<IItemDefinition>>, val starterMechSet: ImmutableMap<String, Registry.Ref<IItemDefinition>>,
val speciesStarterMechBody: ImmutableMap<String, RegistryReference<IItemDefinition>>, val speciesStarterMechBody: ImmutableMap<String, Registry.Ref<IItemDefinition>>,
val enemyDetectRadius: Double, val enemyDetectRadius: Double,
val enemyDetectTypeNames: ImmutableList<String>, val enemyDetectTypeNames: ImmutableList<String>,

View File

@ -4,8 +4,8 @@ import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap import com.google.common.collect.ImmutableMap
import com.google.common.collect.ImmutableSet import com.google.common.collect.ImmutableSet
import com.google.gson.JsonObject import com.google.gson.JsonObject
import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.defs.AssetReference import ru.dbotthepony.kstarbound.defs.AssetReference
import ru.dbotthepony.kstarbound.defs.RegistryReference
import ru.dbotthepony.kstarbound.defs.Species import ru.dbotthepony.kstarbound.defs.Species
import ru.dbotthepony.kstarbound.util.SBPattern import ru.dbotthepony.kstarbound.util.SBPattern
import ru.dbotthepony.kstarbound.defs.animation.AnimationDefinition import ru.dbotthepony.kstarbound.defs.animation.AnimationDefinition
@ -22,14 +22,14 @@ data class PlayerDefinition(
val blueprintAlreadyKnown: SBPattern, val blueprintAlreadyKnown: SBPattern,
val collectableUnlock: SBPattern, val collectableUnlock: SBPattern,
val species: ImmutableSet<RegistryReference<Species>>, val species: ImmutableSet<Registry.Ref<Species>>,
val nametagColor: RGBAColor, val nametagColor: RGBAColor,
val ageItemsEvery: Int, val ageItemsEvery: Int,
val defaultItems: ImmutableSet<RegistryReference<IItemDefinition>>, val defaultItems: ImmutableSet<Registry.Ref<IItemDefinition>>,
val defaultBlueprints: BlueprintLearnList, val defaultBlueprints: BlueprintLearnList,
val defaultCodexes: ImmutableMap<String, ImmutableList<RegistryReference<IItemDefinition>>>, val defaultCodexes: ImmutableMap<String, ImmutableList<Registry.Ref<IItemDefinition>>>,
val metaBoundBox: AABB, val metaBoundBox: AABB,
val movementParameters: PlayerMovementParameters, val movementParameters: PlayerMovementParameters,
val zeroGMovementParameters: PlayerMovementParameters, val zeroGMovementParameters: PlayerMovementParameters,

View File

@ -1,12 +1,14 @@
package ru.dbotthepony.kstarbound.defs.tile package ru.dbotthepony.kstarbound.defs.tile
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableList
import ru.dbotthepony.kstarbound.Registries
import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.defs.AssetReference import ru.dbotthepony.kstarbound.defs.AssetReference
import ru.dbotthepony.kstarbound.world.physics.CollisionType import ru.dbotthepony.kstarbound.world.physics.CollisionType
import ru.dbotthepony.kstarbound.defs.ThingDescription import ru.dbotthepony.kstarbound.defs.ThingDescription
object BuiltinMetaMaterials { object BuiltinMetaMaterials {
private fun make(id: Int, name: String, collisionType: CollisionType) = TileDefinition( private fun make(id: Int, name: String, collisionType: CollisionType) = Registries.tiles.add(name, id, TileDefinition(
materialId = id, materialId = id,
materialName = "metamaterial:$name", materialName = "metamaterial:$name",
descriptionData = ThingDescription.EMPTY, descriptionData = ThingDescription.EMPTY,
@ -15,7 +17,7 @@ object BuiltinMetaMaterials {
renderParameters = RenderParameters.META, renderParameters = RenderParameters.META,
isMeta = true, isMeta = true,
collisionKind = collisionType collisionKind = collisionType
) ))
/** /**
* air * air
@ -38,7 +40,7 @@ object BuiltinMetaMaterials {
val OBJECT_SOLID = make(65500, "objectsolid", CollisionType.BLOCK) val OBJECT_SOLID = make(65500, "objectsolid", CollisionType.BLOCK)
val OBJECT_PLATFORM = make(65501, "objectplatform", CollisionType.PLATFORM) val OBJECT_PLATFORM = make(65501, "objectplatform", CollisionType.PLATFORM)
val MATERIALS: ImmutableList<TileDefinition> = ImmutableList.of( val MATERIALS: ImmutableList<Registry.Entry<TileDefinition>> = ImmutableList.of(
EMPTY, EMPTY,
NULL, NULL,
STRUCTURE, STRUCTURE,

View File

@ -1,7 +1,7 @@
package ru.dbotthepony.kstarbound.defs.tile package ru.dbotthepony.kstarbound.defs.tile
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableList
import ru.dbotthepony.kstarbound.defs.RegistryReference import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.defs.StatusEffectDefinition import ru.dbotthepony.kstarbound.defs.StatusEffectDefinition
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
import ru.dbotthepony.kvector.vector.RGBAColor import ru.dbotthepony.kvector.vector.RGBAColor
@ -14,7 +14,7 @@ data class LiquidDefinition(
val tickDelta: Int = 1, val tickDelta: Int = 1,
val color: RGBAColor, val color: RGBAColor,
val itemDrop: String? = null, val itemDrop: String? = null,
val statusEffects: ImmutableList<RegistryReference<StatusEffectDefinition>> = ImmutableList.of(), val statusEffects: ImmutableList<Registry.Ref<StatusEffectDefinition>> = ImmutableList.of(),
val interactions: ImmutableList<Interaction> = ImmutableList.of(), val interactions: ImmutableList<Interaction> = ImmutableList.of(),
val texture: String, val texture: String,
val bottomLightMix: RGBAColor, val bottomLightMix: RGBAColor,

View File

@ -17,7 +17,7 @@ import jnr.ffi.Pointer
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import org.lwjgl.system.MemoryStack import org.lwjgl.system.MemoryStack
import org.lwjgl.system.MemoryUtil import org.lwjgl.system.MemoryUtil
import ru.dbotthepony.kstarbound.RegistryObject import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.io.json.InternedJsonElementAdapter import ru.dbotthepony.kstarbound.io.json.InternedJsonElementAdapter
import ru.dbotthepony.kvector.api.IStruct2i import ru.dbotthepony.kvector.api.IStruct2i
@ -595,8 +595,8 @@ class LuaState private constructor(private val pointer: Pointer, val stringInter
fun push(value: Boolean) = this@LuaState.push(value) fun push(value: Boolean) = this@LuaState.push(value)
fun push(value: String?) = this@LuaState.push(value) fun push(value: String?) = this@LuaState.push(value)
fun push(value: JsonElement?) = this@LuaState.push(value) fun push(value: JsonElement?) = this@LuaState.push(value)
fun push(value: RegistryObject<*>?) = this@LuaState.push(value) fun push(value: Registry.Entry<*>?) = this@LuaState.push(value)
fun pushFull(value: RegistryObject<*>?) = this@LuaState.pushFull(value) fun pushFull(value: Registry.Entry<*>?) = this@LuaState.pushFull(value)
} }
/** /**
@ -902,7 +902,8 @@ class LuaState private constructor(private val pointer: Pointer, val stringInter
this.setTableValue(table) this.setTableValue(table)
} }
fun setTableValue(key: String, value: String) { fun setTableValue(key: String, value: String?) {
value ?: return
val table = this.stackTop val table = this.stackTop
this.push(key) this.push(key)
this.push(value) this.push(value)
@ -1058,20 +1059,20 @@ class LuaState private constructor(private val pointer: Pointer, val stringInter
} }
} }
fun push(value: RegistryObject<*>?) { fun push(value: Registry.Entry<*>?) {
if (value == null) if (value == null)
push() push()
else else
push(value.toJson()) push(value.json)
} }
fun pushFull(value: RegistryObject<*>?) { fun pushFull(value: Registry.Entry<*>?) {
if (value == null) if (value == null)
push() push()
else { else {
pushTable(hashSize = 2) pushTable(hashSize = 2)
setTableValue("path", value.file.computeFullPath()) setTableValue("path", value.file?.computeFullPath())
setTableValue("config", value.toJson()) setTableValue("config", value.json)
} }
} }

View File

@ -7,7 +7,7 @@ import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
import ru.dbotthepony.kstarbound.Registries import ru.dbotthepony.kstarbound.Registries
import ru.dbotthepony.kstarbound.RegistryObject import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.defs.player.TechDefinition import ru.dbotthepony.kstarbound.defs.player.TechDefinition
import ru.dbotthepony.kstarbound.lua.LuaState import ru.dbotthepony.kstarbound.lua.LuaState
@ -48,9 +48,9 @@ class Avatar(val uniqueId: UUID) {
var cursorItem = ItemStack.EMPTY var cursorItem = ItemStack.EMPTY
private val availableTechs = ObjectOpenHashSet<RegistryObject<TechDefinition>>() private val availableTechs = ObjectOpenHashSet<Registry.Entry<TechDefinition>>()
private val enabledTechs = ObjectOpenHashSet<RegistryObject<TechDefinition>>() private val enabledTechs = ObjectOpenHashSet<Registry.Entry<TechDefinition>>()
private val equippedTechs = Object2ObjectOpenHashMap<String, RegistryObject<TechDefinition>>() private val equippedTechs = Object2ObjectOpenHashMap<String, Registry.Entry<TechDefinition>>()
private val knownBlueprints = ObjectOpenHashSet<ItemStack>() private val knownBlueprints = ObjectOpenHashSet<ItemStack>()
// С подписью NEW // С подписью NEW

View File

@ -51,6 +51,22 @@ class Either<L, R> private constructor(val left: KOptional<L>, val right: KOptio
return orElse.invoke() return orElse.invoke()
} }
override fun equals(other: Any?): Boolean {
return other === this || other is Either<*, *> && other.left == left && other.right == right
}
override fun hashCode(): Int {
return left.hashCode() * 31 + right.hashCode()
}
override fun toString(): String {
if (isLeft) {
return "Either.left[${left.value}]"
} else {
return "Either.right[${right.value}]"
}
}
companion object { companion object {
@JvmStatic @JvmStatic
fun <L, R> left(value: L): Either<L, R> { fun <L, R> left(value: L): Either<L, R> {

View File

@ -5,17 +5,16 @@ import com.google.gson.JsonPrimitive
import com.google.gson.TypeAdapter import com.google.gson.TypeAdapter
import com.google.gson.internal.bind.TypeAdapters import com.google.gson.internal.bind.TypeAdapters
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.kstarbound.RegistryObject import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.defs.item.api.IItemDefinition import ru.dbotthepony.kstarbound.defs.item.api.IItemDefinition
import ru.dbotthepony.kstarbound.io.json.consumeNull import ru.dbotthepony.kstarbound.io.json.consumeNull
class ItemStack private constructor(item: RegistryObject<IItemDefinition>?, count: Long, val parameters: JsonObject, marker: Unit) { class ItemStack private constructor(item: Registry.Entry<IItemDefinition>?, count: Long, val parameters: JsonObject, marker: Unit) {
constructor(item: RegistryObject<IItemDefinition>, count: Long = 1L, parameters: JsonObject = JsonObject()) : this(item, count, parameters, Unit) constructor(item: Registry.Entry<IItemDefinition>, count: Long = 1L, parameters: JsonObject = JsonObject()) : this(item, count, parameters, Unit)
var item: RegistryObject<IItemDefinition>? = item var item: Registry.Entry<IItemDefinition>? = item
private set private set
var size = count var size = count

View File

@ -67,7 +67,7 @@ class KOptional<T> private constructor(private val _value: T, val isPresent: Boo
} }
override fun hashCode(): Int { override fun hashCode(): Int {
return _value.hashCode() return _value.hashCode() + 43839429
} }
override fun toString(): String { override fun toString(): String {

View File

@ -64,7 +64,7 @@ class LightCalculator(val parent: ICellAccess, val width: Int, val height: Int)
val parent = this@LightCalculator.parent.getCell(x, y) ?: return@lazy 0f val parent = this@LightCalculator.parent.getCell(x, y) ?: return@lazy 0f
val lightBlockStrength: Float val lightBlockStrength: Float
if (parent.foreground.material.renderParameters.lightTransparent) { if (parent.foreground.material.value.renderParameters.lightTransparent) {
lightBlockStrength = 0f lightBlockStrength = 0f
} else { } else {
lightBlockStrength = 1f lightBlockStrength = 1f

View File

@ -47,7 +47,7 @@ fun interface TileRayFilter {
} }
val NeverFilter = TileRayFilter { state, fraction, x, y, normal, borderX, borderY -> RayFilterResult.CONTINUE } val NeverFilter = TileRayFilter { state, fraction, x, y, normal, borderX, borderY -> RayFilterResult.CONTINUE }
val NonEmptyFilter = TileRayFilter { state, fraction, x, y, normal, borderX, borderY -> RayFilterResult.of(!state.foreground.material.collisionKind.isEmpty) } val NonEmptyFilter = TileRayFilter { state, fraction, x, y, normal, borderX, borderY -> RayFilterResult.of(!state.foreground.material.value.collisionKind.isEmpty) }
fun ICellAccess.castRay(startPos: Vector2d, direction: Vector2d, length: Double, filter: TileRayFilter) = castRay(startPos, startPos + direction * length, filter) fun ICellAccess.castRay(startPos: Vector2d, direction: Vector2d, length: Double, filter: TileRayFilter) = castRay(startPos, startPos + direction * length, filter)

View File

@ -226,7 +226,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
result.add(CollisionPoly( result.add(CollisionPoly(
BLOCK_POLY + Vector2d(x.toDouble(), y.toDouble()), BLOCK_POLY + Vector2d(x.toDouble(), y.toDouble()),
cell.foreground.material.collisionKind, cell.foreground.material.value.collisionKind,
//velocity = Vector2d(EARTH_FREEFALL_ACCELERATION, 0.0) //velocity = Vector2d(EARTH_FREEFALL_ACCELERATION, 0.0)
)) ))
} }

View File

@ -1,12 +1,11 @@
package ru.dbotthepony.kstarbound.world.api package ru.dbotthepony.kstarbound.world.api
import ru.dbotthepony.kstarbound.Registries import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
import ru.dbotthepony.kstarbound.util.HashTableInterner
import java.io.DataInputStream import java.io.DataInputStream
sealed class AbstractLiquidState { sealed class AbstractLiquidState {
abstract val def: LiquidDefinition? abstract val def: Registry.Entry<LiquidDefinition>?
abstract val level: Float abstract val level: Float
abstract val pressure: Float abstract val pressure: Float
abstract val isInfinite: Boolean abstract val isInfinite: Boolean

View File

@ -1,13 +1,14 @@
package ru.dbotthepony.kstarbound.world.api package ru.dbotthepony.kstarbound.world.api
import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
import ru.dbotthepony.kstarbound.defs.tile.TileDefinition import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
import java.io.DataInputStream import java.io.DataInputStream
sealed class AbstractTileState { sealed class AbstractTileState {
abstract val material: TileDefinition abstract val material: Registry.Entry<TileDefinition>
abstract val modifier: MaterialModifier? abstract val modifier: Registry.Entry<MaterialModifier>?
abstract val color: TileColor abstract val color: TileColor
abstract val hueShift: Float abstract val hueShift: Float
abstract val modifierHueShift: Float abstract val modifierHueShift: Float

View File

@ -1,9 +1,10 @@
package ru.dbotthepony.kstarbound.world.api package ru.dbotthepony.kstarbound.world.api
import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
data class ImmutableLiquidState( data class ImmutableLiquidState(
override val def: LiquidDefinition? = null, override val def: Registry.Entry<LiquidDefinition>? = null,
override val level: Float = 0f, override val level: Float = 0f,
override val pressure: Float = 0f, override val pressure: Float = 0f,
override val isInfinite: Boolean = false, override val isInfinite: Boolean = false,

View File

@ -1,12 +1,13 @@
package ru.dbotthepony.kstarbound.world.api package ru.dbotthepony.kstarbound.world.api
import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
import ru.dbotthepony.kstarbound.defs.tile.TileDefinition import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
data class ImmutableTileState( data class ImmutableTileState(
override var material: TileDefinition = BuiltinMetaMaterials.NULL, override var material: Registry.Entry<TileDefinition> = BuiltinMetaMaterials.NULL,
override var modifier: MaterialModifier? = null, override var modifier: Registry.Entry<MaterialModifier>? = null,
override var color: TileColor = TileColor.DEFAULT, override var color: TileColor = TileColor.DEFAULT,
override var hueShift: Float = 0f, override var hueShift: Float = 0f,
override var modifierHueShift: Float = 0f, override var modifierHueShift: Float = 0f,

View File

@ -1,17 +1,18 @@
package ru.dbotthepony.kstarbound.world.api package ru.dbotthepony.kstarbound.world.api
import ru.dbotthepony.kstarbound.Registries import ru.dbotthepony.kstarbound.Registries
import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
import java.io.DataInputStream import java.io.DataInputStream
data class MutableLiquidState( data class MutableLiquidState(
override var def: LiquidDefinition? = null, override var def: Registry.Entry<LiquidDefinition>? = null,
override var level: Float = 0f, override var level: Float = 0f,
override var pressure: Float = 0f, override var pressure: Float = 0f,
override var isInfinite: Boolean = false, override var isInfinite: Boolean = false,
) : AbstractLiquidState() { ) : AbstractLiquidState() {
fun read(stream: DataInputStream): MutableLiquidState { fun read(stream: DataInputStream): MutableLiquidState {
def = Registries.liquid[stream.readUnsignedByte()]?.value def = Registries.liquid[stream.readUnsignedByte()]
level = stream.readFloat() level = stream.readFloat()
pressure = stream.readFloat() pressure = stream.readFloat()
isInfinite = stream.readBoolean() isInfinite = stream.readBoolean()

View File

@ -1,14 +1,15 @@
package ru.dbotthepony.kstarbound.world.api package ru.dbotthepony.kstarbound.world.api
import ru.dbotthepony.kstarbound.Registries import ru.dbotthepony.kstarbound.Registries
import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
import ru.dbotthepony.kstarbound.defs.tile.TileDefinition import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
import java.io.DataInputStream import java.io.DataInputStream
data class MutableTileState( data class MutableTileState(
override var material: TileDefinition = BuiltinMetaMaterials.NULL, override var material: Registry.Entry<TileDefinition> = BuiltinMetaMaterials.NULL,
override var modifier: MaterialModifier? = null, override var modifier: Registry.Entry<MaterialModifier>? = null,
override var color: TileColor = TileColor.DEFAULT, override var color: TileColor = TileColor.DEFAULT,
override var hueShift: Float = 0f, override var hueShift: Float = 0f,
override var modifierHueShift: Float = 0f, override var modifierHueShift: Float = 0f,
@ -49,10 +50,10 @@ data class MutableTileState(
} }
fun read(stream: DataInputStream): MutableTileState { fun read(stream: DataInputStream): MutableTileState {
material = Registries.tiles[stream.readUnsignedShort()]?.value ?: BuiltinMetaMaterials.EMPTY material = Registries.tiles[stream.readUnsignedShort()] ?: BuiltinMetaMaterials.EMPTY
setHueShift(stream.read()) setHueShift(stream.read())
color = TileColor.of(stream.read()) color = TileColor.of(stream.read())
modifier = Registries.tileModifiers[stream.readUnsignedShort()]?.value modifier = Registries.tileModifiers[stream.readUnsignedShort()]
setModHueShift(stream.read()) setModHueShift(stream.read())
return this return this
} }

View File

@ -6,7 +6,7 @@ import com.google.gson.TypeAdapter
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
import ru.dbotthepony.kstarbound.Registries import ru.dbotthepony.kstarbound.Registries
import ru.dbotthepony.kstarbound.RegistryObject import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.client.world.ClientWorld import ru.dbotthepony.kstarbound.client.world.ClientWorld
import ru.dbotthepony.kstarbound.defs.Drawable import ru.dbotthepony.kstarbound.defs.Drawable
@ -26,9 +26,9 @@ import ru.dbotthepony.kvector.vector.Vector2i
open class WorldObject( open class WorldObject(
val world: World<*, *>, val world: World<*, *>,
val prototype: RegistryObject<ObjectDefinition>, val prototype: Registry.Entry<ObjectDefinition>,
val pos: Vector2i, val pos: Vector2i,
) : JsonDriven(prototype.file.computeDirectory()) { ) : JsonDriven(prototype.file?.computeDirectory() ?: "/") {
constructor(world: World<*, *>, data: JsonObject) : this( constructor(world: World<*, *>, data: JsonObject) : this(
world, world,
Registries.worldObjects[data["name"]?.asString ?: throw IllegalArgumentException("Missing object name")] ?: throw IllegalArgumentException("No such object defined for '${data["name"]}'"), Registries.worldObjects[data["name"]?.asString ?: throw IllegalArgumentException("Missing object name")] ?: throw IllegalArgumentException("No such object defined for '${data["name"]}'"),