diff --git a/src/main/kotlin/de/snowii/extractor/extractors/Particles.kt b/src/main/kotlin/de/snowii/extractor/extractors/Particles.kt index 774f2b4..7b1527d 100644 --- a/src/main/kotlin/de/snowii/extractor/extractors/Particles.kt +++ b/src/main/kotlin/de/snowii/extractor/extractors/Particles.kt @@ -2,9 +2,13 @@ package de.snowii.extractor.extractors import com.google.gson.JsonArray import com.google.gson.JsonElement +import com.google.gson.JsonObject import de.snowii.extractor.Extractor +import net.minecraft.core.particles.ParticleTypes +import net.minecraft.core.particles.SimpleParticleType import net.minecraft.core.registries.BuiltInRegistries import net.minecraft.server.MinecraftServer +import java.lang.reflect.ParameterizedType class Particles : Extractor.Extractor { @@ -13,10 +17,42 @@ class Particles : Extractor.Extractor { } override fun extract(server: MinecraftServer): JsonElement { - val particlesJson = JsonArray() + val particlesJson = JsonObject() + for (particle in BuiltInRegistries.PARTICLE_TYPE) { + val fields = JsonArray() + + // Extract the fields of the particle type, using the class of the particle type + // This is done using the constructor of the particle type and reading the parameters + // SimpleParticleType does not have any fields, so we skip particles of this type + if (particle !is SimpleParticleType) { + val id = BuiltInRegistries.PARTICLE_TYPE.getKey(particle)!!.path + val particleType = ParticleTypes::class.java.getDeclaredField(id.uppercase()).genericType + + if (particleType is ParameterizedType) { + // The registry items are ParticleType, we need to get the type T using reflection + val parameterizedType = particleType as ParameterizedType? + val typeArguments = parameterizedType!!.actualTypeArguments + val typeArg = typeArguments[0] + + if (typeArg is Class<*>) { + // Based on the type T, we convert it back to a class and get the parameters using reflection + val typeClass = typeArg as Class<*>? + + for (field in typeClass?.constructors?.first()?.parameters!!) { + val name = field.name + + // Parameter 'type' is in every constructor, we do not need it for our purposes + if (name != "type") { + fields.add(name) + } + } + } + } + } + particlesJson.add( - BuiltInRegistries.PARTICLE_TYPE.getKey(particle)!!.path, + BuiltInRegistries.PARTICLE_TYPE.getKey(particle)!!.path, fields ) } diff --git a/src/main/resources/extractor.accesswidener b/src/main/resources/extractor.accesswidener index 27242f7..a6ed438 100644 --- a/src/main/resources/extractor.accesswidener +++ b/src/main/resources/extractor.accesswidener @@ -34,4 +34,9 @@ accessible field net/minecraft/stats/Stat value Ljava/lang/Object; accessible field net/minecraft/stats/StatType registry Lnet/minecraft/core/Registry; accessible field net/minecraft/stats/StatType displayName Lnet/minecraft/network/chat/Component; # accessible field net/minecraft/entity/data/TrackedDataHandlerRegistry DATA_HANDLERS Lnet/minecraft/util/collection/Int2ObjectBiMap; -# accessible field net/minecraft/entity/data/DataTracker CLASS_TO_LAST_ID Lnet/minecraft/util/collection/Class2IntMap; \ No newline at end of file +# accessible field net/minecraft/entity/data/DataTracker CLASS_TO_LAST_ID Lnet/minecraft/util/collection/Class2IntMap; + +# For some reason, these constructors are private. We need them to be public to read the parameters from them +accessible method net/minecraft/core/particles/PowerParticleOption (Lnet/minecraft/core/particles/ParticleType;F)V +accessible method net/minecraft/core/particles/SpellParticleOption (Lnet/minecraft/core/particles/ParticleType;IF)V +accessible method net/minecraft/core/particles/ColorParticleOption (Lnet/minecraft/core/particles/ParticleType;I)V \ No newline at end of file