package ru.dbotthepony.kstarbound.item import com.google.common.collect.ImmutableSet import com.google.gson.JsonObject import com.google.gson.JsonPrimitive import org.apache.logging.log4j.LogManager import ru.dbotthepony.kommons.gson.contains import ru.dbotthepony.kommons.gson.get import ru.dbotthepony.kommons.gson.set import ru.dbotthepony.kstarbound.Globals import ru.dbotthepony.kstarbound.IStarboundFile import ru.dbotthepony.kstarbound.Registries import ru.dbotthepony.kstarbound.Registry import ru.dbotthepony.kstarbound.Starbound import ru.dbotthepony.kstarbound.defs.AssetPath import ru.dbotthepony.kstarbound.defs.item.ItemType import ru.dbotthepony.kstarbound.defs.`object`.ObjectDefinition import ru.dbotthepony.kstarbound.fromJson import ru.dbotthepony.kstarbound.json.JsonPatch import ru.dbotthepony.kstarbound.json.builder.JsonFactory import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.Future object ItemRegistry { class Entry( val name: String, val type: ItemType, val json: JsonObject, val isEmpty: Boolean, val friendlyName: String, val itemTags: ImmutableSet, val itemAgingScripts: ImmutableSet, val file: IStarboundFile? ) { val isNotEmpty: Boolean get() = !isEmpty val directory = file?.computeDirectory() ?: "/" } private val LOGGER = LogManager.getLogger() private val entries = HashMap() private val tasks = ConcurrentLinkedQueue() val AIR = Entry("", ItemType.GENERIC, JsonObject(), true, "air", ImmutableSet.of(), ImmutableSet.of(), null) init { entries[""] = AIR } operator fun get(name: String): Entry { return entries[name] ?: AIR } @JsonFactory data class ReadData( val itemName: String, val shortdescription: String = "...", val itemTags: ImmutableSet = ImmutableSet.of(), val itemAgingScripts: ImmutableSet = ImmutableSet.of(), ) @JsonFactory data class ReadDataObj( val shortdescription: String = "...", val itemTags: ImmutableSet = ImmutableSet.of(), val itemAgingScripts: ImmutableSet = ImmutableSet.of(), ) private fun addObjectItem(obj: Registry.Entry) { if (obj.key in entries) { LOGGER.error("Refusing to overwrite item ${obj.key} with generated item for object (old def originate from ${entries[obj.key]!!.file}; new from ${obj.file})") return } val config = obj.jsonObject.deepCopy() if ("inventoryIcon" !in config) { config["inventoryIcon"] = Globals.itemParameters.missingIcon.fullPath LOGGER.warn("inventoryIcon is missing for object item ${obj.key}, using default ${Globals.itemParameters.missingIcon.fullPath}") } config["itemName"] = obj.key if ("tooltipKind" !in config) config["tooltipKind"] = "object" // this is required for Lua call to "get item config" config["printable"] = obj.value.printable // Don't inherit object scripts. this is kind of a crappy solution to prevent // ObjectItems (which are firable and therefore scripted) from trying to // execute scripts intended for objects config.remove("scripts") val readData = Starbound.gson.fromJson(config, ReadDataObj::class.java) entries[obj.key] = Entry( name = obj.key, type = ItemType.OBJECT, json = config, isEmpty = false, friendlyName = readData.shortdescription, itemTags = readData.itemTags, itemAgingScripts = readData.itemAgingScripts, file = obj.file, ) } fun finishLoad() { tasks.forEach { it.run() } tasks.clear() for (obj in Registries.worldObjects.keys.values) { if (obj.value.hasObjectItem) { addObjectItem(obj) } } } fun load(fileTree: Map>, patches: Map>): Collection> { val futures = ArrayList>() val data by lazy { Starbound.gson.getAdapter(ReadData::class.java) } for (type in ItemType.entries) { val files = fileTree[type.extension ?: continue] ?: continue for (file in files) { futures.add(Starbound.EXECUTOR.submit { try { val read = JsonPatch.apply(Starbound.ELEMENTS_ADAPTER.read(file.jsonReader()), patches[file.computeFullPath()]) as JsonObject val readData = data.fromJsonTree(read) tasks.add { if (readData.itemName in entries) { LOGGER.warn("Overwriting item definition at ${readData.itemName} (old def originate from ${entries[readData.itemName]!!.file}; new from $file)") } entries[readData.itemName] = Entry( name = readData.itemName, type = type, json = read, isEmpty = false, friendlyName = readData.shortdescription, itemTags = readData.itemTags, itemAgingScripts = readData.itemAgingScripts, file = file, ) } } catch (err: Throwable) { LOGGER.error("Reading item definition $file", err) } }) } } return futures } }