root.recipesForItem и подгрузка описания рецептов

This commit is contained in:
DBotThePony 2023-03-28 20:52:28 +07:00
parent 046698ddc5
commit 89180a6664
Signed by: DBot
GPG Key ID: DCC23B5715498507
7 changed files with 240 additions and 41 deletions

View File

@ -0,0 +1,47 @@
package ru.dbotthepony.kstarbound
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
import ru.dbotthepony.kstarbound.defs.player.RecipeDefinition
import java.util.Collections
class RecipeRegistry {
private val recipesInternal = ArrayList<RecipeDefinition>()
private val group2recipesInternal = Object2ObjectOpenHashMap<String, ArrayList<RecipeDefinition>>()
private val group2recipesBacking = Object2ObjectOpenHashMap<String, List<RecipeDefinition>>()
private val output2recipesInternal = Object2ObjectOpenHashMap<String, ArrayList<RecipeDefinition>>()
private val output2recipesBacking = Object2ObjectOpenHashMap<String, List<RecipeDefinition>>()
private val input2recipesInternal = Object2ObjectOpenHashMap<String, ArrayList<RecipeDefinition>>()
private val input2recipesBacking = Object2ObjectOpenHashMap<String, List<RecipeDefinition>>()
val recipes: List<RecipeDefinition> = Collections.unmodifiableList(recipesInternal)
val group2recipes: Map<String, List<RecipeDefinition>> = Collections.unmodifiableMap(group2recipesBacking)
val output2recipes: Map<String, List<RecipeDefinition>> = Collections.unmodifiableMap(output2recipesBacking)
val input2recipes: Map<String, List<RecipeDefinition>> = Collections.unmodifiableMap(input2recipesBacking)
fun add(recipe: RecipeDefinition) {
recipesInternal.add(recipe)
for (group in recipe.groups) {
group2recipesInternal.computeIfAbsent(group, Object2ObjectFunction { p ->
ArrayList<RecipeDefinition>().also {
group2recipesBacking[p as String] = Collections.unmodifiableList(it)
}
}).add(recipe)
}
output2recipesInternal.computeIfAbsent(recipe.output.item.name, Object2ObjectFunction { p ->
ArrayList<RecipeDefinition>().also {
output2recipesBacking[p as String] = Collections.unmodifiableList(it)
}
}).add(recipe)
for (input in recipe.input) {
input2recipesInternal.computeIfAbsent(input.item.name, Object2ObjectFunction { p ->
ArrayList<RecipeDefinition>().also {
input2recipesBacking[p as String] = Collections.unmodifiableList(it)
}
}).add(recipe)
}
}
}

View File

@ -35,7 +35,9 @@ import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
import ru.dbotthepony.kstarbound.defs.particle.ParticleDefinition
import ru.dbotthepony.kstarbound.defs.player.BlueprintLearnList
import ru.dbotthepony.kstarbound.defs.player.PlayerDefinition
import ru.dbotthepony.kstarbound.defs.player.RecipeDefinition
import ru.dbotthepony.kstarbound.defs.player.TechDefinition
import ru.dbotthepony.kstarbound.defs.projectile.ProjectileDefinition
import ru.dbotthepony.kstarbound.defs.quest.QuestTemplate
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
import ru.dbotthepony.kstarbound.defs.tile.TileDefinition
@ -120,9 +122,14 @@ class Starbound : ISBFileLocator {
private val _json2Functions = ObjectRegistry<Json2Function>("json 2functions")
val json2Functions = _json2Functions.view
private val _npcTypes = ObjectRegistry<NpcTypeDefinition>("npc types")
private val _npcTypes = ObjectRegistry("npc types", NpcTypeDefinition::type)
val npcTypes = _npcTypes.view
private val _projectiles = ObjectRegistry("projectiles", ProjectileDefinition::projectileName)
val projectiles = _projectiles.view
val recipeRegistry = RecipeRegistry()
val gson: Gson = with(GsonBuilder()) {
serializeNulls()
setDateFormat(DateFormat.LONG)
@ -197,6 +204,8 @@ class Starbound : ISBFileLocator {
registerTypeAdapter(ItemDescriptor.Adapter(this@Starbound))
registerTypeAdapterFactory(ItemReference.Factory(STRINGS))
registerTypeAdapterFactory(with(RegistryReferenceFactory()) {
add(tiles::get)
add(tileModifiers::get)
@ -410,6 +419,28 @@ class Starbound : ISBFileLocator {
TODO()
}
state.setTableFunction("projectileGravityMultiplier", this) { args ->
// float root.projectileGravityMultiplier(String projectileName)
TODO()
}
state.setTableFunction("projectileConfig", this) { args ->
// Json root.projectileConfig(String projectileName)
val name = args.getString()
args.lua.push(projectiles[name]?.copy() ?: throw kotlin.NoSuchElementException("No such Projectile type $name"))
1
}
state.setTableFunction("recipesForItem", this) { args ->
args.lua.push(JsonArray().also { a ->
recipeRegistry.output2recipes[args.getString()]?.stream()?.map { gson.toJsonTree(it) }?.forEach {
a.add(it)
}
})
1
}
state.pop()
state.load(polyfill, "@starbound.jar!/scripts/polyfill.lua")
@ -606,6 +637,7 @@ class Starbound : ISBFileLocator {
loadStage(callback, { loadItemDefinitions(it, ext2files) }, "item definitions")
loadStage(callback, { loadJsonFunctions(it, ext2files["functions"] ?: listOf()) }, "json functions")
loadStage(callback, { loadJson2Functions(it, ext2files["2functions"] ?: listOf()) }, "json 2functions")
loadStage(callback, { loadRecipes(it, ext2files["recipe"] ?: listOf()) }, "recipes")
loadStage(callback, _tiles, ext2files["material"] ?: listOf())
loadStage(callback, _tileModifiers, ext2files["matmod"] ?: listOf())
@ -616,6 +648,7 @@ class Starbound : ISBFileLocator {
loadStage(callback, _questTemplates, ext2files["questtemplate"] ?: listOf())
loadStage(callback, _techs, ext2files["tech"] ?: listOf())
loadStage(callback, _npcTypes, ext2files["npctype"] ?: listOf())
loadStage(callback, _projectiles, ext2files["projectile"] ?: listOf())
pathStack.block("/") {
//playerDefinition = gson.fromJson(locate("/player.config").reader(), PlayerDefinition::class.java)
@ -731,6 +764,21 @@ class Starbound : ISBFileLocator {
}
}
private fun loadRecipes(callback: (String) -> Unit, files: Collection<IStarboundFile>) {
for (listedFile in files) {
try {
callback("Loading $listedFile")
recipeRegistry.add(gson.fromJson(listedFile.reader(), RecipeDefinition::class.java))
} catch (err: Throwable) {
logger.error("Loading recipe definition file $listedFile", err)
}
if (terminateLoading) {
return
}
}
}
companion object {
/**
* Глобальный [Interner] для [String]

View File

@ -0,0 +1,60 @@
package ru.dbotthepony.kstarbound.defs
import com.google.common.collect.Interner
import com.google.gson.Gson
import com.google.gson.JsonObject
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 ru.dbotthepony.kstarbound.defs.item.IItemDefinition
import ru.dbotthepony.kstarbound.io.json.builder.FactoryAdapter
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
import ru.dbotthepony.kstarbound.io.json.consumeNull
import ru.dbotthepony.kstarbound.util.ItemDescriptor
/**
* Прототип [ItemDescriptor] в JSON файлах
*/
data class ItemReference(
val item: RegistryReference<IItemDefinition>,
val count: Long = 1,
val parameters: JsonObject = JsonObject()
) {
fun makeStack(): ItemDescriptor {
return ItemDescriptor(item.value ?: return ItemDescriptor.EMPTY, count, parameters)
}
class Factory(val stringInterner: Interner<String> = Interner { it }) : TypeAdapterFactory {
override fun <T : Any> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
if (type.rawType == ItemReference::class.java) {
return object : TypeAdapter<ItemReference>() {
private val regular = FactoryAdapter.createFor(ItemReference::class, JsonFactory(storesJson = false, logMisses = true, asList = false), gson, stringInterner)
private val references = gson.getAdapter(TypeToken.getParameterized(RegistryReference::class.java, IItemDefinition::class.java)) as TypeAdapter<RegistryReference<IItemDefinition>>
override fun write(out: JsonWriter, value: ItemReference?) {
if (value == null)
out.nullValue()
else
regular.write(out, value)
}
override fun read(`in`: JsonReader): ItemReference? {
if (`in`.consumeNull())
return null
if (`in`.peek() == JsonToken.STRING) {
return ItemReference(references.read(`in`))
} else {
return regular.read(`in`)
}
}
} as TypeAdapter<T>
}
return null
}
}
}

View File

@ -0,0 +1,17 @@
package ru.dbotthepony.kstarbound.defs.player
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
import com.google.common.collect.ImmutableSet
import ru.dbotthepony.kstarbound.defs.ItemReference
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
@JsonFactory
data class RecipeDefinition(
val input: ImmutableList<ItemReference>,
val output: ItemReference,
val groups: ImmutableSet<String> = ImmutableSet.of(),
val matchInputParameters: Boolean = false,
val duration: Double = 0.5,
val currencyInputs: ImmutableMap<String, Long> = ImmutableMap.of()
)

View File

@ -0,0 +1,8 @@
package ru.dbotthepony.kstarbound.defs.projectile
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
@JsonFactory
data class ProjectileDefinition(
val projectileName: String
)

View File

@ -84,3 +84,12 @@ fun TypeAdapterFactory.ifString(reader: (String) -> Any): TypeAdapterFactory {
}
}
}
fun JsonReader.consumeNull(): Boolean {
if (peek() == JsonToken.NULL) {
nextNull()
return true
}
return false
}

View File

@ -19,13 +19,11 @@ import com.google.gson.stream.JsonWriter
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap
import it.unimi.dsi.fastutil.objects.ObjectArraySet
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.defs.util.enrollList
import ru.dbotthepony.kstarbound.defs.util.enrollMap
import ru.dbotthepony.kstarbound.defs.util.flattenJsonElement
import ru.dbotthepony.kstarbound.getValue
import ru.dbotthepony.kstarbound.io.json.consumeNull
import ru.dbotthepony.kstarbound.io.json.ifString
import ru.dbotthepony.kstarbound.setValue
import java.lang.reflect.Constructor
import kotlin.jvm.internal.DefaultConstructorMarker
import kotlin.properties.Delegates
@ -152,7 +150,12 @@ class FactoryAdapter<T : Any> private constructor(
}
}
override fun write(out: JsonWriter, value: T) {
override fun write(out: JsonWriter, value: T?) {
if (value == null) {
out.nullValue()
return
}
out.beginObject()
for ((field, adapter) in types) {
@ -164,7 +167,10 @@ class FactoryAdapter<T : Any> private constructor(
out.endObject()
}
override fun read(reader: JsonReader): T {
override fun read(reader: JsonReader): T? {
if (reader.consumeNull())
return null
// таблица присутствия значений (если значение true то на i было значение внутри json)
val presentValues = BooleanArray(types.size + (if (storesJson) 1 else 0))
val readValues = arrayOfNulls<Any>(types.size + (if (storesJson) 1 else 0))
@ -526,6 +532,44 @@ class FactoryAdapter<T : Any> private constructor(
companion object {
private val LOGGER = LogManager.getLogger()
fun <T : Any> createFor(kclass: KClass<T>, config: JsonFactory, gson: Gson, stringInterner: Interner<String> = Interner { it }): TypeAdapter<T> {
val builder = Builder(kclass)
val properties = kclass.declaredMembers.filterIsInstance<KProperty1<T, *>>()
if (config.asList) {
builder.inputAsList()
}
builder.storesJson(config.storesJson)
builder.logMisses(config.logMisses)
builder.stringInterner = stringInterner
if (properties.isEmpty()) {
throw IllegalArgumentException("${kclass.qualifiedName} has no valid members")
}
val foundConstructor = kclass.primaryConstructor ?: throw NoSuchElementException("Can't determine primary constructor for ${kclass.qualifiedName}")
if (!config.storesJson) {
for (argument in foundConstructor.parameters) {
val property = properties.first { it.name == argument.name && it.returnType.isSupertypeOf(argument.type) }
val config = property.annotations.firstOrNull { it.annotationClass == JsonPropertyConfig::class } as JsonPropertyConfig?
builder.auto(property, isFlat = config?.isFlat ?: false)
}
} else {
val params = foundConstructor.parameters
for (i in 0 until params.size - 1) {
val argument = params[i]
val property = properties.first { it.name == argument.name && it.returnType.isSupertypeOf(argument.type) }
val config = property.annotations.firstOrNull { it.annotationClass == JsonPropertyConfig::class } as JsonPropertyConfig?
builder.auto(property, isFlat = config?.isFlat ?: false)
}
}
return builder.build(gson)
}
}
class Factory(val stringInterner: Interner<String> = Interner { it }) : TypeAdapterFactory {
@ -538,41 +582,7 @@ class FactoryAdapter<T : Any> private constructor(
val bconfig = first[0] as JsonFactory
val kclass = raw.kotlin as KClass<T>
val builder = Builder(kclass)
val properties = kclass.declaredMembers.filterIsInstance<KProperty1<T, *>>()
if (bconfig.asList) {
builder.inputAsList()
}
builder.storesJson(bconfig.storesJson)
builder.logMisses(bconfig.logMisses)
builder.stringInterner = stringInterner
if (properties.isEmpty()) {
throw IllegalArgumentException("${kclass.qualifiedName} has no valid members")
}
val foundConstructor = kclass.primaryConstructor ?: throw NoSuchElementException("Can't determine primary constructor for ${kclass.qualifiedName}")
if (!bconfig.storesJson) {
for (argument in foundConstructor.parameters) {
val property = properties.first { it.name == argument.name && it.returnType.isSupertypeOf(argument.type) }
val config = property.annotations.firstOrNull { it.annotationClass == JsonPropertyConfig::class } as JsonPropertyConfig?
builder.auto(property, isFlat = config?.isFlat ?: false)
}
} else {
val params = foundConstructor.parameters
for (i in 0 until params.size - 1) {
val argument = params[i]
val property = properties.first { it.name == argument.name && it.returnType.isSupertypeOf(argument.type) }
val config = property.annotations.firstOrNull { it.annotationClass == JsonPropertyConfig::class } as JsonPropertyConfig?
builder.auto(property, isFlat = config?.isFlat ?: false)
}
}
return builder.build(gson)
return createFor(kclass, bconfig, gson, stringInterner)
}
return null