From 49b2fc37371a4d4ed330cdffc79233e408dbf851 Mon Sep 17 00:00:00 2001 From: JohnBraham Date: Fri, 23 Oct 2026 16:26:24 +0100 Subject: [PATCH 1/2] Port Power Advantage to Minecraft 1.13.2 --- .github/workflows/ci.yml | 70 +- .github/workflows/codeql-analysis.yml | 50 +- .github/workflows/validate-gradle-build.yml | 4 +- .gitignore | 38 +- CHANGELOG.txt | 13 + README.md | 61 +- build.gradle | 514 +- docs/1.13/CHUNK-AND-DRILLS.md | 339 + docs/1.13/CONNECTED-MIGRATION.md | 223 + docs/1.13/CURRENT-QUALIFICATION.md | 216 + docs/1.13/DIMENSIONS-AND-ENTITY-LIFECYCLE.md | 243 + docs/1.13/DRILL-PROGRESS-REPAIR.md | 156 + docs/1.13/ECLIPSE-METADATA.md | 40 + docs/1.13/ENDURANCE.md | 229 + docs/1.13/MANUFACTURING-PROGRESSION.md | 188 + docs/1.13/MIGRATION-MATRIX.md | 196 + docs/1.13/MIGRATION.md | 56 + docs/1.13/OIL-TRANSFER.md | 65 + .../PARTIAL-NETWORKS-AND-EXTENDED-DRILLS.md | 322 + docs/1.13/PLAYER-AND-ENTITY-MIGRATION.md | 398 + docs/1.13/RECIPE-DISCOVERY.md | 232 + docs/1.13/TRANSFORMER-ORDERING.md | 243 + gradle.properties | 39 +- libs/RF-API.jar | Bin 7251 -> 0 bytes libs/RF-API.zip | Bin 7160 -> 0 bytes .../poweradvantage/PowerAdvantage.java | 743 -- .../compat/BaseMetalsCompat.java | 129 - .../mcmoddev/poweradvantage/init/Blocks.java | 188 - .../poweradvantage/init/Entities.java | 89 - .../mcmoddev/poweradvantage/init/Fluids.java | 146 - .../mcmoddev/poweradvantage/init/Fuels.java | 44 - .../mcmoddev/poweradvantage/init/Items.java | 82 - .../poweradvantage/init/ModSupport.java | 124 - .../mcmoddev/poweradvantage/init/Recipes.java | 85 - .../machines/conveyors/BlockConveyor.java | 127 - .../conveyors/BlockConveyorFilter.java | 124 - .../conveyors/TileEntityOreFilter.java | 24 - .../conveyors/TileEntitySmeltableFilter.java | 14 - .../FluidDischargeTileEntity.java | 273 - .../fluidmachines/FluidDrainTileEntity.java | 275 - .../fluidmachines/StorageTankBlock.java | 148 - .../TerminalFluidPipeTileEntity.java | 177 - .../machines/fluidmachines/package-info.java | 12 - .../NotRecipeModeConditionFactory.java | 19 - .../OreDictEmptyConditionFactory.java | 17 - .../condition/RecipeModeConditionFactory.java | 19 - .../util/FluidHandlerHelper.java | 26 - .../poweradvantage/util/FluidIdHelper.java | 29 - .../poweradvantage/api/ConduitBlock.java | 24 +- .../cyano/poweradvantage/api/ConduitType.java | 2 +- .../api/FluidCategoryRegistry.java | 87 + .../cyano/poweradvantage/api/GUIBlock.java | 57 +- .../api/PowerConnectorContext.java | 8 +- .../poweradvantage/api/PoweredEntity.java | 26 +- .../api/fluid/ColoredFluid.java | 44 +- .../api/fluid/FluidConduitBlock.java | 10 +- .../api/fluid/FluidNetworkApi.java | 86 + .../api/fluid/FluidPoweredEntity.java | 52 +- .../poweradvantage/api/fluid/FluidStack.java | 56 + .../poweradvantage/api/fluid/FluidTank.java | 84 + .../api/fluid/FluidTankInfo.java | 14 + .../api/fluid/IAdvantageFluidHandler.java | 19 + .../api/fluid/InteractiveFluidBlock.java | 124 +- .../api/migration/LegacyConfigMigration.java | 178 + .../api/migration/LegacyMigrationApi.java | 167 + .../migration/LegacyOreSpawnConfigBridge.java | 273 + .../modsupport/LightWeightPowerRegistry.java | 10 +- .../api/modsupport/TileEntityConverter.java | 17 +- .../api/modsupport/Wrappers.java | 224 +- .../api/modsupport/rf/BlockRFConverter.java | 93 +- .../modsupport/rf/TileEntityRFConverter.java | 233 +- .../rf/TileEntityRFElectricityConverter.java | 6 +- .../rf/TileEntityRFQuantumConverter.java | 6 +- .../rf/TileEntityRFSteamConverter.java | 4 +- .../techreborn/BlockTRConverter.java | 114 +- .../techreborn/TileEntityTRConverter.java | 257 +- .../TileEntityTRElectricityConverter.java | 6 +- .../api/simple/BlockSimpleFluidConduit.java | 406 +- .../api/simple/BlockSimpleFluidMachine.java | 4 +- .../api/simple/BlockSimplePowerConduit.java | 467 +- .../api/simple/BlockSimplePowerMachine.java | 106 +- .../api/simple/SimpleMachineGUI.java | 28 +- .../simple/TileEntitySimpleFluidMachine.java | 86 +- .../simple/TileEntitySimplePowerMachine.java | 52 +- .../mc/poweradvantage/PowerAdvantage.java | 147 + .../moddev/mc}/poweradvantage/RecipeMode.java | 2 +- .../mc}/poweradvantage/blocks/BlockFrame.java | 18 +- .../blocks/BlockPowerSwitch.java | 60 +- .../poweradvantage/blocks}/package-info.java | 4 +- .../client/ClientOilRenderer.java | 64 + .../compat/BaseMetalsCompat.java | 40 + .../conduitnetwork/ConduitNetworkManager.java | 33 +- .../conduitnetwork/ConduitRegistry.java | 56 +- .../conduitnetwork/package-info.java | 2 +- .../config/PowerAdvantageConfig.java | 96 + .../poweradvantage/graphics/package-info.java | 2 +- .../gui/FilteredFluidTankGUI.java | 12 +- .../mc}/poweradvantage/gui/FluidTankGUI.java | 75 +- .../gui/FunctionalCreativeTab.java | 18 +- .../mc}/poweradvantage/gui/StillGUI.java | 8 +- .../mc/poweradvantage/gui}/package-info.java | 4 +- .../moddev/mc/poweradvantage/init/Blocks.java | 154 + .../mc/poweradvantage/init/Entities.java | 88 + .../moddev/mc/poweradvantage/init/Fluids.java | 158 + .../moddev/mc/poweradvantage/init/Fuels.java | 32 + .../moddev/mc}/poweradvantage/init/GUI.java | 10 +- .../mc}/poweradvantage/init/ItemGroups.java | 17 +- .../moddev/mc/poweradvantage/init/Items.java | 62 + .../mc}/poweradvantage/init/Materials.java | 2 +- .../mc/poweradvantage/init/ModSupport.java | 64 + .../mc/poweradvantage/init/Recipes.java | 57 + .../poweradvantage/init/TreasureChests.java | 10 +- .../mc}/poweradvantage/init/Villages.java | 10 +- .../mc}/poweradvantage/init/WorldGen.java | 2 +- .../mc}/poweradvantage/init/package-info.java | 2 +- .../poweradvantage/items/RotationTool.java | 25 +- .../poweradvantage/items}/package-info.java | 4 +- .../machines/conveyors/BlockConveyor.java | 52 + .../conveyors/BlockConveyorFilter.java | 68 + .../conveyors/TileEntityBlockFilter.java | 6 +- .../conveyors/TileEntityConveyor.java | 69 +- .../conveyors/TileEntityConveyorFilter.java | 28 +- .../conveyors/TileEntityFoodFilter.java | 6 +- .../conveyors/TileEntityFuelFilter.java | 6 +- .../conveyors/TileEntityInventoryFilter.java | 7 +- .../conveyors/TileEntityOreFilter.java | 19 + .../conveyors/TileEntityOverflowFilter.java | 6 +- .../conveyors/TileEntityPlantFilter.java | 4 +- .../conveyors/TileEntitySmeltableFilter.java | 20 + .../machines/conveyors/package-info.java | 4 +- .../creative/InfiniteEnergyBlock.java | 7 +- .../creative/InfiniteEnergyTileEntity.java | 17 +- .../fluidmachines/FluidDischargeBlock.java | 49 +- .../FluidDischargeTileEntity.java | 105 + .../fluidmachines/FluidDrainBlock.java | 36 +- .../fluidmachines/FluidDrainTileEntity.java | 110 + .../fluidmachines/FluidPipeBlock.java | 8 +- .../fluidmachines/MetalTankBlock.java | 5 +- .../fluidmachines/MetalTankTileEntity.java | 23 +- .../machines/fluidmachines/StillBlock.java | 40 +- .../fluidmachines/StillTileEntity.java | 68 +- .../fluidmachines/StorageTankBlock.java | 92 + .../fluidmachines/StorageTankTileEntity.java | 14 +- .../modsupport/TerminalFluidPipeBlock.java | 17 +- .../TerminalFluidPipeTileEntity.java | 112 + .../machines/fluidmachines/package-info.java | 12 + .../mc}/poweradvantage/math/BlockPos4D.java | 8 +- .../mc}/poweradvantage/math/Integer2D.java | 2 +- .../mc/poweradvantage/math}/package-info.java | 2 +- .../migration/AdvantageLegacyMigrations.java | 92 + .../migration/LegacyWorldDataHook.java | 538 ++ .../mc}/poweradvantage/package-info.java | 2 +- .../poweradvantage/recipe/MaterialTags.java | 42 + .../mc/poweradvantage/recipe/PartsLoot.java | 65 + .../recipe/RecipeConditions.java | 36 + .../recipe/RecipeDiscovery.java | 35 + .../poweradvantage/registry/FuelRegistry.java | 17 +- .../registry/ITileEntityGUI.java | 8 +- .../registry/MachineGUIRegistry.java | 67 +- .../poweradvantage/registry/package-info.java | 2 +- .../still/recipe/DistillationRecipe.java | 14 +- .../recipe/DistillationRecipeRegistry.java | 45 +- .../still/recipe/ExplicitFluidReference.java | 14 +- .../registry/still/recipe/FluidReference.java | 6 +- .../recipe/RegistryNameFluidReference.java | 13 +- .../util/FluidContainerHelper.java | 56 + .../util/FluidHandlerHelper.java | 18 + .../mc/poweradvantage/util/FluidIdHelper.java | 27 + .../poweradvantage/util/HashCodeHelper.java | 2 +- .../poweradvantage/util/InventoryWrapper.java | 18 +- .../mc}/poweradvantage/util/PowerHelper.java | 27 +- .../util/ReversibleHashMap.java | 4 +- .../poweradvantage/util/ReversibleMap.java | 2 +- .../mc/poweradvantage/util}/package-info.java | 2 +- src/main/resources/META-INF/coremods.json | 6 + src/main/resources/META-INF/mods.toml | 40 + .../blockstates/converter_rf_electricity.json | 27 +- .../blockstates/converter_rf_quantum.json | 27 +- .../blockstates/converter_rf_steam.json | 27 +- .../blockstates/converter_tr_electricity.json | 27 +- .../poweradvantage/blockstates/crude_oil.json | 8 +- .../blockstates/fluid_discharge.json | 27 +- .../blockstates/fluid_drain.json | 27 +- .../blockstates/fluid_metal_tank.json | 31 +- .../blockstates/fluid_pipe.json | 455 +- .../blockstates/fluid_pipe_terminal.json | 455 +- .../blockstates/fluid_storage_tank.json | 27 +- .../blockstates/fluid_switch.json | 16 +- .../blockstates/infinite_electricity.json | 27 +- .../blockstates/infinite_quantum.json | 27 +- .../blockstates/infinite_steam.json | 27 +- .../blockstates/item_conveyor.json | 33 +- .../blockstates/item_filter_block.json | 23 +- .../blockstates/item_filter_food.json | 23 +- .../blockstates/item_filter_fuel.json | 23 +- .../blockstates/item_filter_inventory.json | 23 +- .../blockstates/item_filter_ore.json | 23 +- .../blockstates/item_filter_overflow.json | 23 +- .../blockstates/item_filter_plant.json | 23 +- .../blockstates/item_filter_smelt.json | 23 +- .../blockstates/refined_oil.json | 6 +- .../blockstates/steel_frame.json | 12 +- .../poweradvantage/blockstates/still.json | 46 +- .../assets/poweradvantage/lang/de_de.json | 77 + .../assets/poweradvantage/lang/de_de.lang | 55 - .../assets/poweradvantage/lang/en_au.json | 77 + .../assets/poweradvantage/lang/en_au.lang | 54 - .../assets/poweradvantage/lang/en_ca.json | 77 + .../assets/poweradvantage/lang/en_ca.lang | 54 - .../assets/poweradvantage/lang/en_en.json | 77 + .../assets/poweradvantage/lang/en_en.lang | 54 - .../assets/poweradvantage/lang/en_gb.json | 77 + .../assets/poweradvantage/lang/en_gb.lang | 53 - .../assets/poweradvantage/lang/en_pt.json | 77 + .../assets/poweradvantage/lang/en_pt.lang | 53 - .../assets/poweradvantage/lang/en_us.json | 75 + .../assets/poweradvantage/lang/en_us.lang | 52 - .../assets/poweradvantage/lang/es_es.json | 75 + .../assets/poweradvantage/lang/es_es.lang | 56 - .../assets/poweradvantage/lang/es_mx.json | 75 + .../assets/poweradvantage/lang/es_mx.lang | 56 - .../assets/poweradvantage/lang/it_it.json | 75 + .../assets/poweradvantage/lang/it_it.lang | 55 - .../assets/poweradvantage/lang/ja_jp.json | 75 + .../assets/poweradvantage/lang/ja_jp.lang | 56 - .../assets/poweradvantage/lang/ko_kr.json | 75 + .../assets/poweradvantage/lang/ko_kr.lang | 56 - .../assets/poweradvantage/lang/pt_br.json | 75 + .../assets/poweradvantage/lang/pt_br.lang | 54 - .../assets/poweradvantage/lang/zh_cn.json | 75 + .../assets/poweradvantage/lang/zh_cn.lang | 57 - .../models/block/converter_rf_quantum.json | 2 +- .../models/block/crude_oil.json | 6 + .../models/block/refined_oil.json | 6 + .../models/block/still_off.json | 6 +- .../poweradvantage/models/block/still_on.json | 6 +- .../poweradvantage/models/item/crude_oil.json | 3 + .../models/item/crude_oil_bucket.json | 6 + .../models/item/refined_oil.json | 3 + .../models/item/refined_oil_bucket.json | 6 + .../poweradvantage/recipes/_factories.json | 7 - .../recipes/fluid_discharge.json | 30 - .../poweradvantage/recipes/fluid_drain.json | 30 - .../recipes/fluid_metal_tank_iron_normal.json | 28 - .../recipes/fluid_metal_tank_steel.json | 22 - .../fluid_pipe_copper_apocalyptic.json | 25 - .../recipes/fluid_pipe_copper_normal.json | 25 - .../recipes/fluid_pipe_iron_apocalyptic.json | 25 - .../recipes/fluid_pipe_iron_normal.json | 25 - .../recipes/fluid_pipe_iron_tech.json | 25 - .../recipes/fluid_pipe_lead_apocalyptic.json | 25 - .../recipes/fluid_storage_tank.json | 22 - .../poweradvantage/recipes/fluid_switch.json | 24 - .../poweradvantage/recipes/item_conveyor.json | 26 - .../recipes/item_filter_block.json | 24 - .../recipes/item_filter_food.json | 23 - .../recipes/item_filter_fuel.json | 24 - .../recipes/item_filter_inventory.json | 23 - .../recipes/item_filter_ore.json | 24 - .../recipes/item_filter_overflow.json | 19 - .../recipes/item_filter_plant.json | 24 - .../recipes/item_filter_smelt.json | 23 - .../poweradvantage/recipes/rotator_tool.json | 22 - .../sprocket_bronze_nugget_tech_no_rod.json | 33 - .../recipes/sprocket_rod_normal.json | 29 - .../recipes/sprocket_rod_tech.json | 29 - .../recipes/sprocket_stickwood_normal.json | 29 - .../poweradvantage/recipes/steel_frame.json | 18 - .../assets/poweradvantage/recipes/still.json | 23 - .../coremods/poweradvantage_113_migration.js | 96 + .../coremods/poweradvantage_material_tags.js | 28 + .../coremods/poweradvantage_oil_renderer.js | 55 + .../coremods/poweradvantage_parts_loot.js | 25 + .../resources/data/forge/tags/items/bars.json | 7 + .../data/forge/tags/items/bars/steel.json | 7 + .../data/forge/tags/items/dusts/charcoal.json | 4 + .../data/forge/tags/items/dusts/coal.json | 4 + .../data/forge/tags/items/dusts/lead.json | 4 + .../data/forge/tags/items/dusts/redstone.json | 6 + .../data/forge/tags/items/dusts/silver.json | 4 + .../data/forge/tags/items/dusts/tin.json | 4 + .../data/forge/tags/items/frames/steel.json | 6 + .../data/forge/tags/items/gears.json | 6 + .../data/forge/tags/items/gears/steel.json | 6 + .../data/forge/tags/items/gems/diamond.json | 6 + .../data/forge/tags/items/gems/emerald.json | 6 + .../data/forge/tags/items/governors.json | 4 + .../data/forge/tags/items/ingots/brass.json | 4 + .../data/forge/tags/items/ingots/copper.json | 4 + .../data/forge/tags/items/ingots/gold.json | 6 + .../data/forge/tags/items/ingots/iron.json | 6 + .../data/forge/tags/items/ingots/lead.json | 4 + .../data/forge/tags/items/ingots/nickel.json | 4 + .../data/forge/tags/items/ingots/plastic.json | 6 + .../forge/tags/items/ingots/refined_iron.json | 4 + .../data/forge/tags/items/ingots/rubber.json | 4 + .../data/forge/tags/items/ingots/steel.json | 4 + .../data/forge/tags/items/ingots/zinc.json | 4 + .../data/forge/tags/items/nuggets/bronze.json | 4 + .../data/forge/tags/items/nuggets/copper.json | 4 + .../data/forge/tags/items/nuggets/gold.json | 6 + .../data/forge/tags/items/nuggets/iron.json | 6 + .../data/forge/tags/items/nuggets/lead.json | 4 + .../data/forge/tags/items/nuggets/tin.json | 4 + .../data/forge/tags/items/pipes.json | 6 + .../data/forge/tags/items/plastic.json | 6 + .../data/forge/tags/items/plates/copper.json | 4 + .../data/forge/tags/items/plates/iron.json | 4 + .../data/forge/tags/items/plates/steel.json | 4 + .../forge/tags/items/power_supply_units.json | 4 + .../resources/data/forge/tags/items/rods.json | 4 + .../data/forge/tags/items/rods/copper.json | 4 + .../data/forge/tags/items/rods/wooden.json | 6 + .../data/forge/tags/items/rubber.json | 4 + .../data/forge/tags/items/sprockets.json | 6 + .../forge/tags/items/sprockets/steel.json | 6 + .../data/forge/tags/items/stone.json | 6 + .../tags/items/storage_blocks/diamond.json | 6 + .../tags/items/storage_blocks/steel.json | 4 + .../recipes/bioplastic_from_starch.json | 32 + .../recipes/converter_rf_electricity.json | 54 + .../recipes/converter_rf_quantum.json | 54 + .../recipes/converter_rf_steam.json | 54 + .../converter_tr_electricity_iron.json | 43 + ...converter_tr_electricity_refined_iron.json | 43 + .../advancements/recipes/fluid_discharge.json | 65 + .../advancements/recipes/fluid_drain.json | 65 + .../recipes/fluid_metal_tank_iron_normal.json | 43 + .../recipes/fluid_metal_tank_steel.json | 43 + .../fluid_pipe_copper_apocalyptic.json | 32 + .../recipes/fluid_pipe_copper_normal.json | 32 + .../recipes/fluid_pipe_iron_apocalyptic.json | 32 + .../recipes/fluid_pipe_iron_normal.json | 32 + .../recipes/fluid_pipe_iron_tech.json | 32 + .../recipes/fluid_pipe_lead_apocalyptic.json | 32 + .../recipes/fluid_storage_tank.json | 43 + .../advancements/recipes/fluid_switch.json | 54 + .../advancements/recipes/item_conveyor.json | 54 + .../recipes/item_filter_block.json | 54 + .../recipes/item_filter_food.json | 54 + .../recipes/item_filter_fuel.json | 54 + .../recipes/item_filter_inventory.json | 54 + .../advancements/recipes/item_filter_ore.json | 54 + .../recipes/item_filter_overflow.json | 43 + .../recipes/item_filter_plant.json | 54 + .../recipes/item_filter_smelt.json | 54 + .../advancements/recipes/rotator_tool.json | 43 + .../sprocket_bronze_nugget_tech_no_rod.json | 43 + .../recipes/sprocket_rod_normal.json | 43 + .../recipes/sprocket_rod_tech.json | 43 + .../recipes/sprocket_stickwood_normal.json | 32 + .../recipes/starch_from_potato.json | 43 + .../advancements/recipes/steel_frame.json | 32 + .../advancements/recipes/still.json | 54 + .../loot_tables/chests/advantage_parts.json | 72 + .../poweradvantage/orespawn/provider.json | 57 + .../recipes/bioplastic_from_starch.json | 11 + .../recipes/converter_rf_electricity.json | 31 + .../recipes/converter_rf_quantum.json | 27 + .../recipes/converter_rf_steam.json | 31 + .../converter_tr_electricity_iron.json | 34 + ...converter_tr_electricity_refined_iron.json | 30 + .../recipes/crushing_fluid_discharge.json | 20 + .../recipes/crushing_fluid_drain.json | 20 + .../recipes/crushing_fluid_storage_tank.json | 20 + .../recipes/crushing_item_conveyor.json | 24 + .../recipes/crushing_item_filter_block.json | 24 + .../recipes/crushing_item_filter_food.json | 24 + .../recipes/crushing_item_filter_fuel.json | 24 + .../crushing_item_filter_inventory.json | 24 + .../recipes/crushing_item_filter_ore.json | 24 + .../crushing_item_filter_overflow.json | 24 + .../recipes/crushing_item_filter_plant.json | 24 + .../recipes/crushing_item_filter_smelt.json | 24 + .../recipes/crushing_potato_starch.json | 21 + .../recipes/fluid_discharge.json | 44 + .../poweradvantage/recipes/fluid_drain.json | 44 + .../recipes/fluid_metal_tank_iron_normal.json | 34 + .../recipes/fluid_metal_tank_steel.json | 30 + .../fluid_pipe_copper_apocalyptic.json | 28 + .../recipes/fluid_pipe_copper_normal.json | 28 + .../recipes/fluid_pipe_iron_apocalyptic.json | 28 + .../recipes/fluid_pipe_iron_normal.json | 28 + .../recipes/fluid_pipe_iron_tech.json | 28 + .../recipes/fluid_pipe_lead_apocalyptic.json | 28 + .../recipes/fluid_storage_tank.json | 30 + .../poweradvantage/recipes/fluid_switch.json | 32 + .../poweradvantage/recipes/item_conveyor.json | 34 + .../recipes/item_filter_block.json | 24 + .../recipes/item_filter_food.json | 23 + .../recipes/item_filter_fuel.json | 24 + .../recipes/item_filter_inventory.json | 23 + .../recipes/item_filter_ore.json | 29 + .../recipes/item_filter_overflow.json | 19 + .../recipes/item_filter_plant.json | 23 + .../recipes/item_filter_smelt.json | 23 + .../poweradvantage/recipes/rotator_tool.json | 30 + .../sprocket_bronze_nugget_tech_no_rod.json | 39 + .../recipes/sprocket_rod_normal.json | 35 + .../recipes/sprocket_rod_tech.json | 35 + .../recipes/sprocket_stickwood_normal.json | 35 + .../recipes/starch_from_potato.json | 11 + .../poweradvantage/recipes/steel_frame.json | 23 + .../data/poweradvantage/recipes/still.json | 28 + src/main/resources/pack.mcmeta | 2 +- .../api/fluid/FluidNetworkApiTest.java | 93 + .../migration/LegacyConfigMigrationTest.java | 58 + .../LegacyOreSpawnConfigBridgeTest.java | 65 + .../ConduitNetworkManagerTest.java | 67 + .../AdvantageLegacyMigrationsTest.java | 42 + .../migration/LegacyEnchantmentsTest.java | 62 + .../migration/LegacyEntitiesTest.java | 122 + .../resources/ClientResourceContractTest.java | 120 + .../resources/MaterialTagTransformerTest.java | 60 + .../resources/OilRendererTransformerTest.java | 113 + .../resources/PartsLootTransformerTest.java | 45 + .../resources/RecipeUnlockContractTest.java | 131 + .../resources/StarchRecipeTest.java | 46 + .../orespawn-legacy/poweradvantage.json | 18 + test-rig/README.md | 161 + test-rig/build.gradle | 179 + test-rig/client-resources/README.md | 44 + .../client/ClientResourceProbe.java | 129 + .../advantageworks/client/OilRenderProbe.java | 109 + .../client/RecipeBookGroupingProbe.java | 169 + .../client/TankVisualProbe.java | 124 + .../resources/META-INF/mods.toml | 16 + .../client-resources/resources/pack.mcmeta | 6 + test-rig/client-resources/run.ps1 | 79 + test-rig/eclipse/EclipseModelProbe.java | 36 + test-rig/gradle.properties | 8 + test-rig/migration-probe/build.gradle | 44 + test-rig/migration-probe/settings.gradle | 4 + .../migration/LegacyActors.java | 210 + .../migration/LegacyConnected.java | 155 + .../migration/LegacyDimensionEntities.java | 476 ++ .../advantageworks/migration/LegacyProbe.java | 241 + test-rig/migration/README.md | 253 + test-rig/migration/actors.py | 253 + test-rig/migration/actors_native.py | 66 + test-rig/migration/audit_actors.py | 93 + test-rig/migration/audit_actors_native.py | 50 + test-rig/migration/audit_connected.py | 157 + .../migration/audit_connected_progress.py | 66 + test-rig/migration/audit_dimensions.py | 104 + test-rig/migration/audit_drill_restart.py | 85 + test-rig/migration/audit_endurance.py | 65 + .../migration/audit_extended_lifecycle.py | 91 + test-rig/migration/audit_lifecycle.py | 82 + test-rig/migration/audit_pipe_only.py | 84 + test-rig/migration/audit_player_layout.py | 46 + test-rig/migration/audit_respawn.py | 71 + test-rig/migration/audit_results.py | 53 + test-rig/migration/audit_transformer_order.py | 107 + test-rig/migration/connected.py | 362 + test-rig/migration/dimensions.py | 348 + test-rig/migration/drill_restart.py | 119 + test-rig/migration/endurance.py | 342 + test-rig/migration/extended_lifecycle.py | 427 ++ test-rig/migration/lifecycle.py | 434 ++ test-rig/migration/matrix.py | 412 ++ test-rig/migration/oil_transfer.py | 154 + test-rig/migration/partial_network_trace.py | 72 + test-rig/migration/pin_dimension_profiles.py | 55 + test-rig/migration/pin_drill_profiles.py | 100 + test-rig/migration/pipe_only_lifecycle.py | 110 + test-rig/migration/player_layout.py | 80 + test-rig/migration/progression.py | 145 + test-rig/migration/recipe_unlocks.py | 127 + test-rig/migration/requirements.txt | 1 + test-rig/migration/respawn.py | 153 + test-rig/migration/test_actors.py | 75 + test-rig/migration/test_audit_connected.py | 48 + test-rig/migration/test_bridge_profiles.py | 24 + test-rig/migration/test_connected.py | 115 + test-rig/migration/test_connected_progress.py | 40 + test-rig/migration/test_dimensions.py | 117 + test-rig/migration/test_drill_restart.py | 47 + test-rig/migration/test_endurance.py | 151 + test-rig/migration/test_extended_lifecycle.py | 106 + test-rig/migration/test_lifecycle.py | 86 + test-rig/migration/test_matrix.py | 47 + test-rig/migration/test_oil_transfer.py | 54 + .../migration/test_pin_dimension_profiles.py | 62 + .../migration/test_pipe_only_lifecycle.py | 54 + test-rig/migration/test_progression.py | 32 + test-rig/migration/test_recipe_unlocks.py | 32 + test-rig/migration/test_respawn.py | 113 + test-rig/migration/test_transformer_order.py | 181 + .../migration/trace_connected_progress.py | 60 + .../TransformerOrderBridge.java | 232 + test-rig/migration/transformer_order.py | 463 ++ test-rig/profiles/dev-core-apocalyptic.json | 37 + .../profiles/dev-core-tech-progression.json | 37 + test-rig/profiles/dev-core.json | 36 + test-rig/profiles/packaged-current.json | 65 + test-rig/profiles/packaged-decoupled.json | 57 + test-rig/profiles/packaged-mineralogy.json | 68 + .../profiles/packaged-optional-power.json | 57 + test-rig/profiles/worldgen-advantage.json | 150 + test-rig/profiles/worldgen-legacy-import.json | 217 + test-rig/profiles/worldgen-mineralogy.json | 160 + test-rig/settings.gradle | 5 + .../connected/ConnectedSession.java | 120 + .../moddev/mc/advantageworks/ActorProbe.java | 128 + .../advantageworks/AdvantageWorksCommand.java | 78 + .../mc/advantageworks/AdvantageWorksMod.java | 27 + .../mc/advantageworks/ConnectedProbe.java | 113 + .../advantageworks/DimensionEntityProbe.java | 370 + .../mc/advantageworks/JsonNbtConverter.java | 52 + .../mc/advantageworks/LifecycleProbe.java | 418 ++ .../mc/advantageworks/MigrationProbe.java | 93 + .../mc/advantageworks/ProcessProgression.java | 246 + .../mc/advantageworks/ProgressionProbe.java | 260 + .../mc/advantageworks/RecipeBookProbe.java | 92 + .../mc/advantageworks/RecipeUnlockProbe.java | 242 + .../mc/advantageworks/WorksController.java | 1004 +++ .../mc/advantageworks/WorksDefinition.java | 83 + .../advantageworks/WorksDefinitionLoader.java | 256 + .../mc/advantageworks/WorksResultWriter.java | 75 + .../src/main/resources/META-INF/mods.toml | 31 + .../works/works-1.13.2.json | 6547 +++++++++++++++++ .../mc/advantageworks/runner/WorksRunner.java | 990 +++ .../advantageworks/ConnectedSessionTest.java | 33 + .../mc/advantageworks/LifecycleProbeTest.java | 63 + .../advantageworks/ProfileManifestTest.java | 102 + .../advantageworks/WorksDefinitionTest.java | 211 + tools/generate-recipe-unlocks.js | 56 + 528 files changed, 37354 insertions(+), 7148 deletions(-) create mode 100644 docs/1.13/CHUNK-AND-DRILLS.md create mode 100644 docs/1.13/CONNECTED-MIGRATION.md create mode 100644 docs/1.13/CURRENT-QUALIFICATION.md create mode 100644 docs/1.13/DIMENSIONS-AND-ENTITY-LIFECYCLE.md create mode 100644 docs/1.13/DRILL-PROGRESS-REPAIR.md create mode 100644 docs/1.13/ECLIPSE-METADATA.md create mode 100644 docs/1.13/ENDURANCE.md create mode 100644 docs/1.13/MANUFACTURING-PROGRESSION.md create mode 100644 docs/1.13/MIGRATION-MATRIX.md create mode 100644 docs/1.13/MIGRATION.md create mode 100644 docs/1.13/OIL-TRANSFER.md create mode 100644 docs/1.13/PARTIAL-NETWORKS-AND-EXTENDED-DRILLS.md create mode 100644 docs/1.13/PLAYER-AND-ENTITY-MIGRATION.md create mode 100644 docs/1.13/RECIPE-DISCOVERY.md create mode 100644 docs/1.13/TRANSFORMER-ORDERING.md delete mode 100644 libs/RF-API.jar delete mode 100644 libs/RF-API.zip delete mode 100644 src/main/java/com/mcmoddev/poweradvantage/PowerAdvantage.java delete mode 100644 src/main/java/com/mcmoddev/poweradvantage/compat/BaseMetalsCompat.java delete mode 100644 src/main/java/com/mcmoddev/poweradvantage/init/Blocks.java delete mode 100644 src/main/java/com/mcmoddev/poweradvantage/init/Entities.java delete mode 100644 src/main/java/com/mcmoddev/poweradvantage/init/Fluids.java delete mode 100644 src/main/java/com/mcmoddev/poweradvantage/init/Fuels.java delete mode 100644 src/main/java/com/mcmoddev/poweradvantage/init/Items.java delete mode 100644 src/main/java/com/mcmoddev/poweradvantage/init/ModSupport.java delete mode 100644 src/main/java/com/mcmoddev/poweradvantage/init/Recipes.java delete mode 100644 src/main/java/com/mcmoddev/poweradvantage/machines/conveyors/BlockConveyor.java delete mode 100644 src/main/java/com/mcmoddev/poweradvantage/machines/conveyors/BlockConveyorFilter.java delete mode 100644 src/main/java/com/mcmoddev/poweradvantage/machines/conveyors/TileEntityOreFilter.java delete mode 100644 src/main/java/com/mcmoddev/poweradvantage/machines/conveyors/TileEntitySmeltableFilter.java delete mode 100644 src/main/java/com/mcmoddev/poweradvantage/machines/fluidmachines/FluidDischargeTileEntity.java delete mode 100644 src/main/java/com/mcmoddev/poweradvantage/machines/fluidmachines/FluidDrainTileEntity.java delete mode 100644 src/main/java/com/mcmoddev/poweradvantage/machines/fluidmachines/StorageTankBlock.java delete mode 100644 src/main/java/com/mcmoddev/poweradvantage/machines/fluidmachines/modsupport/TerminalFluidPipeTileEntity.java delete mode 100644 src/main/java/com/mcmoddev/poweradvantage/machines/fluidmachines/package-info.java delete mode 100644 src/main/java/com/mcmoddev/poweradvantage/recipe/condition/NotRecipeModeConditionFactory.java delete mode 100644 src/main/java/com/mcmoddev/poweradvantage/recipe/condition/OreDictEmptyConditionFactory.java delete mode 100644 src/main/java/com/mcmoddev/poweradvantage/recipe/condition/RecipeModeConditionFactory.java delete mode 100644 src/main/java/com/mcmoddev/poweradvantage/util/FluidHandlerHelper.java delete mode 100644 src/main/java/com/mcmoddev/poweradvantage/util/FluidIdHelper.java create mode 100644 src/main/java/cyano/poweradvantage/api/FluidCategoryRegistry.java create mode 100644 src/main/java/cyano/poweradvantage/api/fluid/FluidNetworkApi.java create mode 100644 src/main/java/cyano/poweradvantage/api/fluid/FluidStack.java create mode 100644 src/main/java/cyano/poweradvantage/api/fluid/FluidTank.java create mode 100644 src/main/java/cyano/poweradvantage/api/fluid/FluidTankInfo.java create mode 100644 src/main/java/cyano/poweradvantage/api/fluid/IAdvantageFluidHandler.java create mode 100644 src/main/java/cyano/poweradvantage/api/migration/LegacyConfigMigration.java create mode 100644 src/main/java/cyano/poweradvantage/api/migration/LegacyMigrationApi.java create mode 100644 src/main/java/cyano/poweradvantage/api/migration/LegacyOreSpawnConfigBridge.java create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/PowerAdvantage.java rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/RecipeMode.java (96%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/blocks/BlockFrame.java (60%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/blocks/BlockPowerSwitch.java (67%) rename src/main/java/{com/mcmoddev/poweradvantage/items => zone/moddev/mc/poweradvantage/blocks}/package-info.java (76%) create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/client/ClientOilRenderer.java create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/compat/BaseMetalsCompat.java rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/conduitnetwork/ConduitNetworkManager.java (85%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/conduitnetwork/ConduitRegistry.java (87%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/conduitnetwork/package-info.java (87%) create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/config/PowerAdvantageConfig.java rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/graphics/package-info.java (70%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/gui/FilteredFluidTankGUI.java (84%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/gui/FluidTankGUI.java (51%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/gui/FunctionalCreativeTab.java (81%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/gui/StillGUI.java (87%) rename src/main/java/{com/mcmoddev/poweradvantage/blocks => zone/moddev/mc/poweradvantage/gui}/package-info.java (77%) create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/init/Blocks.java create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/init/Entities.java create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/init/Fluids.java create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/init/Fuels.java rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/init/GUI.java (93%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/init/ItemGroups.java (71%) create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/init/Items.java rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/init/Materials.java (77%) create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/init/ModSupport.java create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/init/Recipes.java rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/init/TreasureChests.java (94%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/init/Villages.java (84%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/init/WorldGen.java (93%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/init/package-info.java (77%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/items/RotationTool.java (65%) rename src/main/java/{com/mcmoddev/poweradvantage/gui => zone/moddev/mc/poweradvantage/items}/package-info.java (76%) create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/machines/conveyors/BlockConveyor.java create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/machines/conveyors/BlockConveyorFilter.java rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/machines/conveyors/TileEntityBlockFilter.java (61%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/machines/conveyors/TileEntityConveyor.java (87%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/machines/conveyors/TileEntityConveyorFilter.java (87%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/machines/conveyors/TileEntityFoodFilter.java (61%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/machines/conveyors/TileEntityFuelFilter.java (62%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/machines/conveyors/TileEntityInventoryFilter.java (74%) create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/machines/conveyors/TileEntityOreFilter.java rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/machines/conveyors/TileEntityOverflowFilter.java (86%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/machines/conveyors/TileEntityPlantFilter.java (77%) create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/machines/conveyors/TileEntitySmeltableFilter.java rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/machines/conveyors/package-info.java (75%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/machines/creative/InfiniteEnergyBlock.java (89%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/machines/creative/InfiniteEnergyTileEntity.java (79%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/machines/fluidmachines/FluidDischargeBlock.java (57%) create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/machines/fluidmachines/FluidDischargeTileEntity.java rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/machines/fluidmachines/FluidDrainBlock.java (61%) create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/machines/fluidmachines/FluidDrainTileEntity.java rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/machines/fluidmachines/FluidPipeBlock.java (86%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/machines/fluidmachines/MetalTankBlock.java (89%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/machines/fluidmachines/MetalTankTileEntity.java (83%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/machines/fluidmachines/StillBlock.java (70%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/machines/fluidmachines/StillTileEntity.java (87%) create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/machines/fluidmachines/StorageTankBlock.java rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/machines/fluidmachines/StorageTankTileEntity.java (88%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/machines/fluidmachines/modsupport/TerminalFluidPipeBlock.java (79%) create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/machines/fluidmachines/modsupport/TerminalFluidPipeTileEntity.java create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/machines/fluidmachines/package-info.java rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/math/BlockPos4D.java (97%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/math/Integer2D.java (95%) rename src/main/java/{com/mcmoddev/poweradvantage/util => zone/moddev/mc/poweradvantage/math}/package-info.java (55%) create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/migration/AdvantageLegacyMigrations.java create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/migration/LegacyWorldDataHook.java rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/package-info.java (81%) create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/recipe/MaterialTags.java create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/recipe/PartsLoot.java create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/recipe/RecipeConditions.java create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/recipe/RecipeDiscovery.java rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/registry/FuelRegistry.java (90%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/registry/ITileEntityGUI.java (87%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/registry/MachineGUIRegistry.java (59%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/registry/package-info.java (67%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/registry/still/recipe/DistillationRecipe.java (92%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/registry/still/recipe/DistillationRecipeRegistry.java (79%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/registry/still/recipe/ExplicitFluidReference.java (75%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/registry/still/recipe/FluidReference.java (93%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/registry/still/recipe/RegistryNameFluidReference.java (76%) create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/util/FluidContainerHelper.java create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/util/FluidHandlerHelper.java create mode 100644 src/main/java/zone/moddev/mc/poweradvantage/util/FluidIdHelper.java rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/util/HashCodeHelper.java (96%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/util/InventoryWrapper.java (94%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/util/PowerHelper.java (59%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/util/ReversibleHashMap.java (97%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/poweradvantage/util/ReversibleMap.java (81%) rename src/main/java/{com/mcmoddev/poweradvantage/math => zone/moddev/mc/poweradvantage/util}/package-info.java (55%) create mode 100644 src/main/resources/META-INF/coremods.json create mode 100644 src/main/resources/META-INF/mods.toml create mode 100644 src/main/resources/assets/poweradvantage/lang/de_de.json delete mode 100644 src/main/resources/assets/poweradvantage/lang/de_de.lang create mode 100644 src/main/resources/assets/poweradvantage/lang/en_au.json delete mode 100644 src/main/resources/assets/poweradvantage/lang/en_au.lang create mode 100644 src/main/resources/assets/poweradvantage/lang/en_ca.json delete mode 100644 src/main/resources/assets/poweradvantage/lang/en_ca.lang create mode 100644 src/main/resources/assets/poweradvantage/lang/en_en.json delete mode 100644 src/main/resources/assets/poweradvantage/lang/en_en.lang create mode 100644 src/main/resources/assets/poweradvantage/lang/en_gb.json delete mode 100644 src/main/resources/assets/poweradvantage/lang/en_gb.lang create mode 100644 src/main/resources/assets/poweradvantage/lang/en_pt.json delete mode 100644 src/main/resources/assets/poweradvantage/lang/en_pt.lang create mode 100644 src/main/resources/assets/poweradvantage/lang/en_us.json delete mode 100644 src/main/resources/assets/poweradvantage/lang/en_us.lang create mode 100644 src/main/resources/assets/poweradvantage/lang/es_es.json delete mode 100644 src/main/resources/assets/poweradvantage/lang/es_es.lang create mode 100644 src/main/resources/assets/poweradvantage/lang/es_mx.json delete mode 100644 src/main/resources/assets/poweradvantage/lang/es_mx.lang create mode 100644 src/main/resources/assets/poweradvantage/lang/it_it.json delete mode 100644 src/main/resources/assets/poweradvantage/lang/it_it.lang create mode 100644 src/main/resources/assets/poweradvantage/lang/ja_jp.json delete mode 100644 src/main/resources/assets/poweradvantage/lang/ja_jp.lang create mode 100644 src/main/resources/assets/poweradvantage/lang/ko_kr.json delete mode 100644 src/main/resources/assets/poweradvantage/lang/ko_kr.lang create mode 100644 src/main/resources/assets/poweradvantage/lang/pt_br.json delete mode 100644 src/main/resources/assets/poweradvantage/lang/pt_br.lang create mode 100644 src/main/resources/assets/poweradvantage/lang/zh_cn.json delete mode 100644 src/main/resources/assets/poweradvantage/lang/zh_cn.lang create mode 100644 src/main/resources/assets/poweradvantage/models/block/crude_oil.json create mode 100644 src/main/resources/assets/poweradvantage/models/block/refined_oil.json create mode 100644 src/main/resources/assets/poweradvantage/models/item/crude_oil.json create mode 100644 src/main/resources/assets/poweradvantage/models/item/crude_oil_bucket.json create mode 100644 src/main/resources/assets/poweradvantage/models/item/refined_oil.json create mode 100644 src/main/resources/assets/poweradvantage/models/item/refined_oil_bucket.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/_factories.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/fluid_discharge.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/fluid_drain.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/fluid_metal_tank_iron_normal.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/fluid_metal_tank_steel.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/fluid_pipe_copper_apocalyptic.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/fluid_pipe_copper_normal.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/fluid_pipe_iron_apocalyptic.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/fluid_pipe_iron_normal.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/fluid_pipe_iron_tech.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/fluid_pipe_lead_apocalyptic.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/fluid_storage_tank.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/fluid_switch.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/item_conveyor.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/item_filter_block.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/item_filter_food.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/item_filter_fuel.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/item_filter_inventory.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/item_filter_ore.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/item_filter_overflow.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/item_filter_plant.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/item_filter_smelt.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/rotator_tool.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/sprocket_bronze_nugget_tech_no_rod.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/sprocket_rod_normal.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/sprocket_rod_tech.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/sprocket_stickwood_normal.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/steel_frame.json delete mode 100644 src/main/resources/assets/poweradvantage/recipes/still.json create mode 100644 src/main/resources/coremods/poweradvantage_113_migration.js create mode 100644 src/main/resources/coremods/poweradvantage_material_tags.js create mode 100644 src/main/resources/coremods/poweradvantage_oil_renderer.js create mode 100644 src/main/resources/coremods/poweradvantage_parts_loot.js create mode 100644 src/main/resources/data/forge/tags/items/bars.json create mode 100644 src/main/resources/data/forge/tags/items/bars/steel.json create mode 100644 src/main/resources/data/forge/tags/items/dusts/charcoal.json create mode 100644 src/main/resources/data/forge/tags/items/dusts/coal.json create mode 100644 src/main/resources/data/forge/tags/items/dusts/lead.json create mode 100644 src/main/resources/data/forge/tags/items/dusts/redstone.json create mode 100644 src/main/resources/data/forge/tags/items/dusts/silver.json create mode 100644 src/main/resources/data/forge/tags/items/dusts/tin.json create mode 100644 src/main/resources/data/forge/tags/items/frames/steel.json create mode 100644 src/main/resources/data/forge/tags/items/gears.json create mode 100644 src/main/resources/data/forge/tags/items/gears/steel.json create mode 100644 src/main/resources/data/forge/tags/items/gems/diamond.json create mode 100644 src/main/resources/data/forge/tags/items/gems/emerald.json create mode 100644 src/main/resources/data/forge/tags/items/governors.json create mode 100644 src/main/resources/data/forge/tags/items/ingots/brass.json create mode 100644 src/main/resources/data/forge/tags/items/ingots/copper.json create mode 100644 src/main/resources/data/forge/tags/items/ingots/gold.json create mode 100644 src/main/resources/data/forge/tags/items/ingots/iron.json create mode 100644 src/main/resources/data/forge/tags/items/ingots/lead.json create mode 100644 src/main/resources/data/forge/tags/items/ingots/nickel.json create mode 100644 src/main/resources/data/forge/tags/items/ingots/plastic.json create mode 100644 src/main/resources/data/forge/tags/items/ingots/refined_iron.json create mode 100644 src/main/resources/data/forge/tags/items/ingots/rubber.json create mode 100644 src/main/resources/data/forge/tags/items/ingots/steel.json create mode 100644 src/main/resources/data/forge/tags/items/ingots/zinc.json create mode 100644 src/main/resources/data/forge/tags/items/nuggets/bronze.json create mode 100644 src/main/resources/data/forge/tags/items/nuggets/copper.json create mode 100644 src/main/resources/data/forge/tags/items/nuggets/gold.json create mode 100644 src/main/resources/data/forge/tags/items/nuggets/iron.json create mode 100644 src/main/resources/data/forge/tags/items/nuggets/lead.json create mode 100644 src/main/resources/data/forge/tags/items/nuggets/tin.json create mode 100644 src/main/resources/data/forge/tags/items/pipes.json create mode 100644 src/main/resources/data/forge/tags/items/plastic.json create mode 100644 src/main/resources/data/forge/tags/items/plates/copper.json create mode 100644 src/main/resources/data/forge/tags/items/plates/iron.json create mode 100644 src/main/resources/data/forge/tags/items/plates/steel.json create mode 100644 src/main/resources/data/forge/tags/items/power_supply_units.json create mode 100644 src/main/resources/data/forge/tags/items/rods.json create mode 100644 src/main/resources/data/forge/tags/items/rods/copper.json create mode 100644 src/main/resources/data/forge/tags/items/rods/wooden.json create mode 100644 src/main/resources/data/forge/tags/items/rubber.json create mode 100644 src/main/resources/data/forge/tags/items/sprockets.json create mode 100644 src/main/resources/data/forge/tags/items/sprockets/steel.json create mode 100644 src/main/resources/data/forge/tags/items/stone.json create mode 100644 src/main/resources/data/forge/tags/items/storage_blocks/diamond.json create mode 100644 src/main/resources/data/forge/tags/items/storage_blocks/steel.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/bioplastic_from_starch.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/converter_rf_electricity.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/converter_rf_quantum.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/converter_rf_steam.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/converter_tr_electricity_iron.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/converter_tr_electricity_refined_iron.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/fluid_discharge.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/fluid_drain.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/fluid_metal_tank_iron_normal.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/fluid_metal_tank_steel.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/fluid_pipe_copper_apocalyptic.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/fluid_pipe_copper_normal.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/fluid_pipe_iron_apocalyptic.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/fluid_pipe_iron_normal.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/fluid_pipe_iron_tech.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/fluid_pipe_lead_apocalyptic.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/fluid_storage_tank.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/fluid_switch.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/item_conveyor.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/item_filter_block.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/item_filter_food.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/item_filter_fuel.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/item_filter_inventory.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/item_filter_ore.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/item_filter_overflow.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/item_filter_plant.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/item_filter_smelt.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/rotator_tool.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/sprocket_bronze_nugget_tech_no_rod.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/sprocket_rod_normal.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/sprocket_rod_tech.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/sprocket_stickwood_normal.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/starch_from_potato.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/steel_frame.json create mode 100644 src/main/resources/data/poweradvantage/advancements/recipes/still.json create mode 100644 src/main/resources/data/poweradvantage/loot_tables/chests/advantage_parts.json create mode 100644 src/main/resources/data/poweradvantage/orespawn/provider.json create mode 100644 src/main/resources/data/poweradvantage/recipes/bioplastic_from_starch.json create mode 100644 src/main/resources/data/poweradvantage/recipes/converter_rf_electricity.json create mode 100644 src/main/resources/data/poweradvantage/recipes/converter_rf_quantum.json create mode 100644 src/main/resources/data/poweradvantage/recipes/converter_rf_steam.json create mode 100644 src/main/resources/data/poweradvantage/recipes/converter_tr_electricity_iron.json create mode 100644 src/main/resources/data/poweradvantage/recipes/converter_tr_electricity_refined_iron.json create mode 100644 src/main/resources/data/poweradvantage/recipes/crushing_fluid_discharge.json create mode 100644 src/main/resources/data/poweradvantage/recipes/crushing_fluid_drain.json create mode 100644 src/main/resources/data/poweradvantage/recipes/crushing_fluid_storage_tank.json create mode 100644 src/main/resources/data/poweradvantage/recipes/crushing_item_conveyor.json create mode 100644 src/main/resources/data/poweradvantage/recipes/crushing_item_filter_block.json create mode 100644 src/main/resources/data/poweradvantage/recipes/crushing_item_filter_food.json create mode 100644 src/main/resources/data/poweradvantage/recipes/crushing_item_filter_fuel.json create mode 100644 src/main/resources/data/poweradvantage/recipes/crushing_item_filter_inventory.json create mode 100644 src/main/resources/data/poweradvantage/recipes/crushing_item_filter_ore.json create mode 100644 src/main/resources/data/poweradvantage/recipes/crushing_item_filter_overflow.json create mode 100644 src/main/resources/data/poweradvantage/recipes/crushing_item_filter_plant.json create mode 100644 src/main/resources/data/poweradvantage/recipes/crushing_item_filter_smelt.json create mode 100644 src/main/resources/data/poweradvantage/recipes/crushing_potato_starch.json create mode 100644 src/main/resources/data/poweradvantage/recipes/fluid_discharge.json create mode 100644 src/main/resources/data/poweradvantage/recipes/fluid_drain.json create mode 100644 src/main/resources/data/poweradvantage/recipes/fluid_metal_tank_iron_normal.json create mode 100644 src/main/resources/data/poweradvantage/recipes/fluid_metal_tank_steel.json create mode 100644 src/main/resources/data/poweradvantage/recipes/fluid_pipe_copper_apocalyptic.json create mode 100644 src/main/resources/data/poweradvantage/recipes/fluid_pipe_copper_normal.json create mode 100644 src/main/resources/data/poweradvantage/recipes/fluid_pipe_iron_apocalyptic.json create mode 100644 src/main/resources/data/poweradvantage/recipes/fluid_pipe_iron_normal.json create mode 100644 src/main/resources/data/poweradvantage/recipes/fluid_pipe_iron_tech.json create mode 100644 src/main/resources/data/poweradvantage/recipes/fluid_pipe_lead_apocalyptic.json create mode 100644 src/main/resources/data/poweradvantage/recipes/fluid_storage_tank.json create mode 100644 src/main/resources/data/poweradvantage/recipes/fluid_switch.json create mode 100644 src/main/resources/data/poweradvantage/recipes/item_conveyor.json create mode 100644 src/main/resources/data/poweradvantage/recipes/item_filter_block.json create mode 100644 src/main/resources/data/poweradvantage/recipes/item_filter_food.json create mode 100644 src/main/resources/data/poweradvantage/recipes/item_filter_fuel.json create mode 100644 src/main/resources/data/poweradvantage/recipes/item_filter_inventory.json create mode 100644 src/main/resources/data/poweradvantage/recipes/item_filter_ore.json create mode 100644 src/main/resources/data/poweradvantage/recipes/item_filter_overflow.json create mode 100644 src/main/resources/data/poweradvantage/recipes/item_filter_plant.json create mode 100644 src/main/resources/data/poweradvantage/recipes/item_filter_smelt.json create mode 100644 src/main/resources/data/poweradvantage/recipes/rotator_tool.json create mode 100644 src/main/resources/data/poweradvantage/recipes/sprocket_bronze_nugget_tech_no_rod.json create mode 100644 src/main/resources/data/poweradvantage/recipes/sprocket_rod_normal.json create mode 100644 src/main/resources/data/poweradvantage/recipes/sprocket_rod_tech.json create mode 100644 src/main/resources/data/poweradvantage/recipes/sprocket_stickwood_normal.json create mode 100644 src/main/resources/data/poweradvantage/recipes/starch_from_potato.json create mode 100644 src/main/resources/data/poweradvantage/recipes/steel_frame.json create mode 100644 src/main/resources/data/poweradvantage/recipes/still.json create mode 100644 src/test/java/cyano/poweradvantage/api/fluid/FluidNetworkApiTest.java create mode 100644 src/test/java/cyano/poweradvantage/api/migration/LegacyConfigMigrationTest.java create mode 100644 src/test/java/cyano/poweradvantage/api/migration/LegacyOreSpawnConfigBridgeTest.java create mode 100644 src/test/java/zone/moddev/mc/poweradvantage/conduitnetwork/ConduitNetworkManagerTest.java create mode 100644 src/test/java/zone/moddev/mc/poweradvantage/migration/AdvantageLegacyMigrationsTest.java create mode 100644 src/test/java/zone/moddev/mc/poweradvantage/migration/LegacyEnchantmentsTest.java create mode 100644 src/test/java/zone/moddev/mc/poweradvantage/migration/LegacyEntitiesTest.java create mode 100644 src/test/java/zone/moddev/mc/poweradvantage/resources/ClientResourceContractTest.java create mode 100644 src/test/java/zone/moddev/mc/poweradvantage/resources/MaterialTagTransformerTest.java create mode 100644 src/test/java/zone/moddev/mc/poweradvantage/resources/OilRendererTransformerTest.java create mode 100644 src/test/java/zone/moddev/mc/poweradvantage/resources/PartsLootTransformerTest.java create mode 100644 src/test/java/zone/moddev/mc/poweradvantage/resources/RecipeUnlockContractTest.java create mode 100644 src/test/java/zone/moddev/mc/poweradvantage/resources/StarchRecipeTest.java create mode 100644 src/test/resources/orespawn-legacy/poweradvantage.json create mode 100644 test-rig/README.md create mode 100644 test-rig/build.gradle create mode 100644 test-rig/client-resources/README.md create mode 100644 test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/ClientResourceProbe.java create mode 100644 test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/OilRenderProbe.java create mode 100644 test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/RecipeBookGroupingProbe.java create mode 100644 test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/TankVisualProbe.java create mode 100644 test-rig/client-resources/resources/META-INF/mods.toml create mode 100644 test-rig/client-resources/resources/pack.mcmeta create mode 100644 test-rig/client-resources/run.ps1 create mode 100644 test-rig/eclipse/EclipseModelProbe.java create mode 100644 test-rig/gradle.properties create mode 100644 test-rig/migration-probe/build.gradle create mode 100644 test-rig/migration-probe/settings.gradle create mode 100644 test-rig/migration-probe/src/main/java/zone/moddev/mc/advantageworks/migration/LegacyActors.java create mode 100644 test-rig/migration-probe/src/main/java/zone/moddev/mc/advantageworks/migration/LegacyConnected.java create mode 100644 test-rig/migration-probe/src/main/java/zone/moddev/mc/advantageworks/migration/LegacyDimensionEntities.java create mode 100644 test-rig/migration-probe/src/main/java/zone/moddev/mc/advantageworks/migration/LegacyProbe.java create mode 100644 test-rig/migration/README.md create mode 100644 test-rig/migration/actors.py create mode 100644 test-rig/migration/actors_native.py create mode 100644 test-rig/migration/audit_actors.py create mode 100644 test-rig/migration/audit_actors_native.py create mode 100644 test-rig/migration/audit_connected.py create mode 100644 test-rig/migration/audit_connected_progress.py create mode 100644 test-rig/migration/audit_dimensions.py create mode 100644 test-rig/migration/audit_drill_restart.py create mode 100644 test-rig/migration/audit_endurance.py create mode 100644 test-rig/migration/audit_extended_lifecycle.py create mode 100644 test-rig/migration/audit_lifecycle.py create mode 100644 test-rig/migration/audit_pipe_only.py create mode 100644 test-rig/migration/audit_player_layout.py create mode 100644 test-rig/migration/audit_respawn.py create mode 100644 test-rig/migration/audit_results.py create mode 100644 test-rig/migration/audit_transformer_order.py create mode 100644 test-rig/migration/connected.py create mode 100644 test-rig/migration/dimensions.py create mode 100644 test-rig/migration/drill_restart.py create mode 100644 test-rig/migration/endurance.py create mode 100644 test-rig/migration/extended_lifecycle.py create mode 100644 test-rig/migration/lifecycle.py create mode 100644 test-rig/migration/matrix.py create mode 100644 test-rig/migration/oil_transfer.py create mode 100644 test-rig/migration/partial_network_trace.py create mode 100644 test-rig/migration/pin_dimension_profiles.py create mode 100644 test-rig/migration/pin_drill_profiles.py create mode 100644 test-rig/migration/pipe_only_lifecycle.py create mode 100644 test-rig/migration/player_layout.py create mode 100644 test-rig/migration/progression.py create mode 100644 test-rig/migration/recipe_unlocks.py create mode 100644 test-rig/migration/requirements.txt create mode 100644 test-rig/migration/respawn.py create mode 100644 test-rig/migration/test_actors.py create mode 100644 test-rig/migration/test_audit_connected.py create mode 100644 test-rig/migration/test_bridge_profiles.py create mode 100644 test-rig/migration/test_connected.py create mode 100644 test-rig/migration/test_connected_progress.py create mode 100644 test-rig/migration/test_dimensions.py create mode 100644 test-rig/migration/test_drill_restart.py create mode 100644 test-rig/migration/test_endurance.py create mode 100644 test-rig/migration/test_extended_lifecycle.py create mode 100644 test-rig/migration/test_lifecycle.py create mode 100644 test-rig/migration/test_matrix.py create mode 100644 test-rig/migration/test_oil_transfer.py create mode 100644 test-rig/migration/test_pin_dimension_profiles.py create mode 100644 test-rig/migration/test_pipe_only_lifecycle.py create mode 100644 test-rig/migration/test_progression.py create mode 100644 test-rig/migration/test_recipe_unlocks.py create mode 100644 test-rig/migration/test_respawn.py create mode 100644 test-rig/migration/test_transformer_order.py create mode 100644 test-rig/migration/trace_connected_progress.py create mode 100644 test-rig/migration/transformer-order-probe/TransformerOrderBridge.java create mode 100644 test-rig/migration/transformer_order.py create mode 100644 test-rig/profiles/dev-core-apocalyptic.json create mode 100644 test-rig/profiles/dev-core-tech-progression.json create mode 100644 test-rig/profiles/dev-core.json create mode 100644 test-rig/profiles/packaged-current.json create mode 100644 test-rig/profiles/packaged-decoupled.json create mode 100644 test-rig/profiles/packaged-mineralogy.json create mode 100644 test-rig/profiles/packaged-optional-power.json create mode 100644 test-rig/profiles/worldgen-advantage.json create mode 100644 test-rig/profiles/worldgen-legacy-import.json create mode 100644 test-rig/profiles/worldgen-mineralogy.json create mode 100644 test-rig/settings.gradle create mode 100644 test-rig/shared-probe/zone/moddev/mc/advantageworks/connected/ConnectedSession.java create mode 100644 test-rig/src/main/java/zone/moddev/mc/advantageworks/ActorProbe.java create mode 100644 test-rig/src/main/java/zone/moddev/mc/advantageworks/AdvantageWorksCommand.java create mode 100644 test-rig/src/main/java/zone/moddev/mc/advantageworks/AdvantageWorksMod.java create mode 100644 test-rig/src/main/java/zone/moddev/mc/advantageworks/ConnectedProbe.java create mode 100644 test-rig/src/main/java/zone/moddev/mc/advantageworks/DimensionEntityProbe.java create mode 100644 test-rig/src/main/java/zone/moddev/mc/advantageworks/JsonNbtConverter.java create mode 100644 test-rig/src/main/java/zone/moddev/mc/advantageworks/LifecycleProbe.java create mode 100644 test-rig/src/main/java/zone/moddev/mc/advantageworks/MigrationProbe.java create mode 100644 test-rig/src/main/java/zone/moddev/mc/advantageworks/ProcessProgression.java create mode 100644 test-rig/src/main/java/zone/moddev/mc/advantageworks/ProgressionProbe.java create mode 100644 test-rig/src/main/java/zone/moddev/mc/advantageworks/RecipeBookProbe.java create mode 100644 test-rig/src/main/java/zone/moddev/mc/advantageworks/RecipeUnlockProbe.java create mode 100644 test-rig/src/main/java/zone/moddev/mc/advantageworks/WorksController.java create mode 100644 test-rig/src/main/java/zone/moddev/mc/advantageworks/WorksDefinition.java create mode 100644 test-rig/src/main/java/zone/moddev/mc/advantageworks/WorksDefinitionLoader.java create mode 100644 test-rig/src/main/java/zone/moddev/mc/advantageworks/WorksResultWriter.java create mode 100644 test-rig/src/main/resources/META-INF/mods.toml create mode 100644 test-rig/src/main/resources/assets/advantageworkstest/works/works-1.13.2.json create mode 100644 test-rig/src/runner/java/zone/moddev/mc/advantageworks/runner/WorksRunner.java create mode 100644 test-rig/src/test/java/zone/moddev/mc/advantageworks/ConnectedSessionTest.java create mode 100644 test-rig/src/test/java/zone/moddev/mc/advantageworks/LifecycleProbeTest.java create mode 100644 test-rig/src/test/java/zone/moddev/mc/advantageworks/ProfileManifestTest.java create mode 100644 test-rig/src/test/java/zone/moddev/mc/advantageworks/WorksDefinitionTest.java create mode 100644 tools/generate-recipe-unlocks.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a8f78e58..3dec651c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,81 +1,89 @@ -name: Power Advantage 1.12 CI +name: Power Advantage 1.13 CI on: push: - branches: - - master-1.12.2 - - 'feature/**' + branches: [master-1.13.2, 'feature/**'] pull_request: - branches: - - master-1.12.2 + branches: [master-1.13.2] permissions: contents: read concurrency: - group: poweradvantage-1.12-${{ github.workflow }}-${{ github.ref }} + group: poweradvantage-1.13-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: build: name: Build, test, and audit runs-on: ubuntu-latest - timeout-minutes: 60 - + timeout-minutes: 90 steps: - - name: Check out source + - name: Check out PowerAdvantage uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - + - name: Check out pinned OreSpawn + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + repository: MinecraftModDevelopmentMods/OreSpawn + ref: 6a7c2bb07b9a7771128812fcc07d185f7d9df58b + path: .ci-dependencies/OreSpawn + - name: Check out pinned Base Metals + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + repository: MinecraftModDevelopmentMods/BaseMetals + ref: 1376a20d7d5ba22d3abc77dc34b49a2c88943697 + path: .ci-dependencies/BaseMetals - name: Install Java 8 toolchain uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 with: distribution: temurin java-version: '8.0.502+7' - - name: Install Java 17 for Gradle uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 with: distribution: microsoft java-version: '17' - - name: Set up Gradle uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6 - - - name: Make the wrapper executable - run: chmod +x ./gradlew - - - name: Stage exact RebornCore compile API - id: reborncore + - name: Build pinned development dependencies + id: dependencies + shell: bash run: | - mirror="$RUNNER_TEMP/reborncore-mirror" - bash gradle/stage-reborncore-release.sh "$mirror" - echo "repository=$mirror" >> "$GITHUB_OUTPUT" - + set -euo pipefail + orespawn="$GITHUB_WORKSPACE/.ci-dependencies/OreSpawn" + basemetals="$GITHUB_WORKSPACE/.ci-dependencies/BaseMetals" + chmod +x "$orespawn/gradlew" "$basemetals/gradlew" + (cd "$orespawn" && ./gradlew jar --no-daemon --stacktrace) + (cd "$basemetals" && ./gradlew jar --no-daemon --stacktrace) + osjar="$orespawn/build/libs-dev/OreSpawn-4.1.0.113021-deobf.jar" + bmjar="$basemetals/build/libs-dev/BaseMetals-3.0.1.113021-deobf.jar" + test -f "$osjar" && test -f "$bmjar" + echo "orespawn=$osjar" >> "$GITHUB_OUTPUT" + echo "basemetals=$bmjar" >> "$GITHUB_OUTPUT" - name: Build and audit release artifacts run: >- ./gradlew clean check build javadoc verifyReleaseDependencies verifyReleaseArtifacts writeReleaseChecksums verifyEclipseProductionClasspath - -PrebornCoreVerificationRepository=${{ steps.reborncore.outputs.repository }} + -PbaseMetalsDeobfJar=${{ steps.dependencies.outputs.basemetals }} + -PoreSpawnDeobfJar=${{ steps.dependencies.outputs.orespawn }} --no-daemon --stacktrace - - name: Upload audited release candidate uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: - name: PowerAdvantage-1.12.2-${{ github.sha }} + name: PowerAdvantage-1.13.2-${{ github.sha }} if-no-files-found: error retention-days: 30 path: | - build/libs/PowerAdvantage-2.4.1.112021.jar - build/libs/PowerAdvantage-2.4.1.112021-sources.jar - build/libs/PowerAdvantage-2.4.1.112021-javadoc.jar + build/libs/PowerAdvantage-2.4.4.113021.jar + build/libs/PowerAdvantage-2.4.4.113021-sources.jar + build/libs/PowerAdvantage-2.4.4.113021-javadoc.jar build/release/SHA256SUMS CHANGELOG.txt - - name: Upload diagnostics on failure if: failure() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: - name: PowerAdvantage-1.12.2-diagnostics-${{ github.sha }} + name: PowerAdvantage-1.13.2-diagnostics-${{ github.sha }} if-no-files-found: ignore retention-days: 14 path: | diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 77d6fe58..756d9db5 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -2,12 +2,9 @@ name: CodeQL on: push: - branches: - - master-1.12.2 - - 'feature/**' + branches: [master-1.13.2, 'feature/**'] pull_request: - branches: - - master-1.12.2 + branches: [master-1.13.2] schedule: - cron: '47 7 * * 4' @@ -20,44 +17,55 @@ jobs: analyze: name: Analyze Java runs-on: ubuntu-latest - timeout-minutes: 45 - + timeout-minutes: 75 steps: - - name: Check out source + - name: Check out PowerAdvantage uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - + - name: Check out pinned OreSpawn + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + repository: MinecraftModDevelopmentMods/OreSpawn + ref: 6a7c2bb07b9a7771128812fcc07d185f7d9df58b + path: .ci-dependencies/OreSpawn + - name: Check out pinned Base Metals + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + repository: MinecraftModDevelopmentMods/BaseMetals + ref: 1376a20d7d5ba22d3abc77dc34b49a2c88943697 + path: .ci-dependencies/BaseMetals - name: Install Java 8 toolchain uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 with: distribution: temurin java-version: '8.0.502+7' - - name: Install Java 17 for Gradle uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 with: distribution: microsoft java-version: '17' - - name: Set up Gradle uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6 - - - name: Stage exact RebornCore compile API - id: reborncore + - name: Build pinned development dependencies + id: dependencies + shell: bash run: | - mirror="$RUNNER_TEMP/reborncore-mirror" - bash gradle/stage-reborncore-release.sh "$mirror" - echo "repository=$mirror" >> "$GITHUB_OUTPUT" - + set -euo pipefail + orespawn="$GITHUB_WORKSPACE/.ci-dependencies/OreSpawn" + basemetals="$GITHUB_WORKSPACE/.ci-dependencies/BaseMetals" + chmod +x "$orespawn/gradlew" "$basemetals/gradlew" + (cd "$orespawn" && ./gradlew jar --no-daemon --stacktrace) + (cd "$basemetals" && ./gradlew jar --no-daemon --stacktrace) + echo "orespawn=$orespawn/build/libs-dev/OreSpawn-4.1.0.113021-deobf.jar" >> "$GITHUB_OUTPUT" + echo "basemetals=$basemetals/build/libs-dev/BaseMetals-3.0.1.113021-deobf.jar" >> "$GITHUB_OUTPUT" - name: Initialize CodeQL uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4 with: languages: java-kotlin - - name: Compile production code run: | chmod +x ./gradlew ./gradlew clean classes --rerun-tasks --no-build-cache --no-daemon --stacktrace \ - -PrebornCoreVerificationRepository=${{ steps.reborncore.outputs.repository }} - + -PbaseMetalsDeobfJar=${{ steps.dependencies.outputs.basemetals }} \ + -PoreSpawnDeobfJar=${{ steps.dependencies.outputs.orespawn }} - name: Analyze uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4 diff --git a/.github/workflows/validate-gradle-build.yml b/.github/workflows/validate-gradle-build.yml index 8959c1a8..d4d55ba3 100644 --- a/.github/workflows/validate-gradle-build.yml +++ b/.github/workflows/validate-gradle-build.yml @@ -3,11 +3,11 @@ name: Validate Gradle Wrapper on: push: branches: - - master-1.12.2 + - master-1.13.2 - 'feature/**' pull_request: branches: - - master-1.12.2 + - master-1.13.2 permissions: contents: read diff --git a/.gitignore b/.gitignore index 1104f3ff..f5eaedc2 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,9 @@ /run/ /run-data/ /run.* +/$run/ +/eula.txt +/server.properties /*.log /classes/ /logs/ @@ -52,10 +55,39 @@ /hs_err_pid*.log /replay_pid*.log -# Local dependency experiments; the RF API is an intentional tracked input. +# Nested Advantage Works outputs +/test-rig/.gradle/ +/test-rig/.cache/ +/test-rig/build/ +/test-rig/migration-probe/.gradle/ +/test-rig/migration-probe/build/ +/test-rig/migration/.venv/ +/test-rig/migration/__pycache__/ + +# Local dependency artifacts; 1.13 converters use Forge Energy instead. /libs/* -!/libs/RF-API.jar -!/libs/RF-API.zip +*.jar +*.zip +!/gradle/wrapper/gradle-wrapper.jar /secret.json +# Nested local tooling and generated output +AGENTS.md +AGENT.md +CLAUDE.md +GEMINI.md +ARCHITECT.md +agent-notes/ +.codex/ +.agent/ +.agents/ +.claude/ +.gemini/ +.cursor/ +agent-*.md +codex-*.md +*.log +*.class +__pycache__/ +*.pyc /.DS_Store diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 4f3babda..2d22e857 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,16 @@ +Power Advantage 2.4.4.113021 + +- Port Power Advantage to Minecraft 1.13.2 with ForgeGradle 7 and Java 8 support. +- Keep existing block, item, fluid and saved-state names, the legacy public API and Iron Age Furniture compatibility facades. +- Add direct 1.10 and 1.12 world migration, including inventories, fluids, entities, pets, enchantments and old configuration files. Back up worlds before upgrading. +- Restore conduit connections after reloads without forcing unloaded chunks to load. +- Fix models, translations, oil buckets, fluid gauges and transparent floor edges around containers. +- Add ingredient-based recipe discoveries, separate unrelated recipe-book entries, and allow potatoes to be crafted into starch. +- Restore processing, recycling and parts-loot paths while retaining recipe modes and configured material aliases. +- Use Forge tags, Base Metals crushing and OreSpawn 4 deposits; retain old generation settings and user overrides. +- Keep the RF and Tech Reborn converter names but use Forge Energy instead of obsolete external APIs. +- Add separate Power-only and combined Eclipse launches, reproducible builds and repeatable migration and operating tests. See the qualification notes for remaining coverage gaps. + Power Advantage 2.4.1.112021 - Restore conduit networks and machine GUI state after a world reload. diff --git a/README.md b/README.md index eef70e91..0522cd4e 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,68 @@ # PowerAdvantage -Power Advantage provides shared power, fluid, and item transport systems for its Minecraft 1.12.2 expansion mods. +Power Advantage provides shared power, fluid, item transport, and compatibility APIs for its Minecraft 1.13.2 expansion mods. -## Building Minecraft 1.12.2 +This port keeps existing registry names and includes migration from 1.10 and 1.12. +Always back up a world before upgrading, and check that its other mods also support the target version. +The [qualification notes](docs/1.13/CURRENT-QUALIFICATION.md) separate tested scenarios from remaining gaps; +they are not a guarantee that an entire modpack can be upgraded without losses. -The `master-1.12.2` build uses ForgeGradle 7.0.34 and Gradle 9.6.1. Run Gradle with Java 17; Gradle resolves the Java 8 toolchain used for compilation. +Recipe-book discovery now follows relevant ingredients. See the [all-Advantage material checklist and automated discovery tests](docs/1.13/RECIPE-DISCOVERY.md). To regenerate the checked-in unlock JSON after changing recipes, run `node tools/generate-recipe-unlocks.js`; `check` rejects missing or stale ingredient criteria. + +The [manufacturing progression audit](docs/1.13/MANUFACTURING-PROGRESSION.md) traces survival parts from raw resources, including configured material aliases, processing, loot-only parts and automated restart checks. + +## Building Minecraft 1.13.2 + +The `master-1.13.2` build uses ForgeGradle 7.0.34 and Gradle 9.6.1. Run Gradle with Java 17; Gradle resolves the Java 8 production toolchain. + +Build the pinned Base Metals `3.0.1.113021` and OreSpawn `4.1.0.113021` deobf jars in their sibling checkouts, then run: ```text -gradlew.bat clean check build verifyReleaseDependencies verifyReleaseArtifacts writeReleaseChecksums +gradlew.bat clean check build javadoc verifyReleaseDependencies verifyReleaseArtifacts writeReleaseChecksums verifyEclipseProductionClasspath ``` -Release jars are written to `build/libs`. The deobfuscated development jar for sibling mod compilation is written to `build/libs-dev` by `deobfJar` or `build`. +The default dependency paths follow the local MMD checkout layout. Other layouts can pass `-PbaseMetalsDeobfJar=` and `-PoreSpawnDeobfJar=`. Dependencies are present on development classpaths but are never embedded in PowerAdvantage artifacts. + +Release jars are written to `build/libs`; `deobfJar` writes the sibling-development jar to `build/libs-dev`. -For Eclipse, import the repository as an existing Gradle project and run: +For Eclipse, import this repository as an existing Gradle project and run: ```text gradlew.bat cleanEclipse verifyEclipseProductionClasspath ``` -Base Metals and OreSpawn remain required distribution dependencies but are deliberately absent from the compile classpath. Optional RF and RebornCore integrations compile against SHA-256-pinned API inputs and are not bundled or added to normal development launches. CI stages the exact historical RebornCore Maven release because the later CurseForge archive with the same version has different contents. +Refresh the project in Eclipse, allow its Java build to finish, and launch the generated `runClient.launch`. Eclipse launches discover the mod from `bin/main`; command-line Gradle launches use `build/forge-run-mod-classes/main`. The Eclipse audit checks that discovery points at the same production output Eclipse compiles into, preventing a client that starts without PowerAdvantage. + +Eclipse copies production resources from `build/resources/main`, not directly from `src/main/resources`. This prevents its builder from overwriting the expanded `mods.toml` with raw `${version}` placeholders. After updating this setup, use **Gradle > Refresh Gradle Project**, then **Project > Clean** before relaunching. After editing resources or version properties, run `processResources` or refresh the Gradle project before the Eclipse build. See the [metadata regression notes](docs/1.13/ECLIPSE-METADATA.md). + +To test all three Advantage mods together, run `gradlew.bat prepareCombinedEclipseLaunch`, refresh Eclipse, and launch `runClientAllAdvantage.launch`. This additional profile loads the pinned Steam/Electric deobf jars, Base Metals and OreSpawn alongside Power's Eclipse output. It uses the same `run` directory and test saves; it does not copy, open or modify a save automatically. The ordinary `runClient.launch` remains Power-only. Alternative checkout layouts can pass `-PsteamAdvantageDeobfJar=` and `-PelectricAdvantageDeobfJar=`; supplied jars must match the `test-rig/profiles/dev-core.json` pins. Regenerate the combined launch after refreshing its dependencies or the base launch. + +The combined [client resource probe](test-rig/client-resources/README.md), with `-RequireExtensions`, checks item names, baked models, textures and every valid block state across all three Advantage mods. The legacy Steam/Electric model-path and selector failures are repaired; [the qualification notes](docs/1.13/OIL-TRANSFER.md#combined-resource-repair-21-october-2026) retain the failing evidence and subsequent full-scope result. Native resource checks remain separate from manual graphical/gameplay acceptance. + +The [client resource probe](test-rig/client-resources/README.md) checks native baked models, textures, creative entries, and translated item names without opening a world. Static resource and locale regressions also run under `check`. + +## Compatibility + +The implementation namespace is `zone.moddev.mc.poweradvantage`. The historical `cyano.poweradvantage.api` surface, including `FluidNetworkApi`, remains available to dependent mods. Registry names, fluids, tile IDs, NBT keys, OreSpawn provider IDs, and configuration meaning remain stable. + +PowerAdvantage owns the shared pre-flattening migration hook used by all three Advantage mods. See [the 1.13 migration guide](docs/1.13/MIGRATION.md) before opening an irreplaceable 1.10 or 1.12 world. The [Advantage Works rig](test-rig/README.md) provides repeatable machine, restart, world-generation, and migration checks. + +The [endurance protocol](docs/1.13/ENDURANCE.md) exercises repeat-batch operation in fresh and migrated disposable worlds, followed by a complete server-process restart. Its coverage limits are separate from frozen-state migration and manual client acceptance. + +The [chunk and moving-drill protocol](docs/1.13/CHUNK-AND-DRILLS.md) adds genuine chunk unload/reload witnesses, finite-energy track moves in all six facings, rear-output conservation, and mid-block progress checks. + +The [partial-network and extended-drill protocol](docs/1.13/PARTIAL-NETWORKS-AND-EXTENDED-DRILLS.md) separates endpoint unloading/recovery from whole-network reloads, and covers repeated moves, vertical track travel, junctions, blocked output and redstone interruption. + +The [player and entity migration report](docs/1.13/PLAYER-AND-ENTITY-MIGRATION.md) covers player files, inventories, nested items, ownership, saved entities and full restarts. The eight tested routes pass with the repaired 1.12 bridge. Use Electric Advantage `2.2.3.112021` for the current turbine and drill fixes; the original `2.2.1.112021` jar remains a failing historical control. + +The [additional-dimension protocol](docs/1.13/DIMENSIONS-AND-ENTITY-LIFECYCLE.md) +covers Nether/End player and entity fixtures, native passenger/leash relationships +and separate death/respawn controls. The [transformer-order report](docs/1.13/TRANSFORMER-ORDERING.md) +qualifies both BaseMetals/Advantage application orders and records the +order-dependent provenance of loose-file backups. + +Networks use loaded chunks only. Missing conduit sections interrupt distribution; +normal chunk loading reconnects them without replacing pipes. The same protocol +records acquisition-stack diagnostics and pipe-only bridge regressions. + +The historical 1.10 handbook remains on the [1.10.2 branch](https://github.com/MinecraftModDevelopmentMods/PowerAdvantage/blob/master-1.10.2/docs/1.10/README.md). diff --git a/build.gradle b/build.gradle index d23c0569..fa73db01 100644 --- a/build.gradle +++ b/build.gradle @@ -76,7 +76,7 @@ tasks.withType(AbstractArchiveTask).configureEach { def archiveTextSuffixes = [ '.cfg', '.css', '.html', '.info', '.java', '.js', '.json', '.lang', - '.mcmeta', '.md', '.properties', '.txt', '.xml' + '.mcmeta', '.md', '.properties', '.toml', '.txt', '.xml' ] def archiveTextPatterns = archiveTextSuffixes.collect { "**/*${it}".toString() } archiveTextPatterns.addAll(['**/element-list', '**/package-list']) @@ -90,42 +90,53 @@ minecraft { mappings channel: project.mapping_channel, version: project.mapping_version String configuredRunDirectory = providers.gradleProperty('powerAdvantageRunDirectory') .getOrElse('run') + def forgeRunModClassesDirectory = layout.buildDirectory.dir('forge-run-mod-classes/main') + def forgeMainOutputPath = forgeRunModClassesDirectory.get().asFile.absolutePath + def mainModClasses = "${mod_id}%%${forgeMainOutputPath}${File.pathSeparator}${mod_id}%%${forgeMainOutputPath}" + def forge25RunTargets = [client: 'fmluserdevclient', server: 'fmluserdevserver', data: 'fmluserdevdata'] runs { configureEach { + mainClass = 'net.minecraftforge.userdev.LaunchTesting' workingDir.convention layout.projectDirectory.dir(configuredRunDirectory) - systemProperty 'forge.logging.console.level', 'debug' + systemProperty 'forge.logging.markers', 'REGISTRIES' + systemProperty 'forge.logging.console.level', 'info' + mods { poweradvantage { source sourceSets.main } } + environment 'target', forge25RunTargets[name] + environment 'MCP_MAPPINGS', "${mapping_channel}_${mapping_version}" + environment 'MCP_VERSION', mcp_version + environment 'FORGE_VERSION', forge_version + environment 'FORGE_GROUP', 'net.minecraftforge' + environment 'MC_VERSION', minecraft_version + environment 'MOD_CLASSES', mainModClasses } register('client') register('server') { args '--nogui' } + register('data') { + workingDir.convention layout.projectDirectory.dir('run-data') + args '--mod', project.mod_id, '--all', '--output', file('src/generated/resources/'), + '--existing', file('src/main/resources/') + } } } -// Forge 1.12 expects each development mod to expose classes and resources from -// one classpath root. ForgeGradle 7 splits those outputs, so merge only the -// launch output without contaminating the canonical source-set directories. -def forge12DevelopmentOutput = layout.buildDirectory.dir('forge12-development/main') -def syncForge12DevelopmentResources = tasks.register('syncForge12DevelopmentResources', Sync) { +sourceSets.main.resources { + srcDir 'src/generated/resources' + exclude '.cache/**' +} + +def forgeRunModClassesDirectory = layout.buildDirectory.dir('forge-run-mod-classes/main') +def prepareForgeRunModClasses = tasks.register('prepareForgeRunModClasses', Sync) { group = 'ide' - description = 'Merges classes and processed resources for Forge 1.12 Gradle launches.' + description = 'Merges classes and processed resources for Forge 1.13 launches.' dependsOn tasks.named('classes') - from tasks.named('compileJava', JavaCompile).flatMap { it.destinationDirectory } - from sourceSets.main.output.resourcesDir - into forge12DevelopmentOutput -} -tasks.configureEach { - if (name == 'runClient' || name == 'runServer') { - dependsOn syncForge12DevelopmentResources - doFirst { - Set splitMainOutputs = [ - tasks.named('compileJava', JavaCompile).get().destinationDirectory.get().asFile, - sourceSets.main.output.resourcesDir - ] as Set - classpath = files(forge12DevelopmentOutput, classpath.filter { - !splitMainOutputs.contains(it) - }) - } + from sourceSets.main.output + into forgeRunModClassesDirectory +} +tasks.withType(JavaExec).configureEach { + if (name in ['runClient', 'runServer', 'runData']) { + dependsOn prepareForgeRunModClasses } } @@ -134,14 +145,6 @@ repositories { maven fg.forgeMaven maven fg.minecraftLibsMaven - if (providers.gradleProperty('rebornCoreVerificationRepository').isPresent()) { - maven { - name = 'RebornCoreReleaseVerificationMirror' - url = uri(providers.gradleProperty('rebornCoreVerificationRepository').get()) - metadataSources { artifact() } - content { includeGroup('curse.maven') } - } - } exclusiveContent { forRepository { maven { @@ -156,39 +159,39 @@ repositories { name = 'MinecraftLibraries' url = 'https://libraries.minecraft.net/' } - if (!providers.gradleProperty('rebornCoreVerificationRepository').isPresent()) { - maven { - name = 'PinnedRebornCoreRelease' - url = 'https://maven.modmuss50.me/' - content { includeGroup('RebornCore') } - } - } } -def rfApiJar = file('libs/RF-API.jar') -def usingStagedRebornCore = providers.gradleProperty('rebornCoreVerificationRepository').isPresent() -def rebornCoreCoordinate = usingStagedRebornCore - ? "curse.maven:reborncore-${project.reborncore_curse_project_id}:${project.reborncore_curse_file_id}" - : "RebornCore:RebornCore-1.12.2:${project.reborncore_version}:universal" +// The default paths mirror the local MMD checkout layout without tying the +// build to a drive letter. CI and other layouts can override either path. +def baseMetalsDeobfJar = file(providers.gradleProperty('baseMetalsDeobfJar') + .getOrElse('../../../BaseMetals/BaseMetals 113/BaseMetals/build/libs-dev/' + + 'BaseMetals-3.0.1.113021-deobf.jar')) +def oreSpawnDeobfJar = file(providers.gradleProperty('oreSpawnDeobfJar') + .getOrElse('../../../MinecraftMineralogy/OS113/OreSpawn/build/libs-dev/' + + 'OreSpawn-4.1.0.113021-deobf.jar')) dependencies { implementation minecraft.dependency( "net.minecraftforge:forge:${project.minecraft_version}-${project.forge_version}") - // Mavenizer's legacy metadata omits this compile-time Minecraft library. - compileOnly 'org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209' - // These integrations are optional at runtime. Compile against their APIs, - // but do not package or launch PowerAdvantage with the provider mods. - compileOnly files(rfApiJar) - compileOnly rebornCoreCoordinate + compileOnly files(baseMetalsDeobfJar, oreSpawnDeobfJar) + runtimeOnly files(baseMetalsDeobfJar, oreSpawnDeobfJar) + testImplementation 'junit:junit:4.13.2' } tasks.named('processResources', ProcessResources) { filteringCharset = 'UTF-8' - inputs.property('version', project.version) - inputs.property('mcversion', project.minecraft_version) - filesMatching('mcmod.info') { - expand version: project.version, mcversion: project.minecraft_version + inputs.properties([ + version: project.version, + minecraft_version_range: project.minecraft_version_range, + forge_version_range: project.forge_version_range, + loader_version_range: project.loader_version_range + ]) + filesMatching('META-INF/mods.toml') { + expand version: project.version, + minecraft_version_range: project.minecraft_version_range, + forge_version_range: project.forge_version_range, + loader_version_range: project.loader_version_range } filesMatching(archiveTextPatterns, normalizeArchiveLineEndings) } @@ -310,13 +313,13 @@ tasks.register('verifyReleaseConfiguration') { group = 'verification' description = 'Validates release identity, target metadata, and optional integration policy.' doLast { - if (project.mod_version != '2.4.1.112021') { + if (project.mod_version != '2.4.4.113021') { throw new GradleException("Unexpected PowerAdvantage release version: ${project.mod_version}") } - if (project.minecraft_version != '1.12.2' - || project.forge_version != '14.23.5.2859' - || project.mapping_channel != 'stable' - || project.mapping_version != '39-1.12') { + if (project.minecraft_version != '1.13.2' + || project.forge_version != '25.0.223' + || project.mapping_channel != 'snapshot' + || project.mapping_version != '20180921-1.13') { throw new GradleException('Unexpected Minecraft, Forge, or mappings target') } if (project.loader_name != 'forge' || project.loader_code != '1' @@ -326,36 +329,34 @@ tasks.register('verifyReleaseConfiguration') { throw new GradleException('Unexpected release dispatcher target metadata') } if (project.mod_id != 'poweradvantage' - || project.group.toString() != 'com.mcmoddev.poweradvantage' + || project.group.toString() != 'zone.moddev.mc.poweradvantage' || base.archivesName.get() != 'PowerAdvantage' || project.curseforge_project_id != '240982') { throw new GradleException('Unexpected Forge, Maven, archive, or CurseForge identity') } if (project.cf_requirements != 'base-metals,mmd-orespawn' || project.cf_optionals != 'additional-loot-tables' - || project.cf_versions != '1.12.2') { + || project.cf_versions != '1.13.2') { throw new GradleException('Unexpected CurseForge dependency or game-version metadata') } - if (project.reborncore_curse_project_id != '500866' - || project.reborncore_curse_file_id != '3386724' - || project.reborncore_version != '3.19.4.529' - || project.reborncore_download_url != 'https://maven.modmuss50.me/RebornCore/' - + 'RebornCore-1.12.2/3.19.4.529/RebornCore-1.12.2-3.19.4.529-universal.jar' - || project.reborncore_sha256 != 'B6D11200C5EC66B0031EF4E635AC0203A23CBA6B21F046D402D41E3375798667') { - throw new GradleException('Unexpected RebornCore compile API identity') + if (project.basemetals_version != '3.0.1.113021' + || project.orespawn_version != '4.1.0.113021' + || project.basemetals_source_commit != '1376a20d7d5ba22d3abc77dc34b49a2c88943697' + || project.orespawn_source_commit != '6a7c2bb07b9a7771128812fcc07d185f7d9df58b') { + throw new GradleException('Unexpected Base Metals or OreSpawn dependency identity') } String mainSource = file( - 'src/main/java/com/mcmoddev/poweradvantage/PowerAdvantage.java').getText('UTF-8') + 'src/main/java/zone/moddev/mc/poweradvantage/PowerAdvantage.java').getText('UTF-8') if (!mainSource.contains('MODID = "poweradvantage"') - || !mainSource.contains('VERSION = "2.4.1.112021"') - || !mainSource.contains('dependencies = "after:basemetals"')) { - throw new GradleException('PowerAdvantage source identity or optional BaseMetals policy changed') + || !mainSource.contains('VERSION = "2.4.4.113021"') + || !mainSource.contains('@Mod(PowerAdvantage.MODID)')) { + throw new GradleException('PowerAdvantage source identity changed') } if (!file('src/main/java/cyano/poweradvantage/api/PoweredEntity.java').isFile()) { throw new GradleException('Legacy cyano.poweradvantage.api compatibility surface is missing') } String entitySource = file( - 'src/main/java/com/mcmoddev/poweradvantage/init/Entities.java').getText('UTF-8') + 'src/main/java/zone/moddev/mc/poweradvantage/init/Entities.java').getText('UTF-8') [ 'tileentity.pipe_terminal', 'tileentity.fluid_drain', 'tileentity.fluid_discharge', 'tileentity.fluid_storage_tank', @@ -368,10 +369,49 @@ tasks.register('verifyReleaseConfiguration') { throw new GradleException("Persistent tile entity id changed or disappeared: ${id}") } } - def metadata = new JsonSlurper().parse(file('src/main/resources/mcmod.info')) - def mod = metadata instanceof List ? metadata.first() : metadata - if (mod.modid != 'poweradvantage') { - throw new GradleException('Source mcmod.info does not use the stable poweradvantage mod id') + String modsToml = file('src/main/resources/META-INF/mods.toml').getText('UTF-8') + ['modId="poweradvantage"', 'modId="basemetals"', 'modId="orespawn"', + 'mandatory=true'].each { required -> + if (!modsToml.contains(required)) { + throw new GradleException("Source mods.toml is missing ${required}") + } + } + if (!modsToml.contains('modId="basemetals"\nmandatory=false')) { + throw new GradleException('Base Metals must remain Forge-optional; CurseForge installs it for players') + } + String steelBarsTag = file( + 'src/main/resources/data/forge/tags/items/bars/steel.json').getText('UTF-8') + if (!steelBarsTag.contains('"optional"') || !steelBarsTag.contains('"basemetals:steel_bars"')) { + throw new GradleException('Steel bars must use Forge optional tag entries when Base Metals is absent') + } + [ + 'dusts/coal', 'dusts/lead', 'dusts/redstone', 'dusts/silver', 'dusts/tin', + 'gems/diamond', 'gems/emerald', + 'ingots/brass', 'ingots/copper', 'ingots/gold', 'ingots/iron', + 'ingots/lead', 'ingots/nickel', 'ingots/steel', 'ingots/zinc', + 'nuggets/bronze', 'nuggets/copper', 'nuggets/gold', 'nuggets/iron', + 'nuggets/lead', 'nuggets/tin', + 'plates/copper', 'plates/iron', 'plates/steel', + 'rods/copper', 'rods/wooden', 'stone', + 'storage_blocks/diamond', 'storage_blocks/steel' + ].each { tagPath -> + if (!file("src/main/resources/data/forge/tags/items/${tagPath}.json").isFile()) { + throw new GradleException("Missing decoupled Forge item tag declaration: ${tagPath}") + } + } + Map provider = new JsonSlurper().parse(file( + 'src/main/resources/data/poweradvantage/orespawn/provider.json')) as Map + Map desertOil = provider.fluid_deposits[ + 'poweradvantage:fluid_deposit/desert_crude_oil'].dimensions['minecraft:overworld'] as Map + if (desertOil.biome_dictionary != ['SANDY'] + || (desertOil.excluded_biome_dictionary as Set) != (['BEACH', 'MESA'] as Set)) { + throw new GradleException( + 'Desert crude oil must use Forge SANDY biomes while excluding BEACH and MESA') + } + if ((desertOil.host_families as Set) != ([ + 'sedimentary', 'metamorphic', 'igneous_intrusive', 'igneous_volcanic'] as Set)) { + throw new GradleException( + 'Desert crude oil must remain compatible with OreSpawn geology-family hosts') } } } @@ -381,69 +421,34 @@ tasks.register('verifyReleaseDependencies') { description = 'Resolves and audits Forge plus the exact optional compile APIs.' dependsOn tasks.named('verifyReleaseConfiguration') doLast { - if (!rfApiJar.isFile()) { - throw new GradleException("Missing tracked RF API jar: ${rfApiJar}") - } - String actualRfHash = MessageDigest.getInstance('SHA-256') - .digest(rfApiJar.bytes).encodeHex().toString().toUpperCase() - if (actualRfHash != project.rf_api_sha256) { - throw new GradleException("RF API SHA-256 is ${actualRfHash}, expected ${project.rf_api_sha256}") - } - - def compileArtifacts = configurations.compileClasspath.resolvedConfiguration.resolvedArtifacts - String expectedRebornCoreGroup = usingStagedRebornCore ? 'curse.maven' : 'RebornCore' - String expectedRebornCoreName = usingStagedRebornCore - ? "reborncore-${project.reborncore_curse_project_id}" - : 'RebornCore-1.12.2' - def rebornCoreArtifacts = compileArtifacts.findAll { artifact -> - def id = artifact.moduleVersion.id - id.group == expectedRebornCoreGroup && id.name == expectedRebornCoreName - } - String expectedRebornCoreVersion = usingStagedRebornCore - ? project.reborncore_curse_file_id - : project.reborncore_version - String expectedRebornCore = "${expectedRebornCoreGroup}:${expectedRebornCoreName}:" \ - + expectedRebornCoreVersion - if (rebornCoreArtifacts.size() != 1 - || "${rebornCoreArtifacts.first().moduleVersion.id.group}:" - + "${rebornCoreArtifacts.first().moduleVersion.id.name}:" - + "${rebornCoreArtifacts.first().moduleVersion.id.version}" != expectedRebornCore) { - throw new GradleException("Expected exactly ${expectedRebornCore}, found " - + rebornCoreArtifacts.collect { it.moduleVersion.id }) - } - File rebornCoreJar = rebornCoreArtifacts.first().file - String actualRebornCoreHash = MessageDigest.getInstance('SHA-256') - .digest(rebornCoreJar.bytes).encodeHex().toString().toUpperCase() - if (actualRebornCoreHash != project.reborncore_sha256) { - throw new GradleException("RebornCore SHA-256 is ${actualRebornCoreHash}, expected " - + project.reborncore_sha256) - } - new ZipFile(rebornCoreJar).withCloseable { zip -> - ['reborncore/api/power/IEnergyInterfaceTile.class', - 'reborncore/api/power/EnumPowerTier.class'].each { required -> - if (zip.getEntry(required) == null) { - throw new GradleException("RebornCore compile API is missing ${required}") - } + [(baseMetalsDeobfJar): project.basemetals_deobf_sha256, + (oreSpawnDeobfJar): project.orespawn_deobf_sha256].each { File dependencyJar, String expectedHash -> + if (!dependencyJar.isFile()) { + throw new GradleException("Missing pinned dependency jar: ${dependencyJar}") + } + String actualHash = MessageDigest.getInstance('SHA-256') + .digest(dependencyJar.bytes).encodeHex().toString().toUpperCase() + if (actualHash != expectedHash) { + throw new GradleException("${dependencyJar.name} SHA-256 is ${actualHash}, expected ${expectedHash}") } } - Set compileFiles = configurations.compileClasspath.files.collect { it.canonicalFile } as Set - if (!compileFiles.contains(rfApiJar.canonicalFile) - || !compileFiles.contains(rebornCoreJar.canonicalFile)) { - throw new GradleException('Optional RF or RebornCore API is missing from compileClasspath') + if (!compileFiles.contains(baseMetalsDeobfJar.canonicalFile) + || !compileFiles.contains(oreSpawnDeobfJar.canonicalFile)) { + throw new GradleException('Pinned Base Metals or OreSpawn API is missing from compileClasspath') } Set runtimeFiles = configurations.runtimeClasspath.files.collect { it.canonicalFile } as Set - if (runtimeFiles.contains(rfApiJar.canonicalFile) - || runtimeFiles.contains(rebornCoreJar.canonicalFile)) { - throw new GradleException('Optional RF or RebornCore API leaked onto runtimeClasspath') + if (!runtimeFiles.contains(baseMetalsDeobfJar.canonicalFile) + || !runtimeFiles.contains(oreSpawnDeobfJar.canonicalFile)) { + throw new GradleException('Pinned Base Metals or OreSpawn is missing from development runtime') } - List forbiddenRuntimeMods = runtimeFiles.collect { it.name.toLowerCase(Locale.ROOT) } + List obsoleteRuntimeMods = runtimeFiles.collect { it.name.toLowerCase(Locale.ROOT) } .findAll { name -> - name.contains('basemetals') || name.contains('orespawn') + name.contains('rf-api') || name.contains('redstoneflux') || name.contains('tesla') || name.contains('reborncore') } - if (!forbiddenRuntimeMods.isEmpty()) { - throw new GradleException("Optional mods leaked onto runtimeClasspath: ${forbiddenRuntimeMods}") + if (!obsoleteRuntimeMods.isEmpty()) { + throw new GradleException("Obsolete energy APIs leaked onto runtimeClasspath: ${obsoleteRuntimeMods}") } } } @@ -486,27 +491,33 @@ tasks.register('verifyReleaseArtifacts') { File mainJar = new File(libs, expectedReleaseFiles.get()[0]) new ZipFile(mainJar).withCloseable { zip -> List names = zip.entries().collect { it.name } - ['mcmod.info', 'pack.mcmeta', - 'com/mcmoddev/poweradvantage/PowerAdvantage.class', + ['META-INF/mods.toml', 'pack.mcmeta', + 'zone/moddev/mc/poweradvantage/PowerAdvantage.class', 'cyano/poweradvantage/api/PoweredEntity.class', - 'assets/poweradvantage/lang/en_us.lang'].each { required -> + 'cyano/poweradvantage/api/fluid/FluidNetworkApi.class', + 'assets/poweradvantage/lang/en_us.json', + 'data/poweradvantage/recipes/fluid_drain.json', + 'data/poweradvantage/advancements/recipes/sprocket_rod_normal.json'].each { required -> if (!names.contains(required)) { throw new GradleException("Release jar is missing ${required}") } } ['agent-notes/', '.codex/', 'src/test/', 'fernflower_abstract_parameter_names.txt', 'cofh/api/', 'reborncore/api/', 'cyano/basemetals/', 'com/mcmoddev/lib/', + 'zone/moddev/mc/basemetals/', 'zone/moddev/mc/orespawn/', + 'zone/moddev/mc/advantageworks/', 'com/mojang/serialization/', 'net/minecraft/', 'net/minecraftforge/'].each { forbidden -> if (names.any { it.contains(forbidden) }) { throw new GradleException("Release jar contains forbidden entry matching ${forbidden}") } } - def metadata = new JsonSlurper().parseText(zip.getInputStream(zip.getEntry('mcmod.info')) - .getText(StandardCharsets.UTF_8.name())) - def mod = metadata instanceof List ? metadata.first() : metadata - if (mod.modid != project.mod_id || mod.version != project.mod_version - || mod.mcversion != project.minecraft_version) { - throw new GradleException('Packaged mcmod.info identity, version, or target is incorrect') + String modsToml = zip.getInputStream(zip.getEntry('META-INF/mods.toml')) + .getText(StandardCharsets.UTF_8.name()) + if (!modsToml.contains('modId="poweradvantage"') + || !modsToml.contains("version=\"${project.mod_version}\"") + || !modsToml.contains('modId="basemetals"') + || !modsToml.contains('modId="orespawn"')) { + throw new GradleException('Packaged mods.toml identity, version, or dependencies are incorrect') } def manifest = new Manifest(zip.getInputStream(zip.getEntry('META-INF/MANIFEST.MF'))).mainAttributes if (manifest.getValue('Implementation-Version') != project.mod_version @@ -530,7 +541,7 @@ tasks.register('verifyReleaseArtifacts') { } new ZipFile(new File(libs, expectedReleaseFiles.get()[1])).withCloseable { sourceZip -> - if (sourceZip.getEntry('com/mcmoddev/poweradvantage/PowerAdvantage.java') == null + if (sourceZip.getEntry('zone/moddev/mc/poweradvantage/PowerAdvantage.java') == null || sourceZip.getEntry('cyano/poweradvantage/api/PoweredEntity.java') == null) { throw new GradleException('Sources jar is missing implementation or legacy API sources') } @@ -548,6 +559,7 @@ tasks.register('writeReleaseChecksums') { description = 'Writes SHA-256 checksums for the audited release jars.' dependsOn tasks.named('verifyReleaseArtifacts') def outputFile = layout.buildDirectory.file('release/SHA256SUMS') + inputs.files(provider { expectedReleaseFiles.get().collect { layout.buildDirectory.file("libs/${it}").get().asFile } }) outputs.file(outputFile) doLast { File output = outputFile.get().asFile @@ -698,10 +710,24 @@ eclipse { classpath { downloadSources = true downloadJavadoc = true + file.whenMerged { model -> + // Eclipse must not overwrite expanded metadata with src/main/resources templates. + def rawResources = sourceSets.main.resources.srcDirs.collect { relativePath(it).replace('\\', '/') } + model.entries.removeAll { entry -> + entry instanceof org.gradle.plugins.ide.eclipse.model.SourceFolder + && (rawResources.contains(entry.path) || entry.path == 'build/resources/main') + } + def processed = new org.gradle.plugins.ide.eclipse.model.SourceFolder('build/resources/main', 'bin/main') + processed.entryAttributes['gradle_scope'] = 'main' + processed.entryAttributes['gradle_used_by_scope'] = 'main,test' + model.entries.add(processed) + } } synchronizationTasks 'isolateEclipseProductionRuns' } +tasks.named('eclipseClasspath') { dependsOn tasks.named('processResources') } + tasks.register('configureEclipseBuildship') { group = 'ide' description = 'Creates Buildship preferences for ForgeGradle 7 Eclipse imports.' @@ -751,6 +777,21 @@ tasks.register('isolateEclipseProductionRuns') { contents = contents.replace( 'key="MC_VERSION" value="${MC_VERSION}"', "key=\"MC_VERSION\" value=\"${project.minecraft_version}\"") + if (launch.name in ['runClient.launch', 'runServer.launch', 'runData.launch']) { + // Eclipse compiles into bin/main, not Gradle's merged run output. + def launchXml = new groovy.xml.XmlSlurper().parseText(contents) + def modClasses = launchXml.mapAttribute.find { + it.@key.text() == 'org.eclipse.debug.core.environmentVariables' + }.mapEntry.findAll { it.@key.text() == 'MOD_CLASSES' } + if (modClasses.size() != 1) { + throw new GradleException("${launch.name} must define one MOD_CLASSES entry") + } + String mainOutput = file('bin/main').absolutePath + String eclipseModClasses = "${project.mod_id}%%${mainOutput}" + + "${File.pathSeparator}${project.mod_id}%%${mainOutput}" + modClasses[0].@value = eclipseModClasses + contents = groovy.xml.XmlUtil.serialize(launchXml) + } launch.setText(contents.replace('\r\n', '\n'), 'UTF-8') } ['runClient.launch', 'runServer.launch'].each { name -> @@ -840,21 +881,52 @@ tasks.register('verifyEclipseProductionClasspath') { || !projectMetadata.contains('org.eclipse.buildship.core.gradleprojectbuilder')) { throw new GradleException('Eclipse project metadata is missing its Buildship nature or builder') } - File eclipseModMetadata = file('bin/main/mcmod.info') - def eclipseMetadata = new JsonSlurper().parseText(eclipseModMetadata.getText('UTF-8')) - def eclipseMod = eclipseMetadata instanceof List ? eclipseMetadata.first() : eclipseMetadata - if (eclipseMod.modid != project.mod_id || eclipseMod.version != project.mod_version - || eclipseMod.mcversion != project.minecraft_version) { + File eclipseModMetadata = file('bin/main/META-INF/mods.toml') + String eclipseMetadata = eclipseModMetadata.getText('UTF-8') + if (!eclipseMetadata.contains("modId=\"${project.mod_id}\"") + || !eclipseMetadata.contains("version=\"${project.mod_version}\"")) { throw new GradleException('Eclipse output contains incorrect mod metadata') } + def eclipseClasspath = new groovy.xml.XmlSlurper().parse(file('.classpath')) + def processedResources = eclipseClasspath.classpathentry.findAll { + it.@kind.text() == 'src' && it.@path.text() == 'build/resources/main' + } + def rawResourcePaths = sourceSets.main.resources.srcDirs.collect { relativePath(it).replace('\\', '/') } + if (processedResources.size() != 1 || processedResources[0].@output.text() != 'bin/main' + || eclipseClasspath.classpathentry.any { + it.@kind.text() == 'src' && rawResourcePaths.contains(it.@path.text()) + }) { + throw new GradleException('Eclipse must copy processed resources, never raw mod metadata templates') + } + String mainOutput = eclipseClasspath.classpathentry.find { + it.@kind.text() == 'src' && it.@path.text() == 'src/main/java' + }.@output.text() + if (file(mainOutput).canonicalFile != file('bin/main').canonicalFile) { + throw new GradleException('Eclipse production classes must use bin/main') + } + String expectedModClasses = "${project.mod_id}%%${file(mainOutput).absolutePath}" + + "${File.pathSeparator}${project.mod_id}%%${file(mainOutput).absolutePath}" + ['runClient.launch', 'runServer.launch', 'runData.launch'].each { name -> + def launch = new groovy.xml.XmlSlurper().parse(file(name)) + String modClasses = launch.mapAttribute.find { + it.@key.text() == 'org.eclipse.debug.core.environmentVariables' + }.mapEntry.find { it.@key.text() == 'MOD_CLASSES' }.@value.text() + if (modClasses != expectedModClasses) { + throw new GradleException("${name} does not discover the Eclipse production mod in bin/main") + } + } ['runClient.launch', 'runServer.launch'].each { name -> + def launch = new groovy.xml.XmlSlurper().parse(file(name)) String contents = file(name).getText('UTF-8').replace('\\', '/') - if (!contents.contains('org.eclipse.jdt.launching.ATTR_EXCLUDE_TEST_CODE') - || !contents.contains('value="true"') + if (launch.booleanAttribute.find { + it.@key.text() == 'org.eclipse.jdt.launching.ATTR_EXCLUDE_TEST_CODE' + }.@value.text() != 'true' || contents.contains('src/test') || contents.contains('bin/test')) { throw new GradleException("${name} does not isolate production code") } - if (!contents.contains('PROJECT_ATTR" value="PowerAdvantage"')) { + if (launch.stringAttribute.find { + it.@key.text() == 'org.eclipse.jdt.launching.PROJECT_ATTR' + }.@value.text() != 'PowerAdvantage') { throw new GradleException("${name} targets the wrong Eclipse project") } if (contents.contains('org.eclipse.buildship.core.gradleclasspathcontainer')) { @@ -882,9 +954,9 @@ tasks.register('verifyEclipseProductionClasspath') { throw new GradleException( "${name} does not preserve the Forge bootstrap classpath order") } - if (contents.contains('${MC_VERSION}') - || !contents.contains( - "key=\"MC_VERSION\" value=\"${project.minecraft_version}\"")) { + if (contents.contains('${MC_VERSION}') || launch.mapAttribute.find { + it.@key.text() == 'org.eclipse.debug.core.environmentVariables' + }.mapEntry.find { it.@key.text() == 'MC_VERSION' }.@value.text() != project.minecraft_version) { throw new GradleException("${name} does not define a literal Minecraft version") } } @@ -901,11 +973,27 @@ tasks.register('verifyModelAssets') { inputs.dir(blockstateRoot) doLast { + Set vanillaAssets = [] as Set + configurations.runtimeClasspath.files.findAll { + it.name.startsWith('client-extra-') && it.name.endsWith('.jar') + }.each { File archive -> + new ZipFile(archive).withCloseable { zip -> + zip.entries().each { entry -> vanillaAssets.add(entry.name) } + } + } def verifyReference = { String reference, String assetType, String sourcePath -> if (reference.startsWith('#')) return int separator = reference.indexOf(':') String namespace = separator >= 0 ? reference.substring(0, separator) : 'minecraft' String path = separator >= 0 ? reference.substring(separator + 1) : reference + if (namespace == 'minecraft') { + if (assetType == 'models' && path.startsWith('builtin/')) return + String suffix = assetType == 'textures' ? '.png' : '.json' + if (!vanillaAssets.contains("assets/minecraft/${assetType}/${path}${suffix}".toString())) { + throw new GradleException("${sourcePath} references missing vanilla ${assetType} asset ${reference}") + } + return + } if (namespace != project.mod_id) return File target = new File(resourceRoot, @@ -936,7 +1024,7 @@ tasks.register('verifyModelAssets') { if (node instanceof Map) { node.each { key, value -> if (key == 'model' && value instanceof String) { - verifyReference(value, 'models/block', sourcePath) + verifyReference(value, 'models', sourcePath) } else { verifyBlockstateModels(value, sourcePath) } @@ -946,7 +1034,57 @@ tasks.register('verifyModelAssets') { } } fileTree(blockstateRoot) { include '**/*.json' }.files.sort().each { File blockstateFile -> - verifyBlockstateModels(new JsonSlurper().parse(blockstateFile), blockstateFile.path) + def blockstate = new JsonSlurper().parse(blockstateFile) + if (blockstate.containsKey('forge_marker') + || blockstate.variants?.containsKey('normal') || blockstate.variants?.containsKey('fluid')) { + throw new GradleException("${blockstateFile.path} uses a removed legacy blockstate format") + } + verifyBlockstateModels(blockstate, blockstateFile.path) + } + } +} + +tasks.register('verifyRecipeTagGuards') { + group = 'verification' + description = 'Rejects recipes that can collapse when a Forge material tag is empty.' + File recipeRoot = file("src/main/resources/data/${project.mod_id}/recipes") + inputs.dir(recipeRoot) + doLast { + Closure collectTags + collectTags = { Object node, Set tags -> + if (node instanceof Map) { + node.each { key, value -> + if (key == 'conditions' || key == 'result') return + if (key == 'tag' && value instanceof String && value.startsWith('forge:')) { + tags.add(value) + } else { + collectTags(value, tags) + } + } + } else if (node instanceof Collection) { + node.each { collectTags(it, tags) } + } + } + fileTree(recipeRoot) { include '**/*.json' }.files.sort().each { File recipeFile -> + Map recipe = new JsonSlurper().parse(recipeFile) as Map + Set tags = [] as Set + collectTags(recipe, tags) + Collection conditions = (recipe.conditions ?: []) as Collection + if (conditions.any { !(it instanceof Map) }) { + throw new GradleException("${recipeFile.path} contains a non-object recipe condition") + } + Set guards = conditions + .findAll { it instanceof Map && it.type == 'poweradvantage:tag_not_empty' } + .collect { it.tag as String } as Set + Set emptyGuards = conditions.findAll { it.type == 'poweradvantage:tag_empty' } + .collect { it.tag as String } as Set + if (!(emptyGuards.intersect(guards)).isEmpty()) { + throw new GradleException("${recipeFile.path} requires the same tag to be empty and nonempty") + } + Set missing = tags - guards + if (!missing.isEmpty()) { + throw new GradleException("${recipeFile.path} lacks tag_not_empty guards for ${missing.sort()}") + } } } } @@ -954,4 +1092,72 @@ tasks.register('verifyModelAssets') { tasks.named('check') { dependsOn tasks.named('verifyReleaseConfiguration') dependsOn tasks.named('verifyModelAssets') + dependsOn tasks.named('verifyRecipeTagGuards') +} + +def compileClientResourceProbe = tasks.register('compileClientResourceProbe', JavaCompile) { + group = 'verification' + description = 'Compiles the build-only client model and translation probe.' + dependsOn tasks.named('classes') + source fileTree('test-rig/client-resources/java') + classpath = files(sourceSets.main.output, sourceSets.main.compileClasspath) + destinationDirectory = layout.buildDirectory.dir('client-resource-probe/classes') +} + +tasks.register('clientResourceProbeJar', Jar) { + group = 'verification' + description = 'Builds a test-only client probe; never part of a release or normal launch.' + dependsOn compileClientResourceProbe + archiveFileName = 'advantageclientresources-deobf.jar' + destinationDirectory = layout.buildDirectory.dir('client-resource-probe') + from compileClientResourceProbe.flatMap { it.destinationDirectory } + from 'test-rig/client-resources/resources' +} + +// Optional coordinated play-test launch. The ordinary launch stays Power-only; +// these deobf jars are runtime inputs, never release contents or compile dependencies. +def advantageExtensionJars = [ + steamadvantage: file(providers.gradleProperty('steamAdvantageDeobfJar').getOrElse( + '../SteamAdvantage/SteamAdvantage/build/libs-dev/SteamAdvantage-2.2.5.113021-deobf.jar')), + electricadvantage: file(providers.gradleProperty('electricAdvantageDeobfJar').getOrElse( + '../ElectricAdvantage/ElectricAdvantage/build/libs-dev/ElectricAdvantage-2.2.5.113021-deobf.jar')) +] + +tasks.register('prepareCombinedEclipseLaunch') { + group = 'ide' + description = 'Creates an additional production-only Eclipse client launch with all three Advantage mods.' + dependsOn tasks.named('verifyEclipseProductionClasspath') + doLast { + def profile = new JsonSlurper().parse(file('test-rig/profiles/dev-core.json')) + advantageExtensionJars.each { String id, File jar -> + def pin = profile.mods.find { it.id == id } + if (!jar.isFile()) throw new GradleException("Build ${id} first, or override its *AdvantageDeobfJar property: ${jar}") + String hash = MessageDigest.getInstance('SHA-256').digest(jar.bytes).encodeHex().toString() + if (pin == null || !hash.equalsIgnoreCase(pin.sha256 as String)) { + throw new GradleException("${id} does not match the audited dev-core dependency pin: ${jar}") + } + } + def launch = new groovy.xml.XmlParser().parse(file('runClient.launch')) + def classpath = launch.listAttribute.find { it.@key == 'org.eclipse.jdt.launching.CLASSPATH' } + if (classpath == null) throw new GradleException('Base client launch has no explicit production classpath') + advantageExtensionJars.values().each { File jar -> + String entry = groovy.xml.XmlUtil.serialize(new Node(null, 'runtimeClasspathEntry', + [externalArchive: jar.canonicalPath, type: '2', path: '5'])) + classpath.appendNode('listEntry', [value: entry]) + } + File combined = file('runClientAllAdvantage.launch') + combined.setText(groovy.xml.XmlUtil.serialize(launch), 'UTF-8') + def parsed = new groovy.xml.XmlSlurper().parse(combined) + def archivePaths = parsed.listAttribute.find { + it.@key.text() == 'org.eclipse.jdt.launching.CLASSPATH' + }.listEntry.collect { new groovy.xml.XmlSlurper().parseText(it.@value.text()).@externalArchive.text() } + advantageExtensionJars.values().each { File jar -> + if (archivePaths.count(jar.canonicalPath) != 1) throw new GradleException("Missing or repeated extension: ${jar}") + } + if (combined.text.contains('advantageworkstest') || combined.text.contains('client-resource-probe') + || combined.text.contains('bin/test') || combined.text.contains('buildship.core.gradleclasspathcontainer')) { + throw new GradleException('Combined launch leaked test code or an unfiltered classpath') + } + logger.lifecycle("Prepared ${combined.name}; uses the same run directory and saves as runClient.launch.") + } } diff --git a/docs/1.13/CHUNK-AND-DRILLS.md b/docs/1.13/CHUNK-AND-DRILLS.md new file mode 100644 index 00000000..b4a9f219 --- /dev/null +++ b/docs/1.13/CHUNK-AND-DRILLS.md @@ -0,0 +1,339 @@ +# Chunk recovery and moving-drill qualification + +These tests complement the [frozen migration matrix](MIGRATION-MATRIX.md) and +[repeat-load endurance](ENDURANCE.md). They operate packaged 1.13.2 Advantage +mods in disposable worlds, not Eclipse classes or an irreplaceable player save. + +Latest qualification: [the current network-repair candidate passes](#current-network-repair-regression). +The earlier repair results and original failure evidence remain below for comparison. + +## Protocol + +`test-rig/migration/lifecycle.py` creates a fresh world and a copy of the original +Cyano 1.10 -> maintained 1.12 -> 1.13 conversion fixture. It builds isolated lanes +above superflat terrain, at least 2,048 blocks from spawn. The converted copy's +lanes are newly commissioned equipment; this is not operation coverage for every +original machine in an old world. + +Twelve drills cover both mods in all six facings. Each advances one block along +horizontal track across a chunk boundary, against an immediate bedrock stop. +Each carries seven dirt with identifying custom item NBT and thirteen apples. +Public energy setters commission a finite reserve within the actual capacity: +40/50 steam or 400/500 electricity. There is no generation during the move. + +A test-only world-tick observer records the drill immediately before and after +its native movement. It checks destination, serialized position, facing, exact +inventory including tags, replacement of the old drill with conduit, and cost: +10 steam plus its ordinary 1/16-per-tick decay, or 250 electricity. After recording +the move, the observer adds an external redstone block to hold the new drill. +It never rewrites that drill's NBT, inventory, progress or energy. + +Independent lanes place a steam furnace, electric furnace and fluid receiver on +the opposite side of a chunk seam from their supply/conduit. Native inventory +setters feed four iron ores per furnace; solid fuel is retained in the steam +furnace. Public fluid-network offers supply five measured 1,000 mB water batches +to a 10,000 mB metal tank. Sources and pipes are not replaced across reloads. + +Three cycles release the fixture chunk tickets and queue normal provider unloads. +The non-loading observer requires actual Forge unload events, absence from the +loaded-chunk map, and continued absence after another two seconds. Reload requires +new load events and different tile instances, stable facing/inventory/tank contents, +and resumed ore processing/fluid acceptance. A snapshot must not load the chunk +whose absence it claims to measure. + +The server then saves and exits completely. A new process checks retained moved +drills, inventories and 4,000 mB water before new inputs. Derived active/redstone +flags settle through normal ticks; they are not treated as immutable save state. +The original pipe at each moved drill is now supplied, rear chests are installed, +and bounded mining checks one cobblestone -> one gravel for Steam and nine +cobblestone -> nine cobblestone drops for Electric. All twenty carried items and +their tags must reach the rear chest with the mined output. Another real chunk +cycle checks those results remain intact. + +Finally, long-lived obsidian targets establish nonzero mining progress. Chunk +reload and a second full process restart must retain progress toward the same +target. These observations distinguish retained completed items from lost work +on an unfinished block. No redstone intervention is made during the mid-block +checks, because redstone intentionally cancels progress. + +The repair regression also performs two separate saved-NBT read checks after +the real process restarts. `legacy-read` round-trips each drill's actual saved +record with only the optional target-state snapshot removed. It never invents +inventory, energy or progress values. `changed-target-read` replaces the saved +target's centre with bedrock before reading that record; progress and target +coordinates must clear. These focused read-path checks supplement, rather than +replace, the genuine chunk and process lifecycle tests. + +## Repeating the test + +From the PowerAdvantage checkout, with built/pinned release and harness jars: + +```text +gradlew.bat -p test-rig check assemble +test-rig/migration/.venv/Scripts/python.exe -m unittest discover -s test-rig/migration -p test_*.py -v +test-rig/migration/.venv/Scripts/python.exe test-rig/migration/lifecycle.py --java8 --migrated --output run-lifecycle/new-run --port 25586 +test-rig/migration/.venv/Scripts/python.exe test-rig/migration/audit_lifecycle.py --run run-lifecycle/new-run --output run-lifecycle/new-run/qualification-audit.json +``` + +Use the normal Python executable on other platforms. Output directories must be +new. All source fixture files are fingerprinted before/after; staged jars are +SHA-256 checked. The runner stops its own servers on both success and failure. +Minecraft worlds, logs and reports remain Git-ignored. A completed run may return +`FAIL` for a reproducible gameplay defect; a passing evidence audit validates the +record, not the defective gameplay. + +## Coverage limits + +This gate covers one horizontal track step for each of the six drill facings, +not every track direction, long multi-step travel, track junctions or blocked/full +output behavior. Whole fixture networks unload together; deliberately partial +network unload and asymmetric loading are separate scenarios. It does not claim +client animation/GUI acceptance, elevators, weapons, or a complete Sylvester +modpack upgrade. Known Forge/Base Metals log errors remain visible in the reports. + +The subsequent [partial-network and extended-drill campaign](PARTIAL-NETWORKS-AND-EXTENDED-DRILLS.md) +addresses selected limits without changing what this earlier gate proved. + +## Before repair: 6 October 2026 + +The authoritative bounded-reserve run is `run-lifecycle/20261006-native-final`. +It completed against both a fresh 1.13 world and a disposable copy of the +original 1.10 -> maintained 1.12 -> 1.13 fixture. Earlier fixture trials remain +diagnostic evidence only. No production gameplay code changed in this pass. + +| Acceptance case | Fresh 1.13 | Converted fixture | +| --- | --- | --- | +| Five genuine chunk unload/reload cycles | PASS | PASS | +| Network processing resumes without replacing pipes/sources | PASS | PASS | +| Water conservation: five offers, 5,000 mB retained | PASS | PASS | +| Moved drill positions, six facings, inventories and custom item NBT | PASS | PASS | +| Steam helper-bit orientation after reload | PASS | PASS | +| Steam movement energy cost | PASS | PASS | +| Electric movement energy cost | FAIL | FAIL | +| Bounded mining yield retained across reload | PASS | PASS | +| Output reaches an empty vanilla rear chest | FAIL | FAIL | +| Unfinished mining progress survives chunk/full restart | FAIL | FAIL | +| Independent evidence audit | PASS | PASS | +| Overall machine qualification | FAIL | FAIL | + +Each world exercised 60 unique chunks over five cycles: 300 independently +confirmed unload events per world, 600 total. Each also performed twelve moves +and two full process restarts. All six Steam and six Electric facings were +covered in both worlds. Each drill retained its twenty carried items, their +custom tags, and the exact mined yield; the failed output check means those +items remained inside the drill, not that they disappeared. + +The locally retained `lifecycle-results.json` contains every case, input jar +SHA-256, source-fixture protection result and log diagnostic. +`qualification-audit.json` independently reconciles those verdicts against raw +snapshots, real chunk events and staged jar hashes. The source fixture's before +and after fingerprints match. All six server processes shut down normally; +five emitted Forge's temporary-resource-directory cleanup error on exit. There +were no ticking-block-entity crashes, failed native inventory commands or +"Can't keep up!" warnings. These cleanup diagnostics remain in the report. + +The harness used was `AdvantageWorksTestHarness-1.1.0.113021.jar`, SHA-256 +`559D141BF7798C5BD2748170BDEBC235BB3FE2D732CB5DE75CFA2E5E9405E421`. +All production jar pins remained unchanged. The harness checks passed (nine +Java tests), the migration runner tests passed (33 Python tests), and +PowerAdvantage's `check verifyReleaseArtifacts` passed, including harness +isolation. None of those passes overrides a failed gameplay assertion. + +## Confirmed defect register + +### EA-113-001: Electric track moves do not pay their energy cost + +- Expected: the moved Electric Drill retains its inventory/facing and debits the + defined `ENERGY_COST_MOVE = 250` from the transferred reserve. +- Observed in both worlds: all six moves leave 400 electricity as 400, + rather than 150. The finite reserve is within the drill's 500-unit capacity. +- Cause: `ElectricDrillTileEntity.followTrack()` copies energy into the new tile, + then calls `this.subtractEnergy(...)` on the old, removed tile instead + (before-repair source: `machines/ElectricDrillTileEntity.java:164`). +- Impact: free track travel, not lost inventory. Steam's 10-unit debit works. +- Older versions: the same implementation is present in maintained 1.10 and + 1.12 source. Those versions were not runtime-qualified for this defect here. + +### AW-113-001: Drills cannot insert into a new empty vanilla rear chest + +- Expected: both drills deliver stored and freshly mined items into an available + rear inventory, consistent with their documented automation purpose. +- Observed in both worlds: every rear chest remains empty after 60 seconds. + The drill retains the exact twenty carried items plus the correct mined yield: + one gravel for Steam or nine cobblestone for Electric. Custom item NBT survives. +- Cause: both `inventoryTransfer()` implementations treat only `null` as an empty + destination. `InventoryWrapper.getStackInSlot()` returns vanilla's non-null + `ItemStack.EMPTY`, which matches neither insertion branch. Before-repair locations: + Steam `machines/SteamDrillTileEntity.java:419`, Electric + `machines/ElectricDrillTileEntity.java:426`, Power + `util/InventoryWrapper.java:236`. +- Impact: rear output stalls; this observation is not item disappearance. +- Older versions: maintained 1.12 has the same null-only check. A legacy 1.10 + empty-slot contract is different; do not claim the same runtime failure there. + +### AW-113-002: Reload resets work on an unfinished drill target + +- Expected: saved mining progress continues against an unchanged target, while + inventories, energy and facing are restored. +- Observed in both worlds: progress of 92-98 ticks becomes 0-6 ticks after + chunk reload; progress of 100-107 becomes 1-6 after full restart. The target + remains the same obsidian block in all twelve lanes per world. No redstone + signal or target completion accounts for the reset. +- Cause: both drills read saved `progress`, then reconstruct the target through + `targetBlock()`, which unconditionally sets `progress = 0`. Deferred target + reconstruction repeats this on the first loaded tick. Before-repair reset locations: + Steam `machines/SteamDrillTileEntity.java:324` and Electric + `machines/ElectricDrillTileEntity.java:313`. +- Impact: unfinished work is lost; repeated unloads can prevent a slow block + from ever completing. Already collected items remain intact. +- Older versions: the same reconstruction/reset sequence exists in maintained + 1.10 and 1.12 source. Runtime reproduction here is specifically the 1.13 candidate. + +These are failing acceptance cases, not `EXPECTED_FAIL` baselines or rewritten +gameplay requirements. The observations above describe the original failing +jars; the repair and its independently audited rerun are recorded below. No +defect is silently accepted by this test report. + +## Repair implementation + +The 1.13.2 candidate repairs are confined to the two drill implementations: + +- Electric track movement charges the new tile before marking it dirty and + removing the original. The defined 250-unit price is unchanged. +- Both rear-output paths recognize `null` and `ItemStack.EMPTY`, preserve stack + tags and the one-item transfer rate, and mark source/destination inventories + dirty after successful insertion. +- Target reconstruction retains saved progress, bounded by zero and the + recalculated goal. New target acquisition, redstone cancellation and target + invalidation still reset work. Steam initializes its redstone reading when + restoring the target, avoiding the constructor's default shutdown state. +- Existing `progress`, `targetX`, `targetY` and `targetZ` keys remain unchanged. + Optional `TargetState` (Steam) and `TargetStates` (Electric's nine blocks) + snapshots permit changed-target detection on restoration. Legacy records + without a snapshot remain readable, but cannot prove a target's old identity; + they reconstruct the current target at their saved coordinates. + +Mod versions remain the unpublished `2.2.5.113021` port candidates. No registry, +tile/entity ID, recipe, dependency or PowerAdvantage gameplay changes accompany +these repairs. The 1.10 and 1.12 implementations are not modified here. + +## Repair retest + +The bounded-reserve run `run-lifecycle/20261006-drill-fixes` completed on +6 October 2026. Both profiles and the independent evidence audit passed. + +| Repair acceptance case | Fresh 1.13 | Converted fixture | +| --- | --- | --- | +| All six Steam/Electric movements, inventory/facing and exact energy debit | PASS | PASS | +| Five genuine chunk unload/reload cycles and resumed network processing | PASS | PASS | +| 5,000 mB water conserved | PASS | PASS | +| All carried items, custom tags and mined drops reach the rear chest | PASS | PASS | +| Output and helper-bit orientation retained after chunk reload | PASS | PASS | +| Unfinished progress retained after chunk reload and full process restart | PASS | PASS | +| Legacy record without target snapshot retains progress | PASS | PASS | +| Changed saved target clears progress and target coordinates | PASS | PASS | +| Independent evidence audit and overall machine qualification | PASS | PASS | + +The rerun covered 24 moves, 600 actual fixture-chunk unloads, four full process +restarts and 48 extra saved-record read checks across the two worlds. Electric +movement now leaves 150 from a 400-unit reserve. Steam still pays 10 plus normal +decay. The exact twenty carried items plus one gravel (Steam) or nine cobblestone +(Electric) reached each rear chest and survived reload with their custom tags. + +Unfinished progress of 92-97 ticks continued to 100-105 after chunk reload; +201-206 ticks continued to 208-213 after full restart. The target coordinates +remained unchanged. The legacy read kept progressing; the changed-target read +cleared work rather than applying it to bedrock. + +The original failed run remains intact. Reauditing it with the strengthened +oracle still reports gameplay `FAIL`, while the repaired run reports `PASS`. +The auditor recomputes individual verdicts and overall qualification from raw +snapshots instead of trusting the runner's status labels. + +Fixed candidate release pins: + +| Jar | SHA-256 | +| --- | --- | +| `SteamAdvantage-2.2.5.113021.jar` | `D7D8A2E728E779EF0B624EF8AF3437F3354D618F12E279BE1C134F4AD5E690E3` | +| `ElectricAdvantage-2.2.5.113021.jar` | `72027C2DC273FD28F23AF01BB3EB495C75076E19B27A38CBDC5213B35A6198EB` | +| `AdvantageWorksTestHarness-1.1.0.113021.jar` | `B5043325294299E97B5C2E20559FEF35E9DBF0799B12C43D36E7EC1DB5D39083` | + +PowerAdvantage, Base Metals and OreSpawn pins did not change. Development and +packaged profiles now reference the repaired Steam/Electric hashes. The full +FG7 release suites and Eclipse audits passed for both changed mods, including +six Steam tests and nine Electric tests. The harness's nine Java tests and +34 Python runner tests passed; PowerAdvantage's artifact/isolation audit passed. +Separate forced-rebuild Gradle invocations produced identical release, deobf, +sources and Javadoc hashes for both changed mods. All twenty Steam/Electric +development and packaged profile pins match the rebuilt files. + +The converted source fixture's fingerprints match, and all test servers stopped. +Four processes logged the known Forge temporary-directory cleanup diagnostic; +two fresh-world boots logged its config-file watcher diagnostic. Neither is a +machine crash or a failed assertion. No ticking-block-entity crash, native +inventory command failure or "Can't keep up!" warning was found. The raw logs +and their diagnostics remain in `lifecycle-results.json`. + +This is focused chunk/drill qualification, not a new complete migration-matrix, +30-minute endurance, clean-cache or manual client campaign. The coverage limits +above still apply. No original player save, 1.10/1.12 code, commit or published +artifact was modified. + +## Current network-repair regression + +On 6 October 2026, `run-lifecycle/20261006-current-power-reload-154618` repeated +the unchanged whole-network and mid-block protocol after the PowerAdvantage +[partial-network repair](PARTIAL-NETWORKS-AND-EXTENDED-DRILLS.md). Both the runner +and independent `qualification-audit.json` report **PASS** for fresh 1.13 and +the original Cyano 1.10 -> maintained 1.12 -> 1.13 disposable copy. + +| Acceptance case | Fresh 1.13 | Converted fixture | +| --- | --- | --- | +| All six Steam/Electric drill facings, movement cost, carried items and tags | PASS | PASS | +| Five real whole-network chunk unload/reload cycles | PASS | PASS | +| Furnace processing and fluid delivery resume without replacing equipment | PASS | PASS | +| Exactly 5,000 mB water retained | PASS | PASS | +| Bounded mining yield reaches rear chests and survives reload | PASS | PASS | +| Steam helper-bit orientation restores correctly | PASS | PASS | +| Same-target unfinished progress survives chunk reload and full restart | PASS | PASS | +| Legacy saved record retains progress; changed target clears it | PASS | PASS | +| Independent evidence audit and machine qualification | PASS | PASS | + +The run records 24 native drill moves, 600 real chunk-unload events, four full +process restarts and 48 saved-record read checks across both profiles. Each +profile retains all twenty carried items per drill, including their custom tags, +plus the expected one gravel (Steam) or nine cobblestone (Electric) mining yield. +Fresh-world progress advances from 97-99 to 105-107 ticks after chunk reload and +206-208 to 213-215 after restart. Converted-world progress advances from 92-98 to +100-106 and 201-207 to 208-214 respectively; target coordinates remain unchanged. + +The new artifact pins are: + +| Artifact | SHA-256 | +| --- | --- | +| Power release `2.4.4.113021` | `0CCA0AF27497431C3A64091809F8DD328C84D82500CF05456366770C65485E74` | +| Harness release `1.1.0.113021` | `80402A725EC02B9D169B0E11CE3E99EF0892F6E6468891A618A34A4E5EC7C615` | + +Steam/Electric release hashes remain those in the earlier repair table. All six +staged jars match their manifest pins in both worlds. All 53 Python regression +and negative-control tests pass. The converted source fixture's before/after +fingerprints match, all six server processes stop normally, and port 25586 has +no remaining listener. There is no ticking-machine crash, failed inventory +command or keep-up warning. + +Three Forge temporary-resource-pack cleanup FATALs and two Forge configuration +watcher FATALs remain recorded in `lifecycle-results.json` and the console logs. +They do not abort server startup, assertions or ordinary shutdown; they are not +silently removed from the evidence or described as a clean-log pass. + +No production code, artifacts, source-version worlds or prior results changed +during this rerun. The converted-world lanes are newly commissioned equipment, +not proof that every original player machine has been qualified. The earlier +30-minute endurance results still apply to their older jar hashes; refreshed +endurance, broader player/entity migration and manual client checks remain +separate gates. + +The subsequent [current-candidate endurance rerun](ENDURANCE.md#current-candidate-qualification) +now passes thirty real minutes, complete restarts and resumed processing on +these exact release hashes in both worlds. Broader migration and manual client +coverage remain separate gates; the original endurance evidence is unchanged. diff --git a/docs/1.13/CONNECTED-MIGRATION.md b/docs/1.13/CONNECTED-MIGRATION.md new file mode 100644 index 00000000..33dbefb1 --- /dev/null +++ b/docs/1.13/CONNECTED-MIGRATION.md @@ -0,0 +1,223 @@ +# Connected Legacy Works: Migration and Continued Operation + +## Verdict + +**Follow-up:** the subsequent [legacy drill repair](DRILL-PROGRESS-REPAIR.md) +fixes both drill types in maintained 1.10/1.12 code. Native restart tests and all +eight migration routes pass with the repaired bridge. The verdict below records +the original pre-repair campaign and its failures; those records are retained. + +The 23 October 2026 campaign builds and operates five connected assemblies under +five historical source profiles, then migrates copies of the **already working +equipment**. Nothing is rebuilt, refilled, replaced or manually reconnected after +import. The production jars are unchanged from the +[current-candidate qualification](CURRENT-QUALIFICATION.md#scope). + +All eight routes preserve their frozen saved state and operate successfully in +1.13, including a complete 1.13 process restart. All five direct routes pass. +The three chained routes remain **FAIL** overall because the intermediate 1.12 +Electric Drill loses partial mining progress when ticking resumes after reload. +That failure is not waived because the subsequent 1.13 stage succeeds. + +| Source profile | Direct to 1.13 | Via 1.12, including intermediate restart | 1.13 operation and restart | +| --- | --- | --- | --- | +| Original Cyano 1.10 | PASS | FAIL: `Q-CONNECTED-001` | PASS on both routes | +| Early local 1.10 | PASS | FAIL: `Q-CONNECTED-001` | PASS on both routes | +| Maintained 1.10 | PASS | FAIL: `Q-CONNECTED-001` | PASS on both routes | +| Early local 1.12 | PASS | Not applicable | PASS | +| Maintained 1.12 | PASS | Not applicable | PASS | + +These are the archived profiles described in the +[source provenance ledger](MIGRATION-MATRIX.md), not newly rebuilt historical +mods. There is no original published Cyano 1.12 fixture. The chained runtime uses +the separately pinned repaired Electric 1.12 bridge, without replacing the +historical source profile. + +## What Was Built + +Each source world contains 456 declared block positions in five isolated +assemblies. Targets and helper-bit positions are observed alongside equipment. +The rigs begin with exactly 90,000 mB water, finite coal and item inputs, zero +machine energy, and an empty lithium battery. There are no infinite power sources. +All construction and inventory seeding occur only in the initial legacy process. + +| Assembly | Physical connection and workload | +| --- | --- | +| `C-CONVEYOR` | Chest containing 1,728 dirt, two east-facing conveyors, empty destination chest | +| `C-STEAM` | Three coal boilers, each supplied through a fluid pipe from a water container; steam main, steam tank, three-lane furnace and rock crusher | +| `C-ELECTRIC` | Two supplied coal boilers, steam main, steam-to-electric generator, cables, initially empty lithium battery array and electric furnace | +| `C-SDRILL` | Two supplied boilers, steam pipes, north-facing steam drill, rear output chest and 63 cobblestone targets | +| `C-EDRILL` | Two supplied boilers, generator and cables, north-facing electric drill, rear chest and 32 nine-block target planes | + +The steam drill crushes cobblestone into gravel. The electric drill harvests +ordinary cobblestone; it is not a crusher. Both require exactly one retained item +per removed target in this fixture. Coal/water are not replenished between stages. + +After 803 native source ticks, **each historical profile** had produced: + +| Witness | Measured output | +| --- | --- | +| Conveyor destination | 114 dirt | +| Steam furnace | 12 iron ingots | +| Steam crusher | 5 gravel | +| Electric furnace | 3 iron ingots | +| Steam drill | 24 gravel | +| Electric drill | 9 cobblestone | +| Lithium battery | 8,000 charge, from the connected generator | + +The remaining inputs, current progress, stored steam/electricity and water are +saved by the real source-version serializers. Each source save is fingerprinted +after shutdown and is never started again; conversion uses copies. + +## Observation Protocol + +1. Build the assembly once in a new legacy superflat world. Validate cold input + quantities and zero power; run 803 ticks and require real output. +2. Save, stop the process, fingerprint the source world and copy it. +3. Load the copy with machinery held out of the native ticking list. Compare + actual block states and complete saved tile NBT, including nested inventories, + fluids, energy bits, progress and target coordinates, before any machine work. +4. Permit exactly 512 normal world ticks and check further output, finite water, + inventory accounting and retained tile identity. Save and stop completely. +5. Start another process, compare the saved checkpoint while frozen, then permit + another 512 ticks and check continued operation. +6. On chained routes perform the same load/work/restart sequence under 1.12 + before copying that worked save into 1.13. + +The adapter holds and restores the real tickable tile list without reconstructing +tiles. During measured intervals Minecraft performs normal machine and network +updates. Bounded chunk tickets and an unlogged-in fake-player sentinel keep the +fixture active; this is not a networked-player test. No pressure, inventory, +progress or conduit-cache priming is performed after construction. + +Each original machine carries an NBT witness marker. During a process interval +its object identity must remain unchanged as well. The 1.13 command adapter has +no construction method; it supports only snapshots and tick-bounded observation. +Command logs must contain only those operations, `save-all flush` and `stop`. +Source-only build markers are not copied to later runtimes. + +All 22 conversion/restart state comparisons have zero differences. All sixteen +1.13 operating intervals pass material accounting and work checks. Each route +also requires actual additional items across its two 1.13 intervals; animation +or an increasing progress bar alone cannot satisfy that gate. Steam drills in +the longer chained route finish all 63 targets during the first 1.13 interval; +their subsequent restart checks completed-job retention, not further excavation. + +The shared item totals are conserved: dirt `1,728`, steam-furnace ore plus ingots +`192`, electric-furnace ore plus ingots `64`, and crusher cobblestone plus gravel +`64`. Drill drops must equal removed targets with the correct product identity. +Water cannot increase, and stored energy must be finite and nonnegative. Full NBT +equality applies to frozen boundaries; normal fuel consumption and generation +are allowed during native operation. This does not establish exact energy +efficiency or cross-version throughput equality for every machine. + +The offline audit records 450 passing checks and five failures: the three +intermediate restart checks plus the two enclosing split-campaign verdicts. +These are one reproduced legacy defect, not five separate bugs. All source-world +fingerprints, archived/staged jars and forbidden-mutation checks pass. The +separate first-tick audit reports `REPRODUCED_LEGACY_FAILURE_TARGET_PASS`. + +## Findings Retained + +### Q-CONNECTED-001: Intermediate 1.12 Electric Drill Progress Loss + +The electric drill reaches saved progress `512` of a 576-tick nine-cobblestone +cycle. Frozen reload preserves `512`, but the first native 1.12 tick resets it to +`1`. Repeated 512-tick sessions therefore do not finish that saved target. The +failure is reproduced on all three chained routes. + +An isolated comparison copies that same worked 1.12 world into both runtimes: + +| Runtime | Frozen loaded progress | After one native tick | Continued operation | +| --- | --- | --- | --- | +| Qualified 1.12 bridge | 512 | 1: FAIL | Completes a new cycle when allowed 700 further ticks | +| Current 1.13 | 512 | 513: PASS | Continues mining without losing saved work | + +In the 1.12 implementation, deferred target reconstruction calls `targetBlock`, +which assigns `progress = 0`. The existing 1.13 `restoreTargetBlock` rebuilds the +target and drops while retaining valid saved progress. Neither the source save +nor the migrator is repairing the 1.12 runtime in this test. + +Source locations: legacy Electric +`machines/ElectricDrillTileEntity.java`, methods `tickUpdate`, `powerUpdate`, +`targetBlock` and `loadFrom`; current +[ElectricDrillTileEntity.java](../../../ElectricAdvantage/ElectricAdvantage/src/main/java/zone/moddev/mc/electricadvantage/machines/ElectricDrillTileEntity.java), +methods `restoreTargetBlock` and `read`. + +No mined items disappear and the machine is not permanently stalled; it must +repeat unfinished work. Repairing the maintained 1.12 implementation is separate +work. The three chained campaign failures and the offline audit's nonzero exit +remain visible. No production fix is included in this pass. + +### Dependency Recipe Difference: Obsidian + +An earlier pilot successfully preserved the original saved blocks/NBT and +continued drilling, but did not preserve *newly generated recipe output*: the +old Base Metals pack dropped one obsidian, whereas pinned Base Metals 1.13 +defines `basemetals:obsidian_crushing` as four obsidian powders. Previously saved +obsidian items remained unchanged. This is a dependency recipe difference, not +item duplication during migration, but it prevents claiming identical behaviour +for every old crusher input. The failed pilot remains under +`run-connected-migration/20261023-original-pilot2/`. + +The final fixture uses cobblestone because both source and target packs agree on +its crushing output. Initial setup/oracle mistakes involving `Tank.Empty` and +electric-versus-steam drill products also remain in their original diagnostic +runs. Cold-state and negative oracle tests now guard against those mistakes. + +## Evidence and Repetition + +Local ignored evidence roots: + +```text +run-connected-migration/20261023-working-legacy110/ +run-connected-migration/20261023-working-maintained110/ +run-connected-migration/20261023-working-112/ +run-connected-migration/20261023-connected-audit.json +run-connected-migration/20261023-progress-trace-verified/ +``` + +Each contains raw snapshots, compressed NBT, source fingerprints, staged jar +identities, hash-addressed input archives, commands, console logs and verdicts. +Production/dependency pins match the previous qualification; only test probes +changed. Existing Base Metals/Forge/OreSpawn log diagnostics remain recorded, +including the Base Metals leaves transformer and legacy bucket registration +messages. State/operation success does not mean error-free modpack logs. + +From the PowerAdvantage checkout, after building both legacy probes and the +current harness as described in the [runner instructions](../../test-rig/migration/README.md): + +```text +python test-rig/migration/connected.py --java8 --profile-manifest --output +python test-rig/migration/audit_connected.py --output +``` + +`--sources` and `--port` permit separate campaigns. Supply all completed roots to +the offline audit; it requires all five sources/eight routes, the qualified +803/512-tick protocol, unchanged input jars/source worlds, valid commands and raw +state/operation evidence. It exits nonzero while the intermediate failures remain. +The default profile path is a local archived qualification, not a portable public +download. Supply equivalent hash-pinned inputs on another machine. + +`trace_connected_progress.py` accepts `--fixture` pointing to a completed chained +`*-112` runtime and `--profile-manifest`, `--java8`, `--output`; it uses a separate +port and disposable copies. `audit_connected_progress.py --run +--fixture --output ` independently checks the +one-tick failure and successful target continuation. + +The harness compiles/checks and remains absent from the Power release jar. +All 158 Python tests and 20 harness JUnit tests pass. Regression/oracle tests +include rejection of lost/duplicated items, +replaced tiles, invalid power, unmeasured ticks, progress reset and incomplete +campaign evidence. All production hashes remain unchanged. Nothing is committed, +pushed or published. + +## Remaining Scope + +This closes the specific connected-source-equipment gap for these five +assemblies, not for every card in the works. It does not cover connected imported +elevators, growth chambers, every filter, pumps, oil processing, turrets, muskets, +all drill facings/tracks or third-party converters. Those retain their +[remaining-behaviour checklist](CURRENT-QUALIFICATION.md#remaining-behavioural-checklist). +The pre-existing `Q-RIG-001` moving-drill oracle finding is unchanged. +Real-client interaction and whole-Sylvester-modpack acceptance remain separate. diff --git a/docs/1.13/CURRENT-QUALIFICATION.md b/docs/1.13/CURRENT-QUALIFICATION.md new file mode 100644 index 00000000..db655b3b --- /dev/null +++ b/docs/1.13/CURRENT-QUALIFICATION.md @@ -0,0 +1,216 @@ +# Current Candidate Qualification and Remaining Tests + +## Scope + +This is the 23 October 2026 rerun against the current, unpublished 1.13.2 jars. +It supplements, rather than replaces, the historical failed and repaired reports. +The main campaign and independent audits completed: all 29 steps passed, all ten +protected fixture fingerprints remained unchanged and all 45 input/archive checks +passed. The separate all-station sweep retains two drill-location oracle failures +per phase. This is **core qualification with an open rig finding and coverage +gaps**, not an all-setups-pass or release-approval claim. + +The subsequent [connected legacy works campaign](CONNECTED-MIGRATION.md) closes +the source-operating-fixture gap for five bounded assemblies. All eight 1.13 +stages preserve state and resume work after restart. The three chained routes +retain a **1.12 intermediate** electric-drill progress-reset failure; this is not +an all-routes-pass claim. The results below remain the earlier campaign's record. + +The later [legacy drill repair and rerun](DRILL-PROGRESS-REPAIR.md) resolves that +intermediate failure in both drill types on maintained 1.10/1.12. All eight +connected routes and the two-restart native checks now pass with the repaired +artifacts. The original failed campaign is not overwritten; other coverage gaps +and `Q-RIG-001` remain open. + +| Artifact | SHA-256 | +| --- | --- | +| `PowerAdvantage-2.4.4.113021.jar` | `E5B62769812AE02393A305CF33D2E168BFB2B6D370AB04D43409B94F5740D193` | +| `SteamAdvantage-2.2.5.113021.jar` | `A87F141913D511DB025FEE6D6FBA4000098598D8692B4BA53E7E80C67B14ACCA` | +| `ElectricAdvantage-2.2.5.113021.jar` | `C785155C05133D1A408613D2C9738A32C9818E31C2599ECBD9F2FE76965DB5BA` | +| Works harness `1.1.0.113021` | `0D0F71F25EB0D99FC871ACBD9120E14EA6F14B3349E4DB2F1A0283D2A2A84A1B` | + +The harness hash above belongs to that archived campaign. The connected +extension uses harness SHA-256 +`2A260DF4B66454B57ECB6B021BCF26B3A812CA5FC0A8B9C4C9E904879AA67A1C`; +the three production hashes have not changed. + +The server uses Forge `25.0.223`, Base Metals `3.0.1.113021` and OreSpawn +`4.1.0.113021`. Production and probe jars are archived by content hash before +launch. The original source fixtures, source jars and protected player saves are +never opened as running worlds. Every conversion uses a disposable copy. + +The source profiles retain original Cyano 1.10, early local 1.10, maintained 1.10, +early local 1.12 and maintained 1.12. There is no original published Cyano 1.12 +fixture. Chained routes use the separately pinned repaired Electric 1.12 +`2.2.2.112021` bridge; the historical `2.2.1.112021` source remains unchanged. +See [source provenance](MIGRATION-MATRIX.md) and the +[bridge repair](PLAYER-AND-ENTITY-MIGRATION.md). + +## Rerun Results + +| Gate | Current result | Retained evidence under the run root | +| --- | --- | --- | +| Block/tile state migration | PASS: eight direct/chained routes, conversion/restart/stability, 355-359 block and 332-336 tile witnesses per route | `matrix/matrix-results.json`, `matrix-audit.json` | +| Commissioned operation inside converted copies | Automated assertions accepted in all eight routes, eleven stations before/after restart; manual checks remain open | `operation-/operation*-results.json` | +| Strict player/entity migration | PASS: eight routes, frozen checkpoints, player adapter, native entity ticks and restart | `actors/actor-results.json`, `actors-audit.json` | +| Normal-ticking actor controls and inventory layouts | PASS: eight normal-ticking routes and four inventory-layout routes | `actors-native/native-results.json`, `player-layout/layout-results.json`, corresponding audits | +| Nether/End migration | PASS: all eight routes in both dimensions, native ticks and complete restarts | `dimensions/dimension-results.json`, `dimensions-audit.json` | +| Transformer ordering | Semantic PASS in six runtimes/twelve launches; backup-origin order dependence remains documented, not waived | `transformer-order/summary.json`, `transformer-order-audit.json` | +| Six-facing drills and chunk lifecycle | PASS in fresh and original-release chained worlds: 24 moves, 600 observed chunk unloads, progress/output/energy checks and full restart | `lifecycle/lifecycle-results.json`, `lifecycle-audit.json` | +| Partial networks and extended drills | PASS in both worlds: 144 native moves, 24 partial-unload cases, twelve reload-order cases, scarcity/blockage/redstone and full restart | `extended-lifecycle/extended-results.json`, `extended-lifecycle-audit.json` | +| Pipe-only unloaded bridge | PASS: three fresh-world cycles, absent-bridge isolation, retained endpoints and resumed processing/fluid conservation | `pipe-only/pipe-only-results.json`, `pipe-only-audit.json` | +| Thirty-minute repeated production | PASS in both worlds: thirty timed batches, full restart and resumed processing | `endurance/endurance-results.json`, `endurance-audit.json` | +| Oil bucket/drain/pipe/container lanes | PASS: four crude/refined lanes, exact identities/volumes and two process restarts | `oil-transfer/oil-transfer-results.json` | +| Runner/oracle unit tests | PASS: 140 tests | `runner-unit-tests.log` | +| All 21 walkable bays activated | FAIL: two stale drill-location assertions in each phase; other 19 bays remain manual-qualified only | `all-stations-*.json`; see `Q-RIG-001` below | + +Each endurance world ran for 1,801.563 seconds: 36,233 game ticks in the fresh +world and 36,041 in the converted copy. Each produced 120 items in each of five +processing lanes, routed 480 items through each of the plain and three filter +lanes, and converted 60,000 mB crude oil to 30,000 mB refined oil. These are thirty +distinct timed batches, not repeated readings of unchanged output. The resumed +post-restart batch is checked separately and is not counted as timed production. + +No measured machine-state, item or fluid loss occurred in the accepted cases. +Every test server stopped; no viewer instance was left running. The existing +unrelated server was not stopped or changed. The log diagnostics and the +transformer [backup-provenance limitation](TRANSFORMER-ORDERING.md#backup-provenance-finding) +remain recorded; semantic migration success does not make partial loose-file +backups a substitute for a complete original-world backup. + +## Evidence and Interpretation + +Local evidence is under the ignored directory +`run-qualification/20261023-current/`. It contains archived input profiles, +commands, console logs, raw NBT snapshots, per-stage comparisons, source +fingerprints and independent audit reports. Generated worlds and jars are not +documentation assets and must not be committed. + +Three different claims must stay separate: + +1. **State preservation:** the old serializer's actual saved state survives + conversion and restart. Frozen witnesses do not prove processing throughput. +2. **Operating behaviour:** bounded machinery performs the measured work, with + the specified item/fluid/energy accounting and lifecycle checks. +3. **Presentation and player interaction:** a real client can operate controls, + see correct GUI values, and interact with other players and entities. + +Commissioning new machines inside a converted world is not proof that every +original player-built network runs correctly. Registry/metadata coverage is not +the Cartesian product of every orientation, fuel, network topology, automation +face, shortage, unload timing and upgrade route. + +Station `MANUAL_REQUIRED` is not promoted to `PASS` by a successful runner exit. +An existence assertion proves only that a block/tile is present. A report audit +can pass while faithfully retaining gameplay failures. Known Forge/Base Metals +diagnostic messages remain in logs; successful state assertions do not imply +error-free logs or approval of the whole modpack. + +## Walkable Drill Check Finding + +The additional fresh-world all-station sweep starts all 21 bays, saves, fully +stops Minecraft and checks again after restart. Both phases currently report +94 assertion passes, two failures and three Mineralogy-dependent skips. The +other 19 stations retain `MANUAL_REQUIRED`, not complete gameplay acceptance. + +`Q-RIG-001`: the `S-05` down-facing and `E-06` up-facing assertions require a +drill at its original coordinate even after the station has powered it and +provided track. The snapshots instead contain a down-facing Steam drill one +block above its start and an up-facing Electric drill one block below its start. +Both displaced witnesses remain in the restarted snapshot. The other five +facing assertions in each bay pass. + +This is a stale fixed-position oracle after activation, not evidence by itself +that a drill vanished or rotated. It also is not sufficient evidence of correct +movement, energy consumption or output. The separate movement-aware campaigns +must decide those claims. The all-station result remains **FAIL** until its +moving-witness assertions are repaired and independently rerun; this pass does +not weaken them or label the failures expected passes. Raw records are +`all-stations-commissioned.json`, `all-stations-full-restart.json` and +`all-stations-results.json` under the local evidence root. + +## Remaining Behavioural Checklist + +This is the work queue, not a list of newly discovered defects. A behaviour can +have partial coverage without its complete acceptance card being satisfied. +The definitions in +[`works-1.13.2.json`](../../test-rig/src/main/resources/assets/advantageworkstest/works/works-1.13.2.json) +retain the detailed manual cards. + +| Station | Behaviour still needing dedicated evidence | Suitable next method | +| --- | --- | --- | +| `W-01` fluid transfer | Matching/nonmatching tank filters, redstone fluid isolation, all terminal/third-party handler faces, portable-tank break/place retention versus intentional metal-tank loss | Automated bounded volume/identity fixtures; client check for controls | +| `W-02` drain/discharge | Source versus flowing water, stepped 32-step search boundary, full-destination backpressure and exact discharge-to-world conservation | Automated terrain and blocked-output fixtures | +| `W-03` distillation | Mid-cycle input/progress/fuel restart, full-output and interruption cases; Mineralogy oil equivalence on these exact artifacts | Automated finite-input fixtures with Mineralogy profile | +| `W-04` sorting | Complete zero/one/many configurable-filter routing matrix, every category filter, vertical/entity pickup, all overflow branch blockages, comparator levels and loss/duplication accounting | Automated item-count fixtures, not tile-existence checks | +| `S-01` boilers | All four boilers' valid/invalid fuels, starvation and output saturation, exact electric-to-steam relationship, oil aliases/config overrides and sustained measured rated load | Automated finite-fuel/water/energy fixtures | +| `S-02` distribution | Direct machine-to-machine conduction and short/long-network comparison under measured scarcity; matching GUI/server readings in a connected client | Automated topology fixture plus real client | +| `S-03` storage | Tank backup priority, scarcity/recovery and redstone branch isolation with whole-circuit accounting | Automated finite supply/demand fixture | +| `S-04` processing | All furnace lanes, all automation faces, invalid inputs, full outputs, mid-cycle restart, steam-still throughput and full comparator scale | Automated bounded processor fixtures | +| `S-05` Steam Drill | Continuous naturally supplied multi-step travel; every new/imported orientation combined with every mid-block/unload/track condition; visual helper-bit restoration | Extended automatic cross-product plus real client | +| `S-06` Steam Pump | Lift/depth geometry, pipe-section costs, full output, empty pool and repeated shaft cleanup; pumping throughput rather than isolated tank persistence | Automated finite-pool and energy accounting | +| `S-07` elevator | Actual up/down travel, redstone pulses, ceiling stops, range/steam costs, riders and mid-travel unload/restart recovery without duplicate platforms | Automated movement fixtures; real player riding/control test | +| `S-08` musket | Real loading/firing, cartridge/durability consumption, damage/range, block interactions and every enchantment's effects | Automated firing/target fixtures plus real client; enclosed disposable range | +| `E-01` generation | Photovoltaic time/weather/light matrix, exact steam-conversion rate/cap, turbine damage and player interaction | Automated environmental/energy fixtures plus real client | +| `E-02` batteries | All four batteries charging/discharging at capacity boundaries, mixed-array transfer, backup priority, switching and exact whole-circuit energy accounting | Automated finite generator/consumer fixture; tooltip/GUI checks | +| `E-03` processors | Fabricator and other processors' valid/invalid/full-output/scarce-power cases, automation faces and mid-cycle restart across machine-specific inventories | Automated recipe/output fixtures; not just crafting reachability | +| `E-04` fluid plant | Steam/electric pump geometry and work costs, live pump persistence/resume, powered still/refinery blockage and interrupted fluid/energy paths | Automated isolated finite-fluid fixtures; client controls | +| `E-05` growth | Actual seed/crop/sapling harvests, shared chamber starvation, soil/water/electricity consumption and mid-growth restart | Automated crop-output and resource accounting | +| `E-06` Electric Drill | Same remaining cross-product and continuously powered travel as `S-05`; client laser/render behaviour | Extended automatic fixture plus real client | +| `E-07` light/turret | LED emitted light and priority, turret owner/team permissions, targeting/range, real hits, shot cost and reacquisition across reload | Automated target/energy fixtures plus authenticated players | +| `E-08` assembler | Actual reference-recipe crafting, recursion limits, reversible recipes, output blockage and timeout protection | Isolated time-bounded automatic fixture; no valued world | +| `P-API-01` fluid API | Real IronAgeFurniture caller and third-party interaction beyond the native pipe/tank contract | Integration profile/client with IAF; API contract stays separate | + +### Recommended Order + +1. The [connected-source pilot](CONNECTED-MIGRATION.md) is implemented for + conveyor transfer, supplied steam processing/storage, electric generation and + processing, and both stationary drills. `Q-CONNECTED-001` in the maintained + legacy implementations is now repaired and independently rerun; retain its + original failure evidence. Extend the same no-rebuild protocol to other + assemblies as their dedicated behavioural fixtures are added. The isolated + state matrix and newly commissioned converted Works remain complementary + evidence. +2. Repair `Q-RIG-001`: distinguish initial facing/placement from expected track + movement, then assert moved identity, facing, inventory and energy. Do not + clear a failure by merely removing the assertion or resetting before checking. +3. Elevator movement and growth-chamber output: large functional gaps with + practical, finite inputs and measurable outputs. +4. Conveyor/filter and tank priority matrices: automate the advertised variants + that the walkable bays currently represent only structurally. +5. Pump geometry/costs, boiler fuel/starvation and processor blocked-output cases. +6. Generator environmental states, batteries and optional Forge Energy converters. +7. Musket/turret gameplay, real-player permissions and GUI/server agreement. + +An individual row closes only when its required outputs and retained state are +recorded. The same fixture should be exercised natively and after relevant +direct/chained conversions, not merely copied into an already converted world. + +## Cross-Cutting Limits + +- The latest recipe manufacturing/discovery and native resource/render results + remain in [Manufacturing Progression](MANUFACTURING-PROGRESSION.md) and + [Recipe Discovery](RECIPE-DISCOVERY.md). Those are not machine/network tests. +- Not every original saved machine is connected and operated by the migration + matrix. State lost by an old serializer before conversion cannot be recovered + by declaring the new migrator successful. +- Current optional integration, Mineralogy, OreSpawn world-generation/profile + migration and configuration-import combinations need their own exact-artifact + reruns; superflat machine tests cannot prove terrain-generation behaviour. +- Full network login/handshake, integrated-server host loading, returning to the + menu and replacing worlds within the same client process, GUI controls, + pressure/power bars, sounds, targeting visuals, multiple players and all modpack + interactions remain distinct from dedicated-server adapter tests. +- Chunk/drill campaigns do not exhaust every unload timing, portal travel, + mounted dimension transfer, arbitrary dimensions or natural animal behaviour. +- Advancement/statistic and third-party capability migration is not implied by + item-NBT, player-adapter or owned-entity persistence coverage. +- The [Forge respawn finding](DIMENSIONS-AND-ENTITY-LIFECYCLE.md#forge-25-spawn-lookup-finding) + remains open: a real-client bed/death test is required. The earlier eight + valid-bed/forced-spawn failures are not changed to passes by this campaign. +- A complete Sylvester modpack conversion has not been accepted. Unsupported + unrelated mods are not acceptable silent data loss. Retain a full original + world backup; loose-file backups cannot roll back flattened region files. + +No gameplay fix, version bump, commit, push, PR, tag or release is part of this +qualification pass. diff --git a/docs/1.13/DIMENSIONS-AND-ENTITY-LIFECYCLE.md b/docs/1.13/DIMENSIONS-AND-ENTITY-LIFECYCLE.md new file mode 100644 index 00000000..d39df3e1 --- /dev/null +++ b/docs/1.13/DIMENSIONS-AND-ENTITY-LIFECYCLE.md @@ -0,0 +1,243 @@ +# Additional Dimensions and Entity Lifecycle + +## Scope + +This extends the [Overworld player/entity matrix](PLAYER-AND-ENTITY-MIGRATION.md) +with source-serialized Nether (`-1`) and End (`1`) fixtures. It is packaged, +dedicated-server qualification, not client login, a death-screen test or a +whole-modpack upgrade. No production code or mod jar is changed by these tests. + +Five source profiles are exercised: original Cyano 1.10, early maintained 1.10, +current maintained 1.10, early 1.12 and maintained 1.12. Every profile upgrades +directly; the three 1.10 profiles also upgrade through the repaired Electric +`2.2.2.112021` bridge. The original 1.12 source jar remains unchanged. That gives +eight routes per dimension, sixteen altogether. + +The source worlds are copies of the fingerprinted matrix fixtures. Their actual +legacy serializers build each dimension fixture. The originals and completed +generated source fixtures are hashed before/after use and never started as targets. +The original Sylvester save is not opened or modified. + +## Witnesses and Assertions + +Each selected dimension has a bounded, covered bedrock enclosure containing: + +- A saved player with charged batteries, a damaged enchanted musket, oil items, + Ender inventory, experience, health, UUID and an Overworld spawn coordinate. +- A boat carrying an owned, sitting wolf. Both individual entity state and the + actual vehicle/passenger relationship are checked. +- A tamed horse with saddle, iron armour and a fence-knot leash. Ownership, + health, attributes, equipment and the live leash endpoint must survive. +- An Electric hydroelectric generator and its saved turbine entity. The turbine + UUID and parent coordinate must remain intact without the machine manufacturing + a replacement. Machine updates remain frozen. + +Four persistent entity witnesses must exist exactly once. A native leash knot is +transient in 1.13: its UUID is not a persistent identity guarantee. The comparison +requires its type and fence coordinates, not an unchanged knot UUID. Other live +holder UUIDs, passenger UUIDs and persistent entity UUIDs remain exact. + +Legacy horse `Type=0` becomes `minecraft:horse`; old wolf collar colours are +normalized to the same colour meaning. New Forge attributes are allowed, but +existing attributes may not change. The generator's internal `initDone` cache +flag is excluded. Item quantities, energy tags, damage, enchantments, ownership, +dimensions, parent coordinates and other compared state are not relaxed. + +After native ticks, movement coordinates and rotation are not equality tests. +Actual passenger and leash relationships remain mandatory. There is no reseeding, +replacement-entity creation or inventory refill between migration checkpoints. + +## Timing and Restart Protocol + +The native campaign deliberately permits native entity initialization **before +the first converted save**, then measures at least sixty entity ticks. Chained +routes do the same in 1.12 and completely restart that intermediate process. +Each target has conversion, real player-file adapter, full restart, further live +ticks and another full restart checkpoints. Backups must match predecessor bytes +and remain unchanged on later loads. + +Chunk tickets/force-loading and an unlogged-in sentinel keep the fixture active. +The sentinel has a packet-discarding connection so vanilla entity tracking and +End boss bars can operate without a socket. This is not a player login or Forge +handshake. Native dragon code is not disabled or replaced. + +### Zero-Tick Leash Limit + +An earlier zero-tick diagnostic did **not** preserve the horse's leash. Vanilla +1.13 `EntityLiving` reads the `Leash` tag into pending data, but its writer emits +that tag only from a resolved live holder. Saving before its first initialization +can therefore erase the pending endpoint. The later native-initialization test +is a separate control, not a rewritten zero-tick PASS. + +The cached Forge 25 injected source shows the reader retaining pending NBT, the +writer requiring a live holder and native initialization finding/recreating the +fence knot. Those findings are also recorded in the raw source/converted/runtime +snapshots. The existing strict Overworld matrix does not contain leashes and +must not be cited as zero-tick leash coverage. + +## Death and Respawn Supplement + +Independent cases restore a copied source-serialized player file, then exercise +native death and respawn with a dummy connection. This explicit case reset is +not part of the migration persistence proof. The six cases are `keepInventory` +on/off crossed with forced spawn, valid bed and missing bed, in each dimension. + +Assertions require the same UUID, expected health/dimension, exact retained or +dropped inventories, unchanged Ender inventory, battery charge, musket damage +and enchantments, experience semantics, spawn position and persisted player file. +Dropped stacks are reconciled by identity and quantity, ignoring only bag slot. +Each source has fourteen counted carried items. No missing or duplicated item is +accepted as an expected failure. + +The final results and native spawn diagnostics are recorded separately below. +An evidence replay PASS means records and assertions agree; it does not promote +a native respawn FAIL to gameplay PASS. + +### Forge 25 Spawn Lookup Finding + +The ordinary `NetHandlerPlayServer.PERFORM_RESPAWN` path reproduces the same +failure as the earlier direct `PlayerList` pilot: a valid bed or forced Overworld +spawn is ignored, the player returns to world spawn and saved spawn keys disappear. +The source player has the correct `SpawnX/Y/Z=160/100/24` before and after death; +the live Overworld `getBedLocation` nevertheless returns null before recreation. +Missing-bed cases returning to world spawn are expected and are not failures. + +Inspection of the installed Forge 25 server bytecode and matching injected source +points to a native field mismatch: `setSpawnPoint` and NBT loading/writing use +`EntityPlayer.spawnPos`, whereas Forge's Overworld `getBedLocation(dim)` returns +the distinct `bedLocation` sleeping-position field. The respawn handler resolves +the destination dimension before asking for that location. Passing Overworld +instead of the original dimension would not repair this lookup. + +The cached `net.minecraftforge:forge:1.13.2-25.0.223` injected `EntityPlayer.java` +has the Overworld getter at lines 1580-1582 and setter at 1624-1629. In the +installed SRG class, the getter reads `field_71081_bT` at bytecode offset 8; +the setter writes `field_71077_c` at offset 84. These are distinct fields, not +an inferred failure caused by an incorrectly requested destination dimension. + +This is a candidate **platform-path defect**, not observed Advantage item loss. +The probe does not seed the sleeping-position field, alter vanilla methods or +rewrite expectations to obtain a PASS. The same handler is reached by an actual +respawn request, but an authenticated client sleeping/death test remains necessary +before calling its full gameplay impact confirmed. No production workaround is +included here. + +## Repeating the Tests + +Run from the PowerAdvantage checkout with a hash-pinned manifest containing the +dimension probes. `pin_dimension_profiles.py` replaces only test probes in an +existing qualification manifest and checks that production/dependency bytes +remain unchanged. Preserve or archive those probe jars before rebuilding them. +All output paths must be new, ignored directories. + +```text +python test-rig/migration/pin_dimension_profiles.py --base --output --archive +python test-rig/migration/dimensions.py --profile-manifest --java8 --output --native-load +python test-rig/migration/audit_dimensions.py --run --output +python test-rig/migration/respawn.py --profile-manifest --java8 --fixtures --output --port 25623 +python test-rig/migration/audit_respawn.py --run --output +python -m unittest discover -s test-rig/migration -p test_*.py +``` + +The Python environment requires `nbtlib`; the local configured interpreter is +`test-rig/migration/.venv/Scripts/python.exe`. Servers bind localhost only and +shut down between phases. Existing listeners are never stopped. Reports contain +staged-jar pins, raw binary NBT, semantic comparisons, tick measurements, backups, +source fingerprints and console logs. Worlds, jars and generated reports remain +untracked. + +## Qualified Results, 7 October 2026 + +The final dimension runs use identical archived test pins and unchanged +production/dependency jars. Both commands exit zero; both independent replays +exit zero and require all eight requested route IDs, all four persistent +witnesses' tick evidence and unchanged source fixtures. + +| Campaign | Result | +| --- | --- | +| Nether, all eight direct/chained routes | PASS | +| End, all eight direct/chained routes | PASS | +| Intermediate 1.12 save and full restart on all six chains | PASS | +| Player file/adapter, exact normalized item NBT and charged/damaged/enchantment-bearing items | PASS | +| Persistent UUIDs, ownership, equipment, live passenger/leash endpoints and turbine parent | PASS | +| Target conversion, native ticks and two full process restarts | PASS | +| Predecessor-byte backups and later backup stability | PASS | +| Both independent dimension replays | PASS, zero integrity findings | +| Python regression/oracle suite | 128 tests, zero failures/errors | +| Java probe builds, 110/112/113 | `compileJava check assemble` PASS; legacy tests NO-SOURCE, modern 12 tests PASS | + +The final death/respawn supplement uses those completed dimension fixtures, +not the interrupted pilot worlds. Each dimension contributes eighteen cases: +original 1.10, maintained 1.12 and original-1.10-chained 1.13, each crossed with +the six keep/drop and spawn modes. Every case uses the native `PERFORM_RESPAWN` +handler, archives its saved player and `level.dat`, and checks missing-bed +respawns against the actual saved world-spawn coordinate and `spawnRadius`. + +| Respawn group | Exact result | +| --- | --- | +| Original 1.10, both dimensions | 12 PASS | +| Maintained 1.12, both dimensions | 12 PASS | +| 1.13 missing-bed cases, both keep/drop modes and dimensions | 4 PASS | +| 1.13 valid-bed/forced-spawn cases, both modes and dimensions | 8 FAIL: spawn lookup/retained spawn keys only | +| Carried/drop quantities, item NBT, charge, damage, enchantments, Ender contents, UUID and health/dimension checks | PASS in all 36 cases | +| Saved player files match the native post-respawn observations | PASS in all 36 cases | +| Both independent respawn replays | PASS: recorded failures reproduce, no integrity findings | + +The respawn runners intentionally exit **1**, because eight behavioral assertions +fail. Their audits exit zero because those results agree with the raw records; +this is not an all-tests-pass or release-approval claim. + +### Evidence and Pins + +Large saves, staged libraries, binary snapshots and logs are retained under +`C:\Users\johnb\AppData\Local\AdvantageWorks\qualification\`: + +- `20261007-dimensions-nether-final`: eight routes, scope, raw NBT, tick evidence, + source fingerprints, backups and `integrity-audit.json`. +- `20261007-dimensions-end-final`: the matching eight End routes and replay. +- `20261007-respawn-nether-qualified`: eighteen native cases and their replay. +- `20261007-respawn-end-qualified`: eighteen native cases and their replay. +- `20261007-order-full-r2`: the separately qualified six transformer-order cases. + +Small manifests/reports are retained under ignored `run-dimension-migration/`. +The pin manifest is `20261007-inputs/profiles-qualified.json`; its provenance +requires every production/dependency SHA-256 to match the preceding accepted +actor campaign. The archived test artifacts are: + +```text +AdvantageMigrationProbe-110-1.0.0.jar and AdvantageMigrationProbe-112-1.0.0.jar +38565B6671E249B6E12D5B238355FD7D48218B261789E7E92316F9D9DC29F94A +AdvantageWorksTestHarness-1.1.0.113021.jar +8B39BB591A376A626FC25C7A8167F05F0937338B9BE21D45DCE9D1D60B8C092E +``` + +Reproduction of these split runs uses `dimensions.py --dimensions -1` or `1` +with separate outputs/ports; use `respawn.py --districts nether` or `end` and +its matching final dimension root. Archive copies protect the probe pins from +later builds. Nothing is installed into an active client or server. + +Earlier full-r1/r2, smoke and respawn pilots are retained diagnostics, not the +accepted campaigns. They exposed duplicate freeze-list restoration, pending +vanilla leash serialization, null sentinel connections during tracking/End boss +packets and legacy chunk-ticket double release during shutdown. The final probes +correct the test-only connection/freeze/ticket handling; they do not suppress +native dragon behavior or restore lost production data. Replay now rejects +tracking, crash and shutdown exceptions even if the process exits zero. + +Item-stack dictionaries compare exactly after declared serialization normalization. +Unknown turbine-like names are not aliases; duplicate helper witnesses are +rejected. New native entity defaults outside the selected semantic surface remain +allowed. Existing BaseMetals leaves-fixer/config/resource-pack diagnostics are +retained separately: these results are not a blanket error-free-log claim. + +## Remaining Limits + +Client login/handshake, death-screen interaction, rendering, GUI and ownership +synchronization remain manual gates. This does not test portal travel, mounted +player travel, combat, riding elevators, advancements/statistics or arbitrary +third-party capabilities/entities. Fixed, invulnerable fixture actors establish +persistence and native relationships, not complete natural animal gameplay. + +See [transformer ordering](TRANSFORMER-ORDERING.md) for mixed BaseMetals/Advantage +hook execution and the separate backup-provenance caveat. A complete pre-upgrade +world copy is still required; loose-file backups do not restore converted regions. diff --git a/docs/1.13/DRILL-PROGRESS-REPAIR.md b/docs/1.13/DRILL-PROGRESS-REPAIR.md new file mode 100644 index 00000000..012d0f72 --- /dev/null +++ b/docs/1.13/DRILL-PROGRESS-REPAIR.md @@ -0,0 +1,156 @@ +# Legacy Drill Progress Repair + +## Result + +`Q-CONNECTED-001` is repaired in the maintained 1.10.2 and 1.12.2 Steam and +Electric implementations. The 1.13.2 implementations already had the repair and +remain unchanged. The red native run reproduced the defect in all four affected +mod/version combinations; the repaired native run and all eight connected +migration routes pass. The original failed records remain intact. + +This closes the intermediate drill-progress failure in +[Connected Legacy Works](CONNECTED-MIGRATION.md), not the other entries in the +[remaining behavioural checklist](CURRENT-QUALIFICATION.md#remaining-behavioural-checklist). +No commit, push, PR or publication is included. + +## Implementation and Candidates + +Both legacy drills saved `progress` and `targetX/targetY/targetZ`, but rebuilt +transient drops/hardness after loading through `targetBlock`, which reset that +saved progress. Restoration now remembers the saved value, reconstructs the +target and clamps the value to its current mining goal. Both the deferred +first-tick path and the already-attached-world NBT read path use this helper. + +Steam's cached redstone flag also starts as `true`. Restoring it from the actual +world before the resumed tick prevents that uninitialized flag from discarding +the saved work. Actual redstone disabling, power costs, mining time factors, +output recipes and track behaviour are not changed. + +Reading an untargeted tile clears old progress, target pointers and deferred +state instead of retaining work from an earlier NBT read. No registry, tile, +package, inventory, fluid, recipe, dependency or NBT key is renamed. This is a +runtime repair; existing worlds need no new datafixer for it. + +| Version | Mod | Candidate | Local branch | +| --- | --- | --- | --- | +| 1.10.2 | Steam | `2.2.5.110021` | `feature/steamadvantage-1.10.2-drill-progress` | +| 1.10.2 | Electric | `2.2.5.110021` | `feature/electricadvantage-1.10.2-drill-progress` | +| 1.12.2 | Steam | `2.2.2.112021` | `feature/steamadvantage-1.12.2-drill-progress` | +| 1.12.2 | Electric | `2.2.3.112021` | Existing `feature/electricadvantage-1.12.2-turbine-persistence` | + +The pending Electric 1.12 turbine repair remains present; this candidate builds +on it rather than replacing it. PowerAdvantage production code and all 1.13 +release versions/hashes remain unchanged. The Power 1.10 branch +`feature/poweradvantage-1.10.2-drill-regression-profiles` changes only mutable +rig profiles to reference the new Steam/Electric filenames, versions and hashes. + +| Release jar | SHA-256 | +| --- | --- | +| `SteamAdvantage-2.2.5.110021.jar` | `1C9DE2F9EE29439B727D2087E18BAD5277115BCCF7162F0F9342F322F44F9E9E` | +| `ElectricAdvantage-2.2.5.110021.jar` | `1854A854332FC02151C289BAEAF9DC3C6677ED6CDC1ED04EA8EDD0CB54A085F5` | +| `SteamAdvantage-2.2.2.112021.jar` | `A249E7A90729519BD19040F3BF6042E871E61EFF7A67E4CEF464458B1E9F0A15` | +| `ElectricAdvantage-2.2.3.112021.jar` | `B5AB6769E05FE87CED655EB4B729C3EE016BD9D069DDC7111432F061BA7D08E2` | + +Each jar has exactly the previous entry set. Only its drill class, version-bearing +main class, manifest and `mcmod.info` differ from the preserved pre-fix jar. +Every other resource/class is byte-identical. `javap -protected` confirms the +drill's public/protected API is unchanged. Release audits reject embedded +dependency/test/agent content and require Java 8 bytecode. + +## Native Red-to-Green Test + +`drill_restart.py` starts with copies of the existing working, connected +maintained 1.10 and 1.12 fixtures. The unchanged 1.13 control imports the latter. +It never builds or refills the equipment and writes no inventory, energy or +progress NBT. A maximum of 100 seven-tick intervals is allowed to reach a +natural mid-cycle checkpoint with both drills powered. That checkpoint is saved +and the process stopped. + +Each runtime then undergoes two complete restarts. At each restart the test: + +1. Compares the entire saved block/tile state before any machine ticks. +2. Permits exactly one native tick, requiring both saved targets and original + tile witnesses to remain and each drill's progress to increase by exactly one. +3. Permits 620 more native ticks and checks actual item output and conservation, + finite water/energy and retained tile instances. +4. Saves and shuts down. Before the second restart, bounded natural ticking + again selects a mid-cycle checkpoint; no progress is supplied by the harness. + +At the first restart in both legacy runtimes: + +| Drill | Saved progress | Unpatched first tick | Repaired first tick | +| --- | --- | --- | --- | +| Steam | 17 | 0: FAIL | 18: PASS | +| Electric | 209 | 1: FAIL | 210: PASS | + +Both repaired versions and the unchanged 1.13 control also pass their second +restart and subsequent production interval. This gives twelve successful +first-tick observations across six mod/version cases. The native evidence audit +reports `PASS`; the original run remains `FAIL`, with a separate audit reporting +`REPRODUCED_FOUR_LEGACY_FAILURES` rather than rewriting its verdict. + +## Migration Rerun + +All five historical source profiles remain pinned to their original jars, +including the affected maintained source jars. The only bridge changes are the +explicit new Steam/Electric 1.12 candidates. The 1.13 target jars and every +Base Metals/OreSpawn/Power dependency remain unchanged. + +The eight direct/chained routes rerun the connected source construction, +803-tick source qualification, 512-tick intermediate/target intervals and full +process restarts described in the original protocol. All eight routes now pass, +including all three formerly failing 1.12 intermediate restarts. All 22 frozen +state comparisons have zero differences. The independent audit passes 491 +checks, including all source fingerprints and archived/staged jar checks. + +Historical sources can still contain the old runtime defect: they provide +unchanged saved fixtures, not repaired historical binaries. The native candidate +test separately proves the fixed 1.10 and 1.12 builds operate and restart +correctly. State/operation success is not a clean-log or whole-modpack claim; +known Forge/Base Metals/OreSpawn diagnostics remain in the raw console records. + +## Build and Test Evidence + +All four affected mods pass: + +```text +clean check build javadoc verifyReleaseDependencies verifyReleaseArtifacts writeReleaseChecksums verifyEclipseProductionClasspath +``` + +The four legacy builds pass 18 JUnit tests in total, including ten new drill +persistence tests and the retained turbine/OreSpawn tests. The Python runner and +oracle suite passes 162 tests. Sources, Javadoc, deobf and release jars are built; +Eclipse launch metadata is regenerated with production/test isolation. + +The removed local OreSpawn 4.0.8 deobf dependency was rebuilt from pinned commit +`5a50df1158e948c8db55814e819090b34c12d765` in a disposable checkout. Its SHA-256 +is exactly the existing pin +`33BFB8C9D626D071147A0952A74B633F0BCC6A27E0408B4524370F4833B003C2`. +The build uses `-PoreSpawnDeobfJar=`; it does not upgrade OreSpawn or +alter its active checkout. A future clean build still needs that exact artifact +or its documented pinned-source bootstrap. + +Local ignored evidence is under `run-drill-progress/`: + +```text +20261023-before-artifacts/ Pre-fix jars and failed unit-test records +20261023-red/ Unpatched native reproduction and audit +20261023-candidate-inputs/ Immutable candidate inputs and replacement provenance +20261023-green/ Repaired native run and audit +20261023-matrix-original/ Original/early 1.10 direct and chained routes +20261023-matrix-maintained/ Maintained 1.10 direct and chained routes +20261023-matrix-112/ Early/maintained 1.12 direct routes +20261023-matrix-audit.json Combined eight-route audit +20261023-artifact-comparison.json Exact entry/resource/API comparison +20261023-workspace-profiles/ Mutable 1.10 profile update record +``` + +The runners take explicit hash-pinned manifests. Use `pin_drill_profiles.py` +to archive pre-fix outputs and prepare `native110`/`bridge112` overrides without +changing historical source profiles; `drill_restart.py` performs the native +checks and `audit_drill_restart.py` replays the raw evidence. See each script's +`--help` and the [runner instructions](../../test-rig/migration/README.md). + +Original worlds, earlier failed evidence and immutable historical jars remain +untouched. Real-client GUIs, arbitrary unload timing and other outstanding +machine behaviours are not newly accepted by this focused repair. diff --git a/docs/1.13/ECLIPSE-METADATA.md b/docs/1.13/ECLIPSE-METADATA.md new file mode 100644 index 00000000..752333a8 --- /dev/null +++ b/docs/1.13/ECLIPSE-METADATA.md @@ -0,0 +1,40 @@ +# Eclipse Mod Metadata Regression + +## Failure: 22 October 2026 + +The combined Eclipse launch rejected PowerAdvantage as incompatible with Steam and Electric, despite the built jar having the correct version. The user's `run/logs/latest.log` loaded Power from `bin/main`; that output contained the literal `${version}`, `${loader_version_range}` and other placeholders from the source `META-INF/mods.toml` template. + +Gradle had expanded the template correctly under `build/resources/main`. However, the Eclipse Java build path still listed `src/main/resources`, so an Eclipse build could copy the raw file over the processed copy previously staged in `bin/main`. The earlier disposable client probe merged Gradle outputs and therefore did not exercise this failure. No dependency version range needs weakening. + +## Repair + +All three projects now supply exactly one production resource source root to Eclipse: `build/resources/main`, with output `bin/main`. Raw resource roots are removed from both generated `.classpath` files and the Tooling API Eclipse model imported by Buildship. `eclipseClasspath` depends on `processResources`; existing Gradle synchronization tasks stage processed resources before launch preparation. The Eclipse audits reject raw resource roots and check the expanded mod ID/version in `bin/main`. + +In Eclipse, use **Gradle > Refresh Gradle Project**, then **Project > Clean**, allow the build to finish, and relaunch `runClientAllAdvantage`. Resource/version edits require `processResources` or a Gradle refresh before the Eclipse build. Java classes remain compiled by Eclipse into `bin/main`; normal launches still exclude test code. No gameplay classes, world data, registry identities or dependency ranges were changed. + +## Evidence + +- `build/client-resource-runs/20261022-050744-ad86296d/` retains the failing Eclipse-output snapshot. The new preflight detects its unresolved template without opening a world or starting Minecraft. +- `build/client-resource-runs/20261022-051012-75105698/client-input.json` records `output_source: Eclipse`, the combined launch and the snapshotted metadata hash. No Gradle resources were overlaid on that snapshot. +- The corresponding `client-resources.json` reports actual Forge versions Power `2.4.4.113021`, Steam `2.2.5.113021` and Electric `2.2.5.113021`, plus PASS for all 94 items and 905 block states. Existing oil and tank rendering/synchronization checks also pass. +- `build/eclipse-tooling-models.log` records successful Gradle Tooling API Eclipse-model checks for all three projects: `build/resources/main -> bin/main`, with no raw production resource roots. +- All three generated Eclipse/classpath audits pass, and each `bin/main/META-INF/mods.toml` matches its processed counterpart byte-for-byte. This validates the metadata/resource configuration and actual Eclipse output, not an automated run of the Eclipse GUI itself. + +## Repeat + +Run Gradle with Java 17, then use Java 8 for the disposable client: + +```powershell +./gradlew.bat check clientResourceProbeJar prepareCombinedEclipseLaunch +./test-rig/client-resources/run.ps1 -JavaHome '' -LaunchProfile runClientAllAdvantage.launch -RequireExtensions -OutputSource Eclipse +``` + +After a genuine Eclipse rebuild, repeat the second command without rerunning Gradle staging first; this checks what Eclipse actually left in `bin/main`. The probe copies that output and never opens the user's save. + +The independent Buildship-model check uses the Gradle installation's Tooling API jars and Java 17 source-file mode: + +```text +java --class-path "/lib/*" test-rig/eclipse/EclipseModelProbe.java "" "" "" "" "" +``` + +Both tools are test-only and are excluded from mod artifacts and ordinary play-test launches. Reports and generated IDE files remain local and ignored. diff --git a/docs/1.13/ENDURANCE.md b/docs/1.13/ENDURANCE.md new file mode 100644 index 00000000..6d4a0334 --- /dev/null +++ b/docs/1.13/ENDURANCE.md @@ -0,0 +1,229 @@ +# Advantage Works 1.13.2 endurance qualification + +This is a repeated-load test, not an accelerated clock or an idle server left +running. Read the [migration matrix](MIGRATION-MATRIX.md) separately: preserving +old save data and operating rebuilt Works equipment are different gates. + +Latest qualification: [the current candidates pass](#current-candidate-qualification). +Earlier runs remain recorded below with their original artifact identities. + +## Protocol + +`test-rig/migration/endurance.py` creates two disposable, operator-marked worlds: + +- A fresh packaged 1.13.2 Works world. +- A copy of the original Cyano 1.10 -> maintained 1.12 -> 1.13 conversion result. + +Both use the packaged Advantage jars with Base Metals and OreSpawn. Artifact +hashes and the source fixture's fingerprint are checked. No source world is +opened in Minecraft. The Works bays are rebuilt in the converted **copy**; this +is not an endurance proof for every original machine in that save. + +Servers run concurrently on loopback ports 25584 and 25585. Thirty real one-minute +intervals receive bounded new inputs after the preceding batch passes. Machine +blocks, progress, temperature and conduit networks are not reset between batches. +Only measured feed/output inventories and the oil circuit are replenished, +using native inventory commands and fluid handlers, never tile NBT writes. +NBT inventory writes would trigger Power's loaded-network refresh and could +conceal a stale-network defect; they are not allowed in the qualified run. +At least 18 game ticks per wall-clock second must elapse over the measured run. + +Each interval requires: + +- Steam and electric furnaces: four iron ores become exactly four ingots each. +- Steam and electric crushers: four cobblestone become exactly four gravel each. +- Electric oven: four raw beef become exactly four cooked beef. +- Plain conveyor: all 16 dirt reach the destination with no extra or held items. +- Zero-filter lane: eight dirt and eight apples pass forward, none go below. +- One-filter and multi-filter lanes: eight matching dirt go below and eight + nonmatching apples go forward; no source or held input remains. +- Distillation: 2,000 mB crude oil become exactly 1,000 mB refined oil, with no + crude remaining. All three tanks and the still's input tank are counted because + the shared bidirectional pipe network can return output to the supply tank. +- Disconnected pump, storage and public-API water witnesses retain respectively + 1,000, 2,000 and 4,000 mB of water. +- Geothermal heat stays above zero; energy stays finite and nonnegative; item + counts remain valid; battery charge stays within the four battery capacities. +- Stored partial-charge battery items retain their entire inventory NBT, and the + hydroelectric generator's battery has gained charge. + +After the load run, the runner checks station assertions, leaves power sources +and pipes intact, saves, shuts Minecraft down completely, starts a new process and repeats +the content assertions **before** reseeding. It then resumes operation for another +minute and requires another correct batch. No `build`, `reset`, station restart, +or power-block replacement is performed on the processing lanes after loading. +The public fluid API's saved full tank must retain its contents throughout. + +Minute-by-minute JSON/SNBT checkpoints, throughput ledgers, staged artifact hashes, +console logs and a final `endurance-results.json` remain in the ignored run root. +Station `MANUAL_REQUIRED` results remain manual; the throughput oracle's PASS does +not imply a human GUI acceptance result. Error/fatal log lines are retained in the +report rather than suppressed. + +## Run and repeat + +From the PowerAdvantage checkout, after building the harness: + +```text +gradlew.bat -p test-rig check assemble +test-rig/migration/.venv/Scripts/python.exe -m unittest discover -s test-rig/migration -p test_*.py -v +test-rig/migration/.venv/Scripts/python.exe test-rig/migration/endurance.py --java8 --migrated --output run-endurance/new-run +test-rig/migration/.venv/Scripts/python.exe test-rig/migration/audit_endurance.py --run run-endurance/new-run --output run-endurance/new-run/qualification-audit.json +``` + +Use the normal Python executable on other platforms. The converted runtime must +contain `AdvantageWorksMigration/level.dat`. Output directories must be new. +`--profile-manifest` accepts the migration runner's pinned `target113` profile +format for another installation; `--port` changes the pair of loopback ports. +`--seconds 180` is a preflight, reported as `SMOKE_PASS_NOT_ENDURANCE`, never as a +30-minute qualification. Keep allow markers and harness jars out of real worlds. + +The oracle's negative controls prove that missing, duplicated, unexpected and +unprocessed items, wrong routing, fluid loss/identity errors, closed-tank loss, +saved battery loss, negative energy and overcharge fail the test. + +Preflights also repaired three test-fixture problems, not gameplay code: +initial in-flight conveyor items were not cleared before measuring a new batch; +the oil checker watched only the still rather than the whole circuit; and an +uncontained network-test drill bored into the adjacent scarcity bay. The network +drills now face immediate bedrock stops, protected by a manifest regression test. +The oil station's start action refills its source through the actual fluid handler +rather than merging a fluid compound into an existing empty-tank NBT marker. +Oil is supplied only at start, not during setup, so the initial batch has no +preloaded oil hidden in the still. Native inventory commands use the 1.13 +`container.N` slot names, not the older `slot.container.N` spelling. + +## Coverage limits + +Active bays are `W-01,W-03,W-04,S-01,S-02,S-03,S-04,E-01,E-02,E-03,E-05,P-API-01`. +Additional inert checks cover `S-06,S-05,E-04,E-06`. `E-04` is not activated beside +the exclusive `E-01` environmental test. Extra equipment in those bays may tick, +but only the quantities listed above receive repeated-load qualification. + +This run does not qualify moving drills/track recovery, elevators, weapons, +turrets, loose player data, chunk unload, growth throughput, every fluid machine, +all boiler fuel/environment combinations, every recipe mode, or a full Sylvester +modpack upgrade. GUI rendering and client/server presentation remain manual gates. +There is no system-wide fuel/energy conservation assertion: infinite test supplies +are intentional, and fuel slots are replenished. Material processing ratios and +closed fluid storage do have exact quantity assertions. + +## Results + +This section records the earlier jar set, before the drill and network repairs. +The [current-candidate rerun](#current-candidate-qualification) is recorded separately. + +Qualified locally on 2026-10-06 using Power `2.4.4.113021`, Steam/Electric +`2.2.5.113021`, Base Metals `3.0.1.113021`, OreSpawn `4.1.0.113021`, and the +harness SHA-256 `20E0C1D4F0F12682FDD5CB307B31F50534DBB3F79094FAA75847A24DA93E8246`, +recorded in that run's `input-jars.json`. The current `packaged-current.json` +may pin a newer test-only harness for subsequent qualification; it must not be +used to infer the historical run's harness hash. No Advantage gameplay code or +release jar was changed during this testing pass. + +| Environment | Timed load | Verified batches | Full restart | Resumed work | Audit | +| --- | --- | --- | --- | --- | --- | +| Fresh 1.13.2 Works | 1,801.56 seconds | 30 | PASS | PASS | PASS | +| Original 1.10 -> maintained 1.12 -> 1.13 copy | 1,801.56 seconds | 30 | PASS | PASS | PASS | + +Per environment, the timed window produced 120 items in each of the five +processing lanes (600 total), routed 480 plain-conveyor items plus 480 in each +of the three filter lanes (1,920 total), and converted 60,000 mB crude oil to +exactly 30,000 mB refined oil. Before/after-restart checks are repeated observations +of the last batch, not extra production. The resumed batch is verified separately. +All closed-water and stored-battery witnesses passed; no measured loss, duplication +or incorrect yield was found. The source fixture's fingerprint remained unchanged. + +The independent audit verified all 13,179 inventory-command acknowledgements +during the timed window and 439 after restart, per world. It also checked all six +staged jar hashes, thirty distinct batch records, and that no processing station +was rebuilt/reset/restarted to make the saved networks work. Both servers exited +normally; no test server remains listening on the four temporary test ports. + +Twenty-four Python oracle/audit/migration tests, five Java harness tests, harness +isolation/build checks, and the normal Power release artifact checks passed. +All station-level human checks still report `MANUAL_REQUIRED`. + +Evidence is retained locally under the Git-ignored +`run-endurance/20261006-native-qualified/`: `endurance-results.json`, +`qualification-audit.json`, each world's `endurance-ledger.json`, minute snapshots, +copied saves, and startup/restart console logs. The earlier +`run-endurance/20261006-qualified/` completed its quantity checks but used NBT-based +replenishment: it is **diagnostic only**, not the network-endurance qualification. +All shorter preflights and failed fixture trials are retained separately. + +The logs are not error-free. The migrated world retains the already documented +Base Metals leaves-transformer error. Forge 25 also reports its config-watcher +and temporary mod-pack cleanup issues. These remain in `log_diagnostics`; there +were no `Can't keep up!` warnings in the qualified logs. No Base Metals, OreSpawn, +or Forge source was modified to suppress these messages. + +## Current candidate qualification + +On 6 October 2026, `run-endurance/20261006-current-candidates-160410` repeated +the unchanged native-feed protocol on the current drill- and network-repair +candidates. Both fresh 1.13 and the original Cyano 1.10 -> maintained 1.12 -> +1.13 disposable copy report **PASS**, including complete process restart, +retained-content checks before reseeding, and one new operating batch. + +| Environment | Timed load | Verified batches | Game ticks | Restart/resume | Audits | +| --- | --- | --- | --- | --- | --- | +| Fresh 1.13.2 Works | 1,801.56 seconds | 30 | 36,207 | PASS | PASS | +| Original 1.10 -> maintained 1.12 -> 1.13 copy | 1,801.56 seconds | 30 | 36,037 | PASS | PASS | + +Per world, the timed window produced 120 items in each of the five processing +lanes (600 total), routed 480 plain-conveyor items and 480 through each of the +three filter lanes (1,920 total), and converted 60,000 mB crude oil to exactly +30,000 mB refined oil. There were no residual inputs, unexpected products or +measured item/fluid losses. The resumed batch is a separate successful batch; +before/after-restart observations of the last timed batch are not extra output. + +The isolated pump, storage tank and public-API tank retained respectively +1,000, 2,000 and 4,000 mB water. Stored partial-charge battery item NBT remained +unchanged, hydroelectric lithium batteries reached their 8,000-unit capacity +without overcharge, geothermal temperatures stayed above zero, and measured +energy remained finite and nonnegative through all checkpoints and both restarts. + +Exact release pins: + +| Artifact | SHA-256 | +| --- | --- | +| Power `2.4.4.113021` | `0CCA0AF27497431C3A64091809F8DD328C84D82500CF05456366770C65485E74` | +| Steam `2.2.5.113021` | `D7D8A2E728E779EF0B624EF8AF3437F3354D618F12E279BE1C134F4AD5E690E3` | +| Electric `2.2.5.113021` | `72027C2DC273FD28F23AF01BB3EB495C75076E19B27A38CBDC5213B35A6198EB` | +| Harness `1.1.0.113021` | `80402A725EC02B9D169B0E11CE3E99EF0892F6E6468891A618A34A4E5EC7C615` | + +All six staged artifacts, including Base Metals and OreSpawn, match their +manifest hashes in both worlds. All 53 Python regression and negative-control +tests pass. The independent command/hash audit verifies 13,179 acknowledged +inventory writes during the timed phase and 439 after restart per world, thirty +distinct timed batches, and no processing-station rebuild/reset or replacement +of power sources and pipes after restart. + +A separate offline replay uses the station definitions from each staged +harness jar and reparses all 33 checkpoints per world: thirty timed batches, +before restart, after restart and resumed work. All 1,056 station checkpoint +records agree with the quantity/state oracle and ledgers, including output +totals, closed-water witnesses, stored battery NBT and world-clock progression. +The replay sends no server commands and never rewrites original evidence. +Its result is retained as `raw-snapshot-audit.json` alongside +`qualification-audit.json`, `endurance-results.json`, ledgers, snapshots and logs. + +The runner used loopback ports 25604/25605 after its initial bind check found +an unrelated server on 25584. That server remains untouched. All four test +server processes stopped normally, neither test port remains listening, and +the converted source fixture's before/after fingerprints match. + +The logs retain two known Base Metals leaves-transformer errors in the converted +world (initial startup and restart), and one Forge temporary-resource-pack +cleanup FATAL on fresh-world shutdown. There are no ticking-machine crashes, +failed inventory commands, config-watcher FATALs or keep-up warnings in this run. +The diagnostic lines remain visible; this is not an error-free-log claim. + +No production code, release artifact, immutable fixture or earlier result was +changed, and no commit, push or publication was performed. The converted Works +bays are newly commissioned equipment, not an endurance qualification of every +original player machine. The coverage limits above remain: moving drills and +chunk lifecycle have their separate reports, while broader player/entity +migration, every machine/fuel/recipe combination and manual client acceptance +are not qualified by this endurance run. diff --git a/docs/1.13/MANUFACTURING-PROGRESSION.md b/docs/1.13/MANUFACTURING-PROGRESSION.md new file mode 100644 index 00000000..5c781bcd --- /dev/null +++ b/docs/1.13/MANUFACTURING-PROGRESSION.md @@ -0,0 +1,188 @@ +# Manufacturing Progression: Minecraft 1.13.2 + +## Works Notice + +An entry in the recipe book is not proof of a usable workshop. This audit starts +with natural resources and follows the actual loaded recipes to each registered +Advantage component. Normal and Tech Progression are manufacturing modes; +Apocalyptic deliberately requires salvaged parts. Creative sources and +machine-generated helpers are not survival crafting targets. + +This concerns the local Power 2.4.4.113021 and Steam/Electric 2.2.5.113021 +candidates, using pinned Base Metals 3.0.1.113021 and OreSpawn 4.1.0.113021. +It does not rename existing items, blocks, fluids, tile IDs, NBT or recipes. + +## Material Ledger + +| Material | Supply And Use | +| --- | --- | +| Wire | The electric conduit is a member of `forge:wires`. This supplies the electric boiler, electric switch, LED, photovoltaic generator and Normal PSU recipes. | +| Carbon powder | `forge:dusts/carbon` includes Base Metals coal and charcoal powders through required `forge:dusts/coal` and `forge:dusts/charcoal` references. Coal/charcoal can be crushed with a crackhammer or a powered crusher. | +| Rubber | Bioplastic joins `forge:rubber` and `forge:ingots/rubber` only when Power's `options.plasticEqualsRubber` is true. Turning it off retains the plastic recipe alternatives and preserves any rubber supplied by another data pack or mod. Petrolplastic is plastic, not an unconditional rubber substitute. | +| Plastic | Both `poweradvantage:bioplastic_ingot` and `electricadvantage:petrolplastic_ingot` belong to `forge:plastic` and `forge:ingots/plastic`. | +| Solder | Normal mode additionally accepts lead, tin or silver ingots through `forge:solder`, as the legacy mod did. Tech/Apocalyptic require actual solder unless a user data pack adds alternatives. | +| Circuit board | `forge:circuit_boards` means the finished `electricadvantage:control_circuit`, not the blank board. A blank board cannot bypass the microchip/control-circuit stage. | + +Forge 25's early optional nested-tag resolution can cache an unresolved tag. +The carbon references are therefore required, with empty declarations so reduced +mod sets still load safely. Empty tags remain empty: there is no free substitute +for a missing material provider. + +Configured aliases are applied at the end of item-tag reload, before recipe and +advancement parsing. Server tag serialization therefore includes the same policy +used by recipes. The initial vanilla-only bootstrap is ignored until FML's mod +list and Advantage items exist. Resource reload starts from fresh data-pack tags, +so aliases do not accumulate when a mode or option changes between runs. + +## The First Workshop + +1. Mine and process metals through Base Metals. Iron and copper equipment lead + to crackhammers, steel, brass, bars, rods and plates. +2. Craft one ordinary or poisonous potato into one starch in the inventory grid + or a crafting table. Crackhammers and both powered crushers remain alternative + routes at the same ratio. Smelt starch for one bioplastic ingot (200 ticks, + 0.1 XP). This removes the plastic/refinery bootstrap loop. +3. Crush coal or charcoal for carbon powder. Silicon blend smelts to silicon; + solder blend smelts to solder (200 ticks and 0.5 XP each). +4. Smelt lithium ore or powder for one lithium ingot (200 ticks, 0.5 XP). Crushing + lithium ore yields two powders; crushing an ingot yields one. Nine lithium + nuggets combine into one ingot, and one ingot makes nine nuggets. +5. Sulfur ore can be mined for its existing powder drops or crushed for four + powders. Use sulfur, lithium, solder and circuits for the electrical parts. +6. Use frames, pipes and conveyors to build the steam/electric machinery. Finding + a relevant ingredient earns the matching recipe discovery; no `/recipe give` + command is needed. See the [inventory checklist](RECIPE-DISCOVERY.md). + +Bioplastic is the initial plastic supply. Petrolplastic is a later alternative: +collect crude oil in a bucket, distil two buckets into one refined-oil bucket, +then process refined oil in a powered plastic refinery. The refinery consumes +100 mB refined oil and 1,200 electricity per ingot, unchanged from the machine's +existing code. Oil is acquired from a world source, not crafted out of an empty +bucket. World-generation qualification is a separate test suite. + +## Converters + +All four preserved converters now have acquisition recipes: + +| Converter | Crafting Inputs | +| --- | --- | +| RF-labelled steam | Governor, steel frame and redstone block in a row | +| RF-labelled electricity | PSU, steel frame and redstone block in a row | +| RF-labelled quantum | Ender pearl, steel frame and redstone block in a row | +| Tech Reborn-labelled electricity | PSU surrounded by eight refined-iron ingots; ordinary iron is the fallback only when no refined-iron tag provider exists | + +The labels and saved identities remain historical; the 1.13 implementations use +Forge Energy. Steam or Electric Advantage supplies the relevant governor or PSU. +The recipes remain guarded when that expansion or another ingredient is absent. + +## Apocalyptic Salvage + +Apocalyptic intentionally disables new manufacture of sprockets, governors and +PSUs, and forbids new photovoltaic generators. This is not an empty-tag defect. +Mineshaft, dungeon, blacksmith and stronghold chest pools supply the original +Advantage parts; Steam/Electric also contribute to end-city chests. Native loot +tables now provide a fallback when Additional Loot Tables is absent. Existing +legacy pools, weights, quantities and musket-enchantment functions are retained. +Data packs can replace the namespaced `chests/advantage_parts` tables. + +The native fallback is disabled when Additional Loot Tables is installed, to +avoid injecting both copies. Setting Power's treasure-chest loot factor to zero +also disables it. Positive factor scaling is not newly implemented: the legacy +static pools did not scale their weights with that value. Optional ALT behavior +is not qualified by the default no-ALT pack. + +The original recycling paths recover steel plates, fluid pipes, governors, PSUs, +control circuits and microchips from salvaged equipment. Photovoltaic equipment +remains intentionally unobtainable in a fresh Apocalyptic world; use hydro or +steam generation. Preserved existing photovoltaic blocks are not deleted. + +## Inspector's Procedure + +```text +test-rig/migration/.venv/Scripts/python.exe test-rig/migration/progression.py --java8 --output run-progression/new-current +test-rig/migration/.venv/Scripts/python.exe test-rig/migration/progression.py --java8 --output run-progression/new-baseline --baseline +``` + +The runner creates a marked disposable superflat world, stages hash-checked +packaged jars and exposes an operator-only `/advprogression