Skip to content

Commit 2bafa2c

Browse files
committed
Fix Stable Layers rock height eligibility
1 parent 9b720ee commit 2bafa2c

11 files changed

Lines changed: 182 additions & 35 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ jobs:
5656
if-no-files-found: error
5757
retention-days: 30
5858
path: |
59-
build/libs/OreSpawn-4.0.9.120011.jar
60-
build/libs/OreSpawn-4.0.9.120011-sources.jar
61-
build/libs/OreSpawn-4.0.9.120011-javadoc.jar
59+
build/libs/OreSpawn-4.0.10.120011.jar
60+
build/libs/OreSpawn-4.0.10.120011-sources.jar
61+
build/libs/OreSpawn-4.0.10.120011-javadoc.jar
6262
build/release/SHA256SUMS
6363
CHANGELOG.txt
6464

‎CHANGELOG.txt‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
Version 4.0.10.120011
2+
3+
* Evaluate Stable Layers rock min_y and max_y bounds against actual world Y
4+
instead of the vertically shifted formation coordinate.
5+
* Preserve the shifted formation identity for layer, family, and rock choice
6+
while preventing vanilla Stone fallback near dimension floors and ceilings.
7+
* Apply the correction only while generating new chunks; existing chunks and
8+
saved profiles remain unchanged.
9+
110
Version 4.0.9.120011
211

312
* Replace provider-declared natural terrain hosts during the existing geology

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ End" policy used by mods such as Base Metals.
1717
This is not the unrelated mod that adds mobs and dimensions under the same
1818
name.
1919

20-
This branch builds target-qualified version `4.0.9.120011`: the OreSpawn 4.0.9
20+
This branch builds target-qualified version `4.0.10.120011`: the OreSpawn 4.0.10
2121
feature set for Minecraft 1.20.1 and Forge. See the
2222
[versioning policy](docs/VERSIONS.md) for the encoding and release convention.
2323

‎build.gradle‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,7 @@ def preparedReleaseDir = providers.gradleProperty('preparedReleaseDir')
703703
tasks.register('verifyReleaseConfiguration') {
704704
group = 'verification'
705705
doLast {
706-
if (project.mod_version != '4.0.9.120011'
706+
if (project.mod_version != '4.0.10.120011'
707707
|| project.mod_group != expectedMavenGroup
708708
|| project.minecraft_version != '1.20.1'
709709
|| project.forge_version != '47.4.10'
@@ -717,9 +717,9 @@ tasks.register('verifyReleaseConfiguration') {
717717
throw new GradleException('Unexpected dispatcher or Java target metadata')
718718
}
719719
List<String> expectedPublicArtifacts = [
720-
'OreSpawn-4.0.9.120011.jar',
721-
'OreSpawn-4.0.9.120011-sources.jar',
722-
'OreSpawn-4.0.9.120011-javadoc.jar'
720+
'OreSpawn-4.0.10.120011.jar',
721+
'OreSpawn-4.0.10.120011-sources.jar',
722+
'OreSpawn-4.0.10.120011-javadoc.jar'
723723
]
724724
if (base.archivesName.get() != expectedMavenArtifact
725725
|| expectedReleaseFiles.get().collect { it.toString() } != expectedPublicArtifacts) {
@@ -736,7 +736,7 @@ tasks.register('verifyReleaseConfiguration') {
736736
'src/main/java/zone/moddev/mc/orespawn/worldgen/LegacyMineralogyProfileMigration.java',
737737
'README.md', 'CHANGELOG.txt'
738738
].each { path ->
739-
if (!file(path).getText('UTF-8').contains('4.0.9.120011')) {
739+
if (!file(path).getText('UTF-8').contains('4.0.10.120011')) {
740740
throw new GradleException("Release identity missing from ${path}")
741741
}
742742
}

‎docs/VERSIONS.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ Examples:
5858
| 1.17.1 | Forge | `117011` | `4.0.9.117011` |
5959
| 1.18.2 | Forge | `118021` | `4.0.10.118021` |
6060
| 1.19.4 | Forge | `119041` | `4.0.10.119041` |
61-
| 1.20.1 | Forge | `120011` | `4.0.9.120011` |
61+
| 1.20.1 | Forge | `120011` | `4.0.10.120011` |
6262
| 1.20.6 | Forge | `120061` | `4.0.6.120061` |
6363
| 1.21.11 | Forge | `121111` | `4.0.6.121111` |
6464
| 26.1.2 | Forge | `2601021` | `4.0.6.2601021` |
@@ -152,7 +152,9 @@ If a different branch later receives a shared fix, it uses the next unused
152152
Bug number, such as Forge 1.14.4's `4.0.8.114041`, even though the 4.0.7 repair
153153
was not applicable there. Forge 1.15.2 through 1.20.1 then advanced to their
154154
target-qualified 4.0.9 releases for the provider terrain-host ordering repair.
155-
A branch may therefore legitimately skip functional version numbers.
155+
Forge 1.18.2 through 1.20.1 then advanced to their target-qualified 4.0.10
156+
releases for the distinct Stable Layers actual-height eligibility repair. A
157+
branch may therefore legitimately skip functional version numbers.
156158

157159
This provides three useful guarantees:
158160

‎gradle.properties‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ mcp_version=20230612.114412
1919
mod_id=orespawn
2020
mod_name=MMD OreSpawn
2121
mod_license=LGPL-2.1
22-
mod_version=4.0.9.120011
22+
mod_version=4.0.10.120011
2323
mod_group=zone.moddev.mc.orespawn
2424
mod_authors=SkyBlade1978, dshadowwolf, the MMD Team
2525
mod_description=Configurable, provider-driven terrain, ore, and deposit generation.

‎src/main/java/zone/moddev/mc/orespawn/worldgen/BakedGeomeConfig.java‎

Lines changed: 101 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public final class BakedGeomeConfig {
3535
private final Map<Biome, double[]> biomeWeights;
3636
private final Map<ResourceLocation, double[]> biomeWeightsById;
3737
private final double[] fallbackWeights;
38+
private final RockEntry[] rocks;
3839
private final BlockState[] rockStates;
3940
private final Set<Block> sedimentaryBlocks;
4041
private final Set<Block> oreReplaceableBlocks;
@@ -45,6 +46,9 @@ public final class BakedGeomeConfig {
4546
private WeightedBlockPicker[][][] legacyRockPickers;
4647
private byte[] stableFamilyChoices;
4748
private int[] stableRockChoices;
49+
private int[][] familyRockIndexes;
50+
private double[][][] stableRockLogWeights;
51+
private double[][][] stableRockPriorities;
4852

4953
BakedGeomeConfig(GeomeDefinition[] geomes, double geomeScale, double biomeInfluence,
5054
double regionalNoiseInfluence, double boundaryNoiseInfluence, Map<Biome, double[]> biomeWeights,
@@ -72,6 +76,7 @@ public final class BakedGeomeConfig {
7276
noiseOffsetZ[i] = -((i + 1) * 6151);
7377
}
7478

79+
this.rocks = rocks.clone();
7580
rockStates = new BlockState[rocks.length];
7681
for (int i = 0; i < rocks.length; i++) {
7782
rockStates[i] = rocks[i].state;
@@ -152,15 +157,69 @@ RockFamily pickFamily(int geomeIndex, int y, int formationValue, int diversitySl
152157
return RockFamily.SEDIMENTARY;
153158
}
154159

160+
RockFamily pickStableFamilyAtWorldY(int geomeIndex, int worldY, int formationY,
161+
int formationValue, int diversitySlot) {
162+
RockFamily preferred = pickFamily(geomeIndex, formationY, formationValue, diversitySlot);
163+
if (hasEligibleStableRock(geomeIndex, preferred, worldY, formationY)) {
164+
return preferred;
165+
}
166+
167+
int bucket = formationValue & 0xFF;
168+
int boundedFormationY = clampStableValue(formationY);
169+
double bestScore = Double.NEGATIVE_INFINITY;
170+
RockFamily bestFamily = preferred;
171+
for (RockFamily family : RockFamily.values()) {
172+
if (!hasEligibleStableRock(geomeIndex, family, worldY, formationY)) {
173+
continue;
174+
}
175+
double weight = Math.pow(geomes[geomeIndex].familyWeights[family.ordinal()], 2.5D)
176+
* familyDepthWeight(family, boundedFormationY);
177+
if (weight <= 0.0D) {
178+
continue;
179+
}
180+
double score = Math.log(weight) + gumbelPriority(bucket, geomeIndex, family.ordinal(),
181+
isStableBucket(bucket, -1), 0x6A09E667F3BCC909L);
182+
if (score > bestScore) {
183+
bestScore = score;
184+
bestFamily = family;
185+
}
186+
}
187+
return bestFamily;
188+
}
189+
155190
public BlockState pickRock(int geomeIndex, RockFamily family, int y, int formationValue) {
156191
if (formations.usesStableLayers()) {
157-
int index = stableRockIndex(geomeIndex, family.ordinal(), clampStableY(y), formationValue & 0xFF);
158-
int rockIndex = stableRockChoices[index];
159-
return rockIndex < 0 ? FALLBACK : rockStates[rockIndex];
192+
return pickStableRockAtWorldY(geomeIndex, family, y, y, formationValue);
160193
}
161194
return legacyRockPickers[geomeIndex][family.ordinal()][clampLegacyY(y)].pick(formationValue);
162195
}
163196

197+
BlockState pickStableRockAtWorldY(int geomeIndex, RockFamily family, int worldY,
198+
int formationY, int formationValue) {
199+
int yIndex = clampStableY(formationY);
200+
int bucket = formationValue & 0xFF;
201+
int choiceIndex = stableRockIndex(geomeIndex, family.ordinal(), yIndex, bucket);
202+
int selectedRock = stableRockChoices[choiceIndex];
203+
if (isEligibleStableRock(geomeIndex, selectedRock, worldY, yIndex)) {
204+
return rockStates[selectedRock];
205+
}
206+
207+
double bestScore = Double.NEGATIVE_INFINITY;
208+
int bestRock = -1;
209+
for (int rockIndex : familyRockIndexes[family.ordinal()]) {
210+
if (!isEligibleStableRock(geomeIndex, rockIndex, worldY, yIndex)) {
211+
continue;
212+
}
213+
double score = stableRockLogWeights[geomeIndex][rockIndex][yIndex]
214+
+ stableRockPriorities[geomeIndex][rockIndex][bucket];
215+
if (score > bestScore) {
216+
bestScore = score;
217+
bestRock = rockIndex;
218+
}
219+
}
220+
return bestRock < 0 ? FALLBACK : rockStates[bestRock];
221+
}
222+
164223
public String geomeName(int geomeIndex) {
165224
return geomes[geomeIndex].name;
166225
}
@@ -283,9 +342,9 @@ private void buildStablePickers(RockEntry[] rocks) {
283342
stableRockChoices = new int[geomes.length * RockFamily.values().length * HEIGHT * FORMATION_BUCKETS];
284343
Arrays.fill(stableRockChoices, -1);
285344
int familyCount = RockFamily.values().length;
286-
int[][] familyRockIndexes = groupRockIndexes(rocks);
287-
double[][][] rockLogWeights = new double[geomes.length][rocks.length][HEIGHT];
288-
double[][][] rockPriorities = new double[geomes.length][rocks.length][FORMATION_BUCKETS];
345+
familyRockIndexes = groupRockIndexes(rocks);
346+
stableRockLogWeights = new double[geomes.length][rocks.length][HEIGHT];
347+
stableRockPriorities = new double[geomes.length][rocks.length][FORMATION_BUCKETS];
289348
double[][][] familyLogWeights = new double[geomes.length][familyCount][HEIGHT];
290349
double[][][] familyWeights = new double[geomes.length][familyCount][HEIGHT];
291350
double[][][] familyPriorities = new double[geomes.length][familyCount][FORMATION_BUCKETS];
@@ -294,14 +353,13 @@ private void buildStablePickers(RockEntry[] rocks) {
294353
for (int rockIndex = 0; rockIndex < rocks.length; rockIndex++) {
295354
RockEntry rock = rocks[rockIndex];
296355
for (int y = MIN_Y; y <= MAX_Y; y++) {
297-
double rawWeight = y < rock.minY || y > rock.maxY ? 0.0D
298-
: rock.weight * rock.geomeWeights[geome]
299-
* depthWeight(y, rock.depthPeak, rock.depthSpread);
300-
rockLogWeights[geome][rockIndex][y - MIN_Y] = rawWeight > 0.0D
356+
double rawWeight = rock.weight * rock.geomeWeights[geome]
357+
* depthWeight(y, rock.depthPeak, rock.depthSpread);
358+
stableRockLogWeights[geome][rockIndex][y - MIN_Y] = rawWeight > 0.0D
301359
? Math.log(rawWeight) : Double.NEGATIVE_INFINITY;
302360
}
303361
for (int bucket = 0; bucket < FORMATION_BUCKETS; bucket++) {
304-
rockPriorities[geome][rockIndex][bucket] = gumbelPriority(bucket, geome, rockIndex,
362+
stableRockPriorities[geome][rockIndex][bucket] = gumbelPriority(bucket, geome, rockIndex,
305363
isStableBucket(bucket, rock.family.ordinal()),
306364
0xBB67AE8584CAA73BL ^ ((long) rock.family.ordinal() << 32));
307365
}
@@ -313,7 +371,9 @@ private void buildStablePickers(RockEntry[] rocks) {
313371
int yIndex = y - MIN_Y;
314372
boolean available = false;
315373
for (int rockIndex : familyRockIndexes[familyIndex]) {
316-
if (rockLogWeights[geome][rockIndex][yIndex] != Double.NEGATIVE_INFINITY) {
374+
RockEntry rock = rocks[rockIndex];
375+
if (y >= rock.minY && y <= rock.maxY
376+
&& stableRockLogWeights[geome][rockIndex][yIndex] != Double.NEGATIVE_INFINITY) {
317377
available = true;
318378
break;
319379
}
@@ -363,8 +423,12 @@ private void buildStablePickers(RockEntry[] rocks) {
363423
double bestRockScore = Double.NEGATIVE_INFINITY;
364424
int bestRock = -1;
365425
for (int rockIndex : familyRockIndexes[familyIndex]) {
366-
double rockScore = rockLogWeights[geome][rockIndex][yIndex]
367-
+ rockPriorities[geome][rockIndex][bucket];
426+
RockEntry rock = rocks[rockIndex];
427+
if (y < rock.minY || y > rock.maxY) {
428+
continue;
429+
}
430+
double rockScore = stableRockLogWeights[geome][rockIndex][yIndex]
431+
+ stableRockPriorities[geome][rockIndex][bucket];
368432
if (rockScore > bestRockScore) {
369433
bestRockScore = rockScore;
370434
bestRock = rockIndex;
@@ -377,6 +441,25 @@ private void buildStablePickers(RockEntry[] rocks) {
377441
}
378442
}
379443

444+
private boolean hasEligibleStableRock(int geomeIndex, RockFamily family, int worldY, int formationY) {
445+
int yIndex = clampStableY(formationY);
446+
for (int rockIndex : familyRockIndexes[family.ordinal()]) {
447+
if (isEligibleStableRock(geomeIndex, rockIndex, worldY, yIndex)) {
448+
return true;
449+
}
450+
}
451+
return false;
452+
}
453+
454+
private boolean isEligibleStableRock(int geomeIndex, int rockIndex, int worldY, int formationYIndex) {
455+
if (rockIndex < 0) {
456+
return false;
457+
}
458+
RockEntry rock = rocks[rockIndex];
459+
return worldY >= rock.minY && worldY <= rock.maxY
460+
&& stableRockLogWeights[geomeIndex][rockIndex][formationYIndex] != Double.NEGATIVE_INFINITY;
461+
}
462+
380463
private void fillBalancedFamilyCycle(int geome, int yIndex, int bucket,
381464
double[][][] familyWeights, double[][][] familyPriorities,
382465
int[] quotas, int[] remaining, double[] remainders, boolean[] bonusAwarded) {
@@ -631,6 +714,10 @@ private static int clampStableY(int y) {
631714
return Math.max(MIN_Y, Math.min(MAX_Y, y)) - MIN_Y;
632715
}
633716

717+
private static int clampStableValue(int y) {
718+
return Math.max(MIN_Y, Math.min(MAX_Y, y));
719+
}
720+
634721
private static int clampLegacyY(int y) {
635722
return Math.max(0, Math.min(LEGACY_MAX_Y, y));
636723
}

‎src/main/java/zone/moddev/mc/orespawn/worldgen/GeomeGeology.java‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,6 @@ private boolean replaceStableColumn(ChunkAccess chunk, BlockPos.MutableBlockPos
143143
int layerStart = layerIndex * layerThickness;
144144
int layerGeome = pickStableLayerGeome(geomeScores, geomeIndex, secondGeome,
145145
layerIndex, geomeTransitionPhase);
146-
BlockState replacement = pickStableReplacement(layerGeome, formationRegion, layerIndex);
147146
boolean changed = false;
148147
cursor.set(x, surfaceY, z);
149148

@@ -154,12 +153,12 @@ private boolean replaceStableColumn(ChunkAccess chunk, BlockPos.MutableBlockPos
154153
layerStart -= layerThickness;
155154
layerGeome = pickStableLayerGeome(geomeScores, geomeIndex, secondGeome,
156155
layerIndex, geomeTransitionPhase);
157-
replacement = pickStableReplacement(layerGeome, formationRegion, layerIndex);
158156
}
159157
cursor.setY(y);
160158
if (terrain.isReplaceable(chunk.getBlockState(cursor))
161159
&& chunk.getBlockEntity(cursor) == null) {
162-
chunk.setBlockState(cursor, replacement, false);
160+
chunk.setBlockState(cursor,
161+
pickStableReplacement(layerGeome, formationRegion, layerIndex, y), false);
163162
changed = true;
164163
}
165164
}
@@ -252,7 +251,7 @@ private net.minecraft.world.level.block.state.BlockState pickReplacement(int geo
252251
int stratum = baseRockValue + y;
253252
int layerIndex = Math.floorDiv(stratum, layerThickness);
254253
if (stableLayers) {
255-
return pickStableReplacement(geomeIndex, formationRegion, layerIndex);
254+
return pickStableReplacement(geomeIndex, formationRegion, layerIndex, y);
256255
}
257256

258257
int layerY = y + (layerThickness / 2) - Math.floorMod(stratum, layerThickness);
@@ -262,7 +261,7 @@ private net.minecraft.world.level.block.state.BlockState pickReplacement(int geo
262261
return config.pickRock(geomeIndex, family, layerY, rockHash);
263262
}
264263

265-
private BlockState pickStableReplacement(int geomeIndex, long formationRegion, int layerIndex) {
264+
private BlockState pickStableReplacement(int geomeIndex, long formationRegion, int layerIndex, int worldY) {
266265
// A dipping or uplifted layer keeps the depth identity it had in stratum space.
267266
int formationY = (layerIndex * layerThickness) + (layerThickness / 2);
268267
int layerBucket = layerIndex & 0xFF;
@@ -288,8 +287,9 @@ private BlockState pickStableReplacement(int geomeIndex, long formationRegion, i
288287
// from collapsing onto one exact rock.
289288
rockBucket ^= LITHOLOGY_ROCK_SALTS[familySlot];
290289
}
291-
RockFamily family = config.pickFamily(geomeIndex, formationY, familyBucket, familySlot);
292-
return config.pickRock(geomeIndex, family, formationY, rockBucket);
290+
RockFamily family = config.pickStableFamilyAtWorldY(geomeIndex, worldY, formationY,
291+
familyBucket, familySlot);
292+
return config.pickStableRockAtWorldY(geomeIndex, family, worldY, formationY, rockBucket);
293293
}
294294

295295
int stratumOffsetAt(int x, int z) {

‎src/main/java/zone/moddev/mc/orespawn/worldgen/LegacyConfigMigrator.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ private static void writeReport(Path config, List<String> lines) {
358358

359359
private static void writeUpgradeReport(Path config, int imported, List<String> detail) {
360360
List<String> lines = new ArrayList<>();
361-
lines.add("OreSpawn 4.0.9.120011 Upgrade Report");
361+
lines.add("OreSpawn 4.0.10.120011 Upgrade Report");
362362
lines.add("================================");
363363
lines.add("");
364364
lines.add("RESULT: Legacy OreSpawn settings were imported into the OS4 profile.");

‎src/main/java/zone/moddev/mc/orespawn/worldgen/LegacyMineralogyProfileMigration.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ private static void writeUpgradeReport(Path worldRoot, Path configPath,
204204
Path report = worldRoot.resolve("serverconfig/orespawn-upgrade-report.txt");
205205
List<String> missing = missingBlocks(igneous, metamorphic, sedimentary);
206206
List<String> lines = new ArrayList<>();
207-
lines.add("OreSpawn 4.0.9.120011 Upgrade Report");
207+
lines.add("OreSpawn 4.0.10.120011 Upgrade Report");
208208
lines.add("================================");
209209
lines.add("");
210210
lines.add("RESULT: Existing Mineralogy " + identity.version + " world detected.");

0 commit comments

Comments
 (0)