If you would like to add your own data types - similar to recipes, loot tables, but not those actual things - to Minecraft, you can use the ResourceReloadListener logic from Tim Core. Create an object that extends AbstractReloadListener, define how to load your data from JSON to instances of an encapsulation class.
In Confirm Release, I have the following code as an example:
class ReleaseGuard(
val id: ResourceLocation,
val matcher: PokemonMatcher,
val priority: Int,
) {
object Manager : AbstractReloadListener(Gson(), "release_guard") {
private var guards: MutableMap<Int, MutableList<ReleaseGuard>> = mutableMapOf()
override fun apply(
objectMap: MutableMap<ResourceLocation, JsonElement>,
resourceManager: ResourceManager,
profilerFiller: ProfilerFiller,
) {
guards.clear()
objectMap.entries.forEach { (id, json) ->
val guard = parseGuard(id, json as JsonObject)
if (!guards.containsKey(guard.priority)) guards[guard.priority] = mutableListOf()
val priorityList = guards[guard.priority]!!
priorityList.add(guard)
}
}
private fun parseGuard(id: ResourceLocation, json: JsonObject): ReleaseGuard {
val priority = json.getOrNull("priority")!!.asInt
return ReleaseGuard(id, PokemonMatcher(
json.getOrNull("properties")?.asString ?: "",
json.getOrNull("labels")?.asJsonArray?.map(JsonElement::getAsString) ?: emptyList(),
json.getOrNull("anyLabel")?.asBoolean ?: false,
json.getOrNull("persistentData")?.let {
it.asJsonObject.entrySet().fold(mutableMapOf()) { acc, (k, v) ->
acc[k] = v.asString
acc
}
} ?: mutableMapOf(),
json.getOrNull("anyPersistentData")?.asBoolean ?: false,
json.getOrNull("buckets")?.asJsonArray?.map(JsonElement::getAsString) ?: emptyList(),
json.getOrNull("matchOne")?.asBoolean ?: false
), priority)
}
fun findMatching(pokemon: Pokemon): ReleaseGuard? {
val priorities = guards.keys.sorted()
for (priority in priorities) {
val priorityList = guards[priority]!!
val found = priorityList.find { it.matches(pokemon) }
if (found != null) return found
}
return null
}
}
fun matches(pokemon: Pokemon) = matcher.matches(pokemon)
}
ReleaseGuard is my encapsulation class. All of its types are primitive types or container which themselves use only primitive types (check out PokemonMatcher for more info on what’s being used there); the ResourceLocation being taken is an exception as it’s already been parsed by Minecraft for us. Inside of the class, I define a Manager object, which will keep track of all of the loaded guards; I’ve sprinkled in some priority mapping, but you could just as easily use a list instead. I make a generic Gson instance as I’m not doing anything overly complicated, pass in the name I want this to use, then in the apply, I parse out an incoming JsonObject to an instance of my ReleaseGuard (note to self: need to add a Codec to manage this instead of doing it raw). When the data is reloaded, the Manager clears out what it had before, then loads the new data in, running each entry through apply, and finally adding it to the guards. Then, I can query the manager for any ReleaseGuard s that match the current Pokémon I’m checking in on.
Your usecase will probably not resemble this. The generic idea of loading in data from a JsonObject, then adding it to a list to be queried later will probably stay the same. I’ve used this for custom recipe types in the past, for example.
Then, in the init for my class which extends AbstractMod, I register the Manager using the following line:
init {
registerReloadListener(ReleaseGuard.Manager)
}
As a result, both Fabric and NeoForge will automatically register the Manager at the right time for their platform.