From 9fe1d1417669ecfadfdc6f9df7b56130e0f55b73 Mon Sep 17 00:00:00 2001 From: Joniii11 <88550325+Joniii11@users.noreply.github.com> Date: Sun, 27 Sep 2026 02:35:28 +0200 Subject: [PATCH 1/9] Update extractor for Minecraft 26.3-pre-3 --- gradle.properties | 8 +- settings.gradle | 2 +- .../steelextractor/mixin/AxeItemAccessor.java | 16 - .../mixin/ChunkAccessBiomeCacheMixin.java | 8 +- .../steelextractor/mixin/ChunkStepMixin.java | 4 +- .../MatchingBlocksPredicateAccessor.java | 13 + .../mixin/TerrainPhaseCaptureMixin.java | 69 ++++ .../com/steelextractor/SteelExtractor.kt | 108 ++++--- .../steelextractor/extractors/BiomeHashes.kt | 17 +- .../extractors/BlockEntities.kt | 24 +- .../com/steelextractor/extractors/Blocks.kt | 114 ++++--- .../extractors/BuiltInRegistryKeys.kt | 138 ++++---- .../extractors/ChunkStageHashes.kt | 4 +- .../extractors/ComponentHashes.kt | 297 ++++++++++++++++++ .../extractors/DataComponents.kt | 30 +- .../com/steelextractor/extractors/Entities.kt | 49 +-- .../com/steelextractor/extractors/Items.kt | 41 +-- .../steelextractor/extractors/Strippables.kt | 43 ++- .../com/steelextractor/extractors/Tags.kt | 12 +- .../extractors/WorldgenNoiseSamples.kt | 64 ++++ .../resources/steel-extractor.mixins.json | 10 +- 21 files changed, 772 insertions(+), 299 deletions(-) delete mode 100644 src/main/java/com/steelextractor/mixin/AxeItemAccessor.java create mode 100644 src/main/java/com/steelextractor/mixin/MatchingBlocksPredicateAccessor.java create mode 100644 src/main/java/com/steelextractor/mixin/TerrainPhaseCaptureMixin.java create mode 100644 src/main/kotlin/com/steelextractor/extractors/ComponentHashes.kt create mode 100644 src/main/kotlin/com/steelextractor/extractors/WorldgenNoiseSamples.kt diff --git a/gradle.properties b/gradle.properties index 984f352..1f44fe0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,13 +5,13 @@ org.gradle.parallel=true org.gradle.configuration-cache=false # Fabric Properties # check these on https://fabricmc.net/develop -minecraft_version=26.2 -loader_version=0.19.3 -loom_version=1.16-SNAPSHOT +minecraft_version=26.3-pre-3 +loader_version=0.19.5 +loom_version=1.17-SNAPSHOT fabric_kotlin_version=1.13.12+kotlin.2.4.0 # Mod Properties mod_version=1.0.0 maven_group=com.steelextractor archives_base_name=steel-extractor # Dependencies -fabric_version=0.152.0+26.2 +fabric_version=0.159.3+26.3 diff --git a/settings.gradle b/settings.gradle index 662d147..dc54e65 100644 --- a/settings.gradle +++ b/settings.gradle @@ -10,4 +10,4 @@ pluginManagement { } plugins { id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' -} \ No newline at end of file +} diff --git a/src/main/java/com/steelextractor/mixin/AxeItemAccessor.java b/src/main/java/com/steelextractor/mixin/AxeItemAccessor.java deleted file mode 100644 index ef4b023..0000000 --- a/src/main/java/com/steelextractor/mixin/AxeItemAccessor.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.steelextractor.mixin; - -import net.minecraft.world.item.AxeItem; -import net.minecraft.world.level.block.Block; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -import java.util.Map; - -@Mixin(AxeItem.class) -public interface AxeItemAccessor { - @Accessor("STRIPPABLES") - static Map getStrippables() { - throw new AssertionError(); - } -} diff --git a/src/main/java/com/steelextractor/mixin/ChunkAccessBiomeCacheMixin.java b/src/main/java/com/steelextractor/mixin/ChunkAccessBiomeCacheMixin.java index 67741ed..30efebb 100644 --- a/src/main/java/com/steelextractor/mixin/ChunkAccessBiomeCacheMixin.java +++ b/src/main/java/com/steelextractor/mixin/ChunkAccessBiomeCacheMixin.java @@ -2,8 +2,6 @@ import com.steelextractor.BiomeCacheReset; import net.minecraft.world.level.biome.BiomeResolver; -import net.minecraft.world.level.biome.Climate; -import net.minecraft.world.level.biome.MultiNoiseBiomeSource; import net.minecraft.world.level.chunk.ChunkAccess; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -18,9 +16,7 @@ public abstract class ChunkAccessBiomeCacheMixin { * its R-tree warm-start within the chunk, but not across chunk boundaries. */ @Inject(method = "fillBiomesFromNoise", at = @At("HEAD")) - private void resetBiomeCacheForChunk(BiomeResolver biomeResolver, Climate.Sampler sampler, CallbackInfo ci) { - if (biomeResolver instanceof MultiNoiseBiomeSource) { - BiomeCacheReset.markNeeded(); - } + private void resetBiomeCacheForChunk(BiomeResolver biomeResolver, CallbackInfo ci) { + BiomeCacheReset.markNeeded(); } } diff --git a/src/main/java/com/steelextractor/mixin/ChunkStepMixin.java b/src/main/java/com/steelextractor/mixin/ChunkStepMixin.java index 14ae014..cf98261 100644 --- a/src/main/java/com/steelextractor/mixin/ChunkStepMixin.java +++ b/src/main/java/com/steelextractor/mixin/ChunkStepMixin.java @@ -20,9 +20,7 @@ public class ChunkStepMixin { private static final Set IMMEDIATE_HASH_STAGES = Set.of( - ChunkStatus.NOISE, - ChunkStatus.SURFACE, - ChunkStatus.CARVERS + ChunkStatus.TERRAIN ); @Shadow diff --git a/src/main/java/com/steelextractor/mixin/MatchingBlocksPredicateAccessor.java b/src/main/java/com/steelextractor/mixin/MatchingBlocksPredicateAccessor.java new file mode 100644 index 0000000..db335f4 --- /dev/null +++ b/src/main/java/com/steelextractor/mixin/MatchingBlocksPredicateAccessor.java @@ -0,0 +1,13 @@ +package com.steelextractor.mixin; + +import net.minecraft.core.HolderSet; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.levelgen.blockpredicates.MatchingBlocksPredicate; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(MatchingBlocksPredicate.class) +public interface MatchingBlocksPredicateAccessor { + @Accessor("blocks") + HolderSet getBlocks(); +} diff --git a/src/main/java/com/steelextractor/mixin/TerrainPhaseCaptureMixin.java b/src/main/java/com/steelextractor/mixin/TerrainPhaseCaptureMixin.java new file mode 100644 index 0000000..4a0f392 --- /dev/null +++ b/src/main/java/com/steelextractor/mixin/TerrainPhaseCaptureMixin.java @@ -0,0 +1,69 @@ +package com.steelextractor.mixin; + +import com.steelextractor.BlockHashResult; +import com.steelextractor.ChunkStageHashStorage; +import net.minecraft.core.Holder; +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; +import net.minecraft.world.level.levelgen.NoiseChunk; +import net.minecraft.world.level.levelgen.RandomState; +import net.minecraft.world.level.levelgen.blending.Blender; +import net.minecraft.world.level.levelgen.material.rule.MaterialRule; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.Arrays; +import java.util.Set; + +@Mixin(NoiseBasedChunkGenerator.class) +public class TerrainPhaseCaptureMixin { + @Inject(method = "buildSurface", at = @At("RETURN")) + private void captureSurface( + ChunkAccess chunk, + NoiseChunk noiseChunk, + RandomState randomState, + BiomeManager biomeManager, + Set> possibleBiomes, + MaterialRule materialRule, + CallbackInfo ci + ) { + capture(chunk, "minecraft:surface"); + } + + @Inject(method = "generateCarvers", at = @At("RETURN")) + private void captureCarvers( + ChunkAccess chunk, + Blender blender, + NoiseChunk noiseChunk, + RandomState randomState, + BiomeManager biomeManager, + WorldGenRegion carverBiomeRegion, + MaterialRule materialRule, + CallbackInfo ci + ) { + capture(chunk, "minecraft:carvers"); + } + + private static void capture(ChunkAccess chunk, String stage) { + String dimension = ChunkStageHashStorage.INSTANCE.getCurrentDimension(); + if (!ChunkStageHashStorage.INSTANCE.isTracking(chunk.getPos(), dimension)) { + return; + } + + LevelChunkSection[] sections = chunk.getSections(); + if (ChunkStageHashStorage.INSTANCE.getEnableBinaryDump()) { + BlockHashResult result = ChunkStageHashStorage.INSTANCE.computeBlockHashWithData(Arrays.asList(sections)); + ChunkStageHashStorage.INSTANCE.storeHash(chunk.getPos(), dimension, stage, result.getHash()); + ChunkStageHashStorage.INSTANCE.storeBlockData(chunk.getPos(), dimension, stage, result.getSectionData()); + } else { + String hash = ChunkStageHashStorage.INSTANCE.computeBlockHash(Arrays.asList(sections)); + ChunkStageHashStorage.INSTANCE.storeHash(chunk.getPos(), dimension, stage, hash); + } + } +} diff --git a/src/main/kotlin/com/steelextractor/SteelExtractor.kt b/src/main/kotlin/com/steelextractor/SteelExtractor.kt index 77fe94e..bb631bf 100644 --- a/src/main/kotlin/com/steelextractor/SteelExtractor.kt +++ b/src/main/kotlin/com/steelextractor/SteelExtractor.kt @@ -4,17 +4,16 @@ import com.google.gson.GsonBuilder import com.google.gson.JsonElement import com.steelextractor.extractors.Attributes import com.steelextractor.extractors.Classes +import com.steelextractor.extractors.ComponentHashes +import com.steelextractor.extractors.DataComponents import com.steelextractor.extractors.BlockEntities import com.steelextractor.extractors.Blocks import com.steelextractor.extractors.Entities -import com.steelextractor.extractors.EntityVariantRegistries import com.steelextractor.extractors.EntityEvents -import com.steelextractor.extractors.DataComponents import com.steelextractor.extractors.Fluids import com.steelextractor.extractors.GameRulesExtractor import com.steelextractor.extractors.Items import com.steelextractor.extractors.ParticleTypeRegistryExtractor -import com.steelextractor.extractors.PositionSourceTypeRegistryExtractor import com.steelextractor.extractors.MenuTypes import com.steelextractor.extractors.MobEffects import com.steelextractor.extractors.Packets @@ -24,24 +23,30 @@ import com.steelextractor.extractors.SoundTypes import com.steelextractor.extractors.MultiNoiseBiomeParameters import com.steelextractor.extractors.BiomeHashes import com.steelextractor.extractors.VillagerProfessionRegistryExtractor -import com.steelextractor.extractors.MapDecorationTypeRegistryExtractor import com.steelextractor.extractors.VillagerTypeRegistryExtractor import com.steelextractor.extractors.CandleCakes import com.steelextractor.extractors.ChunkStageHashes +import com.steelextractor.extractors.DecoratedPotPatternRegistryExtractor import com.steelextractor.extractors.CustomStatRegistryExtractor import com.steelextractor.extractors.Commands +import com.steelextractor.extractors.EntityVariantRegistries +import com.steelextractor.extractors.MapDecorationTypeRegistryExtractor +import com.steelextractor.extractors.PositionSourceTypeRegistryExtractor +import com.steelextractor.extractors.StatTypeRegistryExtractor import com.steelextractor.extractors.GameEvents import com.steelextractor.extractors.Weathering import com.steelextractor.extractors.Strippables import net.minecraft.resources.ResourceKey +import net.minecraft.server.level.ChunkLevel import net.minecraft.server.level.ServerLevel +import net.minecraft.server.level.Ticket +import net.minecraft.server.level.TicketType import net.minecraft.world.level.ChunkPos import net.minecraft.world.level.Level import net.minecraft.world.level.chunk.ChunkAccess import net.minecraft.world.level.chunk.status.ChunkStatus import com.steelextractor.extractors.PoiTypesExtractor import com.steelextractor.extractors.Potions -import com.steelextractor.extractors.StatTypeRegistryExtractor import com.steelextractor.extractors.StructureStarts import com.steelextractor.extractors.Tags import com.steelextractor.extractors.Waxables @@ -59,17 +64,22 @@ import java.nio.file.Path import java.nio.file.Paths import java.util.concurrent.CompletableFuture import kotlin.random.Random -import kotlin.system.exitProcess import kotlin.system.measureTimeMillis object SteelExtractor : ModInitializer { private val logger = LoggerFactory.getLogger("steel-extractor") /** Set to false to skip chunk generation and chunk stage hash extraction. */ - private val ENABLE_CHUNK_EXTRACTION = envFlag("STEEL_EXTRACTOR_ENABLE_CHUNK_EXTRACTION") + private val ENABLE_CHUNK_EXTRACTION = envFlag( + "STEEL_EXTRACTOR_ENABLE_CHUNK_EXTRACTION", + defaultValue = true + ) /** Set to false to skip storing per-chunk block data in memory and writing binary dump files. */ - private val ENABLE_BINARY_DUMP = envFlag("STEEL_EXTRACTOR_ENABLE_BINARY_DUMP") + private val ENABLE_BINARY_DUMP = envFlag( + "STEEL_EXTRACTOR_ENABLE_BINARY_DUMP", + defaultValue = true + ) /** Sampling parameters: place random CLUSTER_SIZE x CLUSTER_SIZE clusters within a SAMPLE_HALF_RANGE*2 x SAMPLE_HALF_RANGE*2 area. */ const val CHUNK_SAMPLE_SEED: Long = 123456 @@ -78,7 +88,7 @@ object SteelExtractor : ModInitializer { private const val NUM_CLUSTERS: Int = 25 // 25 clusters * 100 = 2,500 chunks const val NUM_SAMPLE_CHUNKS: Int = NUM_CLUSTERS * CHUNKS_PER_CLUSTER private const val SAMPLE_HALF_RANGE: Int = 500_000 // 1000,000x1000,000 chunk area - private const val CARVER_CHUNKS_PER_TICK = CHUNKS_PER_CLUSTER + private const val TERRAIN_CHUNKS_PER_TICK = CHUNKS_PER_CLUSTER private const val FEATURE_CHUNKS_PER_TICK = CHUNKS_PER_CLUSTER private const val LIGHT_CHUNKS_PER_TICK = CHUNKS_PER_CLUSTER private const val LIGHT_FEATURE_CHUNKS_PER_TICK = CHUNKS_PER_CLUSTER @@ -89,6 +99,7 @@ object SteelExtractor : ModInitializer { private const val DEBUG_CLUSTER_ENV = "STEEL_EXTRACTOR_DEBUG_CLUSTER" private const val DEBUG_DIMENSION_ENV = "STEEL_EXTRACTOR_DEBUG_DIMENSION" private const val DEBUG_SKIP_IMMEDIATE_ENV = "STEEL_EXTRACTOR_SKIP_IMMEDIATE" + private const val EXIT_ON_COMPLETE_ENV = "STEEL_EXTRACTOR_EXIT_ON_COMPLETE" const val LIGHT_DEPENDENCY_RADIUS: Int = 1 /** Generate the same sampled chunk clusters used by chunk stage hash extraction. */ @@ -160,9 +171,20 @@ object SteelExtractor : ModInitializer { return ChunkPos(x, z) } - private fun envFlag(name: String): Boolean { - val value = System.getenv(name)?.takeIf { it.isNotBlank() } ?: return false - return value == "1" || value.equals("true", ignoreCase = true) || value.equals("yes", ignoreCase = true) + private fun envFlag(name: String, defaultValue: Boolean = false): Boolean { + val value = System.getenv(name)?.takeIf { it.isNotBlank() } ?: return defaultValue + return when { + value == "1" || value.equals("true", ignoreCase = true) || value.equals("yes", ignoreCase = true) -> true + value == "0" || value.equals("false", ignoreCase = true) || value.equals("no", ignoreCase = true) -> false + else -> error("$name must be one of 1, 0, true, false, yes, or no; got '$value'") + } + } + + private fun stopWhenComplete(server: MinecraftServer) { + if (!envFlag(EXIT_ON_COMPLETE_ENV)) return + + logger.info("Exiting because $EXIT_ON_COMPLETE_ENV is enabled") + server.halt(false) } private data class LightCaptureProbe( @@ -194,6 +216,13 @@ object SteelExtractor : ModInitializer { return LightCaptureProbe(missingChunks, samples) } + private fun retainLightChunks(level: ServerLevel, positions: List) { + val ticket = Ticket(TicketType.UNKNOWN, ChunkLevel.byStatus(ChunkStatus.LIGHT)) + for (pos in positions) { + level.chunkSource.addTicket(ticket, pos) + } + } + override fun onInitialize() { logger.info("Hello Fabric world!") @@ -204,8 +233,7 @@ object SteelExtractor : ModInitializer { logger.info(test2.toString()) // Build immediate extractors list conditionally. To disable a particular extractor, - // set the environment variable STEEL_EXTRACTOR_DISABLE_=1 (or true/yes). - // Example: STEEL_EXTRACTOR_DISABLE_BLOCKS=1 + // set STEEL_EXTRACTOR_DISABLE_=1 (or true/yes). val immediateExtractors = mutableListOf() fun addUnlessDisabled(name: String, supplier: () -> Extractor) { if (envFlag("STEEL_EXTRACTOR_DISABLE_$name")) { @@ -215,15 +243,18 @@ object SteelExtractor : ModInitializer { } } + addUnlessDisabled("WORLDGEN_NOISE_SAMPLES") { com.steelextractor.extractors.WorldgenNoiseSamples() } addUnlessDisabled("BLOCKS") { Blocks() } addUnlessDisabled("BLOCK_ENTITIES") { BlockEntities() } addUnlessDisabled("COMMANDS") { Commands() } - addUnlessDisabled("DATA_COMPONENTS") { DataComponents() } addUnlessDisabled("ITEMS") { Items() } + addUnlessDisabled("DATA_COMPONENTS") { DataComponents() } + addUnlessDisabled("COMPONENT_HASHES") { ComponentHashes() } addUnlessDisabled("PARTICLE_TYPES") { ParticleTypeRegistryExtractor() } addUnlessDisabled("POSITION_SOURCE_TYPES") { PositionSourceTypeRegistryExtractor() } addUnlessDisabled("VILLAGER_TYPES") { VillagerTypeRegistryExtractor() } addUnlessDisabled("VILLAGER_PROFESSIONS") { VillagerProfessionRegistryExtractor() } + addUnlessDisabled("DECORATED_POT_PATTERNS") { DecoratedPotPatternRegistryExtractor() } addUnlessDisabled("PACKETS") { Packets() } addUnlessDisabled("MENU_TYPES") { MenuTypes() } addUnlessDisabled("ENTITIES") { Entities() } @@ -252,6 +283,7 @@ object SteelExtractor : ModInitializer { addUnlessDisabled("CUSTOM_STATS") { CustomStatRegistryExtractor() } addUnlessDisabled("STAT_TYPES") { StatTypeRegistryExtractor() } + val chunkStageExtractor = ChunkStageHashes() val allDimensions = listOf( @@ -303,7 +335,7 @@ object SteelExtractor : ModInitializer { val outputDirectory: Path try { - outputDirectory = Files.createDirectories(Paths.get("steel_extractor_output")) + outputDirectory = Files.createDirectories(Paths.get(System.getenv("STEEL_EXTRACTOR_OUTPUT_DIRECTORY") ?: "steel_extractor_output")) } catch (e: IOException) { logger.info("Failed to create output directory.", e) return @@ -326,11 +358,7 @@ object SteelExtractor : ModInitializer { if (!ENABLE_CHUNK_EXTRACTION) { logger.info("All extractors complete! (chunk extraction skipped)") - if (envFlag("STEEL_EXTRACTOR_EXIT_ON_COMPLETE")) { - logger.info("Exiting because STEEL_EXTRACTOR_EXIT_ON_COMPLETE is enabled") - ServerLifecycleEvents.SERVER_STOPPING.invoker().onServerStopping(server); - server.halt(false); // false means to do a graceful shutdown - } + stopWhenComplete(server) } }) @@ -343,7 +371,7 @@ object SteelExtractor : ModInitializer { val positions: List, val lightPositions: List, val lightFeaturePositions: List, - val carverQueue: ArrayDeque, + val terrainQueue: ArrayDeque, val featureQueue: ArrayDeque, val lightFeatureQueue: ArrayDeque, val lightQueue: ArrayDeque, @@ -361,7 +389,7 @@ object SteelExtractor : ModInitializer { val dimensionKey: ResourceKey, val dimId: String, val clusters: ArrayDeque, - var carverProgress: Int = 0, + var terrainProgress: Int = 0, var featureProgress: Int = 0, var lightFeatureProgress: Int = 0, var lightProgress: Int = 0 @@ -370,9 +398,9 @@ object SteelExtractor : ModInitializer { val dimWork = dimensions.map { (dimId, key) -> val clusters = ArrayDeque() for (positions in generationClusters) { - val carverQueue = ArrayDeque() + val terrainQueue = ArrayDeque() val featureQueue = ArrayDeque() - carverQueue.addAll(positions) + terrainQueue.addAll(positions) featureQueue.addAll(positions) val lightPositions = lightDependencyPositions(positions) val lightQueue = ArrayDeque() @@ -380,7 +408,7 @@ object SteelExtractor : ModInitializer { val lightFeaturePositions = lightFeatureDependencyPositions(positions) val lightFeatureQueue = ArrayDeque() lightFeatureQueue.addAll(exceptPositions(lightFeaturePositions, positions)) - clusters.add(ClusterWork(positions, lightPositions, lightFeaturePositions, carverQueue, featureQueue, lightFeatureQueue, lightQueue)) + clusters.add(ClusterWork(positions, lightPositions, lightFeaturePositions, terrainQueue, featureQueue, lightFeatureQueue, lightQueue)) } DimensionWork(key, dimId, clusters) } @@ -402,7 +430,7 @@ object SteelExtractor : ModInitializer { // Start generation on first tick after server is ready if (!generationStarted) { generationStarted = true - logger.info("Forcing deterministic generation of $totalChunks chunks across ${dimWork.size} dimensions (carvers $CARVER_CHUNKS_PER_TICK/tick, features $FEATURE_CHUNKS_PER_TICK/tick, light features $LIGHT_FEATURE_CHUNKS_PER_TICK/tick, light $LIGHT_CHUNKS_PER_TICK/tick, order x/z ascending)...") + logger.info("Forcing deterministic generation of $totalChunks chunks across ${dimWork.size} dimensions (terrain $TERRAIN_CHUNKS_PER_TICK/tick, features $FEATURE_CHUNKS_PER_TICK/tick, light features $LIGHT_FEATURE_CHUNKS_PER_TICK/tick, light $LIGHT_CHUNKS_PER_TICK/tick, order x/z ascending)...") } // Generate a batch of chunks per tick, one dimension at a time @@ -431,8 +459,8 @@ object SteelExtractor : ModInitializer { val runningLightFeatureQueue = cluster.featureQueue.isEmpty() && cluster.lightFeatureQueue.isNotEmpty() val (queue, status, batchSize) = when { - cluster.carverQueue.isNotEmpty() -> { - Triple(cluster.carverQueue, ChunkStatus.CARVERS, CARVER_CHUNKS_PER_TICK) + cluster.terrainQueue.isNotEmpty() -> { + Triple(cluster.terrainQueue, ChunkStatus.TERRAIN, TERRAIN_CHUNKS_PER_TICK) } cluster.featureQueue.isNotEmpty() -> { Triple(cluster.featureQueue, ChunkStatus.FEATURES, FEATURE_CHUNKS_PER_TICK) @@ -464,12 +492,12 @@ object SteelExtractor : ModInitializer { } dim.lightProgress++ } else { - dim.carverProgress++ + dim.terrainProgress++ } generatedThisTick++ } - if (cluster.carverQueue.isEmpty() && cluster.featureQueue.isEmpty() && !cluster.featureHashesCaptured) { + if (cluster.terrainQueue.isEmpty() && cluster.featureQueue.isEmpty() && !cluster.featureHashesCaptured) { // Mark any feature chunks loaded from disk as ready. for (pos in cluster.positions) { if (ChunkStageHashStorage.markReady(pos, dim.dimId)) { @@ -490,18 +518,23 @@ object SteelExtractor : ModInitializer { logger.info("Generated deterministic light dependency features for ${cluster.lightFeaturePositions.size} chunks in ${dim.dimId}") } - val dimProgress = dim.carverProgress + dim.featureProgress + dim.lightFeatureProgress + dim.lightProgress + val dimProgress = dim.terrainProgress + dim.featureProgress + dim.lightFeatureProgress + dim.lightProgress val overallProgress = currentDimIdx * (chunksPerDim * 2 + lightFeatureChunksPerDim + lightChunksPerDim) + dimProgress val clusterNumber = clusterCount - dim.clusters.size + 1 - logger.info("Chunk generation progress: $overallProgress/$totalChunkSteps (${dim.dimId}: cluster $clusterNumber/$clusterCount, carvers ${dim.carverProgress}/$chunksPerDim, features ${dim.featureProgress}/$chunksPerDim, light features ${dim.lightFeatureProgress}/$lightFeatureChunksPerDim, light ${dim.lightProgress}/$lightChunksPerDim)") + logger.info("Chunk generation progress: $overallProgress/$totalChunkSteps (${dim.dimId}: cluster $clusterNumber/$clusterCount, terrain ${dim.terrainProgress}/$chunksPerDim, features ${dim.featureProgress}/$chunksPerDim, light features ${dim.lightFeatureProgress}/$lightFeatureChunksPerDim, light ${dim.lightProgress}/$lightChunksPerDim)") if ( - cluster.carverQueue.isEmpty() && + cluster.terrainQueue.isEmpty() && cluster.featureQueue.isEmpty() && cluster.lightFeatureQueue.isEmpty() && cluster.lightQueue.isEmpty() && !cluster.lightHashesCaptured ) { + // getChunk's vanilla UNKNOWN ticket expires after one server tick. Refresh it + // while the asynchronous light updates settle so unloading cannot erase the + // visible DataLayer map before this cluster is captured. + retainLightChunks(level, cluster.lightPositions) + val barrier = cluster.pendingLightCaptureBarrier if (barrier == null) { val lightEngine = level.chunkSource.lightEngine @@ -560,7 +593,7 @@ object SteelExtractor : ModInitializer { } if ( - cluster.carverQueue.isEmpty() && + cluster.terrainQueue.isEmpty() && cluster.featureQueue.isEmpty() && cluster.lightFeatureQueue.isEmpty() && cluster.lightQueue.isEmpty() @@ -597,10 +630,7 @@ object SteelExtractor : ModInitializer { } } logger.info("All extractors complete!") - if (envFlag("STEEL_EXTRACTOR_EXIT_ON_COMPLETE")) { - logger.info("Exiting because STEEL_EXTRACTOR_EXIT_ON_COMPLETE is enabled") - exitProcess(0); - } + stopWhenComplete(server) } } } diff --git a/src/main/kotlin/com/steelextractor/extractors/BiomeHashes.kt b/src/main/kotlin/com/steelextractor/extractors/BiomeHashes.kt index d3aeb17..39ac24a 100644 --- a/src/main/kotlin/com/steelextractor/extractors/BiomeHashes.kt +++ b/src/main/kotlin/com/steelextractor/extractors/BiomeHashes.kt @@ -10,6 +10,7 @@ import net.minecraft.server.level.ServerLevel import net.minecraft.world.level.Level import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator import net.minecraft.world.level.levelgen.RandomState +import net.minecraft.world.level.levelgen.densityfunction.SamplerContext import org.slf4j.LoggerFactory import java.security.MessageDigest @@ -69,13 +70,13 @@ class BiomeHashes : SteelExtractor.Extractor { val noiseRegistry = server.registryAccess().lookupOrThrow(Registries.NOISE) val randomState = if (chunkGenerator is NoiseBasedChunkGenerator) { - RandomState.create(chunkGenerator.generatorSettings().value(), noiseRegistry, SEED) + RandomState.create(noiseRegistry, SEED, chunkGenerator.generatorSettings().value()) } else { logger.warn("Chunk generator for $name is not NoiseBasedChunkGenerator, using level's RandomState") level.chunkSource.randomState() } - val climateSampler = randomState.sampler() + val climateSampler = randomState.createClimateSampler(SamplerContext.EMPTY_UNCACHED) val hashesArray = JsonArray() @@ -110,6 +111,16 @@ class BiomeHashes : SteelExtractor.Extractor { minSectionY: Int, maxSectionY: Int ): String { + val biomeResolver = biomeSource.createResolverForChunk( + climateSampler, + chunkX * 4, + minSectionY * 4, + chunkZ * 4, + 4, + (maxSectionY - minSectionY + 1) * 4, + 4 + ) + // Step 1: Sample biomes in generation order (X outer, Y middle, Z inner) // to match vanilla/Steel world generation cache behavior. val biomes = HashMap() @@ -122,7 +133,7 @@ class BiomeHashes : SteelExtractor.Extractor { val quartY = sectionY * 4 + y val quartZ = chunkZ * 4 + z - val biome = biomeSource.getNoiseBiome(quartX, quartY, quartZ, climateSampler) + val biome = biomeResolver.getNoiseBiome(quartX, quartY, quartZ) val biomeName = biome.unwrapKey() .map { it.identifier().toString() } .orElse("unknown") diff --git a/src/main/kotlin/com/steelextractor/extractors/BlockEntities.kt b/src/main/kotlin/com/steelextractor/extractors/BlockEntities.kt index 4014ea7..546d2c0 100644 --- a/src/main/kotlin/com/steelextractor/extractors/BlockEntities.kt +++ b/src/main/kotlin/com/steelextractor/extractors/BlockEntities.kt @@ -6,6 +6,8 @@ import com.google.gson.JsonObject import com.steelextractor.SteelExtractor import net.minecraft.core.registries.BuiltInRegistries import net.minecraft.server.MinecraftServer +import net.minecraft.world.level.block.Block +import net.minecraft.world.level.block.entity.BlockEntityType class BlockEntities : SteelExtractor.Extractor { override fun fileName(): String { @@ -14,22 +16,26 @@ class BlockEntities : SteelExtractor.Extractor { override fun extract(server: MinecraftServer): JsonElement { val topLevelJson = JsonObject() + val validBlocksField = BlockEntityType::class.java.getDeclaredField("validBlocks") + validBlocksField.isAccessible = true val blockEntitiesJson = JsonArray() for (blockEntity in BuiltInRegistries.BLOCK_ENTITY_TYPE) { val blockEntityJson = JsonObject() - blockEntityJson.addProperty( - "name", - BuiltInRegistries.BLOCK_ENTITY_TYPE.getKey(blockEntity)!!.path, - ) + val key = BuiltInRegistries.BLOCK_ENTITY_TYPE.getKey(blockEntity) + ?: error("Block entity type has no key: $blockEntity") + blockEntityJson.addProperty("id", BuiltInRegistries.BLOCK_ENTITY_TYPE.getId(blockEntity)) + blockEntityJson.addProperty("name", key.path) + @Suppress("UNCHECKED_CAST") + val validBlocks = validBlocksField.get(blockEntity) as Set val validBlocksJson = JsonArray() - for (block in BuiltInRegistries.BLOCK) { - if (blockEntity.isValid(block.defaultBlockState())) { - validBlocksJson.add(BuiltInRegistries.BLOCK.getKey(block).path) - } - } + validBlocks + .map { BuiltInRegistries.BLOCK.getKey(it)!!.path } + .sorted() + .forEach { validBlocksJson.add(it) } blockEntityJson.add("valid_blocks", validBlocksJson) + blockEntitiesJson.add(blockEntityJson) } diff --git a/src/main/kotlin/com/steelextractor/extractors/Blocks.kt b/src/main/kotlin/com/steelextractor/extractors/Blocks.kt index 17db734..c4ee873 100644 --- a/src/main/kotlin/com/steelextractor/extractors/Blocks.kt +++ b/src/main/kotlin/com/steelextractor/extractors/Blocks.kt @@ -19,6 +19,7 @@ import net.minecraft.world.level.block.state.properties.NoteBlockInstrument import net.minecraft.world.level.block.state.properties.Property import net.minecraft.world.level.block.SoundType import net.minecraft.world.level.material.PushReaction +import net.minecraft.world.level.material.FlowingFluid import net.minecraft.world.level.storage.loot.LootTable import net.minecraft.world.phys.AABB import net.minecraft.world.phys.shapes.CollisionContext @@ -413,53 +414,40 @@ class Blocks : SteelExtractor.Extractor { return resultJson } - private fun stateFluidProperties(state: BlockState): StateFluidProperties { - val fluidState = state.fluidState - return StateFluidProperties( - BuiltInRegistries.FLUID.getKey(fluidState.type).path, - fluidState.amount, - fluidState.getOptionalValue(BlockStateProperties.FALLING).orElse(false), - ) - } - - private fun stateFluidPropertiesJson(properties: StateFluidProperties): JsonObject { - val json = JsonObject() - json.addProperty("fluid", properties.fluid) - json.addProperty("amount", properties.amount) - json.addProperty("falling", properties.falling) - return json - } - - private fun createStateFluidPropertiesJson(block: Block): JsonObject { + private fun createStateBooleanPropertiesJson( + block: Block, + valueForState: (BlockState) -> Boolean, + ): JsonObject { val resultJson = JsonObject() val possibleStates = block.stateDefinition.possibleStates if (possibleStates.isEmpty()) { - resultJson.add("default", stateFluidPropertiesJson(StateFluidProperties("empty", 0, false))) + resultJson.addProperty("default", false) resultJson.add("overwrites", JsonArray()) return resultJson } - val propertyCounts = LinkedHashMap() + val propertyCounts = LinkedHashMap() for (state in possibleStates) { - propertyCounts.merge(stateFluidProperties(state), 1, Int::plus) + propertyCounts.merge(valueForState(state), 1, Int::plus) } - var defaultProperties = stateFluidProperties(possibleStates[0]) + var defaultValue = valueForState(possibleStates[0]) var defaultCount = 0 - for ((properties, count) in propertyCounts) { + for ((value, count) in propertyCounts) { if (count > defaultCount) { - defaultProperties = properties + defaultValue = value defaultCount = count } } - resultJson.add("default", stateFluidPropertiesJson(defaultProperties)) + resultJson.addProperty("default", defaultValue) val overwrites = JsonArray() for (i in possibleStates.indices) { - val currentProperties = stateFluidProperties(possibleStates[i]) - if (currentProperties != defaultProperties) { - val overwrite = stateFluidPropertiesJson(currentProperties) + val currentValue = valueForState(possibleStates[i]) + if (currentValue != defaultValue) { + val overwrite = JsonObject() overwrite.addProperty("offset", i) + overwrite.addProperty("value", currentValue) overwrites.add(overwrite) } } @@ -467,40 +455,50 @@ class Blocks : SteelExtractor.Extractor { return resultJson } - private fun createStateBooleanPropertiesJson( - block: Block, - getValue: (BlockState) -> Boolean, - ): JsonObject { + private fun fluidProperties(state: BlockState): StateFluidProperties { + val fluidState = state.fluidState + return StateFluidProperties( + fluid = BuiltInRegistries.FLUID.getKey(fluidState.type).path, + amount = fluidState.amount, + falling = fluidState.hasProperty(FlowingFluid.FALLING) + && fluidState.getValue(FlowingFluid.FALLING), + ) + } + + private fun fluidPropertiesJson(properties: StateFluidProperties): JsonObject { + val json = JsonObject() + json.addProperty("fluid", properties.fluid) + json.addProperty("amount", properties.amount) + json.addProperty("falling", properties.falling) + return json + } + + private fun createFluidStatePropertiesJson(block: Block): JsonObject { val resultJson = JsonObject() val possibleStates = block.stateDefinition.possibleStates - if (possibleStates.isEmpty()) { - resultJson.addProperty("default", false) - resultJson.add("overwrites", JsonArray()) - return resultJson - } + require(possibleStates.isNotEmpty()) { "block ${BuiltInRegistries.BLOCK.getKey(block)} has no states" } - val propertyCounts = LinkedHashMap() + val propertyCounts = LinkedHashMap() for (state in possibleStates) { - propertyCounts.merge(getValue(state), 1, Int::plus) + propertyCounts.merge(fluidProperties(state), 1, Int::plus) } - var defaultValue = getValue(possibleStates[0]) + var defaultProperties = fluidProperties(possibleStates[0]) var defaultCount = 0 - for ((value, count) in propertyCounts) { + for ((properties, count) in propertyCounts) { if (count > defaultCount) { - defaultValue = value + defaultProperties = properties defaultCount = count } } - resultJson.addProperty("default", defaultValue) + resultJson.add("default", fluidPropertiesJson(defaultProperties)) val overwrites = JsonArray() for (i in possibleStates.indices) { - val currentValue = getValue(possibleStates[i]) - if (currentValue != defaultValue) { - val overwrite = JsonObject() + val currentProperties = fluidProperties(possibleStates[i]) + if (currentProperties != defaultProperties) { + val overwrite = fluidPropertiesJson(currentProperties) overwrite.addProperty("offset", i) - overwrite.addProperty("value", currentValue) overwrites.add(overwrite) } } @@ -523,9 +521,7 @@ class Blocks : SteelExtractor.Extractor { colorCounts.merge(color, 1, Int::plus) } - var defaultColor = possibleStates[0] - .getMapColor(EmptyBlockGetter.INSTANCE, BlockPos.ZERO) - .id + var defaultColor = possibleStates[0].getMapColor(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).id var defaultCount = 0 for ((color, count) in colorCounts) { if (count > defaultCount) { @@ -537,9 +533,7 @@ class Blocks : SteelExtractor.Extractor { val overwrites = JsonArray() for (i in possibleStates.indices) { - val color = possibleStates[i] - .getMapColor(EmptyBlockGetter.INSTANCE, BlockPos.ZERO) - .id + val color = possibleStates[i].getMapColor(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).id if (color != defaultColor) { val overwrite = JsonObject() overwrite.addProperty("offset", i) @@ -600,6 +594,7 @@ class Blocks : SteelExtractor.Extractor { behaviourJson.addProperty("friction", getPrivateFieldValue(behaviourProps, "friction")) behaviourJson.addProperty("speedFactor", getPrivateFieldValue(behaviourProps, "speedFactor")) behaviourJson.addProperty("jumpFactor", getPrivateFieldValue(behaviourProps, "jumpFactor")) + behaviourJson.addProperty("bounceRestitution", getPrivateFieldValue(behaviourProps, "bounceRestitution")) behaviourJson.addProperty("dynamicShape", getPrivateFieldValue(behaviourProps, "dynamicShape")) behaviourJson.addProperty("offsetType", getOffsetType(block).name) behaviourJson.addProperty("maxHorizontalOffset", getProtectedFloatMethodValue(block, "getMaxHorizontalOffset")) @@ -614,6 +609,7 @@ class Blocks : SteelExtractor.Extractor { behaviourJson.addProperty("liquid", getPrivateFieldValue(behaviourProps, "liquid")) behaviourJson.addProperty("isAir", getPrivateFieldValue(behaviourProps, "isAir")) + //behaviourJson.addProperty("isRedstoneConductor", getPrivateFieldValue(behaviourProps, "isRedstoneConductor")) //behaviourJson.addProperty("isSuffocating", getPrivateFieldValue(behaviourProps, "isSuffocating")) behaviourJson.addProperty( "requiresCorrectToolForDrops", @@ -641,22 +637,22 @@ class Blocks : SteelExtractor.Extractor { blockJson.add("interaction_shapes", shapesStructureJson.getAsJsonObject("interaction_shapes")) blockJson.add("visual_shapes", shapesStructureJson.getAsJsonObject("visual_shapes")) blockJson.add("light_properties", createLightPropertiesJson(block)) - blockJson.add("fluid_state", createStateFluidPropertiesJson(block)) + blockJson.add("fluid_state", createFluidStatePropertiesJson(block)) blockJson.add("map_color", createStateMapColorJson(block)) blockJson.add( "randomly_ticking", - createStateBooleanPropertiesJson(block) { state -> state.isRandomlyTicking }, + createStateBooleanPropertiesJson(block) { it.isRandomlyTicking }, ) blockJson.add( "suffocating", - createStateBooleanPropertiesJson(block) { state -> - state.isSuffocating(EmptyBlockGetter.INSTANCE, BlockPos.ZERO) + createStateBooleanPropertiesJson(block) { + it.isSuffocating(EmptyBlockGetter.INSTANCE, BlockPos.ZERO) }, ) blockJson.add( "redstone_conductor", - createStateBooleanPropertiesJson(block) { state -> - state.isRedstoneConductor(EmptyBlockGetter.INSTANCE, BlockPos.ZERO) + createStateBooleanPropertiesJson(block) { + it.isRedstoneConductor(EmptyBlockGetter.INSTANCE, BlockPos.ZERO) }, ) diff --git a/src/main/kotlin/com/steelextractor/extractors/BuiltInRegistryKeys.kt b/src/main/kotlin/com/steelextractor/extractors/BuiltInRegistryKeys.kt index 5269841..59db411 100644 --- a/src/main/kotlin/com/steelextractor/extractors/BuiltInRegistryKeys.kt +++ b/src/main/kotlin/com/steelextractor/extractors/BuiltInRegistryKeys.kt @@ -3,20 +3,31 @@ package com.steelextractor.extractors import com.google.gson.JsonArray import com.google.gson.JsonElement import com.google.gson.JsonObject +import com.mojang.serialization.Lifecycle import com.steelextractor.SteelExtractor +import net.minecraft.core.MappedRegistry +import net.minecraft.core.Holder +import net.minecraft.core.HolderGetter +import net.minecraft.core.RegistrationInfo import net.minecraft.core.Registry import net.minecraft.core.particles.ParticleType import net.minecraft.core.particles.ParticleTypes import net.minecraft.core.particles.SimpleParticleType import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.core.registries.Registries +import net.minecraft.data.worldgen.BootstrapContext +import net.minecraft.resources.ResourceKey import net.minecraft.resources.Identifier import net.minecraft.server.MinecraftServer import net.minecraft.sounds.SoundEvent import net.minecraft.stats.StatType import net.minecraft.world.entity.npc.villager.VillagerProfession import net.minecraft.world.entity.npc.villager.VillagerType +import net.minecraft.world.level.block.entity.DecoratedPotPattern +import net.minecraft.world.level.block.entity.DecoratedPotPatterns import net.minecraft.world.level.gameevent.PositionSourceType import net.minecraft.world.level.saveddata.maps.MapDecorationType +import java.util.stream.Stream private fun extractBuiltInRegistry( registry: Registry, @@ -50,30 +61,16 @@ class ParticleTypeRegistryExtractor : SteelExtractor.Extractor { if (particleType is SimpleParticleType) { return "simple" } - return when (particleType) { - ParticleTypes.BLOCK, - ParticleTypes.BLOCK_MARKER, - ParticleTypes.FALLING_DUST, - ParticleTypes.DUST_PILLAR, - ParticleTypes.BLOCK_CRUMBLE -> "block" - - ParticleTypes.ENTITY_EFFECT, - ParticleTypes.TINTED_LEAVES, - ParticleTypes.FLASH -> "color" - + ParticleTypes.BLOCK, ParticleTypes.BLOCK_MARKER, ParticleTypes.FALLING_DUST, + ParticleTypes.DUST_PILLAR, ParticleTypes.BLOCK_CRUMBLE -> "block" + ParticleTypes.ENTITY_EFFECT, ParticleTypes.TINTED_LEAVES, ParticleTypes.FLASH -> "color" ParticleTypes.DUST -> "dust" ParticleTypes.DUST_COLOR_TRANSITION -> "dust_color_transition" - ParticleTypes.GEYSER, - ParticleTypes.GEYSER_PLUME -> "geyser" - - ParticleTypes.GEYSER_BASE, - ParticleTypes.GEYSER_POOF -> "geyser_base" - + ParticleTypes.GEYSER, ParticleTypes.GEYSER_PLUME -> "geyser" + ParticleTypes.GEYSER_BASE, ParticleTypes.GEYSER_POOF -> "geyser_base" ParticleTypes.DRAGON_BREATH -> "power" - ParticleTypes.EFFECT, - ParticleTypes.INSTANT_EFFECT -> "spell" - + ParticleTypes.EFFECT, ParticleTypes.INSTANT_EFFECT -> "spell" ParticleTypes.ITEM -> "item" ParticleTypes.SCULK_CHARGE -> "sculk_charge" ParticleTypes.SHRIEK -> "shriek" @@ -84,41 +81,6 @@ class ParticleTypeRegistryExtractor : SteelExtractor.Extractor { } } -class PositionSourceTypeRegistryExtractor : SteelExtractor.Extractor { - override fun fileName(): String { - return "steel-registry/build_assets/position_source_types.json" - } - - override fun extract(server: MinecraftServer): JsonElement { - return extractBuiltInRegistry(BuiltInRegistries.POSITION_SOURCE_TYPE) { positionSourceType: PositionSourceType<*>, _ -> - when (positionSourceType) { - PositionSourceType.BLOCK, - PositionSourceType.ENTITY -> Unit - - else -> error( - "Unknown position source type: ${BuiltInRegistries.POSITION_SOURCE_TYPE.getKey(positionSourceType)}" - ) - } - } - } -} - -class MapDecorationTypeRegistryExtractor : SteelExtractor.Extractor { - override fun fileName(): String { - return "steel-registry/build_assets/map_decoration_types.json" - } - - override fun extract(server: MinecraftServer): JsonElement { - return extractBuiltInRegistry(BuiltInRegistries.MAP_DECORATION_TYPE) { type: MapDecorationType, json -> - json.addProperty("asset_id", type.assetId().toString()) - json.addProperty("show_on_item_frame", type.showOnItemFrame()) - json.addProperty("map_color", type.mapColor()) - json.addProperty("exploration_map_element", type.explorationMapElement()) - json.addProperty("track_count", type.trackCount()) - } - } -} - class VillagerTypeRegistryExtractor : SteelExtractor.Extractor { override fun fileName(): String { return "steel-registry/build_assets/villager_types.json" @@ -150,22 +112,72 @@ class VillagerProfessionRegistryExtractor : SteelExtractor.Extractor { } } -class CustomStatRegistryExtractor : SteelExtractor.Extractor { +class DecoratedPotPatternRegistryExtractor : SteelExtractor.Extractor { override fun fileName(): String { - return "steel-registry/build_assets/custom_stats.json" + return "steel-registry/build_assets/decorated_pot_patterns.json" } override fun extract(server: MinecraftServer): JsonElement { - return extractBuiltInRegistry(BuiltInRegistries.CUSTOM_STAT) { _: Identifier, _ -> } + val registry = MappedRegistry(Registries.DECORATED_POT_PATTERN, Lifecycle.stable()) + val context = object : BootstrapContext { + override fun register( + key: ResourceKey, + value: DecoratedPotPattern + ): Holder.Reference { + return registry.register(key, value, RegistrationInfo.BUILT_IN) + } + + override fun lookup(key: ResourceKey>): HolderGetter { + error("Decorated pot pattern bootstrap unexpectedly looked up $key") + } + + @Deprecated("Decorated pot pattern bootstrap never lists context elements.") + override fun listContextElements(key: ResourceKey>): Stream> { + error("Decorated pot pattern bootstrap unexpectedly listed $key") + } + } + DecoratedPotPatterns.bootstrap(context) + return extractBuiltInRegistry(registry) { pattern: DecoratedPotPattern, json -> + json.addProperty("asset_id", pattern.assetId().toString()) + } } } -class StatTypeRegistryExtractor : SteelExtractor.Extractor { - override fun fileName(): String { - return "steel-registry/build_assets/stat_types.json" +class PositionSourceTypeRegistryExtractor : SteelExtractor.Extractor { + override fun fileName(): String = "steel-registry/build_assets/position_source_types.json" + + override fun extract(server: MinecraftServer): JsonElement { + return extractBuiltInRegistry(BuiltInRegistries.POSITION_SOURCE_TYPE) { type: PositionSourceType<*>, _ -> + when (type) { + PositionSourceType.BLOCK, PositionSourceType.ENTITY -> Unit + else -> error("Unknown position source type: ${BuiltInRegistries.POSITION_SOURCE_TYPE.getKey(type)}") + } + } } +} + +class MapDecorationTypeRegistryExtractor : SteelExtractor.Extractor { + override fun fileName(): String = "steel-registry/build_assets/map_decoration_types.json" override fun extract(server: MinecraftServer): JsonElement { - return extractBuiltInRegistry(BuiltInRegistries.STAT_TYPE) { _: StatType<*>, _ -> } + return extractBuiltInRegistry(BuiltInRegistries.MAP_DECORATION_TYPE) { type: MapDecorationType, json -> + json.addProperty("asset_id", type.assetId().toString()) + json.addProperty("show_on_item_frame", type.showOnItemFrame()) + json.addProperty("track_count", type.trackCount()) + } } -} \ No newline at end of file +} + +class CustomStatRegistryExtractor : SteelExtractor.Extractor { + override fun fileName(): String = "steel-registry/build_assets/custom_stats.json" + + override fun extract(server: MinecraftServer): JsonElement = + extractBuiltInRegistry(BuiltInRegistries.CUSTOM_STAT) { _: Identifier, _ -> } +} + +class StatTypeRegistryExtractor : SteelExtractor.Extractor { + override fun fileName(): String = "steel-registry/build_assets/stat_types.json" + + override fun extract(server: MinecraftServer): JsonElement = + extractBuiltInRegistry(BuiltInRegistries.STAT_TYPE) { _: StatType<*>, _ -> } +} diff --git a/src/main/kotlin/com/steelextractor/extractors/ChunkStageHashes.kt b/src/main/kotlin/com/steelextractor/extractors/ChunkStageHashes.kt index 5890b2f..8e957c6 100644 --- a/src/main/kotlin/com/steelextractor/extractors/ChunkStageHashes.kt +++ b/src/main/kotlin/com/steelextractor/extractors/ChunkStageHashes.kt @@ -33,8 +33,8 @@ class ChunkStageHashes : SteelExtractor.Extractor { json.addProperty("num_chunks", SteelExtractor.NUM_SAMPLE_CHUNKS) json.addProperty("chunk_generation_order", "x_z_ascending") json.addProperty("feature_hash_capture", "after_all_tracked_features_ready") - json.addProperty("biome_tie_breaker", "vanilla_rtree_chunk_local_cache") - json.addProperty("hashset_iteration_order", "insertion_order") + json.addProperty("biome_tie_breaker", "vanilla_thread_local_rtree") + json.addProperty("hashset_iteration_order", "vanilla") json.addProperty("light_hash_capture", "after_all_tracked_light_ready_pending_tasks_drained_and_light_engine_idle") json.addProperty("light_dependency_radius", SteelExtractor.LIGHT_DEPENDENCY_RADIUS) json.addProperty("light_feature_dependency_capture", "after_tracked_features_before_light_x_z_ascending") diff --git a/src/main/kotlin/com/steelextractor/extractors/ComponentHashes.kt b/src/main/kotlin/com/steelextractor/extractors/ComponentHashes.kt new file mode 100644 index 0000000..7bfbe14 --- /dev/null +++ b/src/main/kotlin/com/steelextractor/extractors/ComponentHashes.kt @@ -0,0 +1,297 @@ +package com.steelextractor.extractors + +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import com.google.common.hash.HashCode +import com.mojang.serialization.JsonOps +import com.steelextractor.SteelExtractor +import net.minecraft.core.component.DataComponents +import net.minecraft.core.component.TypedDataComponent +import net.minecraft.core.component.BlockTransformer +import net.minecraft.nbt.NbtOps +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.resources.RegistryOps +import net.minecraft.server.MinecraftServer +import net.minecraft.util.HashOps +import net.minecraft.world.item.AdventureModePredicate + +/** + * Extracts Vanilla component-hash fixtures used by Steel's hashed item-patch tests. + */ +class ComponentHashes : SteelExtractor.Extractor { + override fun fileName(): String = "steel-registry/test_assets/component_hashes.json" + + override fun extract(server: MinecraftServer): JsonElement { + val root = JsonObject() + val hashes = JsonObject() + val ops = server.registryAccess().createSerializationContext(HashOps.CRC32C_INSTANCE) + + for (item in BuiltInRegistries.ITEM) { + val transformer = item.components().get(DataComponents.BLOCK_TRANSFORMER) ?: continue + val component = TypedDataComponent(DataComponents.BLOCK_TRANSFORMER, transformer) + val hash = component.encodeValue(ops) + .getOrThrow { message -> IllegalArgumentException("Failed to hash $component: $message") } + .let { value -> (value as HashCode).asInt() } + hashes.addProperty(BuiltInRegistries.ITEM.getKey(item).toString(), hash) + } + + root.add("block_transformer", hashes) + + val potteryPatternHashes = JsonObject() + for (item in BuiltInRegistries.ITEM) { + val pattern = item.components().get(DataComponents.PROVIDES_POTTERY_PATTERN) ?: continue + val component = TypedDataComponent(DataComponents.PROVIDES_POTTERY_PATTERN, pattern) + val hash = component.encodeValue(ops) + .getOrThrow { message -> IllegalArgumentException("Failed to hash $component: $message") } + .let { value -> (value as HashCode).asInt() } + potteryPatternHashes.addProperty(BuiltInRegistries.ITEM.getKey(item).toString(), hash) + } + root.add("provides_pottery_pattern", potteryPatternHashes) + + val adventureHashes = JsonObject() + val adventureOps = RegistryOps.create(JsonOps.INSTANCE, server.registryAccess()) + val adventurePredicate = AdventureModePredicate.CODEC.parse( + adventureOps, + JsonParser.parseString( + """ + [ + { + "blocks": ["minecraft:oak_log"], + "state": { + "axis": "y", + "distance": {"min": "1", "max": "7"} + }, + "nbt": "{id:\"minecraft:chest\"}", + "components": {"minecraft:max_damage": 1561}, + "predicates": { + "minecraft:max_stack_size": {}, + "minecraft:damage": {} + } + }, + {"blocks": "#minecraft:mineable/axe"} + ] + """.trimIndent() + ) + ).getOrThrow { message -> + IllegalArgumentException("Failed to decode adventure predicate fixture: $message") + } + val adventureComponent = TypedDataComponent(DataComponents.CAN_BREAK, adventurePredicate) + val adventureHash = adventureComponent.encodeValue(ops) + .getOrThrow { message -> IllegalArgumentException("Failed to hash $adventureComponent: $message") } + .let { value -> (value as HashCode).asInt() } + adventureHashes.addProperty("complex", adventureHash) + + val booleanComponentPredicate = AdventureModePredicate.CODEC.parse( + adventureOps, + JsonParser.parseString( + """ + [ + { + "components": { + "minecraft:enchantment_glint_override": false + } + } + ] + """.trimIndent() + ) + ).getOrThrow { message -> + IllegalArgumentException("Failed to decode boolean adventure predicate fixture: $message") + } + val booleanComponent = TypedDataComponent(DataComponents.CAN_BREAK, booleanComponentPredicate) + val booleanComponentHash = booleanComponent.encodeValue(ops) + .getOrThrow { message -> IllegalArgumentException("Failed to hash $booleanComponent: $message") } + .let { value -> (value as HashCode).asInt() } + adventureHashes.addProperty("boolean_component", booleanComponentHash) + root.add("adventure_mode_predicate", adventureHashes) + + val allVariantsFixture = JsonParser.parseString( + """ + [ + { + "block_state_provider": { + "type": "minecraft:rule_based", + "fallback": { + "type": "minecraft:rotated", + "state": {"id": "minecraft:oak_log"} + }, + "rules": [ + { + "if_true": { + "type": "minecraft:matching_blocks", + "offset": [1, 0, -1], + "blocks": ["minecraft:stone", "minecraft:dirt"] + }, + "then": {"id": "minecraft:dirt"} + }, + { + "if_true": { + "type": "minecraft:matching_block_tag", + "offset": [1, 0, 0], + "tag": "minecraft:logs" + }, + "then": { + "type": "minecraft:weighted", + "entries": [ + {"data": {"id": "minecraft:stone"}, "weight": 1}, + {"data": {"id": "minecraft:dirt"}, "weight": 2} + ] + } + }, + { + "if_true": { + "type": "minecraft:matching_fluids", + "offset": [0, 1, 0], + "fluids": ["minecraft:water", "minecraft:lava"] + }, + "then": { + "type": "minecraft:noise_threshold", + "seed": 12, + "noise": {"base_octave": 0, "amplitudes": [1.0]}, + "scale": 1.0, + "threshold": 0.0, + "high_chance": 0.5, + "default_state": {"id": "minecraft:stone"}, + "low_states": [{"id": "minecraft:dirt"}], + "high_states": [{"id": "minecraft:granite"}] + } + }, + { + "if_true": { + "type": "minecraft:matching_biomes", + "biomes": ["minecraft:plains", "minecraft:desert"] + }, + "then": { + "type": "minecraft:noise", + "seed": 13, + "noise": {"base_octave": 0, "amplitudes": [1.0]}, + "scale": 1.0, + "states": [{"id": "minecraft:stone"}, {"id": "minecraft:dirt"}] + } + }, + { + "if_true": { + "type": "minecraft:has_sturdy_face", + "offset": [0, -1, 0], + "direction": "up" + }, + "then": { + "type": "minecraft:dual_noise", + "variety": {"min_inclusive": 1, "max_inclusive": 2}, + "slow_noise": {"base_octave": -1, "amplitudes": [1.0]}, + "slow_scale": 1.0, + "seed": 14, + "noise": {"base_octave": 0, "amplitudes": [1.0]}, + "scale": 1.0, + "states": [{"id": "minecraft:stone"}, {"id": "minecraft:dirt"}] + } + }, + { + "if_true": {"type": "minecraft:solid", "offset": [0, 0, 0]}, + "then": { + "type": "minecraft:randomized_int", + "source": {"id": "minecraft:wheat"}, + "property": "age", + "values": 0 + } + }, + { + "if_true": {"type": "minecraft:replaceable", "offset": [0, 0, 0]}, + "then": { + "type": "minecraft:copy_properties", + "source": {"id": "minecraft:oak_log"} + } + }, + { + "if_true": { + "type": "minecraft:would_survive", + "offset": [0, 1, 0], + "state": {"id": "minecraft:torch"} + }, + "then": {"id": "minecraft:torch"} + }, + { + "if_true": {"type": "minecraft:inside_world_bounds", "offset": [0, 0, 0]}, + "then": {"id": "minecraft:stone"} + }, + { + "if_true": { + "type": "minecraft:any_of", + "predicates": [ + {"type": "minecraft:true"}, + {"type": "minecraft:matching_blocks", "blocks": "minecraft:dirt"} + ] + }, + "then": {"id": "minecraft:stone"} + }, + { + "if_true": { + "type": "minecraft:all_of", + "predicates": [ + {"type": "minecraft:true"}, + {"type": "minecraft:matching_blocks", "blocks": "minecraft:dirt"} + ] + }, + "then": {"id": "minecraft:stone"} + }, + { + "if_true": { + "type": "minecraft:not", + "predicate": {"type": "minecraft:matching_blocks", "blocks": "minecraft:bedrock"} + }, + "then": {"id": "minecraft:stone"} + }, + { + "if_true": {"type": "minecraft:true"}, + "then": {"id": "minecraft:stone"} + }, + { + "if_true": {"type": "minecraft:unobstructed", "offset": [100, 0, 0]}, + "then": {"id": "minecraft:stone"} + }, + { + "if_true": { + "type": "minecraft:height_range", + "min_inclusive": {"absolute": 0}, + "max_inclusive": {"above_bottom": 32} + }, + "then": {"id": "minecraft:stone"} + } + ] + }, + "consume_on_use": false, + "item_damage_per_use": 2 + } + ] + """.trimIndent() + ) + val allVariantsTransformer = BlockTransformer( + listOf( + BlockTransformer.BlockTransformData.CODEC.parse(adventureOps, allVariantsFixture.asJsonArray[0]) + .getOrThrow { message -> + IllegalArgumentException("Failed to decode all-variants block transformer fixture: $message") + } + ) + ) + val allVariants = JsonObject() + allVariants.add( + "codec", + BlockTransformer.DIRECT_CODEC.encodeStart(adventureOps, allVariantsTransformer) + .getOrThrow { message -> + IllegalArgumentException("Failed to encode all-variants block transformer fixture: $message") + } + ) + val transformerNbtOps = server.registryAccess().createSerializationContext(NbtOps.INSTANCE) + allVariants.addProperty( + "snbt", + BlockTransformer.DIRECT_CODEC.encodeStart(transformerNbtOps, allVariantsTransformer) + .getOrThrow { message -> + IllegalArgumentException("Failed to encode all-variants block transformer NBT fixture: $message") + } + .toString() + ) + root.add("block_transformer_all_variants", allVariants) + + return root + } +} diff --git a/src/main/kotlin/com/steelextractor/extractors/DataComponents.kt b/src/main/kotlin/com/steelextractor/extractors/DataComponents.kt index 1492ab5..65a0dce 100644 --- a/src/main/kotlin/com/steelextractor/extractors/DataComponents.kt +++ b/src/main/kotlin/com/steelextractor/extractors/DataComponents.kt @@ -7,33 +7,21 @@ import com.steelextractor.SteelExtractor import net.minecraft.core.registries.BuiltInRegistries import net.minecraft.server.MinecraftServer +/** Extracts the numeric Vanilla data-component registry used by item patches. */ class DataComponents : SteelExtractor.Extractor { - override fun fileName(): String { - return "steel-registry/build_assets/data_components.json" - } + override fun fileName(): String = "steel-registry/build_assets/data_components.json" override fun extract(server: MinecraftServer): JsonElement { val components = JsonArray() - for (component in BuiltInRegistries.DATA_COMPONENT_TYPE) { - val key = BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(component) - ?: error("Built-in data component type has no key: $component") - val componentJson = JsonObject() - componentJson.addProperty( - "id", - BuiltInRegistries.DATA_COMPONENT_TYPE.getId(component) - ) - componentJson.addProperty("key", key.toString()) - componentJson.addProperty("persistent", !component.isTransient) - componentJson.addProperty( - "ignore_swap_animation", - component.ignoreSwapAnimation() - ) - components.add(componentJson) + val value = JsonObject() + value.addProperty("id", BuiltInRegistries.DATA_COMPONENT_TYPE.getId(component)) + value.addProperty("key", BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(component).toString()) + value.addProperty("persistent", !component.isTransient()) + value.addProperty("ignore_swap_animation", component.ignoreSwapAnimation()) + components.add(value) } - val output = JsonObject() - output.add("components", components) - return output + return JsonObject().apply { add("components", components) } } } diff --git a/src/main/kotlin/com/steelextractor/extractors/Entities.kt b/src/main/kotlin/com/steelextractor/extractors/Entities.kt index 82e5835..d3f154a 100644 --- a/src/main/kotlin/com/steelextractor/extractors/Entities.kt +++ b/src/main/kotlin/com/steelextractor/extractors/Entities.kt @@ -22,9 +22,9 @@ import net.minecraft.world.entity.EntityType import net.minecraft.world.entity.LivingEntity import net.minecraft.world.entity.Pose import net.minecraft.world.entity.player.Player +import net.minecraft.world.entity.monster.zombie.ZombieVillager import net.minecraft.world.entity.npc.villager.Villager import net.minecraft.world.entity.npc.villager.VillagerData -import net.minecraft.world.entity.monster.zombie.ZombieVillager import net.minecraft.world.entity.ai.attributes.Attributes import net.minecraft.world.entity.ai.attributes.DefaultAttributes import net.minecraft.world.entity.boss.enderdragon.EnderDragon @@ -142,7 +142,6 @@ class Entities : SteelExtractor.Extractor { entityTypeJson.addProperty("allowed_in_peaceful", entityType.isAllowedInPeaceful()) entityTypeJson.addProperty("can_serialize", entityType.canSerialize()) entityTypeJson.addProperty("can_spawn_far_from_player", entityType.canSpawnFarFromPlayer()) - entityTypeJson.addProperty("only_op_can_set_nbt", entityType.onlyOpCanSetNbt()) // Synched data entityTypeJson.add("synched_data", extractSynchedData(entityType, world)) @@ -218,25 +217,24 @@ class Entities : SteelExtractor.Extractor { val serializerId = EntityDataSerializers.getSerializedId(accessor.serializer()) val index = accessor.id() val accessorInfo = accessorNames[index] - // Vanilla selects this profession randomly while defining synchronized data. Record - // its stable base plus a runtime initializer instead of this entity's sampled value. val runtimeInitializer = if ( entity is ZombieVillager && accessorInfo?.rawFieldName == "DATA_VILLAGER_DATA" - ) { - "random_villager_profession" + ) "random_villager_profession" else null + val defaultValue = if (runtimeInitializer != null) { + Villager.createDefaultVillagerData() } else { - null + initialValueField.get(dataItem) } - val defaultValue = runtimeInitializer?.let { Villager.createDefaultVillagerData() } - ?: initialValueField.get(dataItem) val fieldJson = synchedFieldJson( index, accessorInfo?.fieldName ?: "unknown", accessorInfo?.rawFieldName, serializerId, - defaultValue, - runtimeInitializer + defaultValue ) + if (runtimeInitializer != null) { + fieldJson.addProperty("runtime_initializer", runtimeInitializer) + } if (accessorInfo == null) { unresolvedFields.add(fieldJson) @@ -259,6 +257,7 @@ class Entities : SteelExtractor.Extractor { serializerId, null ) + annotateRuntimeInitializer(accessorInfo, fieldJson) fieldsByClass.getOrPut(accessorInfo.declaringClass) { mutableListOf() }.add(fieldJson) } @@ -300,8 +299,7 @@ class Entities : SteelExtractor.Extractor { fieldName: String, accessorField: String?, serializerId: Int, - defaultValue: Any?, - runtimeInitializer: String? = null + defaultValue: Any? ): JsonObject { val fieldJson = JsonObject() fieldJson.addProperty("index", index) @@ -312,12 +310,20 @@ class Entities : SteelExtractor.Extractor { fieldJson.addProperty("serializer_id", serializerId) fieldJson.addProperty("serializer", serializerNames[serializerId] ?: "unknown") fieldJson.add("default_value", serializeDefaultValue(defaultValue)) - if (runtimeInitializer != null) { - fieldJson.addProperty("runtime_initializer", runtimeInitializer) - } return fieldJson } + private fun annotateRuntimeInitializer(accessor: SynchedAccessorInfo, fieldJson: JsonObject) { + // ZombieVillager defines this synced value through + // initializeZombieVillagerData(this.random), rather than a static default. + if ( + accessor.declaringClass == ZombieVillager::class.java && + accessor.rawFieldName == "DATA_VILLAGER_DATA" + ) { + fieldJson.addProperty("runtime_initializer", "random_villager_profession") + } + } + private fun entityJavaClass(entityType: EntityType<*>, entity: Entity?): Class? { if (entity != null) { return entity.javaClass @@ -476,7 +482,6 @@ class Entities : SteelExtractor.Extractor { // List of all known attributes to check val attributeHolders = listOf( Attributes.MAX_HEALTH, - Attributes.MAX_ABSORPTION, Attributes.FOLLOW_RANGE, Attributes.KNOCKBACK_RESISTANCE, Attributes.MOVEMENT_SPEED, @@ -486,6 +491,7 @@ class Entities : SteelExtractor.Extractor { Attributes.ATTACK_SPEED, Attributes.ARMOR, Attributes.ARMOR_TOUGHNESS, + Attributes.MAX_ABSORPTION, Attributes.LUCK, Attributes.SPAWN_REINFORCEMENTS_CHANCE, Attributes.JUMP_STRENGTH, @@ -506,6 +512,14 @@ class Entities : SteelExtractor.Extractor { Attributes.BURNING_TIME, Attributes.EXPLOSION_KNOCKBACK_RESISTANCE, Attributes.MOVEMENT_EFFICIENCY, + Attributes.CAMERA_DISTANCE, + Attributes.WAYPOINT_TRANSMIT_RANGE, + Attributes.WAYPOINT_RECEIVE_RANGE, + Attributes.BOUNCINESS, + Attributes.AIR_DRAG_MODIFIER, + Attributes.FRICTION_MODIFIER, + Attributes.NAME_TAG_DISTANCE, + Attributes.BELOW_NAME_DISTANCE, Attributes.TEMPT_RANGE ) @@ -538,7 +552,6 @@ class Entities : SteelExtractor.Extractor { flagsJson.addProperty("is_pushed_by_fluid", entity.isPushedByFluid()) flagsJson.addProperty("can_freeze", entity.canFreeze()) flagsJson.addProperty("can_be_hit_by_projectile", entity.canBeHitByProjectile()) - flagsJson.addProperty("piston_push_reaction", entity.pistonPushReaction.name) if (entity is LivingEntity) { flagsJson.addProperty("is_sensitive_to_water", entity.isSensitiveToWater) diff --git a/src/main/kotlin/com/steelextractor/extractors/Items.kt b/src/main/kotlin/com/steelextractor/extractors/Items.kt index b39937f..83330b3 100644 --- a/src/main/kotlin/com/steelextractor/extractors/Items.kt +++ b/src/main/kotlin/com/steelextractor/extractors/Items.kt @@ -10,36 +10,15 @@ import net.minecraft.core.registries.BuiltInRegistries import net.minecraft.resources.RegistryOps import net.minecraft.server.MinecraftServer import net.minecraft.world.item.BlockItem -import net.minecraft.world.item.Item -import net.minecraft.world.item.SignItem +import net.minecraft.world.item.Items import net.minecraft.world.item.StandingAndWallBlockItem import net.minecraft.world.level.block.Block -import net.minecraft.world.level.block.SignBlock -import org.slf4j.LoggerFactory -import java.lang.reflect.Field class Items : SteelExtractor.Extractor { - private val logger = LoggerFactory.getLogger("steel-extractor-items") - override fun fileName(): String { return "steel-registry/build_assets/items.json" } - fun getConstantName(clazz: Class<*>, value: Any?): String? { - for (f in clazz.getFields()) { // only public fields - try { - // we expect a static final constant, so no instance needed - val fieldValue = f.get(null) - if (fieldValue === value) { // reference equality is what we want - return f.getName() - } - } catch (e: IllegalAccessException) { - // shouldn't happen with getFields(), but ignore it just in case - } - } - return null // no match found - } - private fun sortJsonObjectByKeys(obj: JsonObject): JsonObject { val sorted = JsonObject() obj.keySet().sorted().forEach { key -> @@ -52,6 +31,7 @@ class Items : SteelExtractor.Extractor { val topLevelJson = JsonObject() val itemsJson = JsonArray() + val blockItemMappings = JsonObject() for (item in BuiltInRegistries.ITEM) { @@ -92,19 +72,18 @@ class Items : SteelExtractor.Extractor { itemsJson.add(itemJson) } - - val blockItemMappingsJson = JsonObject() for (block in BuiltInRegistries.BLOCK) { - val item = Item.BY_BLOCK[block] ?: continue - blockItemMappingsJson.addProperty( - BuiltInRegistries.BLOCK.getKey(block).path, - BuiltInRegistries.ITEM.getKey(item).path - ) + val item = block.asItem() + if (item != Items.AIR) { + blockItemMappings.addProperty( + BuiltInRegistries.BLOCK.getKey(block).path, + BuiltInRegistries.ITEM.getKey(item).path + ) + } } - topLevelJson.add("items", itemsJson) - topLevelJson.add("blockItemMappings", blockItemMappingsJson) + topLevelJson.add("blockItemMappings", blockItemMappings) return topLevelJson } diff --git a/src/main/kotlin/com/steelextractor/extractors/Strippables.kt b/src/main/kotlin/com/steelextractor/extractors/Strippables.kt index fa7135a..0a00b3e 100644 --- a/src/main/kotlin/com/steelextractor/extractors/Strippables.kt +++ b/src/main/kotlin/com/steelextractor/extractors/Strippables.kt @@ -1,30 +1,51 @@ package com.steelextractor.extractors -import com.google.gson.JsonArray import com.google.gson.JsonElement import com.google.gson.JsonObject import com.steelextractor.SteelExtractor -import com.steelextractor.mixin.AxeItemAccessor +import com.steelextractor.mixin.MatchingBlocksPredicateAccessor import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.core.registries.Registries import net.minecraft.server.MinecraftServer -import org.slf4j.LoggerFactory - +import net.minecraft.world.item.component.BlockTransformers +import net.minecraft.world.level.levelgen.blockpredicates.MatchingBlocksPredicate +import net.minecraft.world.level.levelgen.feature.stateproviders.CopyPropertiesProvider +import net.minecraft.world.level.levelgen.feature.stateproviders.RuleBasedStateProvider +import net.minecraft.world.level.levelgen.feature.stateproviders.SimpleStateProvider class Strippables : SteelExtractor.Extractor { - private val logger = LoggerFactory.getLogger("steel-extractor-strippables") - override fun fileName(): String { return "steel-core/build/strippables.json" } + @Suppress("CAST_NEVER_SUCCEEDS") override fun extract(server: MinecraftServer): JsonElement { val topLevelJson = JsonObject() + val axe = server.registryAccess() + .lookupOrThrow(Registries.BLOCK_TRANSFORMER) + .getOrThrow(BlockTransformers.AXE) + .value() + + for (transform in axe.transforms()) { + val provider = transform.blockStateProvider().value() as? RuleBasedStateProvider + ?: error("Unexpected axe transform provider: ${transform.blockStateProvider().value()}") + for (rule in provider.rules()) { + val predicate = rule.ifTrue() as? MatchingBlocksPredicate + ?: error("Unexpected axe transform predicate: ${rule.ifTrue()}") + val target = rule.`then`().value() as? CopyPropertiesProvider + ?: error("Unexpected axe transform target: ${rule.`then`().value()}") + val source = target.source().value() as? SimpleStateProvider + ?: error("Unexpected axe transform source: ${target.source().value()}") + val stripped = source.state().block - for ((normal, stripped) in AxeItemAccessor.getStrippables()) { - topLevelJson.addProperty( - BuiltInRegistries.BLOCK.getKey(normal).path, - BuiltInRegistries.BLOCK.getKey(stripped).path - ) + // Fabric Mixin injects this accessor into MatchingBlocksPredicate at runtime. + for (normal in (predicate as MatchingBlocksPredicateAccessor).blocks) { + topLevelJson.addProperty( + BuiltInRegistries.BLOCK.getKey(normal.value()).path, + BuiltInRegistries.BLOCK.getKey(stripped).path + ) + } + } } return topLevelJson diff --git a/src/main/kotlin/com/steelextractor/extractors/Tags.kt b/src/main/kotlin/com/steelextractor/extractors/Tags.kt index a08fb5a..7ab63a5 100644 --- a/src/main/kotlin/com/steelextractor/extractors/Tags.kt +++ b/src/main/kotlin/com/steelextractor/extractors/Tags.kt @@ -20,7 +20,7 @@ class Tags : SteelExtractor.Extractor { val blockTagsJson = JsonObject() BuiltInRegistries.BLOCK.getTags().forEach { namedHolderSet -> - if (namedHolderSet.size() > 0 && namedHolderSet.key().location().namespace != "minecraft") { + if (namedHolderSet.size() > 0) { val entriesArray = JsonArray() namedHolderSet.stream().forEach { holder -> holder.unwrapKey().ifPresent { key -> @@ -31,18 +31,20 @@ class Tags : SteelExtractor.Extractor { } } topLevelJson.add("block", blockTagsJson) + + val itemTagsJson = JsonObject() BuiltInRegistries.ITEM.getTags().forEach { namedHolderSet -> - if (namedHolderSet.size() > 0 && namedHolderSet.key().location().namespace != "minecraft") { + if (namedHolderSet.size() > 0) { val entriesArray = JsonArray() namedHolderSet.stream().forEach { holder -> holder.unwrapKey().ifPresent { key -> entriesArray.add(key.identifier().toString()) } } - blockTagsJson.add(namedHolderSet.key().location().toString(), entriesArray) + itemTagsJson.add(namedHolderSet.key().location().toString(), entriesArray) } } - topLevelJson.add("item", blockTagsJson) + topLevelJson.add("item", itemTagsJson) return topLevelJson } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/steelextractor/extractors/WorldgenNoiseSamples.kt b/src/main/kotlin/com/steelextractor/extractors/WorldgenNoiseSamples.kt new file mode 100644 index 0000000..eb66cab --- /dev/null +++ b/src/main/kotlin/com/steelextractor/extractors/WorldgenNoiseSamples.kt @@ -0,0 +1,64 @@ +package com.steelextractor.extractors + +import com.google.gson.JsonArray +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import com.steelextractor.SteelExtractor +import net.minecraft.core.Holder +import net.minecraft.resources.Identifier +import net.minecraft.server.MinecraftServer +import net.minecraft.util.RandomSource +import net.minecraft.world.level.biome.Biome +import net.minecraft.world.level.levelgen.LegacyRandomSource +import net.minecraft.world.level.levelgen.densityfunction.DensityFunction +import net.minecraft.world.level.levelgen.densityfunction.SamplerContext +import net.minecraft.world.level.levelgen.synth.BlendedNoise +import net.minecraft.world.level.levelgen.synth.Noise +import net.minecraft.world.level.levelgen.synth.NormalNoise + +/** Samples actual Java float results, including coordinates outside the exact integer range of float. */ +class WorldgenNoiseSamples : SteelExtractor.Extractor { + override fun fileName() = "steel-worldgen/test_assets/noise_samples.json" + + override fun extract(server: MinecraftServer): JsonElement { + val samples = JsonArray() + val positions = listOf( + intArrayOf(0, -56, -10000), intArrayOf(20000068, 296, -19999796), + intArrayOf(0, 0, 0), intArrayOf(1, 1, 1), intArrayOf(-1, -1, -1), + intArrayOf(16777217, 100, -16777217), intArrayOf(29999980, -64, -29999980), + intArrayOf(54545, 63, 34234), intArrayOf(-842, 319, 425) + ) + for (seed in listOf(0L, 13579L, -1L)) { + val parameters = NormalNoise.createParity(-7, 1.0, 1.0) + val normal = parameters.create(LegacyRandomSource(seed)) + val nether = parameters.createForLegacyNetherBiome(LegacyRandomSource(seed)) + val context = object : DensityFunction.CompileContext { + override fun createNoiseSampler(parameters: Holder): Noise = + parameters.value().create(LegacyRandomSource(seed)) + override fun createRandom(id: Identifier): RandomSource = LegacyRandomSource(seed) + override fun createEndIslandRandom(): RandomSource = LegacyRandomSource(seed) + } + val blended = BlendedNoise(0.25, 0.125, 80.0, 160.0, 8.0).compileSampler(context) + for (pos in positions) { + val (x, y, z) = pos + fun record(sampler: String, value: Float) { + samples.add(JsonObject().apply { + addProperty("sampler", sampler) + addProperty("seed", seed) + addProperty("x", x) + addProperty("y", y) + addProperty("z", z) + addProperty("value_bits", value.toRawBits().toLong() and 0xffffffffL) + }) + } + record("normal", normal.get(x.toDouble(), y.toDouble(), z.toDouble())) + record("legacy_nether", nether.get(x.toDouble(), y.toDouble(), z.toDouble())) + record("blended", blended.sampleValue(SamplerContext.EMPTY_UNCACHED, x, y, z)) + if (seed == 0L) { + record("frozen_temperature", Biome.FROZEN_TEMPERATURE_NOISE.get(x * 0.05, z * 0.05)) + } + } + } + return samples + } +} diff --git a/src/main/resources/steel-extractor.mixins.json b/src/main/resources/steel-extractor.mixins.json index c0eba98..191b6d9 100644 --- a/src/main/resources/steel-extractor.mixins.json +++ b/src/main/resources/steel-extractor.mixins.json @@ -5,14 +5,8 @@ "mixins": [ "ExampleMixin", "ChunkStepMixin", - "ChunkAccessBiomeCacheMixin", - "ClimateRTreeMixin", - "BiomeSourcePossibleBiomesMixin", - "AxeItemAccessor", - "VegetationPatchFeatureMixin", - "WaterloggedVegetationPatchFeatureMixin", - "TreeFeatureMixin", - "FallenTreeFeatureMixin" + "TerrainPhaseCaptureMixin", + "MatchingBlocksPredicateAccessor" ], "injectors": { "defaultRequire": 1 From dc50e2f5b6f156eb3f2acd1e69796e468c4be454 Mon Sep 17 00:00:00 2001 From: Joniii11 <88550325+Joniii11@users.noreply.github.com> Date: Mon, 28 Sep 2026 16:47:25 +0200 Subject: [PATCH 2/9] Target Minecraft 26.3 release --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 1f44fe0..50e7a56 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ org.gradle.parallel=true org.gradle.configuration-cache=false # Fabric Properties # check these on https://fabricmc.net/develop -minecraft_version=26.3-pre-3 +minecraft_version=26.3 loader_version=0.19.5 loom_version=1.17-SNAPSHOT fabric_kotlin_version=1.13.12+kotlin.2.4.0 From ed907d0b8154cd8ea29c66281f2731cb2fdb142a Mon Sep 17 00:00:00 2001 From: Joniii11 <88550325+Joniii11@users.noreply.github.com> Date: Mon, 28 Sep 2026 17:09:50 +0200 Subject: [PATCH 3/9] Remove diagnostic extractors and target Minecraft 26.3 tooling --- build.gradle | 2 +- gradle.properties | 4 +- gradle/wrapper/gradle-wrapper.properties | 2 +- .../mixin/TerrainPhaseCaptureMixin.java | 69 ---- .../com/steelextractor/SteelExtractor.kt | 3 - .../extractors/ComponentHashes.kt | 297 ------------------ .../extractors/WorldgenNoiseSamples.kt | 64 ---- .../resources/steel-extractor.mixins.json | 1 - 8 files changed, 4 insertions(+), 438 deletions(-) delete mode 100644 src/main/java/com/steelextractor/mixin/TerrainPhaseCaptureMixin.java delete mode 100644 src/main/kotlin/com/steelextractor/extractors/ComponentHashes.kt delete mode 100644 src/main/kotlin/com/steelextractor/extractors/WorldgenNoiseSamples.kt diff --git a/build.gradle b/build.gradle index 8c20edb..3f1615c 100644 --- a/build.gradle +++ b/build.gradle @@ -45,7 +45,7 @@ dependencies { implementation "net.fabricmc:fabric-loader:${project.loader_version}" // Fabric API. This is technically optional, but you probably want it anyway. - implementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" + implementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}" implementation "net.fabricmc:fabric-language-kotlin:${project.fabric_kotlin_version}" } diff --git a/gradle.properties b/gradle.properties index 50e7a56..c4831f8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,11 +7,11 @@ org.gradle.configuration-cache=false # check these on https://fabricmc.net/develop minecraft_version=26.3 loader_version=0.19.5 -loom_version=1.17-SNAPSHOT +loom_version=1.18-SNAPSHOT fabric_kotlin_version=1.13.12+kotlin.2.4.0 # Mod Properties mod_version=1.0.0 maven_group=com.steelextractor archives_base_name=steel-extractor # Dependencies -fabric_version=0.159.3+26.3 +fabric_api_version=0.161.0+26.3 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1a70468..1e922f4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/src/main/java/com/steelextractor/mixin/TerrainPhaseCaptureMixin.java b/src/main/java/com/steelextractor/mixin/TerrainPhaseCaptureMixin.java deleted file mode 100644 index 4a0f392..0000000 --- a/src/main/java/com/steelextractor/mixin/TerrainPhaseCaptureMixin.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.steelextractor.mixin; - -import com.steelextractor.BlockHashResult; -import com.steelextractor.ChunkStageHashStorage; -import net.minecraft.core.Holder; -import net.minecraft.server.level.WorldGenRegion; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeManager; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.LevelChunkSection; -import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; -import net.minecraft.world.level.levelgen.NoiseChunk; -import net.minecraft.world.level.levelgen.RandomState; -import net.minecraft.world.level.levelgen.blending.Blender; -import net.minecraft.world.level.levelgen.material.rule.MaterialRule; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import java.util.Arrays; -import java.util.Set; - -@Mixin(NoiseBasedChunkGenerator.class) -public class TerrainPhaseCaptureMixin { - @Inject(method = "buildSurface", at = @At("RETURN")) - private void captureSurface( - ChunkAccess chunk, - NoiseChunk noiseChunk, - RandomState randomState, - BiomeManager biomeManager, - Set> possibleBiomes, - MaterialRule materialRule, - CallbackInfo ci - ) { - capture(chunk, "minecraft:surface"); - } - - @Inject(method = "generateCarvers", at = @At("RETURN")) - private void captureCarvers( - ChunkAccess chunk, - Blender blender, - NoiseChunk noiseChunk, - RandomState randomState, - BiomeManager biomeManager, - WorldGenRegion carverBiomeRegion, - MaterialRule materialRule, - CallbackInfo ci - ) { - capture(chunk, "minecraft:carvers"); - } - - private static void capture(ChunkAccess chunk, String stage) { - String dimension = ChunkStageHashStorage.INSTANCE.getCurrentDimension(); - if (!ChunkStageHashStorage.INSTANCE.isTracking(chunk.getPos(), dimension)) { - return; - } - - LevelChunkSection[] sections = chunk.getSections(); - if (ChunkStageHashStorage.INSTANCE.getEnableBinaryDump()) { - BlockHashResult result = ChunkStageHashStorage.INSTANCE.computeBlockHashWithData(Arrays.asList(sections)); - ChunkStageHashStorage.INSTANCE.storeHash(chunk.getPos(), dimension, stage, result.getHash()); - ChunkStageHashStorage.INSTANCE.storeBlockData(chunk.getPos(), dimension, stage, result.getSectionData()); - } else { - String hash = ChunkStageHashStorage.INSTANCE.computeBlockHash(Arrays.asList(sections)); - ChunkStageHashStorage.INSTANCE.storeHash(chunk.getPos(), dimension, stage, hash); - } - } -} diff --git a/src/main/kotlin/com/steelextractor/SteelExtractor.kt b/src/main/kotlin/com/steelextractor/SteelExtractor.kt index bb631bf..56ceb27 100644 --- a/src/main/kotlin/com/steelextractor/SteelExtractor.kt +++ b/src/main/kotlin/com/steelextractor/SteelExtractor.kt @@ -4,7 +4,6 @@ import com.google.gson.GsonBuilder import com.google.gson.JsonElement import com.steelextractor.extractors.Attributes import com.steelextractor.extractors.Classes -import com.steelextractor.extractors.ComponentHashes import com.steelextractor.extractors.DataComponents import com.steelextractor.extractors.BlockEntities import com.steelextractor.extractors.Blocks @@ -243,13 +242,11 @@ object SteelExtractor : ModInitializer { } } - addUnlessDisabled("WORLDGEN_NOISE_SAMPLES") { com.steelextractor.extractors.WorldgenNoiseSamples() } addUnlessDisabled("BLOCKS") { Blocks() } addUnlessDisabled("BLOCK_ENTITIES") { BlockEntities() } addUnlessDisabled("COMMANDS") { Commands() } addUnlessDisabled("ITEMS") { Items() } addUnlessDisabled("DATA_COMPONENTS") { DataComponents() } - addUnlessDisabled("COMPONENT_HASHES") { ComponentHashes() } addUnlessDisabled("PARTICLE_TYPES") { ParticleTypeRegistryExtractor() } addUnlessDisabled("POSITION_SOURCE_TYPES") { PositionSourceTypeRegistryExtractor() } addUnlessDisabled("VILLAGER_TYPES") { VillagerTypeRegistryExtractor() } diff --git a/src/main/kotlin/com/steelextractor/extractors/ComponentHashes.kt b/src/main/kotlin/com/steelextractor/extractors/ComponentHashes.kt deleted file mode 100644 index 7bfbe14..0000000 --- a/src/main/kotlin/com/steelextractor/extractors/ComponentHashes.kt +++ /dev/null @@ -1,297 +0,0 @@ -package com.steelextractor.extractors - -import com.google.gson.JsonElement -import com.google.gson.JsonObject -import com.google.gson.JsonParser -import com.google.common.hash.HashCode -import com.mojang.serialization.JsonOps -import com.steelextractor.SteelExtractor -import net.minecraft.core.component.DataComponents -import net.minecraft.core.component.TypedDataComponent -import net.minecraft.core.component.BlockTransformer -import net.minecraft.nbt.NbtOps -import net.minecraft.core.registries.BuiltInRegistries -import net.minecraft.resources.RegistryOps -import net.minecraft.server.MinecraftServer -import net.minecraft.util.HashOps -import net.minecraft.world.item.AdventureModePredicate - -/** - * Extracts Vanilla component-hash fixtures used by Steel's hashed item-patch tests. - */ -class ComponentHashes : SteelExtractor.Extractor { - override fun fileName(): String = "steel-registry/test_assets/component_hashes.json" - - override fun extract(server: MinecraftServer): JsonElement { - val root = JsonObject() - val hashes = JsonObject() - val ops = server.registryAccess().createSerializationContext(HashOps.CRC32C_INSTANCE) - - for (item in BuiltInRegistries.ITEM) { - val transformer = item.components().get(DataComponents.BLOCK_TRANSFORMER) ?: continue - val component = TypedDataComponent(DataComponents.BLOCK_TRANSFORMER, transformer) - val hash = component.encodeValue(ops) - .getOrThrow { message -> IllegalArgumentException("Failed to hash $component: $message") } - .let { value -> (value as HashCode).asInt() } - hashes.addProperty(BuiltInRegistries.ITEM.getKey(item).toString(), hash) - } - - root.add("block_transformer", hashes) - - val potteryPatternHashes = JsonObject() - for (item in BuiltInRegistries.ITEM) { - val pattern = item.components().get(DataComponents.PROVIDES_POTTERY_PATTERN) ?: continue - val component = TypedDataComponent(DataComponents.PROVIDES_POTTERY_PATTERN, pattern) - val hash = component.encodeValue(ops) - .getOrThrow { message -> IllegalArgumentException("Failed to hash $component: $message") } - .let { value -> (value as HashCode).asInt() } - potteryPatternHashes.addProperty(BuiltInRegistries.ITEM.getKey(item).toString(), hash) - } - root.add("provides_pottery_pattern", potteryPatternHashes) - - val adventureHashes = JsonObject() - val adventureOps = RegistryOps.create(JsonOps.INSTANCE, server.registryAccess()) - val adventurePredicate = AdventureModePredicate.CODEC.parse( - adventureOps, - JsonParser.parseString( - """ - [ - { - "blocks": ["minecraft:oak_log"], - "state": { - "axis": "y", - "distance": {"min": "1", "max": "7"} - }, - "nbt": "{id:\"minecraft:chest\"}", - "components": {"minecraft:max_damage": 1561}, - "predicates": { - "minecraft:max_stack_size": {}, - "minecraft:damage": {} - } - }, - {"blocks": "#minecraft:mineable/axe"} - ] - """.trimIndent() - ) - ).getOrThrow { message -> - IllegalArgumentException("Failed to decode adventure predicate fixture: $message") - } - val adventureComponent = TypedDataComponent(DataComponents.CAN_BREAK, adventurePredicate) - val adventureHash = adventureComponent.encodeValue(ops) - .getOrThrow { message -> IllegalArgumentException("Failed to hash $adventureComponent: $message") } - .let { value -> (value as HashCode).asInt() } - adventureHashes.addProperty("complex", adventureHash) - - val booleanComponentPredicate = AdventureModePredicate.CODEC.parse( - adventureOps, - JsonParser.parseString( - """ - [ - { - "components": { - "minecraft:enchantment_glint_override": false - } - } - ] - """.trimIndent() - ) - ).getOrThrow { message -> - IllegalArgumentException("Failed to decode boolean adventure predicate fixture: $message") - } - val booleanComponent = TypedDataComponent(DataComponents.CAN_BREAK, booleanComponentPredicate) - val booleanComponentHash = booleanComponent.encodeValue(ops) - .getOrThrow { message -> IllegalArgumentException("Failed to hash $booleanComponent: $message") } - .let { value -> (value as HashCode).asInt() } - adventureHashes.addProperty("boolean_component", booleanComponentHash) - root.add("adventure_mode_predicate", adventureHashes) - - val allVariantsFixture = JsonParser.parseString( - """ - [ - { - "block_state_provider": { - "type": "minecraft:rule_based", - "fallback": { - "type": "minecraft:rotated", - "state": {"id": "minecraft:oak_log"} - }, - "rules": [ - { - "if_true": { - "type": "minecraft:matching_blocks", - "offset": [1, 0, -1], - "blocks": ["minecraft:stone", "minecraft:dirt"] - }, - "then": {"id": "minecraft:dirt"} - }, - { - "if_true": { - "type": "minecraft:matching_block_tag", - "offset": [1, 0, 0], - "tag": "minecraft:logs" - }, - "then": { - "type": "minecraft:weighted", - "entries": [ - {"data": {"id": "minecraft:stone"}, "weight": 1}, - {"data": {"id": "minecraft:dirt"}, "weight": 2} - ] - } - }, - { - "if_true": { - "type": "minecraft:matching_fluids", - "offset": [0, 1, 0], - "fluids": ["minecraft:water", "minecraft:lava"] - }, - "then": { - "type": "minecraft:noise_threshold", - "seed": 12, - "noise": {"base_octave": 0, "amplitudes": [1.0]}, - "scale": 1.0, - "threshold": 0.0, - "high_chance": 0.5, - "default_state": {"id": "minecraft:stone"}, - "low_states": [{"id": "minecraft:dirt"}], - "high_states": [{"id": "minecraft:granite"}] - } - }, - { - "if_true": { - "type": "minecraft:matching_biomes", - "biomes": ["minecraft:plains", "minecraft:desert"] - }, - "then": { - "type": "minecraft:noise", - "seed": 13, - "noise": {"base_octave": 0, "amplitudes": [1.0]}, - "scale": 1.0, - "states": [{"id": "minecraft:stone"}, {"id": "minecraft:dirt"}] - } - }, - { - "if_true": { - "type": "minecraft:has_sturdy_face", - "offset": [0, -1, 0], - "direction": "up" - }, - "then": { - "type": "minecraft:dual_noise", - "variety": {"min_inclusive": 1, "max_inclusive": 2}, - "slow_noise": {"base_octave": -1, "amplitudes": [1.0]}, - "slow_scale": 1.0, - "seed": 14, - "noise": {"base_octave": 0, "amplitudes": [1.0]}, - "scale": 1.0, - "states": [{"id": "minecraft:stone"}, {"id": "minecraft:dirt"}] - } - }, - { - "if_true": {"type": "minecraft:solid", "offset": [0, 0, 0]}, - "then": { - "type": "minecraft:randomized_int", - "source": {"id": "minecraft:wheat"}, - "property": "age", - "values": 0 - } - }, - { - "if_true": {"type": "minecraft:replaceable", "offset": [0, 0, 0]}, - "then": { - "type": "minecraft:copy_properties", - "source": {"id": "minecraft:oak_log"} - } - }, - { - "if_true": { - "type": "minecraft:would_survive", - "offset": [0, 1, 0], - "state": {"id": "minecraft:torch"} - }, - "then": {"id": "minecraft:torch"} - }, - { - "if_true": {"type": "minecraft:inside_world_bounds", "offset": [0, 0, 0]}, - "then": {"id": "minecraft:stone"} - }, - { - "if_true": { - "type": "minecraft:any_of", - "predicates": [ - {"type": "minecraft:true"}, - {"type": "minecraft:matching_blocks", "blocks": "minecraft:dirt"} - ] - }, - "then": {"id": "minecraft:stone"} - }, - { - "if_true": { - "type": "minecraft:all_of", - "predicates": [ - {"type": "minecraft:true"}, - {"type": "minecraft:matching_blocks", "blocks": "minecraft:dirt"} - ] - }, - "then": {"id": "minecraft:stone"} - }, - { - "if_true": { - "type": "minecraft:not", - "predicate": {"type": "minecraft:matching_blocks", "blocks": "minecraft:bedrock"} - }, - "then": {"id": "minecraft:stone"} - }, - { - "if_true": {"type": "minecraft:true"}, - "then": {"id": "minecraft:stone"} - }, - { - "if_true": {"type": "minecraft:unobstructed", "offset": [100, 0, 0]}, - "then": {"id": "minecraft:stone"} - }, - { - "if_true": { - "type": "minecraft:height_range", - "min_inclusive": {"absolute": 0}, - "max_inclusive": {"above_bottom": 32} - }, - "then": {"id": "minecraft:stone"} - } - ] - }, - "consume_on_use": false, - "item_damage_per_use": 2 - } - ] - """.trimIndent() - ) - val allVariantsTransformer = BlockTransformer( - listOf( - BlockTransformer.BlockTransformData.CODEC.parse(adventureOps, allVariantsFixture.asJsonArray[0]) - .getOrThrow { message -> - IllegalArgumentException("Failed to decode all-variants block transformer fixture: $message") - } - ) - ) - val allVariants = JsonObject() - allVariants.add( - "codec", - BlockTransformer.DIRECT_CODEC.encodeStart(adventureOps, allVariantsTransformer) - .getOrThrow { message -> - IllegalArgumentException("Failed to encode all-variants block transformer fixture: $message") - } - ) - val transformerNbtOps = server.registryAccess().createSerializationContext(NbtOps.INSTANCE) - allVariants.addProperty( - "snbt", - BlockTransformer.DIRECT_CODEC.encodeStart(transformerNbtOps, allVariantsTransformer) - .getOrThrow { message -> - IllegalArgumentException("Failed to encode all-variants block transformer NBT fixture: $message") - } - .toString() - ) - root.add("block_transformer_all_variants", allVariants) - - return root - } -} diff --git a/src/main/kotlin/com/steelextractor/extractors/WorldgenNoiseSamples.kt b/src/main/kotlin/com/steelextractor/extractors/WorldgenNoiseSamples.kt deleted file mode 100644 index eb66cab..0000000 --- a/src/main/kotlin/com/steelextractor/extractors/WorldgenNoiseSamples.kt +++ /dev/null @@ -1,64 +0,0 @@ -package com.steelextractor.extractors - -import com.google.gson.JsonArray -import com.google.gson.JsonElement -import com.google.gson.JsonObject -import com.steelextractor.SteelExtractor -import net.minecraft.core.Holder -import net.minecraft.resources.Identifier -import net.minecraft.server.MinecraftServer -import net.minecraft.util.RandomSource -import net.minecraft.world.level.biome.Biome -import net.minecraft.world.level.levelgen.LegacyRandomSource -import net.minecraft.world.level.levelgen.densityfunction.DensityFunction -import net.minecraft.world.level.levelgen.densityfunction.SamplerContext -import net.minecraft.world.level.levelgen.synth.BlendedNoise -import net.minecraft.world.level.levelgen.synth.Noise -import net.minecraft.world.level.levelgen.synth.NormalNoise - -/** Samples actual Java float results, including coordinates outside the exact integer range of float. */ -class WorldgenNoiseSamples : SteelExtractor.Extractor { - override fun fileName() = "steel-worldgen/test_assets/noise_samples.json" - - override fun extract(server: MinecraftServer): JsonElement { - val samples = JsonArray() - val positions = listOf( - intArrayOf(0, -56, -10000), intArrayOf(20000068, 296, -19999796), - intArrayOf(0, 0, 0), intArrayOf(1, 1, 1), intArrayOf(-1, -1, -1), - intArrayOf(16777217, 100, -16777217), intArrayOf(29999980, -64, -29999980), - intArrayOf(54545, 63, 34234), intArrayOf(-842, 319, 425) - ) - for (seed in listOf(0L, 13579L, -1L)) { - val parameters = NormalNoise.createParity(-7, 1.0, 1.0) - val normal = parameters.create(LegacyRandomSource(seed)) - val nether = parameters.createForLegacyNetherBiome(LegacyRandomSource(seed)) - val context = object : DensityFunction.CompileContext { - override fun createNoiseSampler(parameters: Holder): Noise = - parameters.value().create(LegacyRandomSource(seed)) - override fun createRandom(id: Identifier): RandomSource = LegacyRandomSource(seed) - override fun createEndIslandRandom(): RandomSource = LegacyRandomSource(seed) - } - val blended = BlendedNoise(0.25, 0.125, 80.0, 160.0, 8.0).compileSampler(context) - for (pos in positions) { - val (x, y, z) = pos - fun record(sampler: String, value: Float) { - samples.add(JsonObject().apply { - addProperty("sampler", sampler) - addProperty("seed", seed) - addProperty("x", x) - addProperty("y", y) - addProperty("z", z) - addProperty("value_bits", value.toRawBits().toLong() and 0xffffffffL) - }) - } - record("normal", normal.get(x.toDouble(), y.toDouble(), z.toDouble())) - record("legacy_nether", nether.get(x.toDouble(), y.toDouble(), z.toDouble())) - record("blended", blended.sampleValue(SamplerContext.EMPTY_UNCACHED, x, y, z)) - if (seed == 0L) { - record("frozen_temperature", Biome.FROZEN_TEMPERATURE_NOISE.get(x * 0.05, z * 0.05)) - } - } - } - return samples - } -} diff --git a/src/main/resources/steel-extractor.mixins.json b/src/main/resources/steel-extractor.mixins.json index 191b6d9..8a1a8b5 100644 --- a/src/main/resources/steel-extractor.mixins.json +++ b/src/main/resources/steel-extractor.mixins.json @@ -5,7 +5,6 @@ "mixins": [ "ExampleMixin", "ChunkStepMixin", - "TerrainPhaseCaptureMixin", "MatchingBlocksPredicateAccessor" ], "injectors": { From e8fb0ec84dd9c2ba7802df3a5fc86d89e938f602 Mon Sep 17 00:00:00 2001 From: Joniii11 <88550325+Joniii11@users.noreply.github.com> Date: Mon, 28 Sep 2026 17:13:18 +0200 Subject: [PATCH 4/9] Trim unrelated extractor diff churn --- settings.gradle | 2 +- .../com/steelextractor/extractors/Blocks.kt | 112 ++++++++------- .../extractors/BuiltInRegistryKeys.kt | 132 +++++++++++------- 3 files changed, 140 insertions(+), 106 deletions(-) diff --git a/settings.gradle b/settings.gradle index dc54e65..662d147 100644 --- a/settings.gradle +++ b/settings.gradle @@ -10,4 +10,4 @@ pluginManagement { } plugins { id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' -} +} \ No newline at end of file diff --git a/src/main/kotlin/com/steelextractor/extractors/Blocks.kt b/src/main/kotlin/com/steelextractor/extractors/Blocks.kt index c4ee873..c7f835b 100644 --- a/src/main/kotlin/com/steelextractor/extractors/Blocks.kt +++ b/src/main/kotlin/com/steelextractor/extractors/Blocks.kt @@ -414,40 +414,53 @@ class Blocks : SteelExtractor.Extractor { return resultJson } - private fun createStateBooleanPropertiesJson( - block: Block, - valueForState: (BlockState) -> Boolean, - ): JsonObject { + private fun stateFluidProperties(state: BlockState): StateFluidProperties { + val fluidState = state.fluidState + return StateFluidProperties( + BuiltInRegistries.FLUID.getKey(fluidState.type).path, + fluidState.amount, + fluidState.hasProperty(FlowingFluid.FALLING) && fluidState.getValue(FlowingFluid.FALLING), + ) + } + + private fun stateFluidPropertiesJson(properties: StateFluidProperties): JsonObject { + val json = JsonObject() + json.addProperty("fluid", properties.fluid) + json.addProperty("amount", properties.amount) + json.addProperty("falling", properties.falling) + return json + } + + private fun createStateFluidPropertiesJson(block: Block): JsonObject { val resultJson = JsonObject() val possibleStates = block.stateDefinition.possibleStates if (possibleStates.isEmpty()) { - resultJson.addProperty("default", false) + resultJson.add("default", stateFluidPropertiesJson(StateFluidProperties("empty", 0, false))) resultJson.add("overwrites", JsonArray()) return resultJson } - val propertyCounts = LinkedHashMap() + val propertyCounts = LinkedHashMap() for (state in possibleStates) { - propertyCounts.merge(valueForState(state), 1, Int::plus) + propertyCounts.merge(stateFluidProperties(state), 1, Int::plus) } - var defaultValue = valueForState(possibleStates[0]) + var defaultProperties = stateFluidProperties(possibleStates[0]) var defaultCount = 0 - for ((value, count) in propertyCounts) { + for ((properties, count) in propertyCounts) { if (count > defaultCount) { - defaultValue = value + defaultProperties = properties defaultCount = count } } - resultJson.addProperty("default", defaultValue) + resultJson.add("default", stateFluidPropertiesJson(defaultProperties)) val overwrites = JsonArray() for (i in possibleStates.indices) { - val currentValue = valueForState(possibleStates[i]) - if (currentValue != defaultValue) { - val overwrite = JsonObject() + val currentProperties = stateFluidProperties(possibleStates[i]) + if (currentProperties != defaultProperties) { + val overwrite = stateFluidPropertiesJson(currentProperties) overwrite.addProperty("offset", i) - overwrite.addProperty("value", currentValue) overwrites.add(overwrite) } } @@ -455,50 +468,40 @@ class Blocks : SteelExtractor.Extractor { return resultJson } - private fun fluidProperties(state: BlockState): StateFluidProperties { - val fluidState = state.fluidState - return StateFluidProperties( - fluid = BuiltInRegistries.FLUID.getKey(fluidState.type).path, - amount = fluidState.amount, - falling = fluidState.hasProperty(FlowingFluid.FALLING) - && fluidState.getValue(FlowingFluid.FALLING), - ) - } - - private fun fluidPropertiesJson(properties: StateFluidProperties): JsonObject { - val json = JsonObject() - json.addProperty("fluid", properties.fluid) - json.addProperty("amount", properties.amount) - json.addProperty("falling", properties.falling) - return json - } - - private fun createFluidStatePropertiesJson(block: Block): JsonObject { + private fun createStateBooleanPropertiesJson( + block: Block, + getValue: (BlockState) -> Boolean, + ): JsonObject { val resultJson = JsonObject() val possibleStates = block.stateDefinition.possibleStates - require(possibleStates.isNotEmpty()) { "block ${BuiltInRegistries.BLOCK.getKey(block)} has no states" } + if (possibleStates.isEmpty()) { + resultJson.addProperty("default", false) + resultJson.add("overwrites", JsonArray()) + return resultJson + } - val propertyCounts = LinkedHashMap() + val propertyCounts = LinkedHashMap() for (state in possibleStates) { - propertyCounts.merge(fluidProperties(state), 1, Int::plus) + propertyCounts.merge(getValue(state), 1, Int::plus) } - var defaultProperties = fluidProperties(possibleStates[0]) + var defaultValue = getValue(possibleStates[0]) var defaultCount = 0 - for ((properties, count) in propertyCounts) { + for ((value, count) in propertyCounts) { if (count > defaultCount) { - defaultProperties = properties + defaultValue = value defaultCount = count } } - resultJson.add("default", fluidPropertiesJson(defaultProperties)) + resultJson.addProperty("default", defaultValue) val overwrites = JsonArray() for (i in possibleStates.indices) { - val currentProperties = fluidProperties(possibleStates[i]) - if (currentProperties != defaultProperties) { - val overwrite = fluidPropertiesJson(currentProperties) + val currentValue = getValue(possibleStates[i]) + if (currentValue != defaultValue) { + val overwrite = JsonObject() overwrite.addProperty("offset", i) + overwrite.addProperty("value", currentValue) overwrites.add(overwrite) } } @@ -521,7 +524,9 @@ class Blocks : SteelExtractor.Extractor { colorCounts.merge(color, 1, Int::plus) } - var defaultColor = possibleStates[0].getMapColor(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).id + var defaultColor = possibleStates[0] + .getMapColor(EmptyBlockGetter.INSTANCE, BlockPos.ZERO) + .id var defaultCount = 0 for ((color, count) in colorCounts) { if (count > defaultCount) { @@ -533,7 +538,9 @@ class Blocks : SteelExtractor.Extractor { val overwrites = JsonArray() for (i in possibleStates.indices) { - val color = possibleStates[i].getMapColor(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).id + val color = possibleStates[i] + .getMapColor(EmptyBlockGetter.INSTANCE, BlockPos.ZERO) + .id if (color != defaultColor) { val overwrite = JsonObject() overwrite.addProperty("offset", i) @@ -609,7 +616,6 @@ class Blocks : SteelExtractor.Extractor { behaviourJson.addProperty("liquid", getPrivateFieldValue(behaviourProps, "liquid")) behaviourJson.addProperty("isAir", getPrivateFieldValue(behaviourProps, "isAir")) - //behaviourJson.addProperty("isRedstoneConductor", getPrivateFieldValue(behaviourProps, "isRedstoneConductor")) //behaviourJson.addProperty("isSuffocating", getPrivateFieldValue(behaviourProps, "isSuffocating")) behaviourJson.addProperty( "requiresCorrectToolForDrops", @@ -637,22 +643,22 @@ class Blocks : SteelExtractor.Extractor { blockJson.add("interaction_shapes", shapesStructureJson.getAsJsonObject("interaction_shapes")) blockJson.add("visual_shapes", shapesStructureJson.getAsJsonObject("visual_shapes")) blockJson.add("light_properties", createLightPropertiesJson(block)) - blockJson.add("fluid_state", createFluidStatePropertiesJson(block)) + blockJson.add("fluid_state", createStateFluidPropertiesJson(block)) blockJson.add("map_color", createStateMapColorJson(block)) blockJson.add( "randomly_ticking", - createStateBooleanPropertiesJson(block) { it.isRandomlyTicking }, + createStateBooleanPropertiesJson(block) { state -> state.isRandomlyTicking }, ) blockJson.add( "suffocating", - createStateBooleanPropertiesJson(block) { - it.isSuffocating(EmptyBlockGetter.INSTANCE, BlockPos.ZERO) + createStateBooleanPropertiesJson(block) { state -> + state.isSuffocating(EmptyBlockGetter.INSTANCE, BlockPos.ZERO) }, ) blockJson.add( "redstone_conductor", - createStateBooleanPropertiesJson(block) { - it.isRedstoneConductor(EmptyBlockGetter.INSTANCE, BlockPos.ZERO) + createStateBooleanPropertiesJson(block) { state -> + state.isRedstoneConductor(EmptyBlockGetter.INSTANCE, BlockPos.ZERO) }, ) diff --git a/src/main/kotlin/com/steelextractor/extractors/BuiltInRegistryKeys.kt b/src/main/kotlin/com/steelextractor/extractors/BuiltInRegistryKeys.kt index 59db411..a2df529 100644 --- a/src/main/kotlin/com/steelextractor/extractors/BuiltInRegistryKeys.kt +++ b/src/main/kotlin/com/steelextractor/extractors/BuiltInRegistryKeys.kt @@ -4,30 +4,30 @@ import com.google.gson.JsonArray import com.google.gson.JsonElement import com.google.gson.JsonObject import com.mojang.serialization.Lifecycle -import com.steelextractor.SteelExtractor import net.minecraft.core.MappedRegistry import net.minecraft.core.Holder import net.minecraft.core.HolderGetter import net.minecraft.core.RegistrationInfo +import net.minecraft.core.registries.Registries +import net.minecraft.data.worldgen.BootstrapContext +import net.minecraft.resources.ResourceKey +import net.minecraft.world.level.block.entity.DecoratedPotPattern +import net.minecraft.world.level.block.entity.DecoratedPotPatterns +import java.util.stream.Stream +import com.steelextractor.SteelExtractor import net.minecraft.core.Registry import net.minecraft.core.particles.ParticleType import net.minecraft.core.particles.ParticleTypes import net.minecraft.core.particles.SimpleParticleType import net.minecraft.core.registries.BuiltInRegistries -import net.minecraft.core.registries.Registries -import net.minecraft.data.worldgen.BootstrapContext -import net.minecraft.resources.ResourceKey import net.minecraft.resources.Identifier import net.minecraft.server.MinecraftServer import net.minecraft.sounds.SoundEvent import net.minecraft.stats.StatType import net.minecraft.world.entity.npc.villager.VillagerProfession import net.minecraft.world.entity.npc.villager.VillagerType -import net.minecraft.world.level.block.entity.DecoratedPotPattern -import net.minecraft.world.level.block.entity.DecoratedPotPatterns import net.minecraft.world.level.gameevent.PositionSourceType import net.minecraft.world.level.saveddata.maps.MapDecorationType -import java.util.stream.Stream private fun extractBuiltInRegistry( registry: Registry, @@ -61,16 +61,30 @@ class ParticleTypeRegistryExtractor : SteelExtractor.Extractor { if (particleType is SimpleParticleType) { return "simple" } + return when (particleType) { - ParticleTypes.BLOCK, ParticleTypes.BLOCK_MARKER, ParticleTypes.FALLING_DUST, - ParticleTypes.DUST_PILLAR, ParticleTypes.BLOCK_CRUMBLE -> "block" - ParticleTypes.ENTITY_EFFECT, ParticleTypes.TINTED_LEAVES, ParticleTypes.FLASH -> "color" + ParticleTypes.BLOCK, + ParticleTypes.BLOCK_MARKER, + ParticleTypes.FALLING_DUST, + ParticleTypes.DUST_PILLAR, + ParticleTypes.BLOCK_CRUMBLE -> "block" + + ParticleTypes.ENTITY_EFFECT, + ParticleTypes.TINTED_LEAVES, + ParticleTypes.FLASH -> "color" + ParticleTypes.DUST -> "dust" ParticleTypes.DUST_COLOR_TRANSITION -> "dust_color_transition" - ParticleTypes.GEYSER, ParticleTypes.GEYSER_PLUME -> "geyser" - ParticleTypes.GEYSER_BASE, ParticleTypes.GEYSER_POOF -> "geyser_base" + ParticleTypes.GEYSER, + ParticleTypes.GEYSER_PLUME -> "geyser" + + ParticleTypes.GEYSER_BASE, + ParticleTypes.GEYSER_POOF -> "geyser_base" + ParticleTypes.DRAGON_BREATH -> "power" - ParticleTypes.EFFECT, ParticleTypes.INSTANT_EFFECT -> "spell" + ParticleTypes.EFFECT, + ParticleTypes.INSTANT_EFFECT -> "spell" + ParticleTypes.ITEM -> "item" ParticleTypes.SCULK_CHARGE -> "sculk_charge" ParticleTypes.SHRIEK -> "shriek" @@ -81,6 +95,39 @@ class ParticleTypeRegistryExtractor : SteelExtractor.Extractor { } } +class PositionSourceTypeRegistryExtractor : SteelExtractor.Extractor { + override fun fileName(): String { + return "steel-registry/build_assets/position_source_types.json" + } + + override fun extract(server: MinecraftServer): JsonElement { + return extractBuiltInRegistry(BuiltInRegistries.POSITION_SOURCE_TYPE) { positionSourceType: PositionSourceType<*>, _ -> + when (positionSourceType) { + PositionSourceType.BLOCK, + PositionSourceType.ENTITY -> Unit + + else -> error( + "Unknown position source type: ${BuiltInRegistries.POSITION_SOURCE_TYPE.getKey(positionSourceType)}" + ) + } + } + } +} + +class MapDecorationTypeRegistryExtractor : SteelExtractor.Extractor { + override fun fileName(): String { + return "steel-registry/build_assets/map_decoration_types.json" + } + + override fun extract(server: MinecraftServer): JsonElement { + return extractBuiltInRegistry(BuiltInRegistries.MAP_DECORATION_TYPE) { type: MapDecorationType, json -> + json.addProperty("asset_id", type.assetId().toString()) + json.addProperty("show_on_item_frame", type.showOnItemFrame()) + json.addProperty("track_count", type.trackCount()) + } + } +} + class VillagerTypeRegistryExtractor : SteelExtractor.Extractor { override fun fileName(): String { return "steel-registry/build_assets/villager_types.json" @@ -112,6 +159,26 @@ class VillagerProfessionRegistryExtractor : SteelExtractor.Extractor { } } +class CustomStatRegistryExtractor : SteelExtractor.Extractor { + override fun fileName(): String { + return "steel-registry/build_assets/custom_stats.json" + } + + override fun extract(server: MinecraftServer): JsonElement { + return extractBuiltInRegistry(BuiltInRegistries.CUSTOM_STAT) { _: Identifier, _ -> } + } +} + +class StatTypeRegistryExtractor : SteelExtractor.Extractor { + override fun fileName(): String { + return "steel-registry/build_assets/stat_types.json" + } + + override fun extract(server: MinecraftServer): JsonElement { + return extractBuiltInRegistry(BuiltInRegistries.STAT_TYPE) { _: StatType<*>, _ -> } + } +} + class DecoratedPotPatternRegistryExtractor : SteelExtractor.Extractor { override fun fileName(): String { return "steel-registry/build_assets/decorated_pot_patterns.json" @@ -142,42 +209,3 @@ class DecoratedPotPatternRegistryExtractor : SteelExtractor.Extractor { } } } - -class PositionSourceTypeRegistryExtractor : SteelExtractor.Extractor { - override fun fileName(): String = "steel-registry/build_assets/position_source_types.json" - - override fun extract(server: MinecraftServer): JsonElement { - return extractBuiltInRegistry(BuiltInRegistries.POSITION_SOURCE_TYPE) { type: PositionSourceType<*>, _ -> - when (type) { - PositionSourceType.BLOCK, PositionSourceType.ENTITY -> Unit - else -> error("Unknown position source type: ${BuiltInRegistries.POSITION_SOURCE_TYPE.getKey(type)}") - } - } - } -} - -class MapDecorationTypeRegistryExtractor : SteelExtractor.Extractor { - override fun fileName(): String = "steel-registry/build_assets/map_decoration_types.json" - - override fun extract(server: MinecraftServer): JsonElement { - return extractBuiltInRegistry(BuiltInRegistries.MAP_DECORATION_TYPE) { type: MapDecorationType, json -> - json.addProperty("asset_id", type.assetId().toString()) - json.addProperty("show_on_item_frame", type.showOnItemFrame()) - json.addProperty("track_count", type.trackCount()) - } - } -} - -class CustomStatRegistryExtractor : SteelExtractor.Extractor { - override fun fileName(): String = "steel-registry/build_assets/custom_stats.json" - - override fun extract(server: MinecraftServer): JsonElement = - extractBuiltInRegistry(BuiltInRegistries.CUSTOM_STAT) { _: Identifier, _ -> } -} - -class StatTypeRegistryExtractor : SteelExtractor.Extractor { - override fun fileName(): String = "steel-registry/build_assets/stat_types.json" - - override fun extract(server: MinecraftServer): JsonElement = - extractBuiltInRegistry(BuiltInRegistries.STAT_TYPE) { _: StatType<*>, _ -> } -} From d1e4239f70ef8bc5dab43b5b4c5eea92cb503fc6 Mon Sep 17 00:00:00 2001 From: Joniii11 <88550325+Joniii11@users.noreply.github.com> Date: Mon, 28 Sep 2026 17:30:29 +0200 Subject: [PATCH 5/9] Remove obsolete strippables extraction --- .../MatchingBlocksPredicateAccessor.java | 13 ----- .../com/steelextractor/SteelExtractor.kt | 2 - .../steelextractor/extractors/Strippables.kt | 53 ------------------- .../resources/steel-extractor.mixins.json | 3 +- 4 files changed, 1 insertion(+), 70 deletions(-) delete mode 100644 src/main/java/com/steelextractor/mixin/MatchingBlocksPredicateAccessor.java delete mode 100644 src/main/kotlin/com/steelextractor/extractors/Strippables.kt diff --git a/src/main/java/com/steelextractor/mixin/MatchingBlocksPredicateAccessor.java b/src/main/java/com/steelextractor/mixin/MatchingBlocksPredicateAccessor.java deleted file mode 100644 index db335f4..0000000 --- a/src/main/java/com/steelextractor/mixin/MatchingBlocksPredicateAccessor.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.steelextractor.mixin; - -import net.minecraft.core.HolderSet; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.levelgen.blockpredicates.MatchingBlocksPredicate; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(MatchingBlocksPredicate.class) -public interface MatchingBlocksPredicateAccessor { - @Accessor("blocks") - HolderSet getBlocks(); -} diff --git a/src/main/kotlin/com/steelextractor/SteelExtractor.kt b/src/main/kotlin/com/steelextractor/SteelExtractor.kt index 56ceb27..946b436 100644 --- a/src/main/kotlin/com/steelextractor/SteelExtractor.kt +++ b/src/main/kotlin/com/steelextractor/SteelExtractor.kt @@ -34,7 +34,6 @@ import com.steelextractor.extractors.PositionSourceTypeRegistryExtractor import com.steelextractor.extractors.StatTypeRegistryExtractor import com.steelextractor.extractors.GameEvents import com.steelextractor.extractors.Weathering -import com.steelextractor.extractors.Strippables import net.minecraft.resources.ResourceKey import net.minecraft.server.level.ChunkLevel import net.minecraft.server.level.ServerLevel @@ -271,7 +270,6 @@ object SteelExtractor : ModInitializer { addUnlessDisabled("LEVEL_EVENTS") { LevelEvents() } addUnlessDisabled("TAGS") { Tags() } addUnlessDisabled("STRUCTURE_STARTS") { StructureStarts() } - addUnlessDisabled("STRIPPABLES") { Strippables() } addUnlessDisabled("WEATHERING") { Weathering() } addUnlessDisabled("CANDLE_CAKES") { CandleCakes() } addUnlessDisabled("WAXABLES") { Waxables() } diff --git a/src/main/kotlin/com/steelextractor/extractors/Strippables.kt b/src/main/kotlin/com/steelextractor/extractors/Strippables.kt deleted file mode 100644 index 0a00b3e..0000000 --- a/src/main/kotlin/com/steelextractor/extractors/Strippables.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.steelextractor.extractors - -import com.google.gson.JsonElement -import com.google.gson.JsonObject -import com.steelextractor.SteelExtractor -import com.steelextractor.mixin.MatchingBlocksPredicateAccessor -import net.minecraft.core.registries.BuiltInRegistries -import net.minecraft.core.registries.Registries -import net.minecraft.server.MinecraftServer -import net.minecraft.world.item.component.BlockTransformers -import net.minecraft.world.level.levelgen.blockpredicates.MatchingBlocksPredicate -import net.minecraft.world.level.levelgen.feature.stateproviders.CopyPropertiesProvider -import net.minecraft.world.level.levelgen.feature.stateproviders.RuleBasedStateProvider -import net.minecraft.world.level.levelgen.feature.stateproviders.SimpleStateProvider - -class Strippables : SteelExtractor.Extractor { - override fun fileName(): String { - return "steel-core/build/strippables.json" - } - - @Suppress("CAST_NEVER_SUCCEEDS") - override fun extract(server: MinecraftServer): JsonElement { - val topLevelJson = JsonObject() - val axe = server.registryAccess() - .lookupOrThrow(Registries.BLOCK_TRANSFORMER) - .getOrThrow(BlockTransformers.AXE) - .value() - - for (transform in axe.transforms()) { - val provider = transform.blockStateProvider().value() as? RuleBasedStateProvider - ?: error("Unexpected axe transform provider: ${transform.blockStateProvider().value()}") - for (rule in provider.rules()) { - val predicate = rule.ifTrue() as? MatchingBlocksPredicate - ?: error("Unexpected axe transform predicate: ${rule.ifTrue()}") - val target = rule.`then`().value() as? CopyPropertiesProvider - ?: error("Unexpected axe transform target: ${rule.`then`().value()}") - val source = target.source().value() as? SimpleStateProvider - ?: error("Unexpected axe transform source: ${target.source().value()}") - val stripped = source.state().block - - // Fabric Mixin injects this accessor into MatchingBlocksPredicate at runtime. - for (normal in (predicate as MatchingBlocksPredicateAccessor).blocks) { - topLevelJson.addProperty( - BuiltInRegistries.BLOCK.getKey(normal.value()).path, - BuiltInRegistries.BLOCK.getKey(stripped).path - ) - } - } - } - - return topLevelJson - } -} diff --git a/src/main/resources/steel-extractor.mixins.json b/src/main/resources/steel-extractor.mixins.json index 8a1a8b5..c6ed3cd 100644 --- a/src/main/resources/steel-extractor.mixins.json +++ b/src/main/resources/steel-extractor.mixins.json @@ -4,8 +4,7 @@ "compatibilityLevel": "JAVA_25", "mixins": [ "ExampleMixin", - "ChunkStepMixin", - "MatchingBlocksPredicateAccessor" + "ChunkStepMixin" ], "injectors": { "defaultRequire": 1 From 4c3d697a3e6083b7a93b9118df8b40ed7bec921c Mon Sep 17 00:00:00 2001 From: Joniii11 <88550325+Joniii11@users.noreply.github.com> Date: Mon, 28 Sep 2026 17:33:24 +0200 Subject: [PATCH 6/9] Drop unused decorated pot pattern output --- .../com/steelextractor/SteelExtractor.kt | 2 - .../extractors/BuiltInRegistryKeys.kt | 42 ------------------- 2 files changed, 44 deletions(-) diff --git a/src/main/kotlin/com/steelextractor/SteelExtractor.kt b/src/main/kotlin/com/steelextractor/SteelExtractor.kt index 946b436..800fef5 100644 --- a/src/main/kotlin/com/steelextractor/SteelExtractor.kt +++ b/src/main/kotlin/com/steelextractor/SteelExtractor.kt @@ -25,7 +25,6 @@ import com.steelextractor.extractors.VillagerProfessionRegistryExtractor import com.steelextractor.extractors.VillagerTypeRegistryExtractor import com.steelextractor.extractors.CandleCakes import com.steelextractor.extractors.ChunkStageHashes -import com.steelextractor.extractors.DecoratedPotPatternRegistryExtractor import com.steelextractor.extractors.CustomStatRegistryExtractor import com.steelextractor.extractors.Commands import com.steelextractor.extractors.EntityVariantRegistries @@ -250,7 +249,6 @@ object SteelExtractor : ModInitializer { addUnlessDisabled("POSITION_SOURCE_TYPES") { PositionSourceTypeRegistryExtractor() } addUnlessDisabled("VILLAGER_TYPES") { VillagerTypeRegistryExtractor() } addUnlessDisabled("VILLAGER_PROFESSIONS") { VillagerProfessionRegistryExtractor() } - addUnlessDisabled("DECORATED_POT_PATTERNS") { DecoratedPotPatternRegistryExtractor() } addUnlessDisabled("PACKETS") { Packets() } addUnlessDisabled("MENU_TYPES") { MenuTypes() } addUnlessDisabled("ENTITIES") { Entities() } diff --git a/src/main/kotlin/com/steelextractor/extractors/BuiltInRegistryKeys.kt b/src/main/kotlin/com/steelextractor/extractors/BuiltInRegistryKeys.kt index a2df529..c0b9ffb 100644 --- a/src/main/kotlin/com/steelextractor/extractors/BuiltInRegistryKeys.kt +++ b/src/main/kotlin/com/steelextractor/extractors/BuiltInRegistryKeys.kt @@ -3,17 +3,6 @@ package com.steelextractor.extractors import com.google.gson.JsonArray import com.google.gson.JsonElement import com.google.gson.JsonObject -import com.mojang.serialization.Lifecycle -import net.minecraft.core.MappedRegistry -import net.minecraft.core.Holder -import net.minecraft.core.HolderGetter -import net.minecraft.core.RegistrationInfo -import net.minecraft.core.registries.Registries -import net.minecraft.data.worldgen.BootstrapContext -import net.minecraft.resources.ResourceKey -import net.minecraft.world.level.block.entity.DecoratedPotPattern -import net.minecraft.world.level.block.entity.DecoratedPotPatterns -import java.util.stream.Stream import com.steelextractor.SteelExtractor import net.minecraft.core.Registry import net.minecraft.core.particles.ParticleType @@ -178,34 +167,3 @@ class StatTypeRegistryExtractor : SteelExtractor.Extractor { return extractBuiltInRegistry(BuiltInRegistries.STAT_TYPE) { _: StatType<*>, _ -> } } } - -class DecoratedPotPatternRegistryExtractor : SteelExtractor.Extractor { - override fun fileName(): String { - return "steel-registry/build_assets/decorated_pot_patterns.json" - } - - override fun extract(server: MinecraftServer): JsonElement { - val registry = MappedRegistry(Registries.DECORATED_POT_PATTERN, Lifecycle.stable()) - val context = object : BootstrapContext { - override fun register( - key: ResourceKey, - value: DecoratedPotPattern - ): Holder.Reference { - return registry.register(key, value, RegistrationInfo.BUILT_IN) - } - - override fun lookup(key: ResourceKey>): HolderGetter { - error("Decorated pot pattern bootstrap unexpectedly looked up $key") - } - - @Deprecated("Decorated pot pattern bootstrap never lists context elements.") - override fun listContextElements(key: ResourceKey>): Stream> { - error("Decorated pot pattern bootstrap unexpectedly listed $key") - } - } - DecoratedPotPatterns.bootstrap(context) - return extractBuiltInRegistry(registry) { pattern: DecoratedPotPattern, json -> - json.addProperty("asset_id", pattern.assetId().toString()) - } - } -} From 1345230fe1bb99ff155ea7ad0fe4dc5f20e74cd2 Mon Sep 17 00:00:00 2001 From: Joniii11 <88550325+Joniii11@users.noreply.github.com> Date: Mon, 28 Sep 2026 17:37:08 +0200 Subject: [PATCH 7/9] Extract entity operator-only NBT flag --- src/main/kotlin/com/steelextractor/extractors/Entities.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/com/steelextractor/extractors/Entities.kt b/src/main/kotlin/com/steelextractor/extractors/Entities.kt index d3f154a..5dbf55a 100644 --- a/src/main/kotlin/com/steelextractor/extractors/Entities.kt +++ b/src/main/kotlin/com/steelextractor/extractors/Entities.kt @@ -141,6 +141,7 @@ class Entities : SteelExtractor.Extractor { entityTypeJson.addProperty("summonable", entityType.canSummon()) entityTypeJson.addProperty("allowed_in_peaceful", entityType.isAllowedInPeaceful()) entityTypeJson.addProperty("can_serialize", entityType.canSerialize()) + entityTypeJson.addProperty("only_op_can_set_nbt", entityType.onlyOpCanSetNbt()) entityTypeJson.addProperty("can_spawn_far_from_player", entityType.canSpawnFarFromPlayer()) // Synched data From 24dc65a7c72d71ef6c7d76678d315c9ea9a9cf9c Mon Sep 17 00:00:00 2001 From: Joniii11 <88550325+Joniii11@users.noreply.github.com> Date: Mon, 28 Sep 2026 17:47:42 +0200 Subject: [PATCH 8/9] Restore entity piston push reaction extraction --- src/main/kotlin/com/steelextractor/extractors/Entities.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/com/steelextractor/extractors/Entities.kt b/src/main/kotlin/com/steelextractor/extractors/Entities.kt index 5dbf55a..d8710c3 100644 --- a/src/main/kotlin/com/steelextractor/extractors/Entities.kt +++ b/src/main/kotlin/com/steelextractor/extractors/Entities.kt @@ -553,6 +553,7 @@ class Entities : SteelExtractor.Extractor { flagsJson.addProperty("is_pushed_by_fluid", entity.isPushedByFluid()) flagsJson.addProperty("can_freeze", entity.canFreeze()) flagsJson.addProperty("can_be_hit_by_projectile", entity.canBeHitByProjectile()) + flagsJson.addProperty("piston_push_reaction", entity.getPistonPushReaction().name) if (entity is LivingEntity) { flagsJson.addProperty("is_sensitive_to_water", entity.isSensitiveToWater) From f2d30e4f3d917dbf9524b47d4fdcb321909d2006 Mon Sep 17 00:00:00 2001 From: Joniii11 <88550325+Joniii11@users.noreply.github.com> Date: Mon, 5 Oct 2026 20:54:56 +0200 Subject: [PATCH 9/9] fix(tags): exclude Minecraft tags from extractor output --- src/main/kotlin/com/steelextractor/extractors/Tags.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/steelextractor/extractors/Tags.kt b/src/main/kotlin/com/steelextractor/extractors/Tags.kt index 7ab63a5..2e72921 100644 --- a/src/main/kotlin/com/steelextractor/extractors/Tags.kt +++ b/src/main/kotlin/com/steelextractor/extractors/Tags.kt @@ -20,7 +20,7 @@ class Tags : SteelExtractor.Extractor { val blockTagsJson = JsonObject() BuiltInRegistries.BLOCK.getTags().forEach { namedHolderSet -> - if (namedHolderSet.size() > 0) { + if (namedHolderSet.size() > 0 && namedHolderSet.key().location().namespace != "minecraft") { val entriesArray = JsonArray() namedHolderSet.stream().forEach { holder -> holder.unwrapKey().ifPresent { key -> @@ -34,7 +34,7 @@ class Tags : SteelExtractor.Extractor { val itemTagsJson = JsonObject() BuiltInRegistries.ITEM.getTags().forEach { namedHolderSet -> - if (namedHolderSet.size() > 0) { + if (namedHolderSet.size() > 0 && namedHolderSet.key().location().namespace != "minecraft") { val entriesArray = JsonArray() namedHolderSet.stream().forEach { holder -> holder.unwrapKey().ifPresent { key ->