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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
* text=auto
# Store and check out repository text as LF on every platform. Windows command
# files are the sole exception below.
* text=auto eol=lf

*.bat text eol=crlf
#*.bat text eol=lf
Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
Version 4.0.6

* Fix provider top and filler materials being generated one block below exposed ground.
* Apply underwater materials from the corrected ground and ceiling materials to roof undersides.
* Preserve trees, vegetation, structures and block entities by running surface replacement before late features.
* Honour exact biome-to-geome weights on dynamic biome registries.
* Stagger close Stable Layers geome transitions by layer instead of changing a whole rock column at one boundary.
* Recalibrate Stable Layers edge-detail presets so Average retains natural variation at later rock contacts.
* Existing chunks are not rewritten; the correction applies while generating new chunks.

Version 4.0.5

* Complete native translations for every shipped non-English locale
* Add automatic fresh-and-reload validation for provider-owned custom biomes
* Correct Minecraft 1.21.11 Identifier documentation and ForgeGradle workflow
* Preserve public API major 1 and provider/global/world schemas 4/6/5

Version 3.3.1

* Fix several bugs
Expand Down
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# MMD OreSpawn

OreSpawn 4 is a provider-driven world-generation engine for Minecraft 1.21.1.
OreSpawn 4 is a provider-driven world-generation engine for Minecraft 1.21.11.
It gives mods and modpacks one place to configure ores, deposit shapes, optional
rock strata and geomes, provider-owned underground fluid deposits, biome
palettes and world materials, flat bedrock, and bounded ore retrogen.
Expand Down Expand Up @@ -42,6 +42,9 @@ Important files:

Profile edits affect newly generated chunks. Ore and flat-bedrock retrogen are
separate opt-in features; OreSpawn never retro-generates rock strata.
Stable Layers honours exact biome-ID geome influences on dynamic biome
registries and spreads close geome transitions across layers rather than
changing an entire vertical rock column at one boundary.

To move a configured single-player world to a dedicated server, copy the
world's `serverconfig/orespawn-worldgen.json` with the world and install the
Expand Down Expand Up @@ -79,10 +82,21 @@ exported to `config/orespawn-guide/` without overwriting existing files.
Use Java 21 from the repository root:

```powershell
.\gradlew.bat test processResources build javadoc --no-daemon
.\gradlew.bat genEclipseRuns eclipse --no-daemon
.\gradlew.bat clean build javadoc --no-daemon
.\gradlew.bat genEclipseRuns --no-daemon
```

`build` runs the standard `check` lifecycle. In addition to the JUnit suite,
that lifecycle packages a test-only provider mod and verifies exposed,
underwater, filler, and ceiling surfaces in open and ceiling normal-noise
dimensions. It also proves later vegetation, structures, and block entities
survive, verifies identifier-weighted geology in a dynamic custom biome, then
reopens and checks the exact saved world. The fixture is not included in
OreSpawn's published jars.

Import or refresh the project with Eclipse Buildship. ForgeGradle 7's legacy
`eclipse` task produces Java-only metadata and must not be used for this branch.

Machine-specific `AGENTS.md` and `agent-notes/` files are intentionally ignored.
Public developer and AI integration guidance lives in `docs/` and is included
in the built jar.
Expand Down
86 changes: 86 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,24 @@ minecraft {
applyBenchmarkProperties(delegate, '4', '3', '256')
}

['Fresh', 'Reload'].each { String phase ->
register("surfaceIntegration${phase}") {
mainClass = 'net.minecraftforge.bootstrap.ForgeBootstrap'
args '--launchTarget', 'forge_userdev_server_gametest', '--gameDir', '.'
environment 'MCP_MAPPINGS', "official_${minecraft_version}"
workingDir = layout.buildDirectory.dir('surface-integration-run')
systemProperty 'forge.enableGameTest', 'true'
systemProperty 'forge.enabledGameTestNamespaces', 'surfaceprobe'
systemProperty 'forge.logging.console.level', 'info'
systemProperty 'surfaceprobe.integrationPhase', phase.toLowerCase(Locale.ROOT)
mods {
create(mod_id) {
source sourceSets.main
}
}
}
}

register('data') {
workingDir = layout.projectDirectory.dir('run-data')
args '--mod', mod_id, '--all',
Expand Down Expand Up @@ -207,6 +225,74 @@ tasks.named('test', Test).configure {
useJUnitPlatform()
}

def surfaceIntegrationClasses = layout.buildDirectory.dir('surface-integration-fixture/classes')
def compileSurfaceIntegrationTestMod = tasks.register('compileSurfaceIntegrationTestMod', JavaCompile) {
dependsOn tasks.named('classes')
source fileTree('src/biomeIntegrationTest/java')
classpath = files(sourceSets.main.output, sourceSets.main.compileClasspath)
destinationDirectory.set(surfaceIntegrationClasses)
javaCompiler.set(javaToolchains.compilerFor {
languageVersion = JavaLanguageVersion.of(21)
})
options.release = 21
options.encoding = 'UTF-8'
}

def surfaceIntegrationTestModJar = tasks.register('surfaceIntegrationTestModJar', Jar) {
dependsOn compileSurfaceIntegrationTestMod
archiveFileName = 'surfaceprobe.jar'
destinationDirectory = layout.buildDirectory.dir('surface-integration-fixture')
manifest {
attributes 'MixinConfigs': 'surfaceprobe.mixins.json'
}
from surfaceIntegrationClasses
from 'src/biomeIntegrationTest/resources'
}

def surfaceIntegrationRunDirectory = layout.buildDirectory.dir('surface-integration-run')
def prepareSurfaceIntegrationTest = tasks.register('prepareSurfaceIntegrationTest') {
dependsOn surfaceIntegrationTestModJar
doLast {
delete surfaceIntegrationRunDirectory
copy {
from surfaceIntegrationTestModJar.flatMap { it.archiveFile }
into surfaceIntegrationRunDirectory.map { it.dir('mods') }
}
}
}

tasks.configureEach {
if (name == 'runSurfaceIntegrationFresh') {
dependsOn prepareSurfaceIntegrationTest
} else if (name == 'runSurfaceIntegrationReload') {
dependsOn 'runSurfaceIntegrationFresh'
}
}

def surfaceIntegrationTest = tasks.register('surfaceIntegrationTest') {
group = 'verification'
description = 'Verifies provider surfaces and dynamic-biome geology across fresh and reloaded normal terrain.'
dependsOn 'runSurfaceIntegrationReload'
doLast {
File marker = surfaceIntegrationRunDirectory.get().file(
'gametestserver/gametestworld/surfaceprobe-integration.properties').asFile
if (!marker.isFile()) {
throw new GradleException("Surface integration completion marker is missing: ${marker}")
}
Properties result = new Properties()
marker.withInputStream { result.load(it) }
if (result.getProperty('reload_verified') != 'true') {
throw new GradleException("Surface integration reload was not verified: ${marker}")
}
logger.lifecycle('Provider surfaces and dynamic-biome geology verified: {} dimensions, {} columns each, fresh + reload',
result.getProperty('dimensions'), result.getProperty('columns_per_dimension'))
}
}

tasks.named('check') {
dependsOn surfaceIntegrationTest
}

// ForgeGradle 7 generates a launch for every source set, but its ordinary
// Eclipse launches currently omit JDT's test-code exclusion. Without this,
// Eclipse adds build/sourceSets/test to the Java module path and Java rejects
Expand Down
85 changes: 12 additions & 73 deletions docs/AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,76 +1,15 @@
# OreSpawn Integration Notes For Coding Agents
# OreSpawn Documentation Map

OreSpawn 4.0 is a required Forge mod and declarative world-generation engine.
Public API major version 1 consists only of `zone.moddev.mc.orespawn.api`. Treat
every other Java package as internal and unstable.
This index is for navigating the documentation to learn how to integrate with
and use OreSpawn with a mod or modpack. Start with
[DEVELOPER_GUIDE.md](DEVELOPER_GUIDE.md).

Integration entry points:
Use the focused guides for implementation details:

- Java declarations: `OreSpawnApi.enqueue(WorldgenProvider)` during
`InterModEnqueueEvent`.
- Packaged declarations: `data/<modid>/orespawn/provider.json`.
- Pack overrides: `config/<modid>-orespawn.json`.
- Active queries: `getActiveProfile(MinecraftServer)` and
`createSampler(ServerLevel)`.
- Native-ore takeover: disable only when `isOreTakeoverActive(modid)` is true.

Configuration contracts:

- Global `config/orespawn-worldgen.json`: schema 6.
- World `serverconfig/orespawn-worldgen.json`: schema 5.
- Provider files: schema 4; legacy schemas 1-3 remain accepted.
- Ore placement accepts fixed `quantity` or paired inclusive
`min_quantity`/`max_quantity` values in the range 1-64. A complete range is
authoritative when both forms exist.
- `dimension_selectors.orespawn:all_except_nether_end` applies to ordinary
dimensions but never Nether or End. Explicit dimension entries override it
per ore and must also drive vanilla-feature suppression.
- JSON Schemas and examples are under `META-INF/orespawn/docs/` in the jar.
- Schema 4 providers may declare `biome_palettes` and `dimension_materials`.
Palettes wrap the native dimension biome source. Region presets are 128,
256, 512, 1024, and 2048 blocks.

Lifecycle and ownership:

- Forge setup is parallel. Never mutate OreSpawn internals directly.
- A pack override file is authoritative over packaged and API definitions for
the same provider. A malformed override fails closed.
- Provider rule IDs use the provider namespace. A rule's `block` or weighted
output may reference any installed block.
- Definitions freeze at load completion and change only after restart or an
operator `/orespawn reload`.
- Auto-selected templates apply only to fresh worlds with no explicit
`default_template`. Highest priority wins, then lexical ID. Existing world
profiles never auto-switch.

Performance constraints:

- Do not request callbacks in block-generation loops.
- Registry IDs remain `ResourceLocation` values until setup-time baking.
- Dimension, tag, alias, biome, geome, family, pattern, and block-state
resolution occurs before generation.
- Biome palettes bake holders, climate bounds, namespace filters, weights,
surfaces, and dimension materials. Provider callbacks never run in selection.
- Ore rules support `uniform`, `triangle`, `bottom_triangle`, and
`uniform_bottom_triangle` height distributions plus a 0-1
`discard_chance_on_air_exposure` value for buried deposits.
- The chunk hot path must contain no config reads, registry access, strings,
logging, reflection, or per-block allocation.
- Cache biome filters as registry keys, never `Biome` object identities;
dynamic-registry biome instances are not identity-stable.
- Ore and flat-bedrock retrogen are bounded and marker-based. Terrain strata
are never retrogened.

Compatibility defaults:

- Standalone OreSpawn is passive: no rocks, terrain dimensions, fluid deposits, ore
suppression, retrogen, or flat bedrock are enabled by default.
- The Overworld is the conventional geology target, but a provider must opt it
in. Nether and End terrain remain untouched unless explicitly configured.
- Mineralogy 6 is a provider, not a public-API compatibility facade. Do not use
removed `zone.moddev.mc.mineralogy.api` classes.

Common tasks are documented in `API.md`, `PROVIDERS.md`, `FEATURES.md`,
`TEMPLATES.md`, `BIOMES.md`, and `DIMENSIONS.md`. Start with
`DEVELOPER_GUIDE.md` when the task is broader than one isolated schema or API
question.
- [API.md](API.md) for the supported Java API;
- [PROVIDERS.md](PROVIDERS.md) for packaged and configurable providers;
- [FEATURES.md](FEATURES.md) for rocks, ores, deposits, and geology;
- [BIOMES.md](BIOMES.md) and [DIMENSIONS.md](DIMENSIONS.md) for world integration;
- [TEMPLATES.md](TEMPLATES.md) for selectable world styles;
- [CONFIGURATION.md](CONFIGURATION.md) for configuration behavior;
- [README.md](README.md) for schemas, examples, and the complete documentation index.
40 changes: 21 additions & 19 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Submit declarations during `InterModEnqueueEvent`:

```java
WorldgenProvider provider = WorldgenProvider.builder("examplemod", 1)
.rock(new ResourceLocation("examplemod", "slate"), GeologyFamily.METAMORPHIC, rock -> rock
.rock(Identifier.parse("examplemod:slate"), GeologyFamily.METAMORPHIC, rock -> rock
.depth(12, 36)
.weight(1.2)
.oreReplaceable(true))
Expand All @@ -33,7 +33,7 @@ For a complete ore-only Java example, including dimensions, height curves,
patterns, and host tags, see `DEVELOPER_GUIDE.md`.

Definitions are immutable after `build()`. Registry references remain
`ResourceLocation` values until OreSpawn validates and bakes them. Provider
`Identifier` values until OreSpawn validates and bakes them. Provider
messages are processed through Forge IMC and frozen at load completion; direct
cross-mod mutation during parallel setup is unsupported.

Expand All @@ -56,17 +56,17 @@ FormationDefinition formations = FormationDefinition.builder()
.waviness(FormationPreset.LARGE)
.build();
FluidDepositDefinition brine = FluidDepositDefinition.builder(
new ResourceLocation("examplemod", "fluid_deposit/brine"),
new ResourceLocation("examplemod", "brine"))
.dimension(new ResourceLocation("minecraft", "overworld"), placement -> placement
Identifier.parse("examplemod:fluid_deposit/brine"),
Identifier.parse("examplemod:brine"))
.dimension(Identifier.parse("minecraft:overworld"), placement -> placement
.yRange(-48, 32)
.attempts(0.05)
.radius(4, 10)
.verticalRadius(2, 4)
.maxLobes(3)
.minSolidCover(2)
.minSolidShell(1)
.hostTag(new ResourceLocation("minecraft", "stone_ore_replaceables")))
.hostTag(Identifier.parse("minecraft:stone_ore_replaceables")))
.build();

WorldgenProvider provider = WorldgenProvider.builder("examplemod", 1)
Expand All @@ -83,29 +83,29 @@ provides a small optional convenience for cloning a known biome:
```java
RegistryObject<Biome> candyPlains = OreSpawnBiomes.copyAndRegister(
BIOMES, "candy_plains",
() -> ForgeRegistries.BIOMES.getValue(new ResourceLocation("minecraft", "plains")),
() -> ForgeRegistries.BIOMES.getValue(Identifier.parse("minecraft:plains")),
builder -> builder.temperature(0.8F).downfall(0.4F));
```

Then declare placement and materials through the same provider:

```java
WorldgenProvider provider = WorldgenProvider.builder("examplemod", 1)
.biomePalette(new ResourceLocation("examplemod", "overworld"),
new ResourceLocation("minecraft", "overworld"), palette -> palette
.biomePalette(Identifier.parse("examplemod:overworld"),
Identifier.parse("minecraft:overworld"), palette -> palette
.mode(BiomePlacementMode.REPLACE)
.scope(BiomeReplacementScope.MINECRAFT_ONLY)
.regionSize(BiomeRegionSize.LARGE)
.coverage(1.0)
.fallbackWeight(0.0)
.biome(new ResourceLocation("examplemod", "candy_plains"), biome -> biome
.biome(Identifier.parse("examplemod:candy_plains"), biome -> biome
.weight(3.0)
.similarBiome(new ResourceLocation("minecraft", "plains"))))
.dimensionMaterials(new ResourceLocation("examplemod", "overworld_materials"),
new ResourceLocation("minecraft", "overworld"), materials -> materials
.defaultFluid(new ResourceLocation("examplemod", "lemonade"))
.snowBlock(new ResourceLocation("examplemod", "icing"))
.iceBlock(new ResourceLocation("examplemod", "frozen_lemonade")))
.similarBiome(Identifier.parse("minecraft:plains"))))
.dimensionMaterials(Identifier.parse("examplemod:overworld_materials"),
Identifier.parse("minecraft:overworld"), materials -> materials
.defaultFluid(Identifier.parse("examplemod:lemonade"))
.snowBlock(Identifier.parse("examplemod:icing"))
.iceBlock(Identifier.parse("examplemod:frozen_lemonade")))
.build();
```

Expand All @@ -125,9 +125,11 @@ OreSpawnApi.createSampler(server.overworld()).ifPresent(sampler -> {
});
```

`sampleColumn` performs one biome/geome classification and reuses it for every
Y query. Sampling is read-only and is intended for gameplay decisions,
diagnostics, and compatible generation outside OreSpawn's block loops.
`sampleColumn` performs one biome/dominant-geome classification and reuses its
transition scores for every Y query. `rockAt` therefore matches Stable Layers
when a close geome transition is staggered by layer. Sampling is read-only and
is intended for gameplay decisions, diagnostics, and compatible generation
outside OreSpawn's block loops.
Callbacks inside OreSpawn generation loops are intentionally unsupported.

Custom pattern mods create a Forge `DeferredRegister<OrePatternType>` using
Expand Down
12 changes: 11 additions & 1 deletion docs/BIOMES.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ applying small changes. This is useful for a simple content mod:
```java
RegistryObject<Biome> candyPlains = OreSpawnBiomes.copyAndRegister(
BIOMES, "candy_plains",
() -> ForgeRegistries.BIOMES.getValue(new ResourceLocation("minecraft", "plains")),
() -> ForgeRegistries.BIOMES.getValue(Identifier.parse("minecraft:plains")),
builder -> builder.temperature(0.8F).downfall(0.4F));
```

Expand All @@ -118,6 +118,16 @@ Biome surfaces support:
- `ceiling_block`: optional underside material;
- `filler_depth`: 0-16 blocks.

Provider surfaces run during `LOCAL_MODIFICATIONS`: after Minecraft has built
base surfaces and lakes, but before structures and vegetation. That ordering
lets OreSpawn replace the actual exposed ground while preserving later trees,
plants, authored structures, and block entities. In ceiling dimensions,
`ceiling_block` applies to the roof underside and does not replace the roof top.

Surface correction is generation-only. Installing or updating OreSpawn does
not rewrite already generated chunks; travel into new terrain to see a changed
provider surface definition.

Dimension materials support the ordinary aquifer fluid, a deep aquifer fluid
and threshold, and replacements for vanilla snow and ice. OreSpawn converts
weather products in loaded chunks and around players; it does not replace every
Expand Down
Loading
Loading