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
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,9 @@ jobs:
if-no-files-found: error
retention-days: 30
path: |
build/libs/OreSpawn-4.1.3.112021.jar
build/libs/OreSpawn-4.1.3.112021-sources.jar
build/libs/OreSpawn-4.1.3.112021-javadoc.jar
build/libs/OreSpawn-4.1.6.112021.jar
build/libs/OreSpawn-4.1.6.112021-sources.jar
build/libs/OreSpawn-4.1.6.112021-javadoc.jar
build/release/SHA256SUMS
CHANGELOG.txt

Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
Version 4.1.6.112021

* Let Java providers select an exact ore host, such as plain stone without
granite. Whole-block selectors still work as before.
* Keep imported ore rules and saved choices when moving a world from 1.10.2
or 1.11.2. Back up the profile before removing an untouched duplicate and
leave edited rules for review.
* Choose sulfur and lithium defaults after all mods have loaded so Base
Electric can share the same veins. Existing worlds keep their choices.
* Use the correct Austrian German locale and give Pirate Speak its own menu
and guide text.

Version 4.1.3.112021

* Apply hybrid and replacement background limits to imported built-in ore
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ 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.

This branch builds target-qualified version `4.1.3.112021`: the OreSpawn 4.1.3
This branch builds target-qualified version `4.1.6.112021`: the OreSpawn 4.1.6
feature set for Minecraft 1.12.2 and Forge. It adds stable generation identity,
non-loading geology sampling, per-resource background control, existing-world
provider merge policy, an Ore Sources arbiter for equivalent provider ores, a
Expand Down
6 changes: 6 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ exclusions. These methods work on both explicit `.dimension(...)` rules and
`.dimensionSelector(...)` fallbacks; built definitions and their returned
filter sets are immutable.

Use `.hostState(block, metadata)` to select one legacy ore host variant, or
`.hostState(block, metadata, weight)` to give it a replacement weight.
For example, `.hostState(new ResourceLocation("minecraft", "stone"), 0)`
accepts plain stone without accepting granite or diorite. The existing
`.hostBlock(...)` method still accepts every variant of a block.

Create one `BiomeRegistrar` during normal mod construction. It attaches to the
calling mod's event bus and defers biome factories until Forge's biome registry
event. `OreSpawnBiomes.copyAndRegister` clones a known biome without adding a
Expand Down
4 changes: 4 additions & 0 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ world. Restart after editing JSON by hand.
| `flat_bedrock` | object | Opt-in flat bedrock controls |
| `worldgen_aliases` | ID-to-ID object | Replacement output aliases resolved while baking |

Forge 1.12.2 applies the same gold settings to the normal and extra Mesa
gold passes. With vanilla ore management off and suppression disabled, both
passes keep their normal generation.

## Enumerated Values

| Setting | Accepted values |
Expand Down
10 changes: 10 additions & 0 deletions docs/MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ deterministic machine-readable detail.

## Provider-Aware OS3 Imports

If you move a world with an OreSpawn 1 import already saved by OreSpawn 4
on 1.10.2 or 1.11.2, its rule ID and settings are kept even without the
converted provider file. If an older build added a
second rule, OreSpawn removes it only when the saved rule still matches the
provider default and no custom ore-source selection depends on it. The
original profile is backed up before that repair. Edited or ambiguous
overlaps are left in place and listed under `legacy_ore_review` in the
provider's saved manifest and reported in the log. This does not change
ore blocks in chunks already generated.

When an OreSpawn 2/3 file is named for an installed provider, the migrator now
compares each converted primary output with that provider's ore declarations.
A unique output match is written under the provider's stable rule ID, allowing
Expand Down
11 changes: 6 additions & 5 deletions docs/PROVIDERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,12 @@ unambiguous. NetherMetals and EndMetals stay in their respective dimension
domains. DenseMetals is enrichment, not an interchangeable output, and
BaseSciences has no ordinary ore-source role.

Reviewed fresh-world conflicts start Balanced with all eligible output blocks.
Mineralogy, then BaseMinerals, then ElectricAdvantage is the initial placement
and Single-mode priority for sulfur; BaseMinerals, then ElectricAdvantage is the
corresponding lithium priority. No other conflict is automatically consolidated
without an explicit catalog row. Exact Ore Dictionary `oreX` inference recognizes the curated
Once mods have finished registering their ores, reviewed fresh-world conflicts
start Balanced with all eligible output blocks. Sulfur prefers Mineralogy,
then Base Minerals, then Base Electric (`baseelectric` or the older
`electricadvantage` ID) for placement. Lithium prefers Base Minerals, then
Base Electric. Existing choices are kept. Other conflicts need an explicit
catalog entry for automatic consolidation. Exact Ore Dictionary `oreX` inference recognizes the curated
sulfur/sulphur and aluminum/aluminium aliases but deliberately keeps Niter and
Saltpeter distinct. Multiple unrelated entries are marked for review rather
than fuzzy-matched from block names.
Expand Down
2 changes: 1 addition & 1 deletion docs/VERSIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Examples:
| Minecraft | Loader | Target | Example full OreSpawn version |
| --- | --- | ---: | --- |
| 1.10.2 | Forge | `110021` | `4.0.6.110021` |
| 1.12.2 | Forge | `112021` | `4.1.3.112021` |
| 1.12.2 | Forge | `112021` | `4.1.6.112021` |
| 1.13.2 | Forge | `113021` | `4.0.6.113021` |
| 1.20.6 | Forge | `120061` | `4.0.6.120061` |
| 1.21.11 | Forge | `121111` | `4.0.6.121111` |
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ mod_vendor=Minecraft Mod Development
mod_license=LGPL-2.1
mod_license_name=GNU Lesser General Public License, Version 2.1
mod_license_url=https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt
mod_version=4.1.3.112021
mod_version=4.1.6.112021
mod_group=zone.moddev.mc.orespawn
mod_authors=SkyBlade1978, dshadowwolf, the MMD Team
mod_description=Configurable, provider-driven terrain, ore, and deposit generation.
Expand Down
6 changes: 3 additions & 3 deletions gradle/release/artifacts.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ tasks.register('verifyReleaseConfiguration') {
description = 'Validates the target-qualified release, API, schemas, reports, and publishing identity.'

doLast {
if (project.mod_version != '4.1.3.112021'
if (project.mod_version != '4.1.6.112021'
|| project.mod_group != expectedMavenGroup) {
throw new GradleException("Unexpected OreSpawn release version: ${project.mod_version}")
}
Expand All @@ -108,8 +108,8 @@ tasks.register('verifyReleaseConfiguration') {
'src/main/java/zone/moddev/mc/orespawn/worldgen/LegacyOs3ProfileMigration.java',
'README.md', 'CHANGELOG.txt'
].each { path ->
if (!file(path).getText('UTF-8').contains('4.1.3.112021')) {
throw new GradleException("Authoritative release location does not contain 4.1.3.112021: ${path}")
if (!file(path).getText('UTF-8').contains('4.1.6.112021')) {
throw new GradleException("Authoritative release location does not contain 4.1.6.112021: ${path}")
}
}
if (!file('docs/API.md').getText('UTF-8').contains('orespawn@[4.0.6,5.0.0)')) {
Expand Down
24 changes: 13 additions & 11 deletions gradle/verification/packaged-forge.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,6 @@ def packagedSurfaceIntegrationTestModJar = tasks.named('renameSurfaceIntegration
// Release qualification also exercises the reobfuscated jar through Forge's
// packaged launcher. The official server jar and its library tree are
// machine-local inputs and are never copied into source control or artifacts.
def forge14UniversalRuntime = {
File runtime = new File(gradle.gradleUserHomeDir,
"caches/forge_gradle/maven_downloader/net/minecraftforge/forge/" +
"${minecraft_version}-${forge_version}/" +
"forge-${minecraft_version}-${forge_version}-universal.jar")
if (!runtime.isFile()) {
throw new GradleException("Forge 14 universal runtime is missing: ${runtime}")
}
runtime
}

def packagedForgeRunDirectory = file("${buildDir}/packaged-forge-runtime-run")
def packagedMinecraftServer = {
if (!project.hasProperty('packagedMinecraftServerJar')) {
Expand All @@ -29,6 +18,19 @@ def packagedMinecraftServer = {
}
server
}
def forge14UniversalRuntime = {
String name = "forge-${minecraft_version}-${forge_version}-universal.jar"
File installed = new File(packagedMinecraftServer().parentFile, name)
if (installed.isFile()) return installed
// Some older installations cached the universal jar here.
File cached = new File(gradle.gradleUserHomeDir,
"caches/forge_gradle/maven_downloader/net/minecraftforge/forge/" +
"${minecraft_version}-${forge_version}/${name}")
if (!cached.isFile()) {
throw new GradleException("Forge 14 universal runtime is missing: ${installed} or ${cached}")
}
cached
}
def packagedForgeLibraries = {
if (!project.hasProperty('packagedForgeLibrariesRoot')) {
throw new GradleException('packagedForgeLibrariesRoot is required for packagedForgeRuntimeTest')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,20 @@ public void onClientTick(TickEvent.ClientTickEvent event) {
case 2:
if (minecraft.currentScreen instanceof OreSpawnWorldSettingsScreen && editorFrames >= 2) {
worldSettingsOpened = true;
validateLanguage(minecraft);
validateCaptions((OreSpawnWorldSettingsScreen) minecraft.currentScreen);
validateExpectedDirectory(minecraft.currentScreen);
validateLongEditorRoundTrip(minecraft, minecraft.currentScreen);
if (Boolean.getBoolean("clientprobe.localeOnly")) {
Properties values = new Properties();
values.setProperty("language", minecraft.getLanguageManager().getCurrentLanguage().getLanguageCode());
try (FileOutputStream output = new FileOutputStream("client-locale-pass.properties")) {
values.store(output, "OreSpawn localized editor check");
}
minecraft.shutdown();
nextState(10);
break;
}
nextState(3);
}
break;
Expand Down Expand Up @@ -317,7 +328,7 @@ private void validateOreSourcesSingleScreen(GuiScreen parent, GeologyEditorSessi
if (widget instanceof CompactScrollList && widget.x < right) groupList = true;
if (widget instanceof CompactScrollList && widget.x >= right) outputList = true;
if (widget instanceof TextFieldWidget && widget.x < right) inlineAdministration = true;
if ("Back".equals(caption)) back = true;
if (I18n.format("button.orespawn.back").equals(caption)) back = true;
if (I18n.format("button.orespawn.show_all").equals(caption)) {
showAll = true;
showAllWidth = widget.width;
Expand Down Expand Up @@ -633,13 +644,25 @@ private Button nextNavigationButton(OreSpawnWorldSettingsScreen root) {
Button button = (Button) widget;
String caption = TextFormatting.getTextWithoutFormattingCodes(button.getMessage());
if (!attemptedButtons.add(caption)) continue;
String lower = caption.toLowerCase(java.util.Locale.ROOT);
if (lower.equals("done") || lower.equals("cancel") || lower.contains("recommended")) continue;
if (I18n.format("gui.done").equals(caption) || I18n.format("gui.cancel").equals(caption)
|| I18n.format("button.orespawn.recommended").equals(caption)) continue;
return button;
}
return null;
}

private static void validateLanguage(Minecraft minecraft) {
String expected = System.getProperty("clientprobe.language", "");
if (expected.isEmpty()) return;
String active = minecraft.getLanguageManager().getCurrentLanguage().getLanguageCode();
String caption = I18n.format("button.orespawn.recommended");
if (!expected.equals(active) || !caption.equals(System.getProperty("clientprobe.recommendedCaption"))) {
throw new IllegalStateException("Unexpected locale or caption: " + active + " / " + caption);
}
org.apache.logging.log4j.LogManager.getLogger(MODID)
.info("Client locale {} loaded caption '{}'", active, caption);
}

private static void validateCaptions(OreSpawnScreen screen) {
for (GuiButton widget : screen.buttons) {
if (widget instanceof CogButton || widget instanceof CompactScrollList) continue;
Expand Down Expand Up @@ -757,7 +780,7 @@ private static void pressDone(OreSpawnScreen screen) {
for (GuiButton widget : screen.buttons) {
if (!(widget instanceof Button)) continue;
String caption = TextFormatting.getTextWithoutFormattingCodes(((Button) widget).getMessage());
if ("done".equalsIgnoreCase(caption)) {
if (I18n.format("gui.done").equals(caption)) {
((Button) widget).press();
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -853,7 +853,7 @@ private static void writeHumanUpgradeReport(Path destination) throws IOException
}
}
List<String> lines = new ArrayList<>();
lines.add("OreSpawn 4.1.3.112021 Upgrade Report");
lines.add("OreSpawn 4.1.6.112021 Upgrade Report");
lines.add("================================");
lines.add("");
lines.add("RESULT: Legacy OreSpawn configuration was consumed and translated for OS4.");
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/zone/moddev/mc/orespawn/OreSpawn.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public class OreSpawn {

public static final String MODID = "orespawn";
public static final String NAME = "OreSpawn";
public static final String VERSION = "4.1.3.112021";
public static final String VERSION = "4.1.6.112021";

private static final Logger LOGGER = LogManager.getLogger();

Expand Down Expand Up @@ -86,7 +86,7 @@ public void preInit(FMLPreInitializationEvent event) {
@EventHandler
public void init(FMLInitializationEvent event) {
WorldgenIntegrationManager.initialize();
GeomeConfig.bake();
GeomeConfig.bakeBeforeLoadComplete();
logGeomeSampler();
DocumentationExporter.exportBundledGuide();
StoneReplacer.registerConfiguredFeature();
Expand All @@ -105,7 +105,7 @@ public void processInterMod(FMLInterModComms.IMCEvent event) {
public void postInit(FMLPostInitializationEvent event) {
WorldgenIntegrationManager.processImcMessages();
WorldgenIntegrationManager.freeze();
GeomeConfig.bake();
GeomeConfig.bakeBeforeLoadComplete();
refreshGenerationConfig();
WorldgenIntegrationManager.markFeatureReady();
}
Expand Down
39 changes: 37 additions & 2 deletions src/main/java/zone/moddev/mc/orespawn/api/WorldgenProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,7 @@ public static final class OreDimensionDefinition implements JsonDefinition {
private final Set<GeologyFamily> hostFamilies;
private final Map<ResourceLocation, Double> geomes;
private final Set<ResourceLocation> hostBlocks;
private final Map<ResourceLocation, Map<Integer, Double>> hostStates;
private final Set<ResourceLocation> hostTags;
private final Set<ResourceLocation> biomeIds;
private final Set<ResourceLocation> excludedBiomeIds;
Expand Down Expand Up @@ -596,6 +597,9 @@ private OreDimensionDefinition(Builder builder) {
hostFamilies = Collections.unmodifiableSet(new LinkedHashSet<>(builder.hostFamilies));
geomes = immutableMap(builder.geomes);
hostBlocks = immutableSet(builder.hostBlocks);
Map<ResourceLocation, Map<Integer, Double>> states = new LinkedHashMap<>();
builder.hostStates.forEach((block, variants) -> states.put(block, immutableMap(variants)));
hostStates = immutableMap(states);
hostTags = immutableSet(builder.hostTags);
biomeIds = immutableSet(builder.biomeIds);
excludedBiomeIds = immutableSet(builder.excludedBiomeIds);
Expand Down Expand Up @@ -630,6 +634,8 @@ private OreDimensionDefinition(Builder builder) {
public Set<GeologyFamily> hostFamilies() { return hostFamilies; }
public Map<ResourceLocation, Double> geomes() { return geomes; }
public Set<ResourceLocation> hostBlocks() { return hostBlocks; }
/** Exact legacy variants, separate from whole-block selectors. */
public Map<ResourceLocation, Map<Integer, Double>> hostStates() { return hostStates; }
public Set<ResourceLocation> hostTags() { return hostTags; }
public Set<ResourceLocation> biomeIds() { return biomeIds; }
public Set<ResourceLocation> excludedBiomeIds() { return excludedBiomeIds; }
Expand Down Expand Up @@ -678,7 +684,15 @@ public JsonObject toJson() {
for (GeologyFamily family : hostFamilies) { families.add(family.configName()); }
json.add("host_families", families);
json.add("geomes", weights(geomes));
json.add("host_blocks", weightedIds(hostBlocks, hostBlockWeights, "block"));
JsonArray hosts = weightedIds(hostBlocks, hostBlockWeights, "block");
hostStates.forEach((block, variants) -> variants.forEach((metadata, weight) -> {
JsonObject state = new JsonObject();
state.addProperty("block", block.toString());
state.addProperty("metadata", metadata);
if (weight != 1.0D) state.addProperty("weight", weight);
hosts.add(state);
}));
json.add("host_blocks", hosts);
json.add("host_tags", weightedIds(hostTags, hostTagWeights, "tag"));
json.add("biome_ids", ids(biomeIds));
json.add("excluded_biome_ids", ids(excludedBiomeIds));
Expand Down Expand Up @@ -710,6 +724,7 @@ public static final class Builder {
private final Set<GeologyFamily> hostFamilies = new LinkedHashSet<>();
private final Map<ResourceLocation, Double> geomes = new LinkedHashMap<>();
private final Set<ResourceLocation> hostBlocks = new LinkedHashSet<>();
private final Map<ResourceLocation, Map<Integer, Double>> hostStates = new LinkedHashMap<>();
private final Set<ResourceLocation> hostTags = new LinkedHashSet<>();
private final Set<ResourceLocation> biomeIds = new LinkedHashSet<>();
private final Set<ResourceLocation> excludedBiomeIds = new LinkedHashSet<>();
Expand Down Expand Up @@ -750,6 +765,25 @@ public Builder placementChannel(ResourceLocation value) {
public Builder hostFamily(GeologyFamily value) { hostFamilies.add(value); return this; }
public Builder geomeWeight(ResourceLocation geome, double value) { geomes.put(geome, value); return this; }
public Builder hostBlock(ResourceLocation value) { hostBlocks.add(value); return this; }

/** Selects one legacy block variant, such as plain stone (metadata zero). */
public Builder hostState(ResourceLocation block, int metadata) {
return hostState(block, metadata, 1.0D);
}

/** Adds an exact variant with the same replacement weight as hostBlock. */
public Builder hostState(ResourceLocation block, int metadata, double weight) {
Objects.requireNonNull(block, "block");
if (metadata < 0 || metadata > 15) {
throw new IllegalArgumentException("Legacy host metadata must be within 0..15");
}

double validatedWeight = replacementWeight(weight);
hostStates.computeIfAbsent(block, ignored -> new LinkedHashMap<>())
.put(metadata, validatedWeight);
return this;
}

public Builder hostTag(ResourceLocation value) { hostTags.add(value); return this; }
public Builder biome(ResourceLocation value) { biomeIds.add(value); return this; }
public Builder excludeBiome(ResourceLocation value) { excludedBiomeIds.add(value); return this; }
Expand Down Expand Up @@ -790,7 +824,8 @@ public OreDimensionDefinition build() {
|| nodeSize < 1 || nodeSize > 32) {
throw new IllegalStateException("Invalid ore placement values for " + dimension);
}
if (enabled && hostFamilies.isEmpty() && hostBlocks.isEmpty() && hostTags.isEmpty()) {
if (enabled && hostFamilies.isEmpty() && hostBlocks.isEmpty()
&& hostStates.isEmpty() && hostTags.isEmpty()) {
throw new IllegalStateException("Enabled ore dimension has no hosts: " + dimension);
}
return new OreDimensionDefinition(this);
Expand Down
Loading
Loading