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.0.113021.jar
build/libs/OreSpawn-4.1.0.113021-sources.jar
build/libs/OreSpawn-4.1.0.113021-javadoc.jar
build/libs/OreSpawn-4.1.6.113021.jar
build/libs/OreSpawn-4.1.6.113021-sources.jar
build/libs/OreSpawn-4.1.6.113021-javadoc.jar
build/release/SHA256SUMS
CHANGELOG.txt

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

* Leave vanilla ores to Minecraft when Manage Vanilla Ores is off. When
it is on, an imported rule takes over without an extra copy of the ore.
* Keep saved Ore Sources choices for imported patterns. Built-in patterns
share the standard channel for new choices; custom channels stay separate.
* Show vanilla ores in Ore Sources before creating a world, even when Manage
Vanilla Ores is off.
* Keep imported provider rules when opening a saved world. Back up the
profile before removing an untouched duplicate; keep edited rules for review.
* Choose sulfur and lithium defaults after mods have loaded, including Base
Electric. Existing worlds keep their choices.
* Fix the Austrian German language ID and add Pirate Speak menu and guide text.

Version 4.1.0.113021

* Fix managed Nether quartz hosts on Forge 1.13.2, including saved profiles
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ End" policy used by mods such as Base Metals.
This is not the unrelated mod that adds mobs and dimensions under the same
name.

This branch builds target-qualified version `4.1.0.113021` for Minecraft 1.13.2
This branch builds target-qualified version `4.1.6.113021` for Minecraft 1.13.2
and Forge. It adds material-group ore arbitration, exact biome replacements,
and a directory for installed provider mods to the OreSpawn 4.0.16 base. See the
[versioning policy](docs/VERSIONS.md) for the encoding and release convention.
Expand Down
3 changes: 3 additions & 0 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ Each enabled ore dimension uses:
| `placement_channel` | registry ID | Independent placement budget shared by equivalent ores in consolidated modes |

At least one of `host_families`, `host_blocks`, or `host_tags` must be present.
On 1.13.2, plain stone, granite, diorite, and andesite are separate block
IDs. Use `host_blocks: ["minecraft:stone"]` for plain stone only; no
pre-flattening metadata is needed.

On Forge 1.13.2, the default Nether quartz rule uses
`host_blocks: ["minecraft:netherrack"]`, because Forge does not ship a
Expand Down
12 changes: 12 additions & 0 deletions docs/MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ and are called out in `orespawn-migration/migration-report.txt` for manual
review. Files owned by mods without an active provider retain the original
legacy-ID behaviour.

Importing a rule does not turn on **Manage Vanilla Ores**. With that
setting off, Minecraft still generates its own ores and the imported vanilla
rules remain available in the profile for later use. Turning it on lets an
active imported rule replace the modern default for that ore and dimension.

If a saved world has a proven legacy rule and an unchanged duplicate from the
same provider, OreSpawn keeps the imported rule and backs up the profile as
`serverconfig/orespawn-worldgen.pre-legacy-ore-repair.bak` before removing the
duplicate. Edited or ambiguous rules are kept and noted in the log. Old
metadata is not used to guess a new block ID, and existing chunks are not
regenerated by the repair.

Migration is non-destructive. OreSpawn writes `config/orespawn-worldgen.json`
only when that target does not already exist and retains every source file.
When legacy OreSpawn rules are translated, a concise player-facing summary is
Expand Down
5 changes: 5 additions & 0 deletions docs/PROVIDERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,8 @@ referenced biome; ordinary compatibility hints belong in `similar_biomes`.
See `examples/examplemod-orespawn.json` for rocks, weighted ore output, a
fluid deposit, a custom dimension, biome palette, world materials, and a
selectable template.

Ore Sources chooses defaults once mods finish loading. Both `baseelectric`
and `electricadvantage` are recognised for sulfur and lithium, but
automatic balancing still requires loaded providers with an exact shared
ore tag. Existing worlds keep their saved choices.
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 OreSpawn version |
| --- | --- | ---: | --- |
| 1.13.2 | Forge | `113021` | `4.1.0.113021` |
| 1.13.2 | Forge | `113021` | `4.1.6.113021` |
| 1.20.6 | Forge | `120061` | `4.0.6.120061` |
| 1.21.11 | Forge | `121111` | `4.0.6.121111` |
| 26.1.2 | Forge | `2601021` | `4.0.6.2601021` |
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ mcp_version=20190213.203750
mod_id=orespawn
mod_name=MMD OreSpawn
mod_license=LGPL-2.1
mod_version=4.1.0.113021
mod_version=4.1.6.113021
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
10 changes: 5 additions & 5 deletions gradle/release/artifacts.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def preparedReleaseDir = providers.gradleProperty('preparedReleaseDir')
tasks.register('verifyReleaseConfiguration') {
group = 'verification'
doLast {
if (project.mod_version != '4.1.0.113021'
if (project.mod_version != '4.1.6.113021'
|| project.mod_group != expectedMavenGroup
|| project.minecraft_version != '1.13.2'
|| project.forge_version != '25.0.223'
Expand All @@ -95,9 +95,9 @@ tasks.register('verifyReleaseConfiguration') {
throw new GradleException('Unexpected dispatcher or Java target metadata')
}
List<String> expectedPublicArtifacts = [
'OreSpawn-4.1.0.113021.jar',
'OreSpawn-4.1.0.113021-sources.jar',
'OreSpawn-4.1.0.113021-javadoc.jar'
'OreSpawn-4.1.6.113021.jar',
'OreSpawn-4.1.6.113021-sources.jar',
'OreSpawn-4.1.6.113021-javadoc.jar'
]
if (base.archivesName.get() != expectedMavenArtifact
|| expectedReleaseFiles.get().collect { it.toString() } != expectedPublicArtifacts) {
Expand All @@ -114,7 +114,7 @@ tasks.register('verifyReleaseConfiguration') {
'src/main/java/zone/moddev/mc/orespawn/worldgen/LegacyMineralogyProfileMigration.java',
'README.md', 'CHANGELOG.txt'
].each { path ->
if (!file(path).getText('UTF-8').contains('4.1.0.113021')) {
if (!file(path).getText('UTF-8').contains('4.1.6.113021')) {
throw new GradleException("Release identity missing from ${path}")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import net.minecraft.client.gui.GuiCreateWorld;
import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.world.GameType;
import net.minecraft.world.WorldSettings;
Expand Down Expand Up @@ -48,6 +49,7 @@ public final class ClientProbeTestMod {
private boolean worldSettingsOpened;
private boolean longEditorRoundTrip;
private boolean oreSourceFixturesVerified;
private boolean vanillaOreSourcesVerified;
private List<GuiButton> worldCreationButtons;

public ClientProbeTestMod() {
Expand Down Expand Up @@ -117,6 +119,21 @@ private void handleClientTick() {
worldSettingsOpened = true;
validateCaptions((OreSpawnWorldSettingsScreen) minecraft.currentScreen);
validateLongEditorRoundTrip(minecraft, minecraft.currentScreen);
String localizedCaption = System.getProperty("clientprobe.recommendedCaption", "");
if (!localizedCaption.isEmpty()) {
String actual = I18n.format("button.orespawn.recommended");
if (!localizedCaption.equals(actual)) {
throw new IllegalStateException("Unexpected Recommended Defaults caption: " + actual);
}
Properties values = new Properties();
values.setProperty("recommended", actual);
try (FileOutputStream output = new FileOutputStream("client-locale-pass.properties")) {
values.store(output, "OreSpawn localized editor check");
}
minecraft.shutdown();
nextState(10);
break;
}
if (Boolean.getBoolean("clientprobe.oreSourcesFixtures")) {
validateOreSourceFixtures(minecraft, minecraft.currentScreen);
oreSourceFixturesVerified = true;
Expand Down Expand Up @@ -202,8 +219,8 @@ 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;
Expand All @@ -224,7 +241,7 @@ private static void validateCaptions(OreSpawnScreen screen) {
}
}

private static void validateOreSourcesRoute(Minecraft minecraft, OreSpawnScreen materials) {
private void validateOreSourcesRoute(Minecraft minecraft, OreSpawnScreen materials) {
Button ores = buttonWithCaption(materials,
net.minecraft.client.resources.I18n.format("tab.orespawn.ores"));
if (ores == null) throw new IllegalStateException("Missing ORES tab");
Expand All @@ -239,6 +256,10 @@ private static void validateOreSourcesRoute(Minecraft minecraft, OreSpawnScreen
throw new IllegalStateException("Ore Sources route did not open");
}
OreSourceListScreen opened = (OreSourceListScreen) minecraft.currentScreen;
if (!vanillaOreSourcesVerified && Boolean.getBoolean("clientprobe.vanillaOreSources")) {
validateVanillaOreSources(opened);
vanillaOreSourcesVerified = true;
}
validateEmptyOreSources(minecraft, materials);
Button addGroup = buttonWithCaption(opened, "+");
if (addGroup == null) throw new IllegalStateException("Missing Add Group control");
Expand All @@ -254,6 +275,55 @@ private static void validateOreSourcesRoute(Minecraft minecraft, OreSpawnScreen
}
}

private static void validateVanillaOreSources(OreSourceListScreen screen) {
WorldGeologyProfile pending = WorldGeologyProfileManager.pendingNewWorldProfile();
if (pending.manageVanillaOres()) {
throw new IllegalStateException("The standalone Show All check must run with vanilla management off");
}
validateVanillaOreRows(screen);
GeologyEditorSession session = new GeologyEditorSession(pending);
JsonObject before = session.profile().rootCopy();
for (GeologyEditorSession.OreSourceGroup group : session.oreSourceGroups()) {
if (group.hasManagedPlacementSource() || !"keep_separate".equals(group.mode)) {
throw new IllegalStateException("Show All should not put OreSpawn in charge of vanilla generation");
}
}
if (!before.equals(session.profile().rootCopy())) {
throw new IllegalStateException("Listing vanilla ores must not change the profile");
}
System.out.println("VANILLA_ORE_SOURCES_PASS management=off rows=8 profile=unchanged");
}

private static void validateVanillaOreRows(OreSourceListScreen screen) {
if (firstList(screen).size() != 0) {
throw new IllegalStateException("Routine vanilla ores should start hidden");
}
Button showAll = buttonWithCaption(screen, I18n.format("button.orespawn.show_all"));
if (showAll == null) throw new IllegalStateException("Missing Show All control");
((GuiButton) showAll).onClick(0, 0);
CompactScrollList groups = firstList(screen);
String[] expected = { "Coal", "Diamond", "Emerald", "Gold", "Iron", "Lapis", "Quartz", "Redstone" };
if (groups.size() != expected.length) {
throw new IllegalStateException("Show All should list eight vanilla ores, found " + groups.size());
}
for (int row = 0; row < expected.length; row++) {
if (!groups.rowText(row).startsWith(expected[row] + " - ")) {
throw new IllegalStateException("Unexpected vanilla ore row: " + groups.rowText(row));
}
}
Button hideSingles = buttonWithCaption(screen, I18n.format("button.orespawn.ore_source.hide_single"));
if (hideSingles == null) throw new IllegalStateException("Missing Hide Singles control");
((GuiButton) hideSingles).onClick(0, 0);
if (firstList(screen).size() != 0) throw new IllegalStateException("Hide Singles should hide vanilla ores");
}

private static CompactScrollList firstList(OreSpawnScreen screen) {
for (GuiButton widget : screen.qualificationButtons()) {
if (widget instanceof CompactScrollList) return (CompactScrollList) widget;
}
throw new IllegalStateException("Missing Groups list");
}

private static void validateEmptyOreSources(Minecraft minecraft, GuiScreen parent) {
for (int[] size : new int[][] { { 426, 265 }, { 320, 240 } }) {
OreSourceListScreen empty = new OreSourceListScreen(parent,
Expand Down Expand Up @@ -440,7 +510,7 @@ private static void pressDone(OreSpawnScreen screen) {
for (GuiButton widget : screen.qualificationButtons()) {
if (!(widget instanceof Button)) continue;
String caption = TextFormatting.getTextWithoutFormattingCodes(((Button) widget).getMessage());
if ("done".equalsIgnoreCase(caption)) {
if (I18n.format("gui.done").equals(caption)) {
((GuiButton) widget).onClick(0, 0);
return;
}
Expand All @@ -462,6 +532,7 @@ private void writeMarker() throws IOException {
values.setProperty("world_settings_opened", Boolean.toString(worldSettingsOpened));
values.setProperty("long_editor_roundtrip", Boolean.toString(longEditorRoundTrip));
values.setProperty("ore_source_fixtures_verified", Boolean.toString(oreSourceFixturesVerified));
values.setProperty("vanilla_ore_sources_verified", Boolean.toString(vanillaOreSourcesVerified));
values.setProperty("editor_routes", Integer.toString(editorRoutes.size()));
values.setProperty("editor_classes", editorRoutes.toString());
values.setProperty("first_world_rendered", Boolean.toString(firstWorldFrames >= 8));
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/zone/moddev/mc/orespawn/OreSpawn.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ private void processInterMod(final InterModProcessEvent event) {
// Files are authoritative, so scan them before accepting API submissions.
WorldgenIntegrationManager.initialize();
WorldgenIntegrationManager.processImcMessages();
GeomeConfig.bake();
GeomeConfig.bakeBeforeLoadComplete();
OreSpawnOreGeneration.refreshWorldConfig();
FlatBedrockFeature.refreshWorldConfig();
OreRetrogenManager.refreshWorldConfig();
Expand All @@ -96,7 +96,7 @@ private void processInterMod(final InterModProcessEvent event) {
private void setup(final FMLCommonSetupEvent event) {
OreSpawnConfig.bake();
WorldgenIntegrationManager.initialize();
GeomeConfig.bake();
GeomeConfig.bakeBeforeLoadComplete();
logGeomeSampler();
BiomeWorldgenBootstrap.registerCodecs();
DocumentationExporter.exportBundledGuide();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,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 @@ -454,6 +454,7 @@ List<OreSourceGroup> oreSourceGroups() {
candidates, decimalMap(policy, "outputs"), stringMap(policy, "placement_sources"));
if (!bool(policy, "dormant", false)) result.add(group);
}
OreSourceEditorView.addVanillaPreviews(root, result);
Collections.sort(result, (left, right) -> {
int name = left.displayName.compareToIgnoreCase(right.displayName);
if (name != 0) return name;
Expand Down Expand Up @@ -976,17 +977,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 @@ -998,10 +1001,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 Expand Up @@ -1045,11 +1048,20 @@ static final class OreSourceGroup {
final List<OreSourceCandidate> candidates;
final Map<String, Double> outputs;
final Map<String, String> placements;
final boolean preview;

OreSourceGroup(String key, String material, String domain, String displayName,
List<String> blockTagEntries, boolean curated, String mode, String outputMode, String status,
List<OreSourceCandidate> candidates, Map<String, Double> outputs,
Map<String, String> placements) {
this(key, material, domain, displayName, blockTagEntries, curated, mode, outputMode, status,
candidates, outputs, placements, false);
}

OreSourceGroup(String key, String material, String domain, String displayName,
List<String> blockTagEntries, boolean curated, String mode, String outputMode, String status,
List<OreSourceCandidate> candidates, Map<String, Double> outputs,
Map<String, String> placements, boolean preview) {
this.key = key;
this.material = material;
this.domain = domain;
Expand All @@ -1062,6 +1074,7 @@ static final class OreSourceGroup {
this.candidates = Collections.unmodifiableList(new ArrayList<>(candidates));
this.outputs = outputs;
this.placements = placements;
this.preview = preview;
}

List<String> channels() {
Expand Down
Loading
Loading