├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── settings.gradle.kts ├── src └── main │ ├── resources │ ├── extractor.accesswidener │ └── fabric.mod.json │ └── kotlin │ └── de │ └── snowii │ └── extractor │ ├── extractors │ ├── non_registry │ │ ├── EntityPose.kt │ │ ├── SoundCategory.kt │ │ ├── ScoreboardDisplaySlot.kt │ │ ├── ComposterIncreaseChance.kt │ │ ├── SpawnEgg.kt │ │ ├── WorldEvent.kt │ │ ├── FlowerPotTransformation.kt │ │ ├── EntityStatuses.kt │ │ ├── Translations.kt │ │ ├── RecipeRemainder.kt │ │ └── Properties.kt │ ├── Sounds.kt │ ├── ChunkStatus.kt │ ├── Screens.kt │ ├── Particles.kt │ ├── Fuels.kt │ ├── GameEvent.kt │ ├── DataComponent.kt │ ├── EntityAttributes.kt │ ├── Recipes.kt │ ├── Carver.kt │ ├── PlacedFeatures.kt │ ├── structures │ │ ├── StructureSet.kt │ │ └── Structures.kt │ ├── ConfiguredFeatures.kt │ ├── GameRules.kt │ ├── ChunkGenSetting.kt │ ├── Biome.kt │ ├── Enchantments.kt │ ├── NoiseParameters.kt │ ├── MessageType.kt │ ├── DamageTypes.kt │ ├── Tags.kt │ ├── Items.kt │ ├── SyncedRegistries.kt │ ├── Potion.kt │ ├── Fluids.kt │ ├── Effect.kt │ ├── PotionBrewing.kt │ ├── BiomeMixerTest.kt │ ├── Entities.kt │ ├── Packets.kt │ ├── DensityFunctions.kt │ ├── MultiNoise.kt │ ├── BiomeDumpTests.kt │ ├── Blocks.kt │ └── ChunkDumpTests.kt │ └── Extractor.kt ├── gradle.properties ├── .gitignore ├── LICENSE ├── README.md ├── move_files.sh ├── gradlew.bat └── gradlew /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pumpkin-MC/Extractor/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven("https://maven.fabricmc.net/") { 4 | name = "Fabric" 5 | } 6 | gradlePluginPortal() 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /src/main/resources/extractor.accesswidener: -------------------------------------------------------------------------------- 1 | accessWidener v2 named 2 | 3 | accessible method net/minecraft/block/FireBlock getSpreadChance (Lnet/minecraft/block/BlockState;)I 4 | accessible method net/minecraft/block/FireBlock getBurnChance (Lnet/minecraft/block/BlockState;)I 5 | accessible field net/minecraft/block/FlowerPotBlock CONTENT_TO_POTTED Ljava/util/Map; 6 | 7 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx2G 3 | org.gradle.parallel=true 4 | # Fabric Properties 5 | # check these on https://fabricmc.net/develop/ 6 | minecraft_version=1.21.10 7 | yarn_mappings=1.21.10+build.3 8 | loader_version=0.18.1 9 | kotlin_loader_version=1.13.7+kotlin.2.2.21 10 | # Mod Properties 11 | mod_version=1.0-SNAPSHOT 12 | maven_group=de.snowii 13 | archives_base_name=extractor 14 | fabric_version=0.138.3+1.21.10 15 | -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "extractor", 4 | "version": "${version}", 5 | "name": "pumpkin-extractor", 6 | "description": "", 7 | "authors": [], 8 | "contact": {}, 9 | "license": "MIT", 10 | "environment": "*", 11 | "entrypoints": { 12 | "main": ["de.snowii.extractor.Extractor"] 13 | }, 14 | "depends": { 15 | "fabricloader": ">=${loader_version}", 16 | "fabric-language-kotlin": ">=${kotlin_loader_version}", 17 | "fabric": "*", 18 | "minecraft": "${minecraft_version}" 19 | }, 20 | "accessWidener": "extractor.accesswidener" 21 | } 22 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/non_registry/EntityPose.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors.non_registry 2 | 3 | import com.google.gson.JsonArray 4 | import com.google.gson.JsonElement 5 | import de.snowii.extractor.Extractor 6 | import net.minecraft.entity.EntityPose 7 | import net.minecraft.server.MinecraftServer 8 | 9 | class EntityPose : Extractor.Extractor { 10 | override fun fileName(): String { 11 | return "entity_pose.json" 12 | } 13 | 14 | override fun extract(server: MinecraftServer): JsonElement { 15 | val poseesJson = JsonArray() 16 | for (pose in EntityPose.entries) { 17 | poseesJson.add( 18 | pose.name, 19 | ) 20 | } 21 | 22 | return poseesJson 23 | } 24 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/Sounds.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonArray 4 | import com.google.gson.JsonElement 5 | import de.snowii.extractor.Extractor 6 | import net.minecraft.registry.Registries 7 | import net.minecraft.server.MinecraftServer 8 | 9 | 10 | class Sounds : Extractor.Extractor { 11 | override fun fileName(): String { 12 | return "sounds.json" 13 | } 14 | 15 | override fun extract(server: MinecraftServer): JsonElement { 16 | val soundJson = JsonArray() 17 | for (sound in Registries.SOUND_EVENT) { 18 | soundJson.add( 19 | Registries.SOUND_EVENT.getId(sound)!!.path, 20 | ) 21 | } 22 | 23 | return soundJson 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/ChunkStatus.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonArray 4 | import com.google.gson.JsonElement 5 | import de.snowii.extractor.Extractor 6 | import net.minecraft.registry.Registries 7 | import net.minecraft.server.MinecraftServer 8 | 9 | class ChunkStatus : Extractor.Extractor { 10 | override fun fileName(): String { 11 | return "chunk_status.json" 12 | } 13 | 14 | override fun extract(server: MinecraftServer): JsonElement { 15 | val statusJson = JsonArray() 16 | for (status in Registries.CHUNK_STATUS) { 17 | statusJson.add( 18 | Registries.CHUNK_STATUS.getId(status).path, 19 | ) 20 | } 21 | 22 | return statusJson 23 | } 24 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/Screens.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonArray 4 | import com.google.gson.JsonElement 5 | import de.snowii.extractor.Extractor 6 | import net.minecraft.registry.Registries 7 | import net.minecraft.server.MinecraftServer 8 | 9 | class Screens : Extractor.Extractor { 10 | override fun fileName(): String { 11 | return "screens.json" 12 | } 13 | 14 | override fun extract(server: MinecraftServer): JsonElement { 15 | val screensJson = JsonArray() 16 | for (screen in Registries.SCREEN_HANDLER) { 17 | screensJson.add( 18 | Registries.SCREEN_HANDLER.getId(screen)!!.path, 19 | ) 20 | } 21 | 22 | return screensJson 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/non_registry/SoundCategory.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors.non_registry 2 | 3 | import com.google.gson.JsonArray 4 | import com.google.gson.JsonElement 5 | import de.snowii.extractor.Extractor 6 | import net.minecraft.server.MinecraftServer 7 | import net.minecraft.sound.SoundCategory 8 | 9 | class SoundCategory : Extractor.Extractor { 10 | override fun fileName(): String { 11 | return "sound_category.json" 12 | } 13 | 14 | override fun extract(server: MinecraftServer): JsonElement { 15 | val categoriesJson = JsonArray() 16 | for (category in SoundCategory.entries) { 17 | categoriesJson.add( 18 | category.name, 19 | ) 20 | } 21 | 22 | return categoriesJson 23 | } 24 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/Particles.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonArray 4 | import com.google.gson.JsonElement 5 | import de.snowii.extractor.Extractor 6 | import net.minecraft.registry.Registries 7 | import net.minecraft.server.MinecraftServer 8 | 9 | 10 | class Particles : Extractor.Extractor { 11 | override fun fileName(): String { 12 | return "particles.json" 13 | } 14 | 15 | override fun extract(server: MinecraftServer): JsonElement { 16 | val particlesJson = JsonArray() 17 | for (particle in Registries.PARTICLE_TYPE) { 18 | particlesJson.add( 19 | Registries.PARTICLE_TYPE.getId(particle)!!.path, 20 | ) 21 | } 22 | 23 | return particlesJson 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/non_registry/ScoreboardDisplaySlot.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors.non_registry 2 | 3 | import com.google.gson.JsonArray 4 | import com.google.gson.JsonElement 5 | import de.snowii.extractor.Extractor 6 | import net.minecraft.scoreboard.ScoreboardDisplaySlot 7 | import net.minecraft.server.MinecraftServer 8 | 9 | class ScoreboardDisplaySlot : Extractor.Extractor { 10 | override fun fileName(): String { 11 | return "scoreboard_display_slot.json" 12 | } 13 | 14 | override fun extract(server: MinecraftServer): JsonElement { 15 | val finalJson = JsonArray() 16 | for (slot in ScoreboardDisplaySlot.entries) { 17 | finalJson.add( 18 | slot.name, 19 | ) 20 | } 21 | 22 | return finalJson 23 | } 24 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/Fuels.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import com.google.gson.JsonPrimitive 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.item.Item 8 | import net.minecraft.item.ItemStack 9 | import net.minecraft.server.MinecraftServer 10 | 11 | 12 | 13 | class Fuels : Extractor.Extractor { 14 | override fun fileName(): String { 15 | return "fuels.json" 16 | } 17 | 18 | override fun extract(server: MinecraftServer): JsonElement { 19 | val fuelsJson = JsonObject() 20 | server.fuelRegistry.fuelItems.forEach { fuel -> 21 | fuelsJson.add(Item.getRawId(fuel).toString(), JsonPrimitive(server.fuelRegistry.getFuelTicks(ItemStack(fuel)))) 22 | } 23 | return fuelsJson 24 | } 25 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/GameEvent.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonArray 4 | import com.google.gson.JsonElement 5 | import de.snowii.extractor.Extractor 6 | import net.minecraft.registry.RegistryKeys 7 | import net.minecraft.server.MinecraftServer 8 | 9 | class GameEvent : Extractor.Extractor { 10 | override fun fileName(): String { 11 | return "game_event.json" 12 | } 13 | 14 | override fun extract(server: MinecraftServer): JsonElement { 15 | val gameEventJson = JsonArray() 16 | val gameEventTypeRegistry = 17 | server.registryManager.getOrThrow(RegistryKeys.GAME_EVENT) 18 | for (event in gameEventTypeRegistry) { 19 | gameEventJson.add( 20 | gameEventTypeRegistry.getId(event)!!.path, 21 | ) 22 | } 23 | 24 | return gameEventJson 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/DataComponent.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import de.snowii.extractor.Extractor 6 | import net.minecraft.registry.Registries 7 | import net.minecraft.registry.RegistryKeys 8 | import net.minecraft.server.MinecraftServer 9 | 10 | 11 | class DataComponent : Extractor.Extractor { 12 | override fun fileName(): String { 13 | return "data_component.json" 14 | } 15 | 16 | override fun extract(server: MinecraftServer): JsonElement { 17 | val dataComponentJson = JsonObject() 18 | val list = server.registryManager.getOrThrow(RegistryKeys.DATA_COMPONENT_TYPE).streamEntries().toList(); 19 | for (item in list) { 20 | dataComponentJson.addProperty(item.value().toString(), Registries.DATA_COMPONENT_TYPE.getRawId(item.value())); 21 | } 22 | return dataComponentJson 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/non_registry/ComposterIncreaseChance.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import com.google.gson.JsonPrimitive 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.block.ComposterBlock 8 | import net.minecraft.registry.Registries 9 | import net.minecraft.server.MinecraftServer 10 | 11 | class ComposterIncreaseChance : Extractor.Extractor { 12 | override fun fileName(): String { 13 | return "composter_increase_chance.json" 14 | } 15 | 16 | override fun extract(server: MinecraftServer): JsonElement { 17 | val composterChancesJson = JsonObject() 18 | for ((item, chance) in ComposterBlock.ITEM_TO_LEVEL_INCREASE_CHANCE) { 19 | composterChancesJson.add(Registries.ITEM.getRawId(item.asItem()!!).toString(), JsonPrimitive(chance)) 20 | } 21 | return composterChancesJson 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/non_registry/SpawnEgg.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors.non_registry 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import de.snowii.extractor.Extractor 6 | import net.minecraft.item.SpawnEggItem 7 | import net.minecraft.registry.Registries 8 | import net.minecraft.server.MinecraftServer 9 | 10 | class SpawnEgg : Extractor.Extractor { 11 | override fun fileName(): String { 12 | return "spawn_egg.json" 13 | } 14 | 15 | override fun extract(server: MinecraftServer): JsonElement { 16 | val eggJson = JsonObject() 17 | 18 | for (spawnEggItem in SpawnEggItem.getAll()) { 19 | val type = spawnEggItem.getEntityType(spawnEggItem.defaultStack) 20 | eggJson.addProperty( 21 | Registries.ITEM.getRawId(spawnEggItem).toString(), 22 | Registries.ENTITY_TYPE.getId(type).path 23 | ) 24 | } 25 | return eggJson 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/EntityAttributes.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import de.snowii.extractor.Extractor 6 | import net.minecraft.registry.Registries 7 | import net.minecraft.server.MinecraftServer 8 | 9 | class EntityAttributes : Extractor.Extractor { 10 | override fun fileName(): String { 11 | return "attributes.json" 12 | } 13 | 14 | override fun extract(server: MinecraftServer): JsonElement { 15 | val finalJson = JsonObject() 16 | for (attribute in Registries.ATTRIBUTE) { 17 | var subObject = JsonObject() 18 | subObject.addProperty("id", Registries.ATTRIBUTE.getRawId(attribute)) 19 | subObject.addProperty("default_value", attribute.defaultValue) 20 | 21 | finalJson.add( 22 | Registries.ATTRIBUTE.getId(attribute)!!.path, 23 | subObject 24 | ) 25 | } 26 | 27 | return finalJson 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/Recipes.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonArray 4 | import com.google.gson.JsonElement 5 | import com.mojang.serialization.JsonOps 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.recipe.Recipe 8 | import net.minecraft.registry.RegistryOps 9 | import net.minecraft.server.MinecraftServer 10 | 11 | class Recipes : Extractor.Extractor { 12 | override fun fileName(): String { 13 | return "recipes.json" 14 | } 15 | 16 | override fun extract(server: MinecraftServer): JsonElement { 17 | val recipesJson = JsonArray() 18 | 19 | for (recipeRaw in server.recipeManager.values()) { 20 | val recipe = recipeRaw.value 21 | recipesJson.add( 22 | Recipe.CODEC.encodeStart( 23 | RegistryOps.of(JsonOps.INSTANCE, server.registryManager), 24 | recipe 25 | ).getOrThrow() 26 | ) 27 | } 28 | return recipesJson 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | **/build/ 3 | !src/**/build/ 4 | 5 | # Package Files # 6 | *.jar 7 | *.war 8 | *.nar 9 | *.ear 10 | *.zip 11 | *.tar.gz 12 | *.rar 13 | 14 | # Ignore Gradle GUI config 15 | gradle-app.setting 16 | 17 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 18 | !gradle-wrapper.jar 19 | 20 | # Avoid ignore Gradle wrappper properties 21 | !gradle-wrapper.properties 22 | 23 | # Cache of project 24 | .gradletasknamecache 25 | 26 | # Eclipse Gradle plugin generated files 27 | # Eclipse Core 28 | .project 29 | # JDT-specific (Eclipse Java Development Tools) 30 | .classpath 31 | 32 | # Compiled class file 33 | *.class 34 | 35 | # Log file 36 | *.log 37 | 38 | # BlueJ files 39 | *.ctxt 40 | 41 | # Mobile Tools for Java (J2ME) 42 | .mtj.tmp/ 43 | 44 | .idea 45 | 46 | run/ 47 | 48 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 49 | hs_err_pid* 50 | replay_pid* 51 | 52 | # Kotlin Gradle plugin data, see https://kotlinlang.org/docs/whatsnew20.html#new-directory-for-kotlin-data-in-gradle-projects 53 | .kotlin/ 54 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/non_registry/WorldEvent.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors.non_registry 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import de.snowii.extractor.Extractor 6 | import net.minecraft.server.MinecraftServer 7 | import net.minecraft.world.WorldEvents 8 | 9 | class WorldEvent : Extractor.Extractor { 10 | override fun fileName(): String { 11 | return "world_event.json" 12 | } 13 | 14 | override fun extract(server: MinecraftServer): JsonElement { 15 | val jsonObject = JsonObject() 16 | val fields = WorldEvents::class.java.declaredFields 17 | 18 | for (field in fields) { 19 | if (field.type == Int::class.javaPrimitiveType || field.type == Int::class.java) { 20 | if (field.name.startsWith("field")) continue 21 | field.isAccessible = true 22 | val intValue = field.get(null) as Int 23 | jsonObject.addProperty(field.name, intValue) 24 | } 25 | } 26 | 27 | return jsonObject 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/non_registry/FlowerPotTransformation.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors.non_registry 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import com.google.gson.JsonPrimitive 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.block.FlowerPotBlock 8 | import net.minecraft.registry.Registries 9 | import net.minecraft.server.MinecraftServer 10 | 11 | class FlowerPotTransformation : Extractor.Extractor { 12 | override fun fileName(): String { 13 | return "flower_pot_transformations.json" 14 | } 15 | 16 | override fun extract(server: MinecraftServer): JsonElement { 17 | val flowerPotsJson = JsonObject() 18 | for ((block, pottedBlock) in FlowerPotBlock.CONTENT_TO_POTTED){ 19 | if (Registries.BLOCK.getRawId(block) == 0) continue 20 | flowerPotsJson.add( 21 | Registries.ITEM.getRawId(block.asItem()!!).toString(), 22 | JsonPrimitive(Registries.BLOCK.getRawId(pottedBlock))) 23 | } 24 | 25 | return flowerPotsJson 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/non_registry/EntityStatuses.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors.non_registry 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import de.snowii.extractor.Extractor 6 | import net.minecraft.entity.EntityStatuses 7 | import net.minecraft.server.MinecraftServer 8 | 9 | class EntityStatuses : Extractor.Extractor { 10 | override fun fileName(): String { 11 | return "entity_statuses.json" 12 | } 13 | 14 | override fun extract(server: MinecraftServer): JsonElement { 15 | val jsonObject = JsonObject() 16 | val fields = EntityStatuses::class.java.declaredFields 17 | 18 | for (field in fields) { 19 | if (field.type == Byte::class.javaPrimitiveType || field.type == Byte::class.java) { 20 | if (field.name.startsWith("field")) continue 21 | field.isAccessible = true 22 | val byteValue = field.get(null) as Byte 23 | jsonObject.addProperty(field.name, byteValue) 24 | } 25 | } 26 | 27 | return jsonObject 28 | } 29 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Pumpkin MC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/non_registry/Translations.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors.non_registry 2 | 3 | import com.google.gson.Gson 4 | import com.google.gson.GsonBuilder 5 | import com.google.gson.JsonElement 6 | import com.google.gson.JsonObject 7 | import de.snowii.extractor.Extractor 8 | import net.minecraft.server.MinecraftServer 9 | import java.io.InputStreamReader 10 | import java.nio.charset.StandardCharsets 11 | 12 | class Translations : Extractor.Extractor { 13 | private val gson: Gson = GsonBuilder().disableHtmlEscaping().create() 14 | 15 | override fun fileName(): String { 16 | return "en_us.json" 17 | } 18 | 19 | override fun extract(server: MinecraftServer): JsonElement { 20 | val inputStream = this.javaClass.getResourceAsStream("/assets/minecraft/lang/en_us.json") 21 | ?: throw IllegalArgumentException("Could not find lang en_us.json") 22 | 23 | return inputStream.use { stream -> 24 | gson.fromJson( 25 | InputStreamReader(stream, StandardCharsets.UTF_8), 26 | JsonObject::class.java 27 | ) as JsonObject 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/Carver.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import com.mojang.serialization.JsonOps 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.registry.RegistryKeys 8 | import net.minecraft.registry.RegistryOps 9 | import net.minecraft.server.MinecraftServer 10 | import net.minecraft.world.gen.carver.ConfiguredCarver 11 | 12 | class Carver : Extractor.Extractor { 13 | override fun fileName(): String { 14 | return "carver.json" 15 | } 16 | 17 | override fun extract(server: MinecraftServer): JsonElement { 18 | val finalJson = JsonObject() 19 | val registry = 20 | server.registryManager.getOrThrow(RegistryKeys.CONFIGURED_CARVER) 21 | for (setting in registry) { 22 | finalJson.add( 23 | registry.getId(setting)!!.path, 24 | ConfiguredCarver.CODEC.encodeStart( 25 | RegistryOps.of(JsonOps.INSTANCE, server.registryManager), 26 | setting 27 | ).getOrThrow() 28 | ) 29 | } 30 | 31 | return finalJson 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/PlacedFeatures.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import com.mojang.serialization.JsonOps 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.registry.RegistryKeys 8 | import net.minecraft.registry.RegistryOps 9 | import net.minecraft.server.MinecraftServer 10 | import net.minecraft.world.gen.feature.PlacedFeature 11 | 12 | class PlacedFeatures : Extractor.Extractor { 13 | override fun fileName(): String { 14 | return "placed_feature.json" 15 | } 16 | 17 | override fun extract(server: MinecraftServer): JsonElement { 18 | val finalJson = JsonObject() 19 | val registry = 20 | server.registryManager.getOrThrow(RegistryKeys.PLACED_FEATURE) 21 | for (setting in registry) { 22 | finalJson.add( 23 | registry.getId(setting)!!.path, 24 | PlacedFeature.CODEC.encodeStart( 25 | RegistryOps.of(JsonOps.INSTANCE, server.registryManager), 26 | setting 27 | ).getOrThrow() 28 | ) 29 | } 30 | 31 | return finalJson 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/structures/StructureSet.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors.structures 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import com.mojang.serialization.JsonOps 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.registry.RegistryKeys 8 | import net.minecraft.registry.RegistryOps 9 | import net.minecraft.server.MinecraftServer 10 | import net.minecraft.structure.StructureSet 11 | 12 | class StructureSet : Extractor.Extractor { 13 | override fun fileName(): String { 14 | return "structure_set.json" 15 | } 16 | 17 | override fun extract(server: MinecraftServer): JsonElement { 18 | val finalJson = JsonObject() 19 | val registry = 20 | server.registryManager.getOrThrow(RegistryKeys.STRUCTURE_SET) 21 | for (setting in registry) { 22 | finalJson.add( 23 | registry.getId(setting)!!.path, 24 | StructureSet.CODEC.encodeStart( 25 | RegistryOps.of(JsonOps.INSTANCE, server.registryManager), 26 | setting 27 | ).getOrThrow() 28 | ) 29 | } 30 | 31 | return finalJson 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/structures/Structures.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors.structures 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import com.mojang.serialization.JsonOps 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.registry.RegistryKeys 8 | import net.minecraft.registry.RegistryOps 9 | import net.minecraft.server.MinecraftServer 10 | import net.minecraft.world.gen.structure.Structure 11 | 12 | class Structures : Extractor.Extractor { 13 | override fun fileName(): String { 14 | return "structures.json" 15 | } 16 | 17 | override fun extract(server: MinecraftServer): JsonElement { 18 | val finalJson = JsonObject() 19 | val registry = 20 | server.registryManager.getOrThrow(RegistryKeys.STRUCTURE) 21 | for (setting in registry) { 22 | finalJson.add( 23 | registry.getId(setting)!!.path, 24 | Structure.STRUCTURE_CODEC.encodeStart( 25 | RegistryOps.of(JsonOps.INSTANCE, server.registryManager), 26 | setting 27 | ).getOrThrow() 28 | ) 29 | } 30 | 31 | return finalJson 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/ConfiguredFeatures.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import com.mojang.serialization.JsonOps 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.registry.RegistryKeys 8 | import net.minecraft.registry.RegistryOps 9 | import net.minecraft.server.MinecraftServer 10 | import net.minecraft.world.gen.feature.ConfiguredFeature 11 | 12 | class ConfiguredFeatures : Extractor.Extractor { 13 | override fun fileName(): String { 14 | return "configured_features.json" 15 | } 16 | 17 | override fun extract(server: MinecraftServer): JsonElement { 18 | val finalJson = JsonObject() 19 | val registry = 20 | server.registryManager.getOrThrow(RegistryKeys.CONFIGURED_FEATURE) 21 | for (setting in registry) { 22 | finalJson.add( 23 | registry.getId(setting)!!.path, 24 | ConfiguredFeature.CODEC.encodeStart( 25 | RegistryOps.of(JsonOps.INSTANCE, server.registryManager), 26 | setting 27 | ).getOrThrow() 28 | ) 29 | } 30 | 31 | return finalJson 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/GameRules.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import de.snowii.extractor.Extractor 6 | import net.minecraft.resource.featuretoggle.FeatureFlags 7 | import net.minecraft.server.MinecraftServer 8 | import net.minecraft.world.GameRules 9 | 10 | class GameRules : Extractor.Extractor { 11 | override fun fileName(): String { 12 | return "game_rules.json" 13 | } 14 | 15 | override fun extract(server: MinecraftServer): JsonElement { 16 | val gameEventJson = JsonObject() 17 | val rules = GameRules(FeatureFlags::VANILLA_FEATURES.get()) 18 | rules.accept(object : GameRules.Visitor { 19 | override fun visitBoolean(key: GameRules.Key, type: GameRules.Type) { 20 | gameEventJson.addProperty(key.name, type.createRule()!!.get()) 21 | } 22 | override fun visitInt(key: GameRules.Key, type: GameRules.Type) { 23 | gameEventJson.addProperty(key.name, type.createRule()!!.get()) 24 | } 25 | }) 26 | return gameEventJson 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/ChunkGenSetting.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import com.mojang.serialization.JsonOps 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.registry.RegistryKeys 8 | import net.minecraft.registry.RegistryOps 9 | import net.minecraft.server.MinecraftServer 10 | import net.minecraft.world.gen.chunk.ChunkGeneratorSettings 11 | 12 | class ChunkGenSetting : Extractor.Extractor { 13 | override fun fileName(): String { 14 | return "chunk_gen_settings.json" 15 | } 16 | 17 | override fun extract(server: MinecraftServer): JsonElement { 18 | val finalJson = JsonObject() 19 | val registry = 20 | server.registryManager.getOrThrow(RegistryKeys.CHUNK_GENERATOR_SETTINGS) 21 | for (setting in registry) { 22 | finalJson.add( 23 | registry.getId(setting)!!.path, 24 | ChunkGeneratorSettings.CODEC.encodeStart( 25 | RegistryOps.of(JsonOps.INSTANCE, server.registryManager), 26 | setting 27 | ).getOrThrow() 28 | ) 29 | } 30 | 31 | return finalJson 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/Biome.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import com.mojang.serialization.JsonOps 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.registry.RegistryKeys 8 | import net.minecraft.registry.RegistryOps 9 | import net.minecraft.server.MinecraftServer 10 | import net.minecraft.world.biome.Biome 11 | 12 | class Biome : Extractor.Extractor { 13 | override fun fileName(): String { 14 | return "biome.json" 15 | } 16 | 17 | override fun extract(server: MinecraftServer): JsonElement { 18 | val biomeData = JsonObject() 19 | val biomeRegistry = 20 | server.registryManager.getOrThrow(RegistryKeys.BIOME) 21 | for (biome in biomeRegistry) { 22 | val json = Biome.CODEC.encodeStart( 23 | RegistryOps.of(JsonOps.INSTANCE, server.registryManager), 24 | biome 25 | ).getOrThrow().asJsonObject 26 | json.addProperty("id", biomeRegistry.getRawId(biome)) 27 | biomeData.add( 28 | biomeRegistry.getId(biome)!!.path, json 29 | ) 30 | 31 | } 32 | 33 | return biomeData 34 | } 35 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/Enchantments.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import com.mojang.serialization.JsonOps 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.enchantment.Enchantment 8 | import net.minecraft.registry.RegistryKeys 9 | import net.minecraft.registry.RegistryOps 10 | import net.minecraft.server.MinecraftServer 11 | 12 | class Enchantments : Extractor.Extractor { 13 | override fun fileName(): String { 14 | return "enchantments.json" 15 | } 16 | 17 | override fun extract(server: MinecraftServer): JsonElement { 18 | val finalJson = JsonObject() 19 | val registry = 20 | server.registryManager.getOrThrow(RegistryKeys.ENCHANTMENT) 21 | for (enchantment in registry) { 22 | val sub = Enchantment.CODEC.encodeStart( 23 | RegistryOps.of(JsonOps.INSTANCE, server.registryManager), enchantment 24 | ).getOrThrow() as JsonObject 25 | sub.addProperty("id", registry.getRawId(enchantment)) 26 | finalJson.add( 27 | registry.getId(enchantment)!!.toString(), sub 28 | ) 29 | } 30 | return finalJson 31 | } 32 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/NoiseParameters.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import com.mojang.serialization.JsonOps 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.registry.RegistryKeys 8 | import net.minecraft.registry.RegistryOps 9 | import net.minecraft.server.MinecraftServer 10 | import net.minecraft.util.math.noise.DoublePerlinNoiseSampler 11 | 12 | class NoiseParameters : Extractor.Extractor { 13 | override fun fileName(): String { 14 | return "noise_parameters.json" 15 | } 16 | 17 | override fun extract(server: MinecraftServer): JsonElement { 18 | val noisesJson = JsonObject() 19 | val noiseParameterRegistry = 20 | server.registryManager.getOrThrow(RegistryKeys.NOISE_PARAMETERS) 21 | for (noise in noiseParameterRegistry) { 22 | noisesJson.add( 23 | noiseParameterRegistry.getId(noise)!!.path, 24 | DoublePerlinNoiseSampler.NoiseParameters.CODEC.encodeStart( 25 | RegistryOps.of(JsonOps.INSTANCE, server.registryManager), 26 | noise 27 | ).getOrThrow() 28 | ) 29 | } 30 | 31 | return noisesJson 32 | } 33 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 4 | ![Current version)](https://img.shields.io/badge/current_version-1.21.10-blue) 5 | 6 | Extractor is a Fabric mod that extracts Minecraft data (blocks, items, entities, etc.) into JSON files 7 |
8 | 9 | ### Supported Extractors 10 | - [x] Blocks 11 | - [x] Entities 12 | - [x] Items 13 | - [x] Packets 14 | - [x] World Event 15 | - [x] Multi Noise 16 | - [x] Message Type 17 | - [x] Biomes 18 | - [x] Entity Pose 19 | - [x] Attributes 20 | - [x] Sound Category 21 | - [x] Chunk Status 22 | - [x] Game Event 23 | - [x] Game Rule 24 | - [x] Translation (en_us) 25 | - [x] Noise Parameters 26 | - [x] Particles 27 | - [x] Recipes 28 | - [x] Entity Statuses 29 | - [x] Status Effects 30 | - [x] Screens 31 | - [x] Spawn Eggs 32 | - [x] Sounds 33 | - [x] SyncedRegistries 34 | - [x] Tags 35 | - [x] Tests 36 | 37 | ### Running 38 | - Gradle >= 9.1.0 39 | 40 | 1. Clone the repo 41 | 2. run `./gradlew runServer` or alternatively `./gralde runClient` (Join World) 42 | 3. See JSON Files in the new folder called `pumpkin_extractor_output` 43 | 44 | ### Porting 45 | How to port to a new Minecraft version: 46 | 1. Update versions in `gradle.properties` 47 | 2. Attempt to run and fix any errors that come up 48 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/non_registry/RecipeRemainder.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors.non_registry 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import com.google.gson.JsonPrimitive 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.item.Item 8 | import net.minecraft.item.ItemStack 9 | import net.minecraft.registry.Registries 10 | import net.minecraft.registry.RegistryKeys 11 | import net.minecraft.server.MinecraftServer 12 | 13 | class RecipeRemainder : Extractor.Extractor { 14 | override fun fileName(): String { 15 | return "recipe_remainder.json" 16 | } 17 | 18 | override fun extract(server: MinecraftServer): JsonElement { 19 | val recipeRemainderJson = JsonObject() 20 | 21 | for (item in server.registryManager.getOrThrow(RegistryKeys.ITEM).streamEntries().toList()) { 22 | val realItem: Item = item.value() 23 | val remainder = realItem.recipeRemainder; 24 | if (remainder == ItemStack.EMPTY) { 25 | continue 26 | } 27 | 28 | 29 | recipeRemainderJson.add( 30 | Registries.ITEM.getRawId(realItem).toString(), 31 | JsonPrimitive(Registries.ITEM.getRawId(remainder.item)), 32 | ) 33 | 34 | } 35 | return recipeRemainderJson 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/MessageType.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import com.mojang.serialization.JsonOps 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.network.message.MessageType 8 | import net.minecraft.registry.RegistryKeys 9 | import net.minecraft.registry.RegistryOps 10 | import net.minecraft.server.MinecraftServer 11 | 12 | class MessageType : Extractor.Extractor { 13 | override fun fileName(): String { 14 | return "message_type.json" 15 | } 16 | 17 | override fun extract(server: MinecraftServer): JsonElement { 18 | val messagesJson = JsonObject() 19 | val messageTypeRegistry = 20 | server.registryManager.getOrThrow(RegistryKeys.MESSAGE_TYPE) 21 | for (type in messageTypeRegistry) { 22 | val json = JsonObject() 23 | json.addProperty("id", messageTypeRegistry.getRawId(type)) 24 | json.add( 25 | "components", MessageType.CODEC.encodeStart( 26 | RegistryOps.of(JsonOps.INSTANCE, server.registryManager), type 27 | ).getOrThrow() 28 | ) 29 | messagesJson.add( 30 | messageTypeRegistry.getId(type)!!.path, 31 | json 32 | ) 33 | } 34 | 35 | return messagesJson 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/DamageTypes.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import com.mojang.serialization.JsonOps 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.entity.damage.DamageType 8 | import net.minecraft.registry.RegistryKeys 9 | import net.minecraft.registry.RegistryOps 10 | import net.minecraft.server.MinecraftServer 11 | 12 | class DamageTypes : Extractor.Extractor { 13 | override fun fileName(): String { 14 | return "damage_type.json" 15 | } 16 | 17 | override fun extract(server: MinecraftServer): JsonElement { 18 | val damageTypesJson = JsonObject() 19 | val damageTypeRegistry = server.registryManager.getOrThrow(RegistryKeys.DAMAGE_TYPE) 20 | for (type in damageTypeRegistry) { 21 | val json = JsonObject() 22 | json.addProperty("id", damageTypeRegistry.getRawId(type)) 23 | json.add( 24 | "components", 25 | DamageType.CODEC 26 | .encodeStart( 27 | RegistryOps.of(JsonOps.INSTANCE, server.registryManager), 28 | type 29 | ) 30 | .getOrThrow() 31 | ) 32 | damageTypesJson.add(damageTypeRegistry.getId(type)!!.path, json) 33 | } 34 | 35 | return damageTypesJson 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/Tags.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonArray 4 | import com.google.gson.JsonElement 5 | import com.google.gson.JsonObject 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.registry.tag.TagPacketSerializer 8 | import net.minecraft.server.MinecraftServer 9 | 10 | 11 | class Tags : Extractor.Extractor { 12 | override fun fileName(): String { 13 | return "tags.json" 14 | } 15 | 16 | override fun extract(server: MinecraftServer): JsonElement { 17 | val tagsJson = JsonObject() 18 | 19 | val tags = TagPacketSerializer.serializeTags(server.combinedDynamicRegistries) 20 | 21 | for (tag in tags.entries) { 22 | val tagGroupTagsJson = JsonObject() 23 | val tagValues = 24 | tag.value.toRegistryTags(server.combinedDynamicRegistries.combinedRegistryManager.getOrThrow(tag.key)) 25 | for (value in tagValues.tags) { 26 | val tagGroupTagsJsonArray = JsonArray() 27 | for (tagVal in value.value) { 28 | tagGroupTagsJsonArray.add(tagVal.key.orElseThrow().value.path) 29 | } 30 | tagGroupTagsJson.add(value.key.id.toString(), tagGroupTagsJsonArray) 31 | } 32 | tagsJson.add(tag.key.value.path, tagGroupTagsJson) 33 | } 34 | 35 | return tagsJson 36 | } 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/Items.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import com.mojang.serialization.JsonOps 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.component.ComponentMap 8 | import net.minecraft.item.Item 9 | import net.minecraft.registry.Registries 10 | import net.minecraft.registry.RegistryKeys 11 | import net.minecraft.registry.RegistryOps 12 | import net.minecraft.server.MinecraftServer 13 | 14 | 15 | class Items : Extractor.Extractor { 16 | override fun fileName(): String { 17 | return "items.json" 18 | } 19 | 20 | 21 | override fun extract(server: MinecraftServer): JsonElement { 22 | val itemsJson = JsonObject() 23 | 24 | for (item in server.registryManager.getOrThrow(RegistryKeys.ITEM).streamEntries().toList()) { 25 | val itemJson = JsonObject() 26 | val realItem: Item = item.value() 27 | 28 | itemJson.addProperty("id", Registries.ITEM.getRawId(realItem)) 29 | itemJson.add( 30 | "components", 31 | ComponentMap.CODEC.encodeStart( 32 | RegistryOps.of(JsonOps.INSTANCE, server.registryManager), 33 | realItem.components 34 | ).getOrThrow() 35 | ) 36 | 37 | itemsJson.add(Registries.ITEM.getId(realItem).path, itemJson) 38 | } 39 | return itemsJson 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/SyncedRegistries.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonElement 4 | import com.google.gson.JsonObject 5 | import com.mojang.serialization.Codec 6 | import com.mojang.serialization.JsonOps 7 | import de.snowii.extractor.Extractor 8 | import net.minecraft.registry.* 9 | import net.minecraft.server.MinecraftServer 10 | import java.util.stream.Stream 11 | 12 | 13 | class SyncedRegistries : Extractor.Extractor { 14 | override fun fileName(): String { 15 | return "synced_registries.json" 16 | } 17 | 18 | override fun extract(server: MinecraftServer): JsonElement { 19 | val registries: Stream> = RegistryLoader.SYNCED_REGISTRIES.stream() 20 | val json = JsonObject() 21 | registries.forEach { entry -> 22 | json.add( 23 | entry.key().value.path, 24 | mapJson(entry, server.registryManager, server.combinedDynamicRegistries) 25 | ) 26 | } 27 | return json 28 | } 29 | 30 | private fun mapJson( 31 | registryEntry: RegistryLoader.Entry, 32 | registryManager: DynamicRegistryManager.Immutable, 33 | combinedRegistries: CombinedDynamicRegistries 34 | ): JsonObject { 35 | val codec: Codec = registryEntry.elementCodec() 36 | val registry: Registry = registryManager.getOrThrow(registryEntry.key()) 37 | val json = JsonObject() 38 | registry.streamEntries().forEach { entry -> 39 | json.add( 40 | entry.key.orElseThrow().value.path, 41 | codec.encodeStart( 42 | combinedRegistries.combinedRegistryManager.getOps(JsonOps.INSTANCE), 43 | entry.value() 44 | ).getOrThrow() 45 | ) 46 | } 47 | return json 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/Potion.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonArray 4 | import com.google.gson.JsonElement 5 | import com.google.gson.JsonObject 6 | import com.mojang.serialization.JsonOps 7 | import de.snowii.extractor.Extractor 8 | import net.minecraft.entity.effect.StatusEffectInstance 9 | import net.minecraft.registry.Registries 10 | import net.minecraft.registry.RegistryKeys 11 | import net.minecraft.registry.RegistryOps 12 | import net.minecraft.server.MinecraftServer 13 | 14 | 15 | class Potion : Extractor.Extractor { 16 | override fun fileName(): String { 17 | return "potion.json" 18 | } 19 | 20 | 21 | override fun extract(server: MinecraftServer): JsonElement { 22 | val json = JsonObject() 23 | for (potion in server.registryManager.getOrThrow(RegistryKeys.POTION).streamEntries().toList()) { 24 | val itemJson = JsonObject() 25 | val realPotion = potion.value() 26 | val array = JsonArray() 27 | itemJson.addProperty("id", Registries.POTION.getRawId(realPotion)) 28 | itemJson.addProperty("base_name", realPotion.baseName) 29 | for (effect in realPotion.effects) { 30 | val obj = JsonObject() 31 | obj.addProperty("effect_type", effect.effectType.key.get().value.toString()) 32 | obj.addProperty("duration", effect.duration) 33 | obj.addProperty("amplifier", effect.amplifier) 34 | obj.addProperty("ambient", effect.isAmbient) 35 | obj.addProperty("show_particles", effect.shouldShowParticles()) 36 | obj.addProperty("show_icon", effect.shouldShowIcon()) 37 | array.add(obj) 38 | } 39 | itemJson.add("effects", array) 40 | Registries.POTION.getId(realPotion)?.let { json.add(it.path, itemJson) } 41 | } 42 | return json 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/Fluids.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonArray 4 | import com.google.gson.JsonElement 5 | import com.google.gson.JsonObject 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.block.Block 8 | import net.minecraft.registry.Registries 9 | import net.minecraft.server.MinecraftServer 10 | 11 | class Fluids : Extractor.Extractor { 12 | override fun fileName(): String { 13 | return "fluids.json" 14 | } 15 | 16 | override fun extract(server: MinecraftServer): JsonElement { 17 | val topLevelJson = JsonArray() 18 | 19 | for (fluid in Registries.FLUID) { 20 | val fluidJson = JsonObject() 21 | fluidJson.addProperty("id", Registries.FLUID.getRawId(fluid)) 22 | fluidJson.addProperty("name", Registries.FLUID.getId(fluid).path) 23 | 24 | val propsJson = JsonArray() 25 | for (prop in fluid.stateManager.properties) { 26 | val propJson = JsonObject() 27 | 28 | propJson.addProperty("name", prop.name) 29 | 30 | val valuesJson = JsonArray() 31 | for (value in prop.values) { 32 | valuesJson.add(value.toString().lowercase()) 33 | } 34 | propJson.add("values", valuesJson) 35 | 36 | propsJson.add(propJson) 37 | } 38 | fluidJson.add("properties", propsJson) 39 | 40 | val statesJson = JsonArray() 41 | for ((index, state) in fluid.stateManager.states.withIndex()) { 42 | val stateJson = JsonObject() 43 | stateJson.addProperty("height", state.height) 44 | stateJson.addProperty("level", state.level) 45 | stateJson.addProperty("is_empty", state.isEmpty) 46 | stateJson.addProperty("blast_resistance", state.blastResistance) 47 | stateJson.addProperty("block_state_id", Block.getRawIdFromState(state.blockState)) 48 | stateJson.addProperty("is_still", state.isStill) 49 | // TODO: Particle effects 50 | 51 | if (fluid.defaultState == state) { 52 | fluidJson.addProperty("default_state_index", index) 53 | } 54 | 55 | statesJson.add(stateJson) 56 | } 57 | fluidJson.add("states", statesJson) 58 | 59 | topLevelJson.add(fluidJson) 60 | } 61 | 62 | return topLevelJson 63 | } 64 | } -------------------------------------------------------------------------------- /move_files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | PUMPKIN_ROOT="$1" 6 | SCRIPT_ROOT="$(dirname -- "$(readlink -f -- "$0")")" 7 | OUTPUT_DIRECTORY="$SCRIPT_ROOT/run/pumpkin_extractor_output" 8 | 9 | if ! [ -d "$PUMPKIN_ROOT" ]; then 10 | echo "The input must be a valid directory!" 11 | exit 1 12 | fi 13 | 14 | if ! [ -d "$OUTPUT_DIRECTORY" ]; then 15 | echo "Invalid file location directory!" 16 | exit 1 17 | fi 18 | 19 | # Base asset file 20 | OUTPUT_SUBDIR="$PUMPKIN_ROOT/assets" 21 | 22 | if ! [ -d "$OUTPUT_SUBDIR" ]; then 23 | echo "Invalid output location directory!" 24 | exit 1 25 | fi 26 | 27 | declare -a FILES=( 28 | "items.json" 29 | "noise_parameters.json" 30 | "message_type.json" 31 | "en_us.json" 32 | "entity_statuses.json" 33 | "sound_category.json" 34 | "fluids.json" 35 | "tags.json" 36 | "entity_pose.json" 37 | "carver.json" 38 | "game_rules.json" 39 | "game_event.json" 40 | "sounds.json" 41 | "status_effects.json" 42 | "entities.json" 43 | "scoreboard_display_slot.json" 44 | "attributes.json" 45 | "placed_feature.json" 46 | "synced_registries.json" 47 | "spawn_egg.json" 48 | "particles.json" 49 | "multi_noise_biome_tree.json" 50 | "properties.json" 51 | "damage_type.json" 52 | "packets.json" 53 | "screens.json" 54 | "gen_features.json" 55 | "biome.json" 56 | "blocks.json" 57 | "recipes.json" 58 | "chunk_status.json" 59 | "chunk_gen_settings.json" 60 | "density_function.json" 61 | "world_event.json" 62 | ) 63 | 64 | for FILE in "${FILES[@]}"; do 65 | cp "$OUTPUT_DIRECTORY/$FILE" "$OUTPUT_SUBDIR/$FILE" 66 | done 67 | 68 | # World test files 69 | OUTPUT_SUBDIR="$PUMPKIN_ROOT/pumpkin-world/assets" 70 | 71 | if ! [ -d "$OUTPUT_SUBDIR" ]; then 72 | echo "Invalid output location directory!" 73 | exit 1 74 | fi 75 | 76 | declare -a FILES=( 77 | "no_blend_no_beard_only_cell_cache_once_cache_0_0.chunk" 78 | "no_blend_no_beard_only_cell_cache_0_0.chunk" 79 | "biome_no_blend_no_beard_0.json" 80 | "multi_noise_sample_no_blend_no_beard_0_0_0.json" 81 | "no_blend_no_beard_only_cell_cache_interpolated_0_0.chunk" 82 | "no_blend_no_beard_-595_544.chunk" 83 | "no_blend_no_beard_0_0.chunk" 84 | "no_blend_no_beard_surface_badlands_-595_544.chunk" 85 | "no_blend_no_beard_-119_183.chunk" 86 | "no_blend_no_beard_7_4.chunk" 87 | "no_blend_no_beard_surface_frozen_ocean_-119_183.chunk" 88 | "no_blend_no_beard_surface_0_0.chunk" 89 | "multi_noise_biome_source_test.json" 90 | "no_blend_no_beard_only_cell_cache_flat_cache_0_0.chunk" 91 | "density_function_tests.json" 92 | "no_blend_no_beard_13579_-6_11.chunk" 93 | "no_blend_no_beard_surface_13579_-6_11.chunk" 94 | "no_blend_no_beard_13579_-2_15.chunk" 95 | "no_blend_no_beard_surface_13579_-2_15.chunk" 96 | "no_blend_no_beard_surface_13579_-7_9.chunk" 97 | "biome_mixer.json" 98 | ) 99 | 100 | for FILE in "${FILES[@]}"; do 101 | cp "$OUTPUT_DIRECTORY/$FILE" "$OUTPUT_SUBDIR/$FILE" 102 | done 103 | 104 | echo "Files moved." 105 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH= 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/Effect.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonArray 4 | import com.google.gson.JsonElement 5 | import com.google.gson.JsonObject 6 | import com.mojang.serialization.JsonOps 7 | import de.snowii.extractor.Extractor 8 | import net.minecraft.entity.attribute.EntityAttribute 9 | import net.minecraft.entity.attribute.EntityAttributeModifier 10 | import net.minecraft.entity.effect.StatusEffect 11 | import net.minecraft.entity.effect.StatusEffectInstance 12 | import net.minecraft.recipe.Ingredient 13 | import net.minecraft.registry.Registries 14 | import net.minecraft.registry.RegistryKeys 15 | import net.minecraft.registry.RegistryOps 16 | import net.minecraft.registry.entry.RegistryEntry 17 | import net.minecraft.server.MinecraftServer 18 | import net.minecraft.sound.SoundEvent 19 | import net.minecraft.util.Identifier 20 | import java.util.Optional 21 | 22 | 23 | class Effect : Extractor.Extractor { 24 | override fun fileName(): String { 25 | return "effect.json" 26 | } 27 | 28 | 29 | override fun extract(server: MinecraftServer): JsonElement { 30 | val json = JsonObject() 31 | for (potion in server.registryManager.getOrThrow(RegistryKeys.STATUS_EFFECT).streamEntries().toList()) { 32 | val itemJson = JsonObject() 33 | val realPotion = potion.value() 34 | itemJson.addProperty("id", Registries.STATUS_EFFECT.getRawId(realPotion)) 35 | itemJson.addProperty("category", realPotion.category.toString()) 36 | itemJson.addProperty("color", realPotion.color) 37 | if (realPotion.fadeInTicks != 0 || realPotion.fadeOutTicks != 0 || realPotion.fadeOutThresholdTicks != 0) { 38 | itemJson.addProperty("fade_in_ticks", realPotion.fadeInTicks) 39 | itemJson.addProperty("fade_out_ticks", realPotion.fadeOutTicks) 40 | itemJson.addProperty("fade_out_threshold_ticks", realPotion.fadeOutThresholdTicks) 41 | } 42 | itemJson.addProperty("translation_key", realPotion.translationKey) 43 | 44 | val t3 = StatusEffect::class.java.getDeclaredField("applySound") 45 | t3.isAccessible = true 46 | val applySound = t3.get(realPotion) as? Optional 47 | applySound?.ifPresent { soundEvent -> itemJson.addProperty("apply_sound", Registries.SOUND_EVENT.getId(soundEvent)!!.path)} 48 | 49 | val attributesRegistry = 50 | server.registryManager.getOrThrow(RegistryKeys.ATTRIBUTE) 51 | 52 | val attributeModifiersJson = JsonArray() 53 | realPotion.forEachAttributeModifier(0) { reg, mod -> 54 | val potionJson = JsonObject() 55 | potionJson.addProperty("attribute", attributesRegistry.getId(reg.value())!!.path) 56 | potionJson.addProperty("operation", mod.operation.toString()) 57 | potionJson.addProperty("id", mod.id.toString()) 58 | potionJson.addProperty("baseValue", mod.value) 59 | attributeModifiersJson.add(potionJson) 60 | } 61 | itemJson.add("attribute_modifiers", attributeModifiersJson) 62 | 63 | Registries.STATUS_EFFECT.getId(realPotion)?.let { json.add(it.path, itemJson) } 64 | } 65 | return json 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/non_registry/Properties.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors.non_registry 2 | 3 | import com.google.gson.JsonArray 4 | import com.google.gson.JsonElement 5 | import com.google.gson.JsonObject 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.server.MinecraftServer 8 | import net.minecraft.state.property.BooleanProperty 9 | import net.minecraft.state.property.EnumProperty 10 | import net.minecraft.state.property.IntProperty 11 | import net.minecraft.state.property.Property 12 | import java.lang.reflect.Modifier 13 | 14 | class Properties : Extractor.Extractor { 15 | override fun fileName(): String { 16 | return "properties.json" 17 | } 18 | 19 | override fun extract(server: MinecraftServer): JsonElement { 20 | val topLevelJson = JsonArray() 21 | 22 | for (field in net.minecraft.state.property.Properties::class.java.declaredFields) { 23 | if (Modifier.isStatic(field.modifiers)) { 24 | val maybeProperty = field.get(null) 25 | if (maybeProperty is Property<*>) { 26 | val property = JsonObject() 27 | // The key used by Blocks.json to map to a property 28 | property.addProperty("hash_key", maybeProperty.hashCode()) 29 | // The unique enum name 30 | property.addProperty("enum_name", field.name.lowercase()) 31 | // What the enum is serialized as, may overlap with others 32 | property.addProperty("serialized_name", maybeProperty.name.lowercase()) 33 | 34 | when (maybeProperty) { 35 | is BooleanProperty -> { 36 | property.addProperty("type", "boolean") 37 | } 38 | 39 | is IntProperty -> { 40 | var min: Int? = null 41 | var max: Int? = null 42 | for (intField in IntProperty::class.java.declaredFields) { 43 | intField.trySetAccessible() 44 | if (intField.name == "min") { 45 | min = intField.get(maybeProperty) as Int 46 | } else if (intField.name == "max") { 47 | max = intField.get(maybeProperty) as Int 48 | } 49 | } 50 | property.addProperty("type", "int") 51 | property.addProperty("min", min!!) 52 | property.addProperty("max", max!!) 53 | } 54 | 55 | is EnumProperty<*> -> { 56 | property.addProperty("type", "enum") 57 | val enumArr = JsonArray() 58 | for (value in maybeProperty.values) { 59 | enumArr.add(value.toString().lowercase()) 60 | } 61 | property.add("values", enumArr) 62 | } 63 | 64 | else -> throw Exception("Unhandled property type: " + maybeProperty.javaClass.toString()) 65 | } 66 | 67 | topLevelJson.add(property) 68 | } 69 | } 70 | } 71 | return topLevelJson 72 | } 73 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/PotionBrewing.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonArray 4 | import com.google.gson.JsonElement 5 | import com.google.gson.JsonObject 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.potion.Potion 8 | import net.minecraft.recipe.Ingredient 9 | import net.minecraft.registry.Registries 10 | import net.minecraft.registry.entry.RegistryEntry 11 | import net.minecraft.server.MinecraftServer 12 | 13 | 14 | class PotionBrewing : Extractor.Extractor { 15 | override fun fileName(): String { 16 | return "potion_brewing.json" 17 | } 18 | 19 | 20 | override fun extract(server: MinecraftServer): JsonElement { 21 | val json = JsonObject() 22 | val reg = server.brewingRecipeRegistry 23 | 24 | val t = reg.javaClass.getDeclaredField("potionTypes") 25 | t.isAccessible = true 26 | val potionTypes = t.get(reg) as? List 27 | val types = JsonArray() 28 | for (type in potionTypes!!) { 29 | val items = JsonArray() 30 | for (item in type.getMatchingItems()) { 31 | items.add(item.value().toString()) 32 | } 33 | types.add(items) 34 | } 35 | json.add("potion_types", types) 36 | 37 | val t2 = reg.javaClass.getDeclaredField("potionRecipes") 38 | t2.isAccessible = true 39 | val potionRecipes = t2.get(reg) as? List<*> 40 | val recipes = JsonArray() 41 | for (recipe in potionRecipes!!) { 42 | val recipeJson = JsonObject() 43 | val clazz = recipe?.javaClass 44 | clazz?.let { 45 | for (field in it.declaredFields) { 46 | field.isAccessible = true 47 | val value = field.get(recipe) 48 | if (value is RegistryEntry<*>) { 49 | recipeJson.addProperty(field.name, value.key.get().value.toString()) 50 | } else if (value is Ingredient) { 51 | val tags = JsonArray() 52 | for (tag in value.getMatchingItems()) { 53 | tags.add(tag.value().toString()) 54 | } 55 | recipeJson.add(field.name, tags) 56 | } 57 | } 58 | } 59 | recipes.add(recipeJson) 60 | } 61 | json.add("potion_recipes", recipes) 62 | 63 | val t3 = reg.javaClass.getDeclaredField("itemRecipes") 64 | t3.isAccessible = true 65 | val itemRecipes = t3.get(reg) as? List<*> 66 | val recipes2 = JsonArray() 67 | for (recipe in itemRecipes!!) { 68 | val recipeJson = JsonObject() 69 | val clazz = recipe?.javaClass 70 | clazz?.let { 71 | for (field in it.declaredFields) { 72 | field.isAccessible = true 73 | val value = field.get(recipe) 74 | if (value is RegistryEntry<*>) { 75 | recipeJson.addProperty(field.name, value.key.get().value.toString()) 76 | } else if (value is Ingredient) { 77 | val tags = JsonArray() 78 | for (tag in value.getMatchingItems()) { 79 | tags.add(tag.value().toString()) 80 | } 81 | recipeJson.add(field.name, tags) 82 | } 83 | } 84 | } 85 | recipes2.add(recipeJson) 86 | } 87 | json.add("item_recipes", recipes2) 88 | 89 | return json 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/BiomeMixerTest.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonArray 4 | import com.google.gson.JsonElement 5 | import de.snowii.extractor.Extractor 6 | import net.minecraft.server.MinecraftServer 7 | import net.minecraft.util.math.BlockPos 8 | import net.minecraft.util.math.MathHelper 9 | import net.minecraft.world.biome.source.BiomeAccess 10 | import net.minecraft.world.biome.source.SeedMixer 11 | 12 | class BiomeMixerTest : Extractor.Extractor { 13 | override fun fileName(): String { 14 | return "biome_mixer.json" 15 | } 16 | 17 | companion object { 18 | private fun method_38106(l: Long, i: Int, j: Int, k: Int, d: Double, e: Double, f: Double): Double { 19 | var m = SeedMixer.mixSeed(l, i.toLong()) 20 | m = SeedMixer.mixSeed(m, j.toLong()) 21 | m = SeedMixer.mixSeed(m, k.toLong()) 22 | m = SeedMixer.mixSeed(m, i.toLong()) 23 | m = SeedMixer.mixSeed(m, j.toLong()) 24 | m = SeedMixer.mixSeed(m, k.toLong()) 25 | val g = method_38108(m) 26 | m = SeedMixer.mixSeed(m, l) 27 | val h = method_38108(m) 28 | m = SeedMixer.mixSeed(m, l) 29 | val n = method_38108(m) 30 | return MathHelper.square(f + n) + MathHelper.square(e + h) + MathHelper.square(d + g) 31 | } 32 | 33 | private fun method_38108(l: Long): Double { 34 | val d = Math.floorMod(l shr 24, 1024) / 1024.0 35 | return (d - 0.5) * 0.9 36 | } 37 | 38 | fun getBiome(seed: Long, pos: BlockPos): BlockPos { 39 | val i = pos.x - 2 40 | val j = pos.y - 2 41 | val k = pos.z - 2 42 | val l = i shr 2 43 | val m = j shr 2 44 | val n = k shr 2 45 | val d = (i and 3) / 4.0 46 | val e = (j and 3) / 4.0 47 | val f = (k and 3) / 4.0 48 | var o = 0 49 | var g = Double.POSITIVE_INFINITY 50 | 51 | for (p in 0..7) { 52 | val bl = (p and 4) == 0 53 | val bl2 = (p and 2) == 0 54 | val bl3 = (p and 1) == 0 55 | val q = if (bl) l else l + 1 56 | val r = if (bl2) m else m + 1 57 | val s = if (bl3) n else n + 1 58 | val h = if (bl) d else d - 1.0 59 | val t = if (bl2) e else e - 1.0 60 | val u = if (bl3) f else f - 1.0 61 | val v = method_38106(seed, q, r, s, h, t, u) 62 | if (g > v) { 63 | o = p 64 | g = v 65 | } 66 | } 67 | 68 | val px = if ((o and 4) == 0) l else l + 1 69 | val w = if ((o and 2) == 0) m else m + 1 70 | val x = if ((o and 1) == 0) n else n + 1 71 | 72 | return BlockPos(px, w, x) 73 | } 74 | } 75 | 76 | override fun extract(server: MinecraftServer): JsonElement { 77 | val valuesJson = JsonArray() 78 | val startX = (-2).shl(2) 79 | val startZ = (-2).shl(2) 80 | 81 | println(BiomeAccess.hashSeed(-777)) 82 | 83 | for (x in 0..15) { 84 | for (y in -64..256) { 85 | for (z in 0..15) { 86 | val xAbs = startX + x 87 | val zAbs = startZ + z 88 | val result = getBiome(BiomeAccess.hashSeed(-777), BlockPos(xAbs, y, zAbs)) 89 | val point = JsonArray() 90 | point.add(xAbs) 91 | point.add(y) 92 | point.add(zAbs) 93 | point.add(result.x) 94 | point.add(result.y) 95 | point.add(result.z) 96 | 97 | valuesJson.add(point) 98 | } 99 | } 100 | } 101 | 102 | return valuesJson 103 | } 104 | } -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/Entities.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonArray 4 | import com.google.gson.JsonElement 5 | import com.google.gson.JsonObject 6 | import com.mojang.serialization.JsonOps 7 | import de.snowii.extractor.Extractor 8 | import net.minecraft.entity.LivingEntity 9 | import net.minecraft.entity.SpawnLocationTypes 10 | import net.minecraft.entity.SpawnReason 11 | import net.minecraft.entity.SpawnRestriction 12 | import net.minecraft.entity.mob.MobEntity 13 | import net.minecraft.loot.LootTable 14 | import net.minecraft.registry.Registries 15 | import net.minecraft.registry.RegistryKey 16 | import net.minecraft.registry.RegistryOps 17 | import net.minecraft.server.MinecraftServer 18 | 19 | class Entities : Extractor.Extractor { 20 | override fun fileName(): String { 21 | return "entities.json" 22 | } 23 | 24 | override fun extract(server: MinecraftServer): JsonElement { 25 | val entitiesJson = JsonObject() 26 | for (entityType in Registries.ENTITY_TYPE) { 27 | val entityJson = JsonObject() 28 | entityJson.addProperty("id", Registries.ENTITY_TYPE.getRawId(entityType)) 29 | val entity = entityType.create(server.overworld!!, SpawnReason.NATURAL) 30 | if (entity != null) { 31 | if (entity is LivingEntity) { 32 | entityJson.addProperty("max_health", entity.maxHealth) 33 | } 34 | entityJson.addProperty("attackable", entity.isAttackable) 35 | entityJson.addProperty("mob", entity is MobEntity) 36 | entityJson.addProperty("limit_per_chunk", (entity as? MobEntity)?.limitPerChunk?: 0) 37 | } 38 | entityJson.addProperty("summonable", entityType.isSummonable) 39 | entityJson.addProperty("saveable", entityType.isSaveable) 40 | entityJson.addProperty("fire_immune", entityType.isFireImmune) 41 | entityJson.addProperty("category", entityType.spawnGroup.name) 42 | entityJson.addProperty("can_spawn_far_from_player", entityType.isSpawnableFarFromPlayer) 43 | val dimension = JsonArray() 44 | dimension.add(entityType.width) 45 | dimension.add(entityType.height) 46 | entityJson.add("dimension", dimension) 47 | entityJson.addProperty("eye_height", entityType.dimensions.eyeHeight) 48 | if (entityType.lootTableKey.isPresent) { 49 | val table = server.reloadableRegistries 50 | .getLootTable(entityType.lootTableKey.get() as RegistryKey) 51 | entityJson.add( 52 | "loot_table", LootTable::CODEC.get().encodeStart( 53 | RegistryOps.of(JsonOps.INSTANCE, server.registryManager), 54 | table 55 | ).getOrThrow() 56 | ) 57 | } 58 | val spawnRestriction = JsonObject() 59 | val location = SpawnRestriction.getLocation(entityType) 60 | val locationName = when (location) { 61 | SpawnLocationTypes::IN_LAVA.get() -> { 62 | "IN_LAVA" 63 | } 64 | 65 | SpawnLocationTypes::IN_WATER.get() -> { 66 | "IN_WATER" 67 | } 68 | 69 | SpawnLocationTypes::ON_GROUND.get() -> { 70 | "ON_GROUND" 71 | } 72 | 73 | SpawnLocationTypes::UNRESTRICTED.get() -> { 74 | "UNRESTRICTED" 75 | } 76 | 77 | else -> { 78 | "" 79 | } 80 | } 81 | 82 | spawnRestriction.addProperty("location", locationName) 83 | spawnRestriction.addProperty("heightmap", SpawnRestriction.getHeightmapType(entityType).toString()) 84 | entityJson.add("spawn_restriction", spawnRestriction) 85 | 86 | entitiesJson.add( 87 | Registries.ENTITY_TYPE.getId(entityType).path, entityJson 88 | ) 89 | } 90 | 91 | return entitiesJson 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/Packets.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonArray 4 | import com.google.gson.JsonElement 5 | import com.google.gson.JsonObject 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.SharedConstants 8 | import net.minecraft.network.NetworkPhase 9 | import net.minecraft.network.packet.PacketType 10 | import net.minecraft.network.state.* 11 | import net.minecraft.server.MinecraftServer 12 | 13 | 14 | class Packets : Extractor.Extractor { 15 | override fun fileName(): String { 16 | return "packets.json" 17 | } 18 | 19 | override fun extract(server: MinecraftServer): JsonElement { 20 | val packetsJson = JsonObject() 21 | 22 | val clientBound = arrayOf( 23 | QueryStates.S2C_FACTORY.buildUnbound(), 24 | LoginStates.S2C_FACTORY.buildUnbound(), 25 | ConfigurationStates.S2C_FACTORY.buildUnbound(), 26 | PlayStateFactories.S2C.buildUnbound() 27 | ) 28 | 29 | val serverBound = arrayOf( 30 | HandshakeStates.C2S_FACTORY.buildUnbound(), 31 | QueryStates.C2S_FACTORY.buildUnbound(), 32 | LoginStates.C2S_FACTORY.buildUnbound(), 33 | ConfigurationStates.C2S_FACTORY.buildUnbound(), 34 | PlayStateFactories.C2S.buildUnbound() 35 | ) 36 | val serverBoundJson = serializeServerBound(serverBound) 37 | val clientBoundJson = serializeClientBound(clientBound) 38 | packetsJson.addProperty("version", SharedConstants.getProtocolVersion()) 39 | packetsJson.add("serverbound", serverBoundJson) 40 | packetsJson.add("clientbound", clientBoundJson) 41 | return packetsJson 42 | } 43 | 44 | 45 | private fun serializeServerBound( 46 | packets: Array 47 | ): JsonObject { 48 | val handshakeArray = JsonArray() 49 | val statusArray = JsonArray() 50 | val loginArray = JsonArray() 51 | val configArray = JsonArray() 52 | val playArray = JsonArray() 53 | 54 | for (factory in packets) { 55 | factory.forEachPacketType { type: PacketType<*>, _: Int -> 56 | when (factory.phase()!!) { 57 | NetworkPhase.HANDSHAKING -> handshakeArray.add(type.id().path) 58 | NetworkPhase.PLAY -> playArray.add(type.id().path) 59 | NetworkPhase.STATUS -> statusArray.add(type.id().path) 60 | NetworkPhase.LOGIN -> loginArray.add(type.id().path) 61 | NetworkPhase.CONFIGURATION -> configArray.add(type.id().path) 62 | } 63 | } 64 | } 65 | 66 | val finalJson = JsonObject() 67 | finalJson.add("handshake", handshakeArray) 68 | finalJson.add("status", statusArray) 69 | finalJson.add("login", loginArray) 70 | finalJson.add("config", configArray) 71 | finalJson.add("play", playArray) 72 | return finalJson 73 | } 74 | 75 | private fun serializeClientBound( 76 | packets: Array 77 | ): JsonObject { 78 | val statusArray = JsonArray() 79 | val loginArray = JsonArray() 80 | val configArray = JsonArray() 81 | val playArray = JsonArray() 82 | 83 | for (factory in packets) { 84 | factory.forEachPacketType { type: PacketType<*>, _: Int -> 85 | when (factory.phase()!!) { 86 | NetworkPhase.HANDSHAKING -> error("Client bound Packet should have no handshake") 87 | NetworkPhase.PLAY -> playArray.add(type.id().path) 88 | NetworkPhase.STATUS -> statusArray.add(type.id().path) 89 | NetworkPhase.LOGIN -> loginArray.add(type.id().path) 90 | NetworkPhase.CONFIGURATION -> configArray.add(type.id().path) 91 | } 92 | } 93 | } 94 | val finalJson = JsonObject() 95 | finalJson.add("status", statusArray) 96 | finalJson.add("login", loginArray) 97 | finalJson.add("config", configArray) 98 | finalJson.add("play", playArray) 99 | return finalJson 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/Extractor.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor 2 | 3 | import com.google.gson.GsonBuilder 4 | import com.google.gson.JsonElement 5 | import de.snowii.extractor.extractors.* 6 | import de.snowii.extractor.extractors.non_registry.* 7 | import de.snowii.extractor.extractors.structures.StructureSet 8 | import de.snowii.extractor.extractors.structures.Structures 9 | import net.fabricmc.api.ModInitializer 10 | import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents 11 | import net.minecraft.server.MinecraftServer 12 | import org.slf4j.Logger 13 | import org.slf4j.LoggerFactory 14 | import java.io.FileWriter 15 | import java.io.IOException 16 | import java.nio.charset.StandardCharsets 17 | import java.nio.file.Files 18 | import java.nio.file.Path 19 | import java.nio.file.Paths 20 | import kotlin.system.measureTimeMillis 21 | 22 | 23 | class Extractor : ModInitializer { 24 | private val modID: String = "pumpkin_extractor" 25 | private val logger: Logger = LoggerFactory.getLogger(modID) 26 | 27 | override fun onInitialize() { 28 | logger.info("Starting Pumpkin Extractor") 29 | 30 | val extractors = arrayOf( 31 | Effect(), 32 | PotionBrewing(), 33 | Potion(), 34 | Sounds(), 35 | Recipes(), 36 | Biome(), 37 | BiomeMixerTest(), 38 | WorldEvent(), 39 | Carver(), 40 | Enchantments(), 41 | ScoreboardDisplaySlot(), 42 | Particles(), 43 | EntityAttributes(), 44 | ChunkStatus(), 45 | EntityStatuses(), 46 | MessageType(), 47 | SoundCategory(), 48 | EntityPose(), 49 | GameEvent(), 50 | GameRules(), 51 | SpawnEgg(), 52 | SyncedRegistries(), 53 | ChunkGenSetting(), 54 | Packets(), 55 | Screens(), 56 | PlacedFeatures(), 57 | Tags(), 58 | NoiseParameters(), 59 | Structures(), 60 | StructureSet(), 61 | Entities(), 62 | ConfiguredFeatures(), 63 | Items(), 64 | DataComponent(), 65 | Blocks(), 66 | MultiNoise(), 67 | MultiNoise().Sample(), 68 | ChunkGenSetting(), 69 | Translations(), 70 | DensityFunctions(), 71 | DensityFunctions().Tests(), 72 | DamageTypes(), 73 | Fluids(), 74 | Properties(), 75 | ComposterIncreaseChance(), 76 | FlowerPotTransformation(), 77 | Fuels(), 78 | RecipeRemainder(), 79 | ChunkDumpTests.NoiseDump( 80 | "no_blend_no_beard_0_0.chunk", 81 | 0, 82 | 0, 83 | 0, 84 | arrayListOf("Interpolated", "CacheOnce", "FlatCache", "Cache2D") 85 | ), 86 | ChunkDumpTests.NoiseDump( 87 | "no_blend_no_beard_7_4.chunk", 88 | 0, 89 | 7, 90 | 4, 91 | arrayListOf("Interpolated", "CacheOnce", "FlatCache", "Cache2D") 92 | ), 93 | ChunkDumpTests.NoiseDump( 94 | "no_blend_no_beard_only_cell_cache_0_0.chunk", 95 | 0, 96 | 0, 97 | 0, 98 | ArrayList() 99 | ), 100 | ChunkDumpTests.NoiseDump( 101 | "no_blend_no_beard_only_cell_cache_flat_cache_0_0.chunk", 102 | 0, 103 | 0, 104 | 0, 105 | arrayListOf("FlatCache") 106 | ), 107 | ChunkDumpTests.NoiseDump( 108 | "no_blend_no_beard_only_cell_cache_interpolated_0_0.chunk", 109 | 0, 110 | 0, 111 | 0, 112 | arrayListOf("Interpolated") 113 | ), 114 | ChunkDumpTests.NoiseDump( 115 | "no_blend_no_beard_only_cell_cache_once_cache_0_0.chunk", 116 | 0, 117 | 0, 118 | 0, 119 | arrayListOf("CacheOnce") 120 | ), 121 | ChunkDumpTests.NoiseDump( 122 | "no_blend_no_beard_-595_544.chunk", 123 | 0, 124 | -595, 125 | 544, 126 | arrayListOf("Interpolated", "CacheOnce", "FlatCache", "Cache2D") 127 | ), 128 | ChunkDumpTests.NoiseDump( 129 | "no_blend_no_beard_-119_183.chunk", 130 | 0, 131 | -119, 132 | 183, 133 | arrayListOf("Interpolated", "CacheOnce", "FlatCache", "Cache2D") 134 | ), 135 | ChunkDumpTests.NoiseDump( 136 | "no_blend_no_beard_13579_-6_11.chunk", 137 | 13579, 138 | -6, 139 | 11, 140 | arrayListOf("Interpolated", "CacheOnce", "FlatCache", "Cache2D") 141 | ), 142 | ChunkDumpTests.NoiseDump( 143 | "no_blend_no_beard_13579_-2_15.chunk", 144 | 13579, 145 | -2, 146 | 15, 147 | arrayListOf("Interpolated", "CacheOnce", "FlatCache", "Cache2D") 148 | ), 149 | BiomeDumpTests(), 150 | BiomeDumpTests().MultiNoiseBiomeSourceTest(), 151 | ChunkDumpTests.SurfaceDump("no_blend_no_beard_surface_0_0.chunk", 0, 0, 0), 152 | ChunkDumpTests.SurfaceDump("no_blend_no_beard_surface_badlands_-595_544.chunk", 0, -595, 544), 153 | ChunkDumpTests.SurfaceDump("no_blend_no_beard_surface_frozen_ocean_-119_183.chunk", 0, -119, 183), 154 | ChunkDumpTests.SurfaceDump("no_blend_no_beard_surface_13579_-6_11.chunk", 13579, -6, 11), 155 | ChunkDumpTests.SurfaceDump("no_blend_no_beard_surface_13579_-2_15.chunk", 13579, -2, 15), 156 | ChunkDumpTests.SurfaceDump("no_blend_no_beard_surface_13579_-7_9.chunk", 13579, -7, 9) 157 | ) 158 | 159 | val outputDirectory: Path 160 | try { 161 | outputDirectory = Files.createDirectories(Paths.get("pumpkin_extractor_output")) 162 | } catch (e: IOException) { 163 | logger.info("Failed to create output directory.", e) 164 | return 165 | } 166 | 167 | val gson = GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create() 168 | 169 | ServerLifecycleEvents.SERVER_STARTED.register(ServerLifecycleEvents.ServerStarted { server: MinecraftServer -> 170 | val timeInMillis = measureTimeMillis { 171 | for (ext in extractors) { 172 | try { 173 | val out = outputDirectory.resolve(ext.fileName()) 174 | val fileWriter = FileWriter(out.toFile(), StandardCharsets.UTF_8) 175 | gson.toJson(ext.extract(server), fileWriter) 176 | fileWriter.close() 177 | logger.info("Wrote " + out.toAbsolutePath()) 178 | } catch (e: java.lang.Exception) { 179 | logger.error(("Extractor for \"" + ext.fileName()) + "\" failed.", e) 180 | } 181 | } 182 | } 183 | logger.info("Done, took ${timeInMillis}ms") 184 | }) 185 | } 186 | 187 | interface Extractor { 188 | fun fileName(): String 189 | 190 | @Throws(Exception::class) 191 | fun extract(server: MinecraftServer): JsonElement 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/DensityFunctions.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonArray 4 | import com.google.gson.JsonElement 5 | import com.google.gson.JsonObject 6 | import com.google.gson.JsonPrimitive 7 | import de.snowii.extractor.Extractor 8 | import net.minecraft.registry.BuiltinRegistries 9 | import net.minecraft.registry.RegistryKey 10 | import net.minecraft.registry.RegistryKeys 11 | import net.minecraft.server.MinecraftServer 12 | import net.minecraft.util.Identifier 13 | import net.minecraft.util.math.Spline 14 | import net.minecraft.util.math.noise.OctavePerlinNoiseSampler 15 | import net.minecraft.util.math.noise.SimplexNoiseSampler 16 | import net.minecraft.world.gen.densityfunction.DensityFunction 17 | import net.minecraft.world.gen.densityfunction.DensityFunction.Noise 18 | import net.minecraft.world.gen.densityfunction.DensityFunctionTypes 19 | import net.minecraft.world.gen.densityfunction.DensityFunctionTypes.RegistryEntryHolder 20 | import net.minecraft.world.gen.noise.NoiseRouter 21 | 22 | class DensityFunctions : Extractor.Extractor { 23 | override fun fileName(): String = "density_function.json" 24 | 25 | private fun serializeSpline(spline: Spline<*, *>): JsonObject { 26 | val obj = JsonObject() 27 | 28 | when (spline) { 29 | is Spline.Implementation<*, *> -> { 30 | obj.add("_type", JsonPrimitive("standard")) 31 | 32 | val value = JsonObject() 33 | val functionWrapper = spline.locationFunction() as DensityFunctionTypes.Spline.DensityFunctionWrapper 34 | value.add("locationFunction", serializeFunction(functionWrapper.function.value())) 35 | 36 | val locationArr = JsonArray() 37 | for (location in spline.locations()) { 38 | locationArr.add(JsonPrimitive(location)) 39 | } 40 | value.add("locations", locationArr) 41 | 42 | val valueArr = JsonArray() 43 | for (splineValue in spline.values()) { 44 | valueArr.add(serializeSpline(splineValue)) 45 | } 46 | value.add("values", valueArr) 47 | 48 | val derivativeArr = JsonArray() 49 | for (derivative in spline.derivatives()) { 50 | derivativeArr.add(JsonPrimitive(derivative)) 51 | } 52 | value.add("derivatives", derivativeArr) 53 | 54 | obj.add("value", value) 55 | } 56 | 57 | is Spline.FixedFloatFunction<*, *> -> { 58 | obj.add("_type", JsonPrimitive("fixed")) 59 | 60 | val value = JsonObject() 61 | value.add("value", JsonPrimitive(spline.value())) 62 | 63 | obj.add("value", value) 64 | } 65 | 66 | else -> throw Exception("Unknown spline: $obj (${obj.javaClass})") 67 | } 68 | 69 | return obj 70 | } 71 | 72 | private fun serializeValue(name: String, obj: Any, parent: String): JsonElement { 73 | return when (obj) { 74 | is DensityFunction -> serializeFunction(obj) 75 | is Noise -> JsonPrimitive(obj.noiseData.key.get().value.path) 76 | is Spline<*, *> -> serializeSpline(obj) 77 | is Int -> JsonPrimitive(obj) 78 | is Float -> { 79 | /* 80 | if (obj.isNaN()) { 81 | throw Exception("Bad float ($name) from $parent") 82 | } 83 | */ 84 | JsonPrimitive(obj) 85 | } 86 | 87 | is Double -> { 88 | /* 89 | if (obj.isNaN()) { 90 | throw Exception("Bad double ($name) from $parent") 91 | } 92 | */ 93 | JsonPrimitive(obj) 94 | } 95 | 96 | is Boolean -> JsonPrimitive(obj) 97 | is String -> JsonPrimitive(obj) 98 | is Char -> JsonPrimitive(obj) 99 | is Enum<*> -> JsonPrimitive(obj.name) 100 | else -> throw Exception("Unknown value to serialize: $obj ($name) from $parent") 101 | } 102 | } 103 | 104 | private fun serializeFunction(function: DensityFunction): JsonObject { 105 | val obj = JsonObject() 106 | 107 | if (function is RegistryEntryHolder) { 108 | return serializeFunction(function.function.value()) 109 | } 110 | 111 | obj.add("_class", JsonPrimitive(function.javaClass.simpleName)) 112 | 113 | val value = JsonObject() 114 | for (field in function.javaClass.declaredFields) { 115 | if (field.name.first().isUpperCase()) { 116 | // We only want to serialize the used values 117 | continue 118 | } 119 | 120 | // These are constant 121 | if (function.javaClass.simpleName == "BlendDensity") { 122 | if (field.name == "maxValue") { 123 | continue 124 | } 125 | if (field.name == "minValue") { 126 | continue 127 | } 128 | } 129 | 130 | if (function is DensityFunctionTypes.Spline) { 131 | value.add("minValue", JsonPrimitive(function.minValue())) 132 | value.add("maxValue", JsonPrimitive(function.maxValue())) 133 | } 134 | 135 | // These aren't used 136 | if (field.name.startsWith("field_")) { 137 | continue 138 | } 139 | 140 | field.trySetAccessible() 141 | val fieldValue = field.get(function) 142 | when (fieldValue) { 143 | // SimplexNoiseSampler is initialized with a random value during runtime 144 | is SimplexNoiseSampler -> continue 145 | // OctavePerlinNoiseSampler is initialized with a random value during runtime 146 | is OctavePerlinNoiseSampler -> continue 147 | } 148 | 149 | val serialized = serializeValue(field.name, fieldValue, function.javaClass.simpleName) 150 | value.add(field.name, serialized) 151 | } 152 | 153 | if (!value.isEmpty) { 154 | obj.add("value", value) 155 | } 156 | 157 | return obj 158 | } 159 | 160 | private fun serializeRouter(router: NoiseRouter): JsonObject { 161 | val obj = JsonObject() 162 | 163 | for (field in router.javaClass.declaredFields) { 164 | if (field.name.first().isUpperCase()) { 165 | // CODEC is not a value we want 166 | continue 167 | } 168 | 169 | field.trySetAccessible() 170 | val function = field.get(router) 171 | val serialized = serializeFunction(function as DensityFunction) 172 | obj.add(field.name, serialized) 173 | } 174 | 175 | return obj 176 | } 177 | 178 | override fun extract(server: MinecraftServer): JsonElement { 179 | val topLevelJson = JsonObject() 180 | 181 | val lookup = BuiltinRegistries.createWrapperLookup() 182 | val wrapper = lookup.getOrThrow(RegistryKeys.CHUNK_GENERATOR_SETTINGS) 183 | 184 | wrapper.streamKeys().forEach { key -> 185 | val entry = wrapper.getOrThrow(key) 186 | val settings = entry.value() 187 | 188 | val obj = serializeRouter(settings.noiseRouter) 189 | topLevelJson.add(key.value.path, obj) 190 | } 191 | return topLevelJson 192 | } 193 | 194 | // Dump the building blocks of the density functions to validate proper results 195 | inner class Tests : Extractor.Extractor { 196 | override fun fileName(): String = "density_function_tests.json" 197 | 198 | override fun extract(server: MinecraftServer): JsonElement { 199 | val topLevelJson = JsonObject() 200 | 201 | val functionNames = arrayOf( 202 | "overworld/base_3d_noise", 203 | "overworld/caves/entrances", 204 | "overworld/caves/noodle", 205 | "overworld/caves/pillars", 206 | "overworld/caves/spaghetti_2d", 207 | "overworld/caves/spaghetti_2d_thickness_modulator", 208 | "overworld/caves/spaghetti_roughness_function", 209 | "overworld/offset", 210 | "overworld/depth", 211 | "overworld/factor", 212 | "overworld/sloped_cheese" 213 | ) 214 | 215 | val lookup = BuiltinRegistries.createWrapperLookup() 216 | val functionLookup = lookup.getOrThrow(RegistryKeys.DENSITY_FUNCTION) 217 | for (functionName in functionNames) { 218 | val functionKey = 219 | RegistryKey.of( 220 | RegistryKeys.DENSITY_FUNCTION, 221 | Identifier.ofVanilla(functionName) 222 | ) 223 | val function = functionLookup.getOrThrow(functionKey).value() 224 | topLevelJson.add(functionName, serializeFunction(function)) 225 | } 226 | 227 | return topLevelJson 228 | } 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/MultiNoise.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonArray 4 | import com.google.gson.JsonElement 5 | import com.google.gson.JsonObject 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.registry.BuiltinRegistries 8 | import net.minecraft.registry.DynamicRegistryManager 9 | import net.minecraft.registry.Registry 10 | import net.minecraft.registry.RegistryKeys 11 | import net.minecraft.registry.entry.RegistryEntry 12 | import net.minecraft.server.MinecraftServer 13 | import net.minecraft.util.math.ChunkPos 14 | import net.minecraft.world.biome.Biome 15 | import net.minecraft.world.biome.source.MultiNoiseBiomeSource 16 | import net.minecraft.world.biome.source.MultiNoiseBiomeSourceParameterList 17 | import net.minecraft.world.biome.source.MultiNoiseBiomeSourceParameterLists 18 | import net.minecraft.world.biome.source.util.MultiNoiseUtil.* 19 | import net.minecraft.world.gen.chunk.Blender 20 | import net.minecraft.world.gen.chunk.ChunkGeneratorSettings 21 | import net.minecraft.world.gen.chunk.ChunkNoiseSampler 22 | import net.minecraft.world.gen.chunk.GenerationShapeConfig 23 | import net.minecraft.world.gen.densityfunction.DensityFunction.EachApplier 24 | import net.minecraft.world.gen.densityfunction.DensityFunction.NoisePos 25 | import net.minecraft.world.gen.densityfunction.DensityFunctionTypes 26 | import net.minecraft.world.gen.noise.NoiseConfig 27 | import java.lang.reflect.Field 28 | import java.lang.reflect.Method 29 | import kotlin.reflect.KFunction 30 | import kotlin.reflect.full.declaredFunctions 31 | 32 | /** 33 | * An extractor for MultiNoiseBiomeSourceParameterList that fully serializes NoiseHypercube and ParameterRange data. 34 | */ 35 | class MultiNoise : Extractor.Extractor { 36 | 37 | override fun fileName(): String { 38 | return "multi_noise_biome_tree.json" 39 | } 40 | 41 | fun extract_tree_node(node: Any?): JsonObject { 42 | val json = JsonObject() 43 | for (f: Field in node!!::class.java.fields) { 44 | if (f.name == "parameters") { 45 | f.trySetAccessible() 46 | val parameters = JsonArray() 47 | val ranges = f.get(node) as Array 48 | for (range in ranges) { 49 | val parameter = JsonObject() 50 | parameter.addProperty("min", range.min) 51 | parameter.addProperty("max", range.max) 52 | parameters.add(parameter) 53 | } 54 | json.add("parameters", parameters) 55 | } 56 | if (f.name == "subTree") { 57 | f.trySetAccessible() 58 | val subTree = JsonArray() 59 | val nodes = f.get(node) as Array 60 | for (childNode in nodes) { 61 | subTree.add(extract_tree_node(childNode)) 62 | } 63 | json.add("subTree", subTree) 64 | json.addProperty("_type", "branch") 65 | } 66 | if (f.name == "value") { 67 | f.trySetAccessible() 68 | val value = f.get(node) as RegistryEntry 69 | json.addProperty("biome", value.idAsString) 70 | json.addProperty("_type", "leaf") 71 | } 72 | } 73 | return json 74 | } 75 | 76 | fun extract_search_tree(tree: Any?): JsonObject { 77 | var field: Field? = null 78 | for (f: Field in tree!!::class.java.declaredFields) { 79 | if (f.name == "firstNode") { 80 | f.trySetAccessible() 81 | field = f 82 | } 83 | } 84 | 85 | return extract_tree_node(field!!.get(tree)) 86 | } 87 | 88 | // Only overworld and nether use multi noise sampler for biomes 89 | override fun extract(server: MinecraftServer): JsonElement { 90 | val registryManager: DynamicRegistryManager.Immutable = server.registryManager 91 | val multiNoiseRegistry: Registry = 92 | registryManager.getOrThrow(RegistryKeys.MULTI_NOISE_BIOME_SOURCE_PARAMETER_LIST) 93 | 94 | val overworldBiomeSource = 95 | MultiNoiseBiomeSource.create(multiNoiseRegistry.getOrThrow(MultiNoiseBiomeSourceParameterLists.OVERWORLD)) 96 | 97 | var method: Method? = null 98 | for (m: Method in overworldBiomeSource::class.java.declaredMethods) { 99 | if (m.name == "getBiomeEntries") { 100 | m.trySetAccessible() 101 | method = m 102 | break 103 | } 104 | } 105 | 106 | val overworldEntries = method!!.invoke(overworldBiomeSource) as Entries> 107 | 108 | var field: Field? = null 109 | for (f: Field in overworldEntries::class.java.declaredFields) { 110 | if (f.name == "tree") { 111 | f.trySetAccessible() 112 | field = f 113 | } 114 | } 115 | 116 | val overworld = extract_search_tree(field!!.get(overworldEntries)) 117 | 118 | val netherBiomeSource = 119 | MultiNoiseBiomeSource.create(multiNoiseRegistry.getOrThrow(MultiNoiseBiomeSourceParameterLists.NETHER)) 120 | 121 | method = null 122 | for (m: Method in netherBiomeSource::class.java.declaredMethods) { 123 | if (m.name == "getBiomeEntries") { 124 | m.trySetAccessible() 125 | method = m 126 | break 127 | } 128 | } 129 | 130 | val netherEntries = method!!.invoke(netherBiomeSource) as Entries> 131 | 132 | field = null 133 | for (f: Field in netherEntries::class.java.declaredFields) { 134 | if (f.name == "tree") { 135 | f.trySetAccessible() 136 | field = f 137 | } 138 | } 139 | 140 | val nether = extract_search_tree(field!!.get(netherEntries)) 141 | 142 | val returnValue = JsonObject() 143 | returnValue.add("overworld", overworld) 144 | returnValue.add("nether", nether) 145 | return returnValue 146 | } 147 | 148 | inner class Sample : Extractor.Extractor { 149 | override fun fileName(): String { 150 | return "multi_noise_sample_no_blend_no_beard_0_0_0.json" 151 | } 152 | 153 | override fun extract(server: MinecraftServer): JsonElement { 154 | val rootJson = JsonArray() 155 | 156 | val seed = 0L 157 | val chunkPos = ChunkPos(0, 0) 158 | 159 | val lookup = BuiltinRegistries.createWrapperLookup() 160 | val wrapper = lookup.getOrThrow(RegistryKeys.CHUNK_GENERATOR_SETTINGS) 161 | val noiseParams = lookup.getOrThrow(RegistryKeys.NOISE_PARAMETERS) 162 | 163 | val ref = wrapper.getOrThrow(ChunkGeneratorSettings.OVERWORLD) 164 | val settings = ref.value() 165 | val config = NoiseConfig.create(settings, noiseParams, seed) 166 | 167 | // Overworld shape config 168 | val shape = GenerationShapeConfig(-64, 384, 1, 2) 169 | val testSampler = 170 | ChunkNoiseSampler( 171 | 16 / shape.horizontalCellBlockCount(), config, chunkPos.startX, chunkPos.startZ, 172 | shape, object : DensityFunctionTypes.Beardifying { 173 | override fun maxValue(): Double = 0.0 174 | override fun minValue(): Double = 0.0 175 | override fun sample(pos: NoisePos): Double = 0.0 176 | override fun fill(densities: DoubleArray, applier: EachApplier) { 177 | densities.fill(0.0) 178 | } 179 | }, settings, null, Blender.getNoBlending() 180 | ) 181 | 182 | var method: KFunction<*>? = null 183 | for (m: KFunction<*> in testSampler::class.declaredFunctions) { 184 | if (m.name == "createMultiNoiseSampler") { 185 | method = m 186 | break 187 | } 188 | } 189 | val sampler = method!!.call(testSampler, config.noiseRouter, listOf()) as MultiNoiseSampler 190 | for (x in 0..15) { 191 | for (y in -64..319) { 192 | for (z in 0..15) { 193 | val result = sampler.sample(x, y, z) 194 | 195 | val valueArr = JsonArray() 196 | valueArr.add(x) 197 | valueArr.add(y) 198 | valueArr.add(z) 199 | 200 | valueArr.add(result.temperatureNoise) 201 | valueArr.add(result.humidityNoise) 202 | valueArr.add(result.continentalnessNoise) 203 | valueArr.add(result.erosionNoise) 204 | valueArr.add(result.depth) 205 | valueArr.add(result.weirdnessNoise) 206 | 207 | rootJson.add(valueArr) 208 | } 209 | } 210 | } 211 | 212 | return rootJson 213 | } 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH="\\\"\\\"" 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | if ! command -v java >/dev/null 2>&1 137 | then 138 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 139 | 140 | Please set the JAVA_HOME variable in your environment to match the 141 | location of your Java installation." 142 | fi 143 | fi 144 | 145 | # Increase the maximum file descriptors if we can. 146 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 147 | case $MAX_FD in #( 148 | max*) 149 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 150 | # shellcheck disable=SC2039,SC3045 151 | MAX_FD=$( ulimit -H -n ) || 152 | warn "Could not query maximum file descriptor limit" 153 | esac 154 | case $MAX_FD in #( 155 | '' | soft) :;; #( 156 | *) 157 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 158 | # shellcheck disable=SC2039,SC3045 159 | ulimit -n "$MAX_FD" || 160 | warn "Could not set maximum file descriptor limit to $MAX_FD" 161 | esac 162 | fi 163 | 164 | # Collect all arguments for the java command, stacking in reverse order: 165 | # * args from the command line 166 | # * the main class name 167 | # * -classpath 168 | # * -D...appname settings 169 | # * --module-path (only if needed) 170 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 171 | 172 | # For Cygwin or MSYS, switch paths to Windows format before running java 173 | if "$cygwin" || "$msys" ; then 174 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 175 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 176 | 177 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 178 | 179 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 180 | for arg do 181 | if 182 | case $arg in #( 183 | -*) false ;; # don't mess with options #( 184 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 185 | [ -e "$t" ] ;; #( 186 | *) false ;; 187 | esac 188 | then 189 | arg=$( cygpath --path --ignore --mixed "$arg" ) 190 | fi 191 | # Roll the args list around exactly as many times as the number of 192 | # args, so each arg winds up back in the position where it started, but 193 | # possibly modified. 194 | # 195 | # NB: a `for` loop captures its iteration list before it begins, so 196 | # changing the positional parameters here affects neither the number of 197 | # iterations, nor the values presented in `arg`. 198 | shift # remove old arg 199 | set -- "$@" "$arg" # push replacement arg 200 | done 201 | fi 202 | 203 | 204 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 205 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 206 | 207 | # Collect all arguments for the java command: 208 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 209 | # and any embedded shellness will be escaped. 210 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 211 | # treated as '${Hostname}' itself on the command line. 212 | 213 | set -- \ 214 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 215 | -classpath "$CLASSPATH" \ 216 | -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ 217 | "$@" 218 | 219 | # Stop when "xargs" is not available. 220 | if ! command -v xargs >/dev/null 2>&1 221 | then 222 | die "xargs is not available" 223 | fi 224 | 225 | # Use "xargs" to parse quoted args. 226 | # 227 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 228 | # 229 | # In Bash we could simply go: 230 | # 231 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 232 | # set -- "${ARGS[@]}" "$@" 233 | # 234 | # but POSIX shell has neither arrays nor command substitution, so instead we 235 | # post-process each arg (as a line of input to sed) to backslash-escape any 236 | # character that might be a shell metacharacter, then use eval to reverse 237 | # that process (while maintaining the separation between arguments), and wrap 238 | # the whole thing up as a single "set" statement. 239 | # 240 | # This will of course break if any of these variables contains a newline or 241 | # an unmatched quote. 242 | # 243 | 244 | eval "set -- $( 245 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 246 | xargs -n1 | 247 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 248 | tr '\n' ' ' 249 | )" '"$@"' 250 | 251 | exec "$JAVACMD" "$@" 252 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/BiomeDumpTests.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonArray 4 | import com.google.gson.JsonElement 5 | import com.google.gson.JsonObject 6 | import de.snowii.extractor.Extractor 7 | import net.minecraft.registry.BuiltinRegistries 8 | import net.minecraft.registry.DynamicRegistryManager 9 | import net.minecraft.registry.Registry 10 | import net.minecraft.registry.RegistryKeys 11 | import net.minecraft.server.MinecraftServer 12 | import net.minecraft.util.math.ChunkPos 13 | import net.minecraft.world.HeightLimitView 14 | import net.minecraft.world.biome.source.* 15 | import net.minecraft.world.biome.source.util.MultiNoiseUtil.MultiNoiseSampler 16 | import net.minecraft.world.biome.source.util.MultiNoiseUtil.NoiseHypercube 17 | import net.minecraft.world.chunk.ChunkStatus 18 | import net.minecraft.world.chunk.ProtoChunk 19 | import net.minecraft.world.chunk.UpgradeData 20 | import net.minecraft.world.gen.WorldPresets 21 | import net.minecraft.world.gen.chunk.Blender 22 | import net.minecraft.world.gen.chunk.ChunkGeneratorSettings 23 | import net.minecraft.world.gen.chunk.ChunkNoiseSampler 24 | import net.minecraft.world.gen.chunk.GenerationShapeConfig 25 | import net.minecraft.world.gen.densityfunction.DensityFunction.EachApplier 26 | import net.minecraft.world.gen.densityfunction.DensityFunction.NoisePos 27 | import net.minecraft.world.gen.densityfunction.DensityFunctionTypes 28 | import net.minecraft.world.gen.noise.NoiseConfig 29 | import java.lang.reflect.Field 30 | import java.lang.reflect.Method 31 | 32 | class BiomeDumpTests : Extractor.Extractor { 33 | override fun fileName(): String = "biome_no_blend_no_beard_0.json" 34 | 35 | companion object { 36 | fun createMultiNoiseSampler(config: NoiseConfig, sampler: ChunkNoiseSampler): MultiNoiseSampler { 37 | var createMultiNoiseSampler: Method? = null 38 | for (m: Method in sampler.javaClass.declaredMethods) { 39 | if (m.name == "createMultiNoiseSampler") { 40 | m.trySetAccessible() 41 | createMultiNoiseSampler = m 42 | break 43 | } 44 | } 45 | 46 | val noiseSampler = createMultiNoiseSampler!!.invoke( 47 | sampler, 48 | config.noiseRouter, 49 | listOf() 50 | ) as MultiNoiseSampler 51 | 52 | return noiseSampler 53 | } 54 | } 55 | 56 | override fun extract(server: MinecraftServer): JsonElement { 57 | val topLevelJson = JsonArray() 58 | val seed = 0L 59 | 60 | val biomeRegistry = server.registryManager.getOrThrow(RegistryKeys.BIOME) 61 | 62 | // Overworld shape config 63 | val shape = GenerationShapeConfig(-64, 384, 1, 2) 64 | 65 | val lookup = BuiltinRegistries.createWrapperLookup() 66 | val wrapper = lookup.getOrThrow(RegistryKeys.CHUNK_GENERATOR_SETTINGS) 67 | val noiseParams = lookup.getOrThrow(RegistryKeys.NOISE_PARAMETERS) 68 | 69 | val ref = wrapper.getOrThrow(ChunkGeneratorSettings.OVERWORLD) 70 | val settings = ref.value() 71 | val config = NoiseConfig.create(settings, noiseParams, seed) 72 | 73 | 74 | val options = WorldPresets.getDefaultOverworldOptions(lookup) 75 | 76 | var biomeSource: BiomeSource? = null 77 | for (f: Field in options.chunkGenerator.javaClass.fields) { 78 | if (f.name == "biomeSource") { 79 | biomeSource = f.get(options.chunkGenerator) as BiomeSource 80 | } 81 | } 82 | 83 | println(biomeRegistry.javaClass) 84 | println(options.chunkGenerator.javaClass) 85 | 86 | for (x in 5..5) { 87 | for (z in 5..5) { 88 | val biomeData = JsonObject() 89 | biomeData.addProperty("x", x) 90 | biomeData.addProperty("z", z) 91 | 92 | val chunkPos = ChunkPos(x, z) 93 | val chunk = ProtoChunk( 94 | chunkPos, UpgradeData.NO_UPGRADE_DATA, 95 | HeightLimitView.create(options.chunkGenerator.minimumY, options.chunkGenerator.worldHeight), 96 | server.overworld.palettesFactory, null 97 | ) 98 | 99 | if (chunk.hasBelowZeroRetrogen()) { 100 | throw Exception("Chunk has below zero retrogen") 101 | } 102 | 103 | val testSampler = 104 | ChunkNoiseSampler( 105 | 16 / shape.horizontalCellBlockCount(), config, chunkPos.startX, chunkPos.startZ, 106 | shape, object : DensityFunctionTypes.Beardifying { 107 | override fun maxValue(): Double = 0.0 108 | override fun minValue(): Double = 0.0 109 | override fun sample(pos: NoisePos): Double = 0.0 110 | override fun fill(densities: DoubleArray, applier: EachApplier) { 111 | densities.fill(0.0) 112 | } 113 | }, settings, null, Blender.getNoBlending() 114 | ) 115 | val testNoiseSampler = createMultiNoiseSampler(config, testSampler) 116 | 117 | // We don't have retro gen and we don't want structures 118 | chunk.populateBiomes(biomeSource!!, testNoiseSampler) 119 | chunk.status = ChunkStatus.BIOMES 120 | 121 | val minBiomeY = BiomeCoords.fromBlock(chunk.bottomY) 122 | val maxBiomeY = BiomeCoords.fromBlock(chunk.topYInclusive) 123 | 124 | val data = JsonArray() 125 | for (biomeX in 0..3) { 126 | for (biomeZ in 0..3) { 127 | for (biomeY in minBiomeY..maxBiomeY) { 128 | val chunkData = JsonArray() 129 | 130 | val biome = chunk.getBiomeForNoiseGen(biomeX, biomeY, biomeZ) 131 | // Weird work-around because java 132 | val entry = biomeRegistry.get(biome.key.orElseThrow()) 133 | val id = biomeRegistry.getRawIdOrThrow(entry) 134 | 135 | chunkData.add(biomeX) 136 | chunkData.add(biomeY) 137 | chunkData.add(biomeZ) 138 | chunkData.add(id) 139 | 140 | data.add(chunkData) 141 | } 142 | } 143 | } 144 | 145 | biomeData.add("data", data) 146 | topLevelJson.add(biomeData) 147 | } 148 | } 149 | 150 | return topLevelJson 151 | } 152 | 153 | inner class MultiNoiseBiomeSourceTest : Extractor.Extractor { 154 | override fun fileName(): String = "multi_noise_biome_source_test.json" 155 | 156 | override fun extract(server: MinecraftServer): JsonElement { 157 | val registryManager: DynamicRegistryManager.Immutable = server.registryManager 158 | val multiNoiseRegistry: Registry = 159 | registryManager.getOrThrow(RegistryKeys.MULTI_NOISE_BIOME_SOURCE_PARAMETER_LIST) 160 | 161 | val overworldBiomeSource = MultiNoiseBiomeSource.create( 162 | multiNoiseRegistry.getOrThrow( 163 | MultiNoiseBiomeSourceParameterLists.OVERWORLD 164 | ) 165 | ) 166 | 167 | val seed = 0L 168 | val chunkPos = ChunkPos(0, 0) 169 | 170 | val lookup = BuiltinRegistries.createWrapperLookup() 171 | val wrapper = lookup.getOrThrow(RegistryKeys.CHUNK_GENERATOR_SETTINGS) 172 | val noiseParams = lookup.getOrThrow(RegistryKeys.NOISE_PARAMETERS) 173 | 174 | val ref = wrapper.getOrThrow(ChunkGeneratorSettings.OVERWORLD) 175 | val settings = ref.value() 176 | val config = NoiseConfig.create(settings, noiseParams, seed) 177 | 178 | // Overworld shape config 179 | val shape = GenerationShapeConfig(-64, 384, 1, 2) 180 | val testSampler = 181 | ChunkNoiseSampler( 182 | 16 / shape.horizontalCellBlockCount(), config, chunkPos.startX, chunkPos.startZ, 183 | shape, object : DensityFunctionTypes.Beardifying { 184 | override fun maxValue(): Double = 0.0 185 | override fun minValue(): Double = 0.0 186 | override fun sample(pos: NoisePos): Double = 0.0 187 | override fun fill(densities: DoubleArray, applier: EachApplier) { 188 | densities.fill(0.0) 189 | } 190 | }, settings, null, Blender.getNoBlending() 191 | ) 192 | 193 | val noiseSampler = createMultiNoiseSampler(config, testSampler) 194 | 195 | val topLevelJson = JsonArray() 196 | for (x in -50..50) { 197 | for (y in -20..50) { 198 | for (z in -50..50) { 199 | val biome = overworldBiomeSource.getBiome(x, y, z, noiseSampler) 200 | val id = server.registryManager.getOrThrow(RegistryKeys.BIOME).getRawId(biome.value()) 201 | 202 | val datum = JsonArray() 203 | datum.add(x) 204 | datum.add(y) 205 | datum.add(z) 206 | datum.add(id) 207 | 208 | topLevelJson.add(datum) 209 | } 210 | } 211 | } 212 | return topLevelJson 213 | } 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/Blocks.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonArray 4 | import com.google.gson.JsonElement 5 | import com.google.gson.JsonObject 6 | import com.mojang.serialization.JsonOps 7 | import de.snowii.extractor.Extractor 8 | import net.minecraft.block.Block 9 | import net.minecraft.block.ExperienceDroppingBlock 10 | import net.minecraft.block.SideShapeType 11 | import net.minecraft.loot.LootTable 12 | import net.minecraft.registry.Registries 13 | import net.minecraft.registry.RegistryKey 14 | import net.minecraft.registry.RegistryOps 15 | import net.minecraft.server.MinecraftServer 16 | import net.minecraft.util.math.BlockPos 17 | import net.minecraft.util.math.Box 18 | import net.minecraft.util.math.Direction; 19 | import net.minecraft.world.EmptyBlockView 20 | import java.util.* 21 | 22 | class Blocks : Extractor.Extractor { 23 | 24 | companion object { 25 | private const val AIR : Int = 1 shl 0 26 | private const val BURNABLE : Int = 1 shl 1 27 | private const val TOOL_REQUIRED : Int = 1 shl 2 28 | private const val SIDED_TRANSPARENCY: Int = 1 shl 3 29 | private const val REPLACEABLE : Int = 1 shl 4 30 | private const val IS_LIQUID : Int = 1 shl 5 31 | private const val IS_SOLID : Int = 1 shl 6 32 | private const val IS_FULL_CUBE : Int = 1 shl 7 33 | private const val IS_SOLID_BLOCK : Int = 1 shl 8 34 | private const val HAS_RANDOM_TICKS : Int = 1 shl 9 35 | 36 | private const val DOWN_SIDE_SOLID : Int = 1 shl 0; 37 | private const val UP_SIDE_SOLID : Int = 1 shl 1; 38 | private const val NORTH_SIDE_SOLID : Int = 1 shl 2; 39 | private const val SOUTH_SIDE_SOLID : Int = 1 shl 3; 40 | private const val WEST_SIDE_SOLID : Int = 1 shl 4; 41 | private const val EAST_SIDE_SOLID : Int = 1 shl 5; 42 | private const val DOWN_CENTER_SOLID : Int = 1 shl 6; 43 | private const val UP_CENTER_SOLID : Int = 1 shl 7; 44 | } 45 | 46 | override fun fileName(): String { 47 | return "blocks.json" 48 | } 49 | 50 | private fun getFlammableData(): Map> { 51 | val flammableData = mutableMapOf>() 52 | val fireBlock = net.minecraft.block.Blocks.FIRE as net.minecraft.block.FireBlock; 53 | for (block in Registries.BLOCK) { 54 | val defaultState = block.defaultState 55 | val spreadChance = fireBlock.getSpreadChance(defaultState) 56 | val burnChance = fireBlock.getBurnChance(defaultState) 57 | if (spreadChance > 0 || burnChance > 0) { 58 | flammableData[block] = Pair(spreadChance, burnChance) 59 | } 60 | } 61 | 62 | return flammableData 63 | } 64 | 65 | override fun extract(server: MinecraftServer): JsonElement { 66 | val topLevelJson = JsonObject() 67 | 68 | val blocksJson = JsonArray() 69 | 70 | val shapes: LinkedHashMap = LinkedHashMap() 71 | 72 | val flammableData = getFlammableData() 73 | 74 | for (block in Registries.BLOCK) { 75 | val blockJson = JsonObject() 76 | blockJson.addProperty("id", Registries.BLOCK.getRawId(block)) 77 | blockJson.addProperty("name", Registries.BLOCK.getId(block).path) 78 | blockJson.addProperty("translation_key", block.translationKey) 79 | blockJson.addProperty("slipperiness", block.slipperiness) 80 | blockJson.addProperty("velocity_multiplier", block.velocityMultiplier) 81 | blockJson.addProperty("jump_velocity_multiplier", block.jumpVelocityMultiplier) 82 | blockJson.addProperty("hardness", block.hardness) 83 | blockJson.addProperty("blast_resistance", block.blastResistance) 84 | blockJson.addProperty("item_id", Registries.ITEM.getRawId(block.asItem())) 85 | 86 | // Add flammable data if this block is flammable 87 | flammableData[block]?.let { (spreadChance, burnChance) -> 88 | val flammableJson = JsonObject() 89 | flammableJson.addProperty("spread_chance", spreadChance) 90 | flammableJson.addProperty("burn_chance", burnChance) 91 | blockJson.add("flammable", flammableJson) 92 | } 93 | 94 | if (block is ExperienceDroppingBlock) { 95 | blockJson.add( 96 | "experience", ExperienceDroppingBlock.CODEC.codec().encodeStart( 97 | RegistryOps.of(JsonOps.INSTANCE, server.registryManager), 98 | block, 99 | ).getOrThrow() 100 | ) 101 | } 102 | if (block.lootTableKey.isPresent) { 103 | val table = server.reloadableRegistries 104 | .getLootTable(block.lootTableKey.get() as RegistryKey) 105 | blockJson.add( 106 | "loot_table", LootTable::CODEC.get().encodeStart( 107 | RegistryOps.of(JsonOps.INSTANCE, server.registryManager), 108 | table 109 | ).getOrThrow() 110 | ) 111 | } 112 | val propsJson = JsonArray() 113 | for (prop in block.stateManager.properties) { 114 | // Use the hashcode to map to a property later; the property names are not unique 115 | propsJson.add(prop.hashCode()) 116 | } 117 | blockJson.add("properties", propsJson) 118 | 119 | val statesJson = JsonArray() 120 | for (state in block.stateManager.states) { 121 | val stateJson = JsonObject() 122 | var stateFlags = 0 123 | var sideFlags = 0 124 | 125 | if (state.isAir) stateFlags = stateFlags or AIR 126 | if (state.isBurnable) stateFlags = stateFlags or BURNABLE 127 | if (state.isToolRequired) stateFlags = stateFlags or TOOL_REQUIRED 128 | if (state.hasSidedTransparency()) stateFlags = stateFlags or SIDED_TRANSPARENCY 129 | if (state.isReplaceable) stateFlags = stateFlags or REPLACEABLE 130 | if (state.isLiquid) stateFlags = stateFlags or IS_LIQUID 131 | if (state.isSolid) stateFlags = stateFlags or IS_SOLID 132 | if (state.isFullCube(EmptyBlockView.INSTANCE, BlockPos.ORIGIN)) stateFlags = stateFlags or IS_FULL_CUBE 133 | if (state.isSolidBlock(EmptyBlockView.INSTANCE, BlockPos.ORIGIN)) stateFlags = stateFlags or IS_SOLID_BLOCK 134 | if (state.hasRandomTicks()) stateFlags = stateFlags or HAS_RANDOM_TICKS 135 | 136 | 137 | if (state.isSideSolidFullSquare(EmptyBlockView.INSTANCE, BlockPos.ORIGIN, Direction.DOWN)) sideFlags = sideFlags or DOWN_SIDE_SOLID 138 | if (state.isSideSolidFullSquare(EmptyBlockView.INSTANCE, BlockPos.ORIGIN, Direction.UP)) sideFlags = sideFlags or UP_SIDE_SOLID 139 | if (state.isSideSolidFullSquare(EmptyBlockView.INSTANCE, BlockPos.ORIGIN, Direction.NORTH)) sideFlags = sideFlags or NORTH_SIDE_SOLID 140 | if (state.isSideSolidFullSquare(EmptyBlockView.INSTANCE, BlockPos.ORIGIN, Direction.SOUTH)) sideFlags = sideFlags or SOUTH_SIDE_SOLID 141 | if (state.isSideSolidFullSquare(EmptyBlockView.INSTANCE, BlockPos.ORIGIN, Direction.WEST)) sideFlags = sideFlags or WEST_SIDE_SOLID 142 | if (state.isSideSolidFullSquare(EmptyBlockView.INSTANCE, BlockPos.ORIGIN, Direction.EAST)) sideFlags = sideFlags or EAST_SIDE_SOLID 143 | if (state.isSideSolid(EmptyBlockView.INSTANCE, BlockPos.ORIGIN, Direction.DOWN, SideShapeType.CENTER)) sideFlags = sideFlags or DOWN_CENTER_SOLID 144 | if (state.isSideSolid(EmptyBlockView.INSTANCE, BlockPos.ORIGIN, Direction.UP, SideShapeType.CENTER)) sideFlags = sideFlags or UP_CENTER_SOLID 145 | 146 | stateJson.addProperty("id", Block.getRawIdFromState(state)) 147 | stateJson.addProperty("state_flags", stateFlags and 0xFFFF) 148 | stateJson.addProperty("side_flags", sideFlags and 0xFF) 149 | stateJson.addProperty("instrument", state.instrument.name) 150 | stateJson.addProperty("luminance", state.luminance) 151 | stateJson.addProperty("piston_behavior", state.pistonBehavior.name) 152 | stateJson.addProperty("hardness", state.getHardness(null, null)) 153 | 154 | stateJson.addProperty("opacity", state.opacity) 155 | 156 | if (block.defaultState == state) { 157 | blockJson.addProperty("default_state_id", Block.getRawIdFromState(state)) 158 | } 159 | 160 | val collisionShapeIdxsJson = JsonArray() 161 | for (box in state.getCollisionShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN).boundingBoxes) { 162 | val idx = shapes.putIfAbsent(box, shapes.size) 163 | collisionShapeIdxsJson.add(Objects.requireNonNullElseGet(idx) { shapes.size - 1 }) 164 | } 165 | 166 | stateJson.add("collision_shapes", collisionShapeIdxsJson) 167 | 168 | val outlineShapeIdxsJson = JsonArray() 169 | for (box in state.getOutlineShape(EmptyBlockView.INSTANCE, BlockPos.ORIGIN).boundingBoxes) { 170 | val idx = shapes.putIfAbsent(box, shapes.size) 171 | outlineShapeIdxsJson.add(Objects.requireNonNullElseGet(idx) { shapes.size - 1 }) 172 | } 173 | 174 | stateJson.add("outline_shapes", outlineShapeIdxsJson) 175 | 176 | for (blockEntity in Registries.BLOCK_ENTITY_TYPE) { 177 | if (blockEntity.supports(state)) { 178 | stateJson.addProperty("block_entity_type", Registries.BLOCK_ENTITY_TYPE.getRawId(blockEntity)) 179 | } 180 | } 181 | 182 | statesJson.add(stateJson) 183 | } 184 | blockJson.add("states", statesJson) 185 | 186 | blocksJson.add(blockJson) 187 | } 188 | 189 | val blockEntitiesJson = JsonArray() 190 | for (blockEntity in Registries.BLOCK_ENTITY_TYPE) { 191 | blockEntitiesJson.add(Registries.BLOCK_ENTITY_TYPE.getId(blockEntity)!!.path) 192 | } 193 | 194 | val shapesJson = JsonArray() 195 | for (shape in shapes.keys) { 196 | val shapeJson = JsonObject() 197 | val min = JsonArray() 198 | min.add(shape.minX) 199 | min.add(shape.minY) 200 | min.add(shape.minZ) 201 | val max = JsonArray() 202 | max.add(shape.maxX) 203 | max.add(shape.maxY) 204 | max.add(shape.maxZ) 205 | shapeJson.add("min", min) 206 | shapeJson.add("max", max) 207 | shapesJson.add(shapeJson) 208 | } 209 | 210 | topLevelJson.add("block_entity_types", blockEntitiesJson) 211 | topLevelJson.add("shapes", shapesJson) 212 | topLevelJson.add("blocks", blocksJson) 213 | 214 | return topLevelJson 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /src/main/kotlin/de/snowii/extractor/extractors/ChunkDumpTests.kt: -------------------------------------------------------------------------------- 1 | package de.snowii.extractor.extractors 2 | 3 | import com.google.gson.JsonArray 4 | import com.google.gson.JsonElement 5 | import de.snowii.extractor.Extractor 6 | import net.minecraft.SharedConstants 7 | import net.minecraft.block.Block 8 | import net.minecraft.block.BlockState 9 | import net.minecraft.block.Blocks 10 | import net.minecraft.registry.BuiltinRegistries 11 | import net.minecraft.registry.RegistryKeys 12 | import net.minecraft.server.MinecraftServer 13 | import net.minecraft.util.math.BlockPos 14 | import net.minecraft.util.math.ChunkPos 15 | import net.minecraft.world.HeightLimitView 16 | import net.minecraft.world.Heightmap 17 | import net.minecraft.world.biome.source.BiomeAccess 18 | import net.minecraft.world.biome.source.BiomeSource 19 | import net.minecraft.world.biome.source.util.MultiNoiseUtil.MultiNoiseSampler 20 | import net.minecraft.world.biome.source.util.MultiNoiseUtil.NoiseHypercube 21 | import net.minecraft.world.chunk.Chunk 22 | import net.minecraft.world.chunk.ChunkStatus 23 | import net.minecraft.world.chunk.ProtoChunk 24 | import net.minecraft.world.chunk.UpgradeData 25 | import net.minecraft.world.gen.HeightContext 26 | import net.minecraft.world.gen.WorldPresets 27 | import net.minecraft.world.gen.chunk.* 28 | import net.minecraft.world.gen.densityfunction.DensityFunction 29 | import net.minecraft.world.gen.densityfunction.DensityFunction.* 30 | import net.minecraft.world.gen.densityfunction.DensityFunctionTypes 31 | import net.minecraft.world.gen.densityfunction.DensityFunctionTypes.RegistryEntryHolder 32 | import net.minecraft.world.gen.noise.NoiseConfig 33 | import java.lang.reflect.Field 34 | import java.lang.reflect.Method 35 | import kotlin.reflect.KFunction 36 | import kotlin.reflect.full.declaredFunctions 37 | import kotlin.system.exitProcess 38 | 39 | class ChunkDumpTests { 40 | 41 | companion object { 42 | private fun createFluidLevelSampler(settings: ChunkGeneratorSettings): AquiferSampler.FluidLevelSampler { 43 | val fluidLevel = AquiferSampler.FluidLevel(-54, Blocks.LAVA.defaultState) 44 | val i = settings.seaLevel() 45 | val fluidLevel2 = AquiferSampler.FluidLevel(i, settings.defaultFluid()) 46 | return AquiferSampler.FluidLevelSampler { _, y, _ -> if (y < Math.min(-54, i)) fluidLevel else fluidLevel2 } 47 | } 48 | 49 | private fun getIndex(config: GenerationShapeConfig, x: Int, y: Int, z: Int): Int { 50 | if (x < 0 || y < 0 || z < 0) { 51 | println("Bad local pos") 52 | exitProcess(1) 53 | } 54 | return config.height() * 16 * x + 16 * y + z 55 | } 56 | 57 | private fun populateNoise( 58 | settings: ChunkGeneratorSettings, 59 | chunkNoiseSampler: ChunkNoiseSampler, 60 | shapeConfig: GenerationShapeConfig, 61 | chunk: Chunk, 62 | ): Chunk { 63 | var sampleBlockState: KFunction? = null 64 | for (method: KFunction<*> in chunkNoiseSampler::class.declaredFunctions) { 65 | if (method.name == "sampleBlockState") { 66 | sampleBlockState = method as KFunction 67 | } 68 | } 69 | 70 | val heightmap = chunk.getHeightmap(Heightmap.Type.OCEAN_FLOOR_WG) 71 | val heightmap2 = chunk.getHeightmap(Heightmap.Type.WORLD_SURFACE_WG) 72 | val chunkPos = chunk.pos 73 | val i = chunkPos.startX 74 | val j = chunkPos.startZ 75 | val aquiferSampler = chunkNoiseSampler.aquiferSampler 76 | chunkNoiseSampler.sampleStartDensity() 77 | val mutable = BlockPos.Mutable() 78 | val k = shapeConfig.horizontalCellBlockCount() 79 | val l = shapeConfig.verticalCellBlockCount() 80 | val m = 16 / k 81 | val n = 16 / k 82 | 83 | val cellHeight = shapeConfig.height() / l 84 | val minimumCellY = shapeConfig.minimumY() / l 85 | 86 | for (o in 0.. in sampler::class.declaredFunctions) { 161 | if (method.name.equals("sampleBlockState")) { 162 | sampler.sampleStartDensity() 163 | val k = config.horizontalCellBlockCount() 164 | val l = config.verticalCellBlockCount() 165 | 166 | val m = 16 / k 167 | val n = 16 / k 168 | 169 | val cellHeight = config.height() / l 170 | val minimumCellY = config.minimumY() / l 171 | 172 | for (o in 0..) : DensityFunctionVisitor { 213 | override fun apply(densityFunction: DensityFunction?): DensityFunction { 214 | when (densityFunction) { 215 | is DensityFunctionTypes.Wrapper -> { 216 | val name = densityFunction.type().toString() 217 | if (wrappersToKeep.contains(name)) { 218 | return densityFunction 219 | } 220 | return this.apply(densityFunction.wrapped()) 221 | } 222 | 223 | is RegistryEntryHolder -> { 224 | return this.apply(densityFunction.function.value()) 225 | } 226 | 227 | else -> return densityFunction!! 228 | } 229 | } 230 | } 231 | 232 | class WrapperValidateVisitor(private val wrappersToKeep: Iterable) : DensityFunctionVisitor { 233 | override fun apply(densityFunction: DensityFunction?): DensityFunction { 234 | when (densityFunction) { 235 | is DensityFunctionTypes.Wrapper -> { 236 | val name = densityFunction.type().toString() 237 | if (wrappersToKeep.contains(name)) { 238 | return densityFunction 239 | } 240 | throw Exception(name + "is still in the function!") 241 | } 242 | 243 | is RegistryEntryHolder -> { 244 | return this.apply(densityFunction.function.value()) 245 | } 246 | 247 | else -> return densityFunction!! 248 | } 249 | } 250 | } 251 | 252 | // Available: 253 | //Interpolated 254 | //CacheOnce 255 | //FlatCache 256 | //Cache2D 257 | // 258 | //CellCache is only added inside the ChunkSampler itself so it cannot be removed and will always be in the function 259 | private fun removeWrappers(config: NoiseConfig, wrappersToKeep: Iterable) { 260 | val noiseRouter = config.noiseRouter.apply(WrapperRemoverVisitor(wrappersToKeep)) 261 | for (field in config.javaClass.declaredFields) { 262 | if (field.name.equals("noiseRouter")) { 263 | field.trySetAccessible() 264 | field.set(config, noiseRouter) 265 | return 266 | } 267 | } 268 | throw Exception("Failed to replace router") 269 | } 270 | 271 | fun createMultiNoiseSampler(config: NoiseConfig, sampler: ChunkNoiseSampler): MultiNoiseSampler { 272 | var createMultiNoiseSampler: Method? = null 273 | for (m: Method in sampler.javaClass.declaredMethods) { 274 | if (m.name == "createMultiNoiseSampler") { 275 | m.trySetAccessible() 276 | createMultiNoiseSampler = m 277 | break 278 | } 279 | } 280 | 281 | val noiseSampler = createMultiNoiseSampler!!.invoke( 282 | sampler, 283 | config.noiseRouter, 284 | listOf() 285 | ) as MultiNoiseSampler 286 | 287 | return noiseSampler 288 | } 289 | } 290 | 291 | internal class SurfaceDump( 292 | private val filename: String, 293 | private val seed: Long, 294 | private val chunkX: Int, 295 | private val chunkZ: Int, 296 | ) : Extractor.Extractor { 297 | override fun fileName(): String = this.filename 298 | 299 | override fun extract(server: MinecraftServer): JsonElement { 300 | val biomeRegistry = server.registryManager.getOrThrow(RegistryKeys.BIOME) 301 | 302 | val chunkPos = ChunkPos(this.chunkX, this.chunkZ) 303 | 304 | val lookup = BuiltinRegistries.createWrapperLookup() 305 | val wrapper = lookup.getOrThrow(RegistryKeys.CHUNK_GENERATOR_SETTINGS) 306 | val noiseParams = lookup.getOrThrow(RegistryKeys.NOISE_PARAMETERS) 307 | 308 | val ref = wrapper.getOrThrow(ChunkGeneratorSettings.OVERWORLD) 309 | val settings = ref.value() 310 | val config = NoiseConfig.create(settings, noiseParams, seed) 311 | 312 | val options = WorldPresets.getDefaultOverworldOptions(lookup) 313 | var biomeSource: BiomeSource? = null 314 | for (f: Field in options.chunkGenerator.javaClass.fields) { 315 | if (f.name == "biomeSource") { 316 | biomeSource = f.get(options.chunkGenerator) as BiomeSource 317 | } 318 | } 319 | 320 | // Overworld shape config 321 | val shape = GenerationShapeConfig(-64, 384, 1, 2) 322 | val testSampler = 323 | ChunkNoiseSampler( 324 | 16 / shape.horizontalCellBlockCount(), config, chunkPos.startX, chunkPos.startZ, 325 | shape, object : DensityFunctionTypes.Beardifying { 326 | override fun maxValue(): Double = 0.0 327 | override fun minValue(): Double = 0.0 328 | override fun sample(pos: NoisePos): Double = 0.0 329 | override fun fill(densities: DoubleArray, applier: EachApplier) { 330 | densities.fill(0.0) 331 | } 332 | }, settings, createFluidLevelSampler(settings), Blender.getNoBlending() 333 | ) 334 | 335 | val chunk = ProtoChunk( 336 | chunkPos, UpgradeData.NO_UPGRADE_DATA, 337 | HeightLimitView.create(options.chunkGenerator.minimumY, options.chunkGenerator.worldHeight), 338 | server.overworld.palettesFactory, null 339 | ) 340 | 341 | val biomeNoiseSampler = createMultiNoiseSampler(config, testSampler) 342 | chunk.populateBiomes(biomeSource!!, biomeNoiseSampler) 343 | chunk.status = ChunkStatus.BIOMES 344 | 345 | populateNoise(settings, testSampler, shape, chunk) 346 | chunk.status = ChunkStatus.NOISE 347 | 348 | val biomeMixer = BiomeAccess(chunk, BiomeAccess.hashSeed(seed)) 349 | val heightContext = HeightContext(options.chunkGenerator, chunk) 350 | config.surfaceBuilder.buildSurface( 351 | config, 352 | biomeMixer, 353 | biomeRegistry, 354 | settings.usesLegacyRandom, 355 | heightContext, 356 | chunk, 357 | testSampler, 358 | settings.surfaceRule 359 | ) 360 | chunk.status = ChunkStatus.SURFACE 361 | 362 | val result = IntArray(16 * 16 * chunk.height) 363 | for (x in 0..15) { 364 | for (y in chunk.bottomY..chunk.topYInclusive) { 365 | for (z in 0..15) { 366 | val pos = BlockPos(x, y, z) 367 | val blockState = chunk.getBlockState(pos) 368 | val index = getIndex(shape, x, y - chunk.bottomY, z) 369 | result[index] = Block.getRawIdFromState(blockState) 370 | } 371 | } 372 | } 373 | 374 | val topLevelJson = JsonArray() 375 | result.forEach { state -> 376 | topLevelJson.add(state) 377 | } 378 | return topLevelJson 379 | } 380 | } 381 | 382 | internal class NoiseDump( 383 | private val filename: String, 384 | private val seed: Long, 385 | private val chunkX: Int, 386 | private val chunkZ: Int, 387 | private val allowedWrappers: Iterable 388 | ) : Extractor.Extractor { 389 | override fun fileName(): String = this.filename 390 | 391 | // Dumps a chunk to an array of block state ids 392 | override fun extract(server: MinecraftServer): JsonElement { 393 | val topLevelJson = JsonArray() 394 | val chunkPos = ChunkPos(this.chunkX, this.chunkZ) 395 | 396 | val lookup = BuiltinRegistries.createWrapperLookup() 397 | val wrapper = lookup.getOrThrow(RegistryKeys.CHUNK_GENERATOR_SETTINGS) 398 | val noiseParams = lookup.getOrThrow(RegistryKeys.NOISE_PARAMETERS) 399 | 400 | val ref = wrapper.getOrThrow(ChunkGeneratorSettings.OVERWORLD) 401 | val settings = ref.value() 402 | val config = NoiseConfig.create(settings, noiseParams, seed) 403 | // Always have cellcache wrappers 404 | removeWrappers(config, this.allowedWrappers) 405 | config.noiseRouter.apply(WrapperValidateVisitor(this.allowedWrappers)) 406 | 407 | // Overworld shape config 408 | val shape = GenerationShapeConfig(-64, 384, 1, 2) 409 | val testSampler = 410 | ChunkNoiseSampler( 411 | 16 / shape.horizontalCellBlockCount(), config, chunkPos.startX, chunkPos.startZ, 412 | shape, object : DensityFunctionTypes.Beardifying { 413 | override fun maxValue(): Double = 0.0 414 | override fun minValue(): Double = 0.0 415 | override fun sample(pos: NoisePos): Double = 0.0 416 | override fun fill(densities: DoubleArray, applier: EachApplier) { 417 | densities.fill(0.0) 418 | } 419 | }, settings, createFluidLevelSampler(settings), Blender.getNoBlending() 420 | ) 421 | 422 | val data = dumpPopulateNoise(chunkPos.startX, chunkPos.startZ, testSampler, shape, settings) 423 | data?.forEach { state -> 424 | topLevelJson.add(state) 425 | } 426 | 427 | return topLevelJson 428 | } 429 | } 430 | } --------------------------------------------------------------------------------