Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,18 @@ classes
/hs_err_pid*.log
/replay_pid*.log

# generated test results and runtime diagnostics, including nested runs
**/test-results/
**/test-reports/
**/test-output/
**/smoke-output/
**/crash-reports/
*.log
*.jfr
*.hprof
hs_err_pid*.log
replay_pid*.log

# local regression, benchmark, and profiling evidence
/benchmark-*/
/preferred-seed-*/
Expand Down Expand Up @@ -82,6 +94,10 @@ __pycache__/
[Cc][Oo][Pp][Ii][Ll][Oo][Tt]-[Ii][Nn][Ss][Tt][Rr][Uu][Cc][Tt][Ii][Oo][Nn][Ss].md
[Aa][Gg][Ee][Nn][Tt]-*.md
[Cc][Oo][Dd][Ee][Xx]-*.md
.aider*
.continue/
.windsurf/
.opencode/

# local validation/audit reports
**/VALIDATION.md
Expand Down
34 changes: 28 additions & 6 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,30 @@
Base Metals 3.0.1.113021

- Backport the complete native Base Metals implementation to Minecraft 1.13.2 and Forge 25.0.223.
- Preserve the established registry catalogue, mechanics, recipes, fluids, loot, trades, advancements, and Forge-tag contracts without MMDLib.
- Use OreSpawn 4.0.16.113021 as the sole ore-placement engine, with all eleven historical rules and Base Metals copper enabled.
- Add direct pre-flattening migration support for Cyano and MMD Base Metals worlds from Minecraft 1.10.2 and 1.12.2.
- Render all 36 molten-fluid buckets through target-native layered models and catalogue-driven item colours, avoiding Forge 25's unfinished dynamic bucket loader.
- Retain ForgeGradle 7.0.34, Gradle 9.6.1, exact Java toolchains, deterministic release artifacts, checksums, CI audits, and isolated Eclipse launches.
- Bring the 1.18.2 port to Minecraft 1.13.2 and Forge 25.0.223, running on Java 8.
- Keep the historical blocks, items, recipes, fluids, loot, trades and
advancements without requiring MMDLib or Additional Loot Tables.
- Use the published OreSpawn 4.1.0.113021 release for all ore placement.
All eleven historical rules remain enabled, including Base Metals copper.
- Support upgrades from Cyano and MMD Base Metals worlds made in Minecraft
1.10.2 and 1.12.2. Preserve old block states, equipment and fluid buckets,
including chunks whose only Base Metals content is inside containers.
- Give all 36 molten-fluid buckets working models and material colours.
- Use ForgeGradle 7.0.34 and Gradle 9.6.1, with checked dependency downloads,
reproducible release jars and separate test and Eclipse launch classpaths.

Fixes carried forward from the 1.12 release

- Metal nuggets and powders no longer burn as furnace fuel. Coal and charcoal
keep their normal fuel values (#473).
- Diamond, Emerald, Gold and Quartz doors use six ingredients, so their recipes
no longer collide with trapdoors (#474).
- Steel rail crafting produces 16 rails. Bows and crossbows now combine their
material's attack damage with the ammunition material before adding Power (#467).
- Plate repairs preserve the original item's name, enchantments and other NBT,
and reject incomplete recipes and extra ingredients. They stay out of the
recipe book.
- Crafting recipes unlock from matching ingredients, including Forge tags.
Recipe-book groups are specific to the resulting item.
- Nine Obsidian nuggets produce an ingot, not a block. Obsidian blocks use nine
ingots, with a pattern distinct from bars and doors. Vanilla Bits swords and
bolts no longer cost less material than their recycling recipes return.
45 changes: 26 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,10 @@ shields, bows, crossbows, arrows, and bolts. The mod also includes Crack
Hammers, scythes, metal anvils, the human detector, villager trades,
advancements, and structure loot.

The Minecraft 1.13.2 line is a native Forge/Java 8 backport of the completed
1.18.2 implementation. Its current version is `3.0.1.113021`: functional
version `3.0.1`, built for the `113021` target (Minecraft 1.13.2, Forge). It
preserves the historical `basemetals` registry IDs while replacing MMDLib,
Additional Loot Tables, and the old Base Metals world generator with native
Forge registries, tags, recipes, loot tables, and advancements.
This is the Minecraft 1.13.2 version, `3.0.1.113021`, adapted from the 1.18.2
port. It keeps the historical `basemetals` block and item IDs, but no longer
needs MMDLib or Additional Loot Tables. Forge handles registration, recipes,
loot and advancements, and OreSpawn handles ore placement.

## Requirements

Expand All @@ -26,7 +24,7 @@ Forge registries, tags, recipes, loot tables, and advancements.
- Java 8
- OreSpawn `[4.0.16.113021,5.0.0)` on both client and server

Put `BaseMetals-3.0.1.113021.jar` and OreSpawn `4.0.16.113021` (or a later
Put `BaseMetals-3.0.1.113021.jar` and OreSpawn `4.1.0.113021` (or a later
compatible OreSpawn 4 build for 1.13.2) in the `mods` directory. MMDLib and
Additional Loot Tables are not dependencies. Mineralogy is optional.

Expand All @@ -39,10 +37,9 @@ Base Metals provides 22 materials:
- Alloys: Brass, Bronze, Cupronickel, Electrum, Invar, Pewter, and Steel.
- Fantasy materials: Adamantine, Aquarium, Cold Iron, Mithril, and Starsteel.

The available forms intentionally vary by material to preserve the established
Base Metals content. Forge tags are used throughout recipes, so compatible
ores, ingots, powders, plates, rods, gears, and other components from different
mods can interoperate without a Java API dependency.
Not every material has every form. Recipes use Forge tags, so compatible ores,
ingots, powders, plates, rods, gears and other components from different mods
can be used together.

Notable mechanics include:

Expand All @@ -56,8 +53,12 @@ Notable mechanics include:
protection events, drops, enchantments, and durability.
- Material shields can be upgraded at an anvil with one strictly harder tagged
plate while retaining their enchantments.
- One damaged armour piece or shield and a matching plate fully repair the
item without losing its name, enchantments or other saved data. These repair
recipes stay out of the recipe book.
- Base Metals bows fire material arrows. Its legacy-style crossbows are
draw-and-release weapons which fire material bolts.
draw-and-release weapons which fire material bolts. Both the launcher and
ammunition materials contribute to damage; Power is added afterward.
- Configurable armour and melee effects give Adamantine, Aquarium, Cold Iron,
Lead, Mithril, and Starsteel their distinctive behaviour. Held Starsteel
equipment repairs by one durability every 200 server ticks; its armour does
Expand All @@ -68,8 +69,9 @@ Notable mechanics include:

The common configuration has four default-enabled switches: special equipment
effects, Starsteel regeneration, mercury immersion effects, and villager
trades. Registries, materials, recipes, and world generation remain stable data
contracts and are not individually configuration-gated.
trades. These switches do not remove registered items or blocks. Individual
materials and recipes cannot be disabled through this configuration; edit ore
placement through OreSpawn.

## Ore generation

Expand All @@ -90,12 +92,12 @@ contract.

## Updating an old world

This release is the first Minecraft version after the flattening and supports
Minecraft 1.13 changed how worlds store blocks and items. This port supports
direct upgrades from Base Metals 1.10 and 1.12, including the original Cyano
2.4 line and later MMD releases. Its pre-flattening migration hook restores
legacy Base Metals and MMDLib block identities before Mojang converts numeric
chunk data, and also migrates historical item aliases, durability, and universal
fluid buckets.
2.4 line and later MMD releases. It restores old Base Metals and MMDLib block
IDs before Minecraft converts the chunks, and also converts old item names,
durability and universal fluid buckets. Chunks containing only Base Metals
items in containers are protected too.

Always perform the first upgrade on a copy of the world and keep the untouched
original. Read [Migration](docs/MIGRATION.md) before opening an old save. A
Expand Down Expand Up @@ -139,6 +141,11 @@ checksums, reproducibility checks, CodeQL, wrapper validation, and Eclipse
classpath isolation. Test probes and historical fixtures are excluded from the
published JAR.

Development and CI use the published
[OreSpawn 4.1.0.113021 release](https://www.curseforge.com/minecraft/mc-mods/mmd-orespawn/files/9035885)
from CurseMaven. The build checks its exact file ID and SHA-256 before release
auditing and packaged tests; no local OreSpawn build is needed.

Release artifacts use Maven coordinate
`zone.moddev.mc.basemetals:BaseMetals:3.0.1.113021`.

Expand Down
30 changes: 22 additions & 8 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,10 @@ def verifyLegacyUpgradeRun = tasks.register('verifyLegacyUpgradeRun') {
if (!text.contains('BASEMETALS_LEGACY_UPGRADE_PROBE PASS')) {
throw new GradleException('Legacy-world runtime probe did not report PASS')
}
File fixtureManifest = new File(runDirectory, 'world/legacy_registry_manifest_runtime.json')
if (fixtureManifest.isFile() && !text.contains('BASEMETALS_1_12_TO_1_13_UPGRADE PASS')) {
throw new GradleException('Complete 1.12 fixture verification did not report PASS')
}
if (!text.contains('legacy Base Metals states') || !text.contains('protected')) {
throw new GradleException('Legacy Base Metals registry/chunk preparation was not observed')
}
Expand All @@ -309,6 +313,7 @@ tasks.matching { it.name == 'runLegacyUpgradeServer' }.configureEach {

def orespawnModule = "mmd-orespawn-${project.orespawn_curse_project_id}"
def orespawnVerificationRepository = project.findProperty('orespawnVerificationRepository')
def orespawnCoordinate = "curse.maven:${orespawnModule}:${orespawn_curse_file_id}"
repositories {
minecraft.mavenizer(it)
maven fg.forgeMaven
Expand All @@ -334,8 +339,8 @@ repositories {

dependencies {
implementation minecraft.dependency("net.minecraftforge:forge:${minecraft_version}-${forge_version}")
runtimeOnly renamer.dependency("curse.maven:${orespawnModule}:${orespawn_curse_file_id}")
orespawnRelease "curse.maven:${orespawnModule}:${orespawn_curse_file_id}"
runtimeOnly renamer.dependency(orespawnCoordinate)
orespawnRelease orespawnCoordinate
testImplementation platform('org.junit:junit-bom:5.10.2')
testImplementation 'org.junit.jupiter:junit-jupiter'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.10.2'
Expand Down Expand Up @@ -493,18 +498,28 @@ tasks.register('verifyReleaseConfiguration') {
tasks.register('verifyReleaseDependencies') {
group = 'verification'
doLast {
def expected = [version: '4.0.16.113021', curseProjectId: '245586', curseFileId: '8836169',
sha256: '38C091390486AFCEF8F43404071AF595E53FF5750DB99E0F8F851858C85BA11C']
def expected = [version: '4.1.0.113021', curseProjectId: '245586', curseFileId: '9035885',
sha256: '66C9CC5F8BE8F08F3DD5FA26EBEA79B52972B2FB86CB4DADBEEBC29AC01D3435']
if (configuredOreSpawnIdentity != expected) {
throw new GradleException("Unexpected OreSpawn identity: ${configuredOreSpawnIdentity}")
}
File artifact = configurations.orespawnRelease.resolvedConfiguration.resolvedArtifacts
.find { it.moduleVersion.id.group == 'curse.maven' && it.file.name.endsWith('.jar') }?.file
File artifact = configurations.orespawnRelease.singleFile
if (artifact == null || !artifact.isFile()) throw new GradleException('Released OreSpawn did not resolve')
String actual = sha256(artifact)
if (actual != configuredOreSpawnIdentity.sha256) {
throw new GradleException("OreSpawn SHA-256 mismatch: ${actual}")
}
new ZipFile(artifact).withCloseable { zip ->
def metadataEntry = zip.getEntry('META-INF/mods.toml')
if (metadataEntry == null) throw new GradleException('OreSpawn mod metadata is missing')
String metadata = zip.getInputStream(metadataEntry).getText('UTF-8')
if (!metadata.contains('modId="orespawn"')
|| !metadata.contains('version="4.1.0.113021"')
|| !metadata.contains('versionRange="[1.13.2]"')
|| zip.getEntry('zone/moddev/mc/orespawn/api/OreSpawnApi.class') == null) {
throw new GradleException('Expected the published Minecraft 1.13.2 OreSpawn 4.1 release and public API')
}
}
}
}

Expand Down Expand Up @@ -961,8 +976,7 @@ def prepareBuildOwnedRunDirectory = { File candidate, String expectedName ->
runDirectory
}
def releasedOreSpawnJar = {
File artifact = configurations.orespawnRelease.resolvedConfiguration.resolvedArtifacts
.find { it.moduleVersion.id.group == 'curse.maven' && it.file.name.endsWith('.jar') }?.file
File artifact = configurations.orespawnRelease.singleFile
if (artifact == null || !artifact.isFile() || sha256(artifact) != configuredOreSpawnIdentity.sha256) {
throw new GradleException('Exact released OreSpawn artifact is unavailable for packaged testing')
}
Expand Down
2 changes: 1 addition & 1 deletion docs/MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ blocks cannot be reconstructed by Mojang's vanilla fixer alone, so Base Metals
installs a narrowly scoped migration hook before Forge reads an old world.

Always upgrade a copy and retain the original 1.10/1.12 world. Install Base
Metals `3.0.1.113021`, OreSpawn `4.0.16.113021`, and any other 1.13.2 mods needed
Metals `3.0.1.113021`, OreSpawn `4.1.0.113021`, and any other 1.13.2 mods needed
by that world. Do not carry MMDLib, Additional Loot Tables, the OreSpawn 3
plugin, or Base Metals' old fallback generator into the new instance.

Expand Down
9 changes: 7 additions & 2 deletions docs/VERSIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ Java 8. The Gradle build itself runs on Java `17.0.1+12` and uses ForgeGradle
8 requirement of the released mod.

Development, CI, and release qualification use the exact public OreSpawn
`4.0.16.113021` release (CurseForge project `245586`, file `8836169`, SHA-256
`38C091390486AFCEF8F43404071AF595E53FF5750DB99E0F8F851858C85BA11C`).
`4.1.0.113021` release (CurseForge project `245586`, file `9035885`, SHA-256
`66C9CC5F8BE8F08F3DD5FA26EBEA79B52972B2FB86CB4DADBEEBC29AC01D3435`).
The runtime contract is OreSpawn `[4.0.16.113021,5.0.0)` for Minecraft 1.13.2.

The published dependency is resolved through CurseMaven. CI stages the same
checksum-verified file in a temporary Maven mirror. Local candidate repositories
are no longer used. The schema-3 provider remains compatible with the supported
OreSpawn 4 versions, so the runtime dependency range is unchanged.

Release artifacts use Maven coordinate
`zone.moddev.mc.basemetals:BaseMetals:3.0.1.113021`.
2 changes: 1 addition & 1 deletion docs/WORLDGEN.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Base Metals requires OreSpawn `[4.0.16.113021,5.0.0)` and packages provider
schema 3, revision 1 at `data/basemetals/orespawn/provider.json`. Development,
CI, and release qualification use the exact OreSpawn `4.0.16.113021` artifact.
CI, and release qualification use the exact OreSpawn `4.1.0.113021` release.

The provider declares ores only: it has no rocks, geomes, biome rules, terrain
dimensions, formations, fluid deposits, vanilla suppression, or strata
Expand Down
6 changes: 3 additions & 3 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ curseforge_project_id=240967

# Exact released OreSpawn used for development and qualification. Runtime
# metadata intentionally remains compatible with supported OreSpawn 4 builds.
orespawn_version=4.0.16.113021
orespawn_version=4.1.0.113021
orespawn_curse_project_id=245586
orespawn_curse_file_id=8836169
orespawn_sha256=38C091390486AFCEF8F43404071AF595E53FF5750DB99E0F8F851858C85BA11C
orespawn_curse_file_id=9035885
orespawn_sha256=66C9CC5F8BE8F08F3DD5FA26EBEA79B52972B2FB86CB4DADBEEBC29AC01D3435
# The Forge version range can use any version of Forge as bounds or match the loader version range
forge_version_range=[25,)
# The loader version range can only use the major version of Forge/FML as bounds
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"parent": "minecraft:recipes/root",
"criteria": {
"has_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": {
"recipe": "basemetals:activator_rail"
}
},
"has_ingredient_1": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [
{
"item": "minecraft:redstone_torch"
}
]
}
},
"has_ingredient_2": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [
{
"tag": "forge:ingots/steel"
}
]
}
}
},
"requirements": [
[
"has_recipe",
"has_ingredient_1",
"has_ingredient_2"
]
],
"rewards": {
"recipes": [
"basemetals:activator_rail"
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"parent": "minecraft:recipes/root",
"criteria": {
"has_recipe": {
"trigger": "minecraft:recipe_unlocked",
"conditions": {
"recipe": "basemetals:adamantine_anvil"
}
},
"has_ingredient_1": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [
{
"tag": "forge:storage_blocks/adamantine"
}
]
}
},
"has_ingredient_2": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [
{
"tag": "forge:ingots/adamantine"
}
]
}
}
},
"requirements": [
[
"has_recipe",
"has_ingredient_1",
"has_ingredient_2"
]
],
"rewards": {
"recipes": [
"basemetals:adamantine_anvil"
]
}
}
Loading
Loading