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
15 changes: 15 additions & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
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.
* 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
* Verify both public biome-registration helpers on Forge 45
* Confirm Minecraft 1.19.4 ResourceLocation 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
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,20 @@ exported to `config/orespawn-guide/` without overwriting existing files.
Use Java 17 from the repository root:

```powershell
.\gradlew.bat test processResources build javadoc --no-daemon
.\gradlew.bat clean build javadoc --no-daemon
.\gradlew.bat genEclipseRuns eclipse --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, then reopens and checks the exact saved world. The fixture is not
included in OreSpawn's published jars.

Run both `genEclipseRuns` and `eclipse` after importing or refreshing this
ForgeGradle 6 project in Eclipse.

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
112 changes: 111 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,16 @@ minecraft {
// Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources.
args '--mod', mod_id, '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/')
}

['Fresh', 'Reload'].each { String phase ->
create("surfaceIntegration${phase}") {
parent runs.server
workingDirectory layout.buildDirectory.dir('surface-integration-run').get().asFile
property 'forge.logging.console.level', 'info'
property 'surfaceprobe.integrationPhase', phase.toLowerCase(Locale.ROOT)
args '--nogui'
}
}
}
}

Expand Down Expand Up @@ -291,6 +301,84 @@ 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(17)
})
options.release = 17
options.encoding = 'UTF-8'
}

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

def surfaceIntegrationRunDirectory = layout.buildDirectory.dir('surface-integration-run')
def prepareSurfaceIntegrationTest = tasks.register('prepareSurfaceIntegrationTest') {
dependsOn surfaceIntegrationTestModJar
doLast {
File runDirectory = surfaceIntegrationRunDirectory.get().asFile
delete runDirectory
runDirectory.mkdirs()
copy {
from surfaceIntegrationTestModJar.flatMap { it.archiveFile }
into surfaceIntegrationRunDirectory.map { it.dir('mods') }
}
new File(runDirectory, 'server.properties').setText('''\
level-name=surface-integration-world
level-seed=0
level-type=minecraft:normal
online-mode=false
allow-nether=true
generate-structures=false
spawn-protection=0
max-tick-time=-1
''', 'UTF-8')
new File(runDirectory, 'eula.txt').setText('eula=true\n', 'UTF-8')
}
}

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(
'surface-integration-world/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
}

// Keep every Eclipse launch input on one physical Gradle cache. Mixing Buildship's
// cache with a command-line cache duplicates named Java modules such as FML and Mixin.
def syncEclipseRunClasspaths = tasks.register('syncEclipseRunClasspaths') {
Expand Down Expand Up @@ -463,6 +551,7 @@ def syncEclipseRunClasspaths = tasks.register('syncEclipseRunClasspaths') {
}

int changedLaunches = 0
int changedTestExclusions = 0
if (eclipseLaunchDir.isDirectory()) {
File eclipseClasses = file('bin/main').canonicalFile
String modClasses = "${mod_id}%%${eclipseClasses.absolutePath}"
Expand All @@ -480,6 +569,27 @@ def syncEclipseRunClasspaths = tasks.register('syncEclipseRunClasspaths') {
/<mapEntry key="MOD_CLASSES" value="[^"]*"\/>/) {
"<mapEntry key=\"MOD_CLASSES\" value=\"${modClasses}\"/>"
}
String excludeTestKey = 'org.eclipse.jdt.launching.ATTR_EXCLUDE_TEST_CODE'
String excludeTestAttribute =
"<booleanAttribute key=\"${excludeTestKey}\" value=\"true\"/>"
String beforeTestExclusion = synced
if (synced.contains("key=\"${excludeTestKey}\"")) {
synced = synced.replaceFirst(
/<booleanAttribute key="org\.eclipse\.jdt\.launching\.ATTR_EXCLUDE_TEST_CODE" value="[^"]*"\/>/,
excludeTestAttribute)
} else {
int launchHeaderEnd = synced.indexOf('\n', synced.indexOf('<launchConfiguration'))
if (launchHeaderEnd < 0) {
throw new GradleException("Malformed Eclipse Java launch configuration: ${launchFile}")
}
String lineSeparator = synced.contains('\r\n') ? '\r\n' : '\n'
synced = "${synced.substring(0, launchHeaderEnd + 1)}" +
" ${excludeTestAttribute}${lineSeparator}" +
synced.substring(launchHeaderEnd + 1)
}
if (beforeTestExclusion != synced) {
changedTestExclusions++
}
if (original != synced) {
launchFile.setText(synced, 'UTF-8')
changedLaunches++
Expand All @@ -505,7 +615,7 @@ def syncEclipseRunClasspaths = tasks.register('syncEclipseRunClasspaths') {
changedLaunchGroups++
}
}
logger.lifecycle("Eclipse runtime paths aligned with ${eclipseCache} (${stableClasspaths.size()} Forge classpaths, ${changedProjectClasspath} project classpath, ${changedResourceExclusions} metadata exclusion, ${changedPrepareLaunches} prepare launches, ${changedLaunches} slim launches, ${changedLaunchGroups} direct project launches updated)")
logger.lifecycle("Eclipse runtime paths aligned with ${eclipseCache} (${stableClasspaths.size()} Forge classpaths, ${changedProjectClasspath} project classpath, ${changedResourceExclusions} metadata exclusion, ${changedPrepareLaunches} prepare launches, ${changedLaunches} slim launches, ${changedTestExclusions} test exclusions, ${changedLaunchGroups} direct project launches updated)")
}
}

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.
10 changes: 10 additions & 0 deletions docs/BIOMES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
15 changes: 15 additions & 0 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,21 @@ When a control is `custom`, its value comes from `formations.custom`:
| `edge_octaves` | 1-8 | Number of boundary-detail scales |
| `continuity` | 0-1 | Proportion of formations retaining global identity |

For Stable Layers, the Edge Detail presets use these
`wavelength / amplitude / octaves` values:

| Preset | Edge detail |
|---|---:|
| Tiny | `48 / 4 / 1` |
| Small | `64 / 12 / 2` |
| Average | `96 / 24 / 3` |
| Large | `128 / 48 / 4` |
| Huge | `192 / 96 / 5` |

Average is calibrated to retain visible variation at later layer contacts.
Custom profiles keep their explicit values; these numbers are only used by the
named presets and as defaults for new Custom settings.

Cyano settings use `cyano.geome_size` (4-32767),
`cyano.rock_layer_noise` (1-32767), and `cyano.rock_layer_thickness` (1-255).
They are ignored by Sky.
Expand Down
9 changes: 9 additions & 0 deletions docs/DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,3 +197,12 @@ bounded ore or bedrock retrogen is enabled.
and without compatibility mods.
6. Confirm the provider appears in `/orespawn status`.
7. Test a new world; profile edits do not rewrite already generated terrain.

OreSpawn's own standard `check` lifecycle includes a consumer-style surface
integration test. A separate test provider creates independently marked
Grass/Dirt, underwater, filler, and roof columns in open and ceiling
normal-noise dimensions. The gate verifies biome and chunk edges, late tree,
vegetation, structure and chest sentinels, the roof underside, and exact save
reload behavior. Run `gradlew check` (or `gradlew build`, which includes it)
before publishing any change to biome registration, palettes, surfaces,
feature ordering, height handling, or profile persistence.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ mod_name=MMD OreSpawn
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
mod_license=LGPL-2.1
# The mod version. See https://semver.org/
mod_version=4.0.4
mod_version=4.0.6
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
# This should match the base package used for the mod sources.
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
Expand Down
Loading
Loading