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.2.110021.jar
build/libs/OreSpawn-4.1.2.110021-sources.jar
build/libs/OreSpawn-4.1.2.110021-javadoc.jar
build/libs/OreSpawn-4.1.3.110021.jar
build/libs/OreSpawn-4.1.3.110021-sources.jar
build/libs/OreSpawn-4.1.3.110021-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.3.110021

* Apply hybrid and replacement background limits to imported built-in ore
patterns, just as for ordinary native rules. Keep explicit channels and
third-party deposit patterns independent.
* Retain older Ore Sources selections under their saved pattern names without
rewriting imported settings or regenerating existing chunks.
* Show imported placement choices in the editor without duplicate old-channel
rows. Distinguish an inactive source from a missing mod, and keep custom
channels and saved choices intact.
* Keep the empty Ore Sources message inside its pane in smaller windows.

Version 4.1.2.110021

* Respect Manage Vanilla Ores for imported OS1/OS3 rules. With it off, leave
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.2.110021`: the OreSpawn 4.1.2
This branch builds target-qualified version `4.1.3.110021`: the OreSpawn 4.1.3
feature set for Minecraft 1.10.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 and
Expand Down
14 changes: 14 additions & 0 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,14 @@ informational because the original rules remain independent. The compact help
and row tooltips explain fixed, selectable, missing and inactive states without
reducing the alias-list height.

Built-in patterns use the standard channel whether their names are short
(`default`, `vein`) or fully qualified (`orespawn:default`, `orespawn:vein`).
This lets a child's hybrid or replacement policy reduce imported ordinary
veins too. An explicit `placement_channel` always wins, and third-party pattern
channels remain independent. Older saved source selections still select the
same rule; changing this classification does not rewrite their ore settings
or existing terrain.

Native vanilla ore rules are active placement sources only when
`manage_vanilla_ores` is enabled. Changing that option refreshes the pending
Ore Sources snapshot before the editor opens. While it is disabled, Group
Expand Down Expand Up @@ -337,6 +345,12 @@ BaseSciences has no ordinary ore-source role. An Ore Dictionary member whose
generator is not controlled by OreSpawn is reported as external generation and
is never suppressed, so total abundance may still be increased by that mod.

Older imported profiles may still store a built-in pattern name as their
placement channel. The Ore Sources editor shows a single ordinary placement
row for that choice; opening the screen does not rewrite the saved profile.
Explicit custom channels stay separate. A source whose placement is switched
off remains listed as output-only, rather than being reported as a missing mod.

## Fluid Deposits, Retrogen, And Bedrock

Each `fluid_deposits` entry has a stable provider-namespaced rule ID, an
Expand Down
7 changes: 7 additions & 0 deletions docs/MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,13 @@ Original), preserving prior placement frequency, ordering and output behaviour.
Missing selected sources and dormant policies remain recorded. Policy changes
affect new chunks only and do not add retrogen.

Legacy imports may spell built-in patterns as `orespawn:default` or another
fully qualified built-in name. These are ordinary generation, so hybrid and
replacement background limits apply to them. Saved Ore Sources choices under
those older inferred channel names remain valid without changing the saved
ore rule. Explicit channels, custom patterns, imported quantities and heights,
and already generated chunks are left alone.

The old `place_crude_oil` and singleton `oil` fields migrate to
`place_fluid_deposits` and a provider-owned rule. A valid block becomes
`<namespace>:fluid_deposit/<path>`; `minecraft:air` creates no rule. Mineralogy
Expand Down
2 changes: 1 addition & 1 deletion docs/VERSIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Examples:

| Minecraft | Loader | Target | Example full version |
| --- | --- | ---: | --- |
| 1.10.2 | Forge | `110021` | `4.1.2.110021` |
| 1.10.2 | Forge | `110021` | `4.1.3.110021` |
| 1.12.2 | Forge | `112021` | `4.0.6.112021` |
| 1.13.2 | Forge | `113021` | `4.0.6.113021` |
| 1.20.6 | Forge | `120061` | `4.0.6.120061` |
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.2.110021
mod_version=4.1.3.110021
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 @@ -77,7 +77,7 @@ tasks.register('verifyReleaseConfiguration') {
description = 'Validates the target-qualified release, API, schemas, reports, and publishing identity.'

doLast {
if (project.mod_version != '4.1.2.110021'
if (project.mod_version != '4.1.3.110021'
|| project.mod_group != expectedMavenGroup) {
throw new GradleException("Unexpected OreSpawn release version: ${project.mod_version}")
}
Expand Down Expand Up @@ -106,8 +106,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.2.110021')) {
throw new GradleException("Authoritative release location does not contain 4.1.2.110021: ${path}")
if (!file(path).getText('UTF-8').contains('4.1.3.110021')) {
throw new GradleException("Authoritative release location does not contain 4.1.3.110021: ${path}")
}
}
if (!file('docs/API.md').getText('UTF-8').contains('orespawn@[4.0.6,5.0.0)')) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1027,7 +1027,7 @@ private static void writeHumanUpgradeReport(Path destination) throws IOException
}
}
List<String> lines = new ArrayList<>();
lines.add("OreSpawn 4.1.2.110021 Upgrade Report");
lines.add("OreSpawn 4.1.3.110021 Upgrade Report");
lines.add("================================");
lines.add("");
lines.add("RESULT: Legacy OreSpawn configuration was consumed and translated for OS4.");
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/cyano/orespawn/OreSpawn.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class OreSpawn {
public static OreSpawn INSTANCE;
public static final String MODID = "orespawn";
public static final String NAME = "OreSpawn";
public static final String VERSION = "4.1.2.110021";
public static final String VERSION = "4.1.3.110021";
public static final List<Path> oreSpawnConfigFiles = new ArrayList<>();
public static final List<String> additionalStoneBlocks = new ArrayList<>();
public static boolean disableVanillaOreGen;
Expand Down
2 changes: 1 addition & 1 deletion 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.2.110021";
public static final String VERSION = "4.1.3.110021";

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ List<OreSourceGroup> oreSourceGroups() {
List<OreSourceGroup> result = new ArrayList<>();
for (Entry<String, JsonElement> entry : section("ore_source_policies").entrySet()) {
if (!entry.getValue().isJsonObject()) continue;
JsonObject policy = entry.getValue().getAsJsonObject();
JsonObject policy = OreSourceEditorView.policy(root, entry.getValue().getAsJsonObject());
String material = string(policy, "material", "");
String domain = string(policy, "domain", "");
if (!validResource(material) || !validResource(domain)) continue;
Expand Down Expand Up @@ -983,17 +983,19 @@ private static boolean hasManagedPlacementSource(JsonObject policy) {
}

private static void refreshOreSourceStatus(JsonObject policy) {
Set<String> missing = new HashSet<>();
Set<String> inactivePlacements = new HashSet<>();
Set<String> loaded = new HashSet<>();
Set<String> loadedPlacements = new HashSet<>();
boolean external = false;
boolean review = bool(policy, "review_required", false);
if (policy.has("candidates") && policy.get("candidates").isJsonArray()) {
for (JsonElement element : policy.getAsJsonArray("candidates")) {
if (!element.isJsonObject()) continue;
JsonObject candidate = element.getAsJsonObject();
String sourceId = string(candidate, "source_id", "");
if (!bool(candidate, "loaded", false)) missing.add(sourceId);
if (!bool(candidate, "placement_active", false)) inactivePlacements.add(sourceId);
if (bool(candidate, "loaded", false)) {
loaded.add(sourceId);
loadedPlacements.add(sourceId + "|" + string(candidate, "placement_channel", "orespawn:standard"));
}
external |= bool(candidate, "external", false) && bool(candidate, "loaded", false);
}
}
Expand All @@ -1005,10 +1007,10 @@ private static void refreshOreSourceStatus(JsonObject policy) {
return;
}
for (String id : outputs.keySet()) {
if (missing.contains(id)) { policy.addProperty("status", "missing_source"); return; }
if (!loaded.contains(id)) { policy.addProperty("status", "missing_source"); return; }
}
for (String id : placements.values()) {
if (missing.contains(id) || inactivePlacements.contains(id)) {
for (Entry<String, String> placement : placements.entrySet()) {
if (!loadedPlacements.contains(placement.getValue() + "|" + placement.getKey())) {
policy.addProperty("status", "missing_source"); return;
}
}
Expand Down
120 changes: 120 additions & 0 deletions src/main/java/zone/moddev/mc/orespawn/client/OreSourceEditorView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package zone.moddev.mc.orespawn.client;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import zone.moddev.mc.orespawn.util.JsonCopies;

/** Presents old inferred channel names without rewriting the world's saved choices. */
final class OreSourceEditorView {
private static final String STANDARD = "orespawn:standard";
private static final Set<String> OLD_CHANNELS = new HashSet<>(Arrays.asList(
"orespawn:default", "orespawn:vein", "orespawn:normal_cloud",
"orespawn:precision", "orespawn:clusters", "orespawn:underfluids"));

private OreSourceEditorView() { }

static JsonObject policy(JsonObject root, JsonObject saved) {
JsonObject view = JsonCopies.copy(saved);
JsonObject placements = object(view, "placement_sources");
JsonArray candidates = view.has("candidates") && view.get("candidates").isJsonArray()
? view.getAsJsonArray("candidates") : new JsonArray();
String domain = text(view, "domain");
List<String> aliases = new ArrayList<>();

for (Map.Entry<String, JsonElement> entry : placements.entrySet()) {
String channel = entry.getKey();
if (!OLD_CHANNELS.contains(channel) || !entry.getValue().isJsonPrimitive()) continue;

String source = entry.getValue().getAsString();
if (inferredAlias(root, source, domain, channel)
&& loadedCandidate(candidates, source, STANDARD) != null) {
aliases.add(channel);
}
}

// More than one old pattern may have been selected deliberately. Keep
// those rows separate rather than choosing a winner on the player's behalf.
boolean combine = placements.has(STANDARD) || aliases.size() == 1;
if (combine) {
for (String alias : aliases) {
if (!placements.has(STANDARD)) placements.add(STANDARD, placements.get(alias));
placements.remove(alias);
}
}

JsonArray visible = new JsonArray();
for (JsonElement element : candidates) {
if (!element.isJsonObject()) continue;
JsonObject candidate = element.getAsJsonObject();
String source = text(candidate, "source_id");
String channel = text(candidate, "placement_channel");
JsonObject current = loadedCandidate(candidates, source, STANDARD);

if (!combine && !aliases.isEmpty() && STANDARD.equals(channel) && current != null) {
boolean projected = false;
for (String alias : aliases) {
if (!inferredAlias(root, source, domain, alias)) continue;

JsonObject retained = JsonCopies.copy(current);
retained.addProperty("placement_channel", alias);
visible.add(retained);
projected = true;
}
if (projected) continue;
}

if (current != null && OLD_CHANNELS.contains(channel)
&& inferredAlias(root, source, domain, channel)) {
continue;
}
visible.add(candidate);
}

view.add("candidates", visible);
view.add("placement_sources", placements);
return view;
}

private static boolean inferredAlias(JsonObject root, String source, String domain, String channel) {
JsonObject ore = object(object(root, "ores"), source);
JsonObject dimensions = object(ore, "dimensions");
JsonObject rule = dimensions.has(domain) ? object(dimensions, domain)
: object(object(ore, "dimension_selectors"), domain);
if (rule.has("placement_channel")) return false;

JsonElement pattern = rule.get("pattern");
if (pattern == null || (!pattern.isJsonObject() && !pattern.isJsonPrimitive())) return false;
return channel.equals(pattern.isJsonObject()
? text(pattern.getAsJsonObject(), "type") : pattern.getAsString());
}

private static JsonObject loadedCandidate(JsonArray candidates, String source, String channel) {
for (JsonElement element : candidates) {
if (!element.isJsonObject()) continue;
JsonObject candidate = element.getAsJsonObject();
if (source.equals(text(candidate, "source_id"))
&& channel.equals(text(candidate, "placement_channel"))
&& candidate.has("loaded") && candidate.get("loaded").getAsBoolean()) {
return candidate;
}
}
return null;
}

private static JsonObject object(JsonObject parent, String key) {
return parent.has(key) && parent.get(key).isJsonObject()
? parent.getAsJsonObject(key) : new JsonObject();
}

private static String text(JsonObject parent, String key) {
return parent.has(key) && parent.get(key).isJsonPrimitive() ? parent.get(key).getAsString() : "";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,10 @@ protected void init() {
@Override protected String rowText(int index) {
String channel = channels.get(index);
List<OreSourceCandidate> choices = OreSourceListScreen.placementCandidates(group, channel);
String selected = group.placements.get(channel);
int choice = OreSourceListScreen.indexOf(choices, selected);
String source = choice < 0
OreSourceCandidate selected = selectedPlacementCandidate(group, channel);
String source = selected == null
? I18n.format("label.orespawn.ore_source.missing")
: OreSourceListScreen.candidateLabel(choices.get(choice));
: OreSourceListScreen.candidateLabel(selected);
return channelLabel(channel) + (placementSelectable(group, channel) ? " > " : " = ")
+ placementSourceLabel(source, choices.size());
}
Expand Down Expand Up @@ -189,6 +188,9 @@ protected void init() {
lines.add(I18n.format("tooltip.orespawn.ore_source.placement_rules"));
if ("keep_separate".equals(group.mode)) {
lines.add(I18n.format("tooltip.orespawn.ore_source.placement_requires_consolidated"));
} else if (selectedPlacementCandidate(group, channel) != null
&& !selectedPlacementCandidate(group, channel).active) {
lines.add(I18n.format("tooltip.orespawn.ore_source.output_only"));
} else if (choices.isEmpty()) {
lines.add(I18n.format("tooltip.orespawn.ore_source.placement_missing"));
} else if (choices.size() == 1) {
Expand Down Expand Up @@ -325,6 +327,16 @@ static boolean placementSelectable(OreSourceGroup group, String channel) {
&& OreSourceListScreen.placementCandidates(group, channel).size() > 1;
}

static OreSourceCandidate selectedPlacementCandidate(OreSourceGroup group, String channel) {
String selected = group.placements.get(channel);
for (OreSourceCandidate candidate : group.candidates) {
if (candidate.loaded && candidate.sourceId.equals(selected) && candidate.channel.equals(channel)) {
return candidate;
}
}
return null;
}

static String placementStatus(OreSourceGroup group, List<String> channels) {
if (channels.isEmpty()) return I18n.format("label.orespawn.ore_source.no_placement_rules");
if ("keep_separate".equals(group.mode)) {
Expand All @@ -333,7 +345,11 @@ static String placementStatus(OreSourceGroup group, List<String> channels) {
boolean selectable = false;
for (String channel : channels) {
int choices = OreSourceListScreen.placementCandidates(group, channel).size();
if (choices == 0) return I18n.format("tooltip.orespawn.ore_source.placement_missing");
if (choices == 0) {
return I18n.format(selectedPlacementCandidate(group, channel) == null
? "tooltip.orespawn.ore_source.placement_missing"
: "tooltip.orespawn.ore_source.output_only");
}
selectable |= choices > 1;
}
return I18n.format(selectable ? "tooltip.orespawn.ore_source.placement_select"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -507,8 +507,12 @@ public void render(int mouseX, int mouseY, float partialTick) {
new TextComponentString(error), width - 24)), width / 2, height - 39, 0xFF5555);
}
if (selected(orderedGroups()) == null) {
drawCenteredString(font, new TextComponentTranslation("label.orespawn.ore_source.none"),
leftPaneX + (leftPaneWidth / 2), CONTENT_TOP + 10, 0xA0A0A0);
int y = CONTENT_TOP + 10;
for (String line : font.listFormattedStringToWidth(
I18n.format("label.orespawn.ore_source.none"), leftPaneWidth - 12)) {
drawString(font, new TextComponentString(line), leftPaneX + 6, y, 0xA0A0A0);
y += font.FONT_HEIGHT + 2;
}
}
updateWeightBounds();
super.render(mouseX, mouseY, partialTick);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ private static void writeUpgradeReport(Path worldRoot, Path configDirectory,
}

List<String> lines = new ArrayList<>();
lines.add("OreSpawn 4.1.2.110021 Upgrade Report");
lines.add("OreSpawn 4.1.3.110021 Upgrade Report");
lines.add("================================");
lines.add("");
lines.add("RESULT: Existing Mineralogy " + mineralogyVersion
Expand Down
Loading
Loading