Public Access
Complete Milestone 6 acceptance and runtime cleanup (#74)
Build / build (push) Canceled after 0s
Build / build (push) Canceled after 0s
This commit is contained in:
1 parent
44205f80e0
commit
c2c2d7a6f8
9 files changed
+154
-18
No files matched your search
@@ -1,5 +1,11 @@
|
|||||||
# Frozen Age instability score (Milestone 6, issue #64)
|
# Frozen Age instability score (Milestone 6, issue #64)
|
||||||
|
|
||||||
|
The original issue #64 notes below describe the frozen authored score. Later
|
||||||
|
Milestone 6 slices added a separate live block-profile score. For new
|
||||||
|
version-4 plans, ordered period/phase modifiers combine until consumed by a
|
||||||
|
celestial page; each modifier kind left dangling at the end adds +100. Saved
|
||||||
|
version-3 plans keep their earlier stage-sorted meaning and writing cost.
|
||||||
|
|
||||||
Every newly created Age now gets a version-1 `Instability` compound inside its server-owned `rivencraft_ages` saved definition. It contains the total `Score` and an ordered `Breakdown` of reason, original page index (zero-based, or -1 for an Age-wide cause), points, and explanation. `AgeRegistrySavedData.instability(UUID)` is the read-only server query. No client tooltip or live data-pack lookup is needed to read a saved score.
|
Every newly created Age now gets a version-1 `Instability` compound inside its server-owned `rivencraft_ages` saved definition. It contains the total `Score` and an ordered `Breakdown` of reason, original page index (zero-based, or -1 for an Age-wide cause), points, and explanation. `AgeRegistrySavedData.instability(UUID)` is the read-only server query. No client tooltip or live data-pack lookup is needed to read a saved score.
|
||||||
|
|
||||||
The baseline is explicitly zero for page-free and Link-Panel-only random Ages, and for authored Ages. Incomplete writing receives seed-based completion and a zero-point explanatory entry. Supported writing defects use the provisional Milestone 6 grammar costs: duplicate/conflicting single-choice pages +500, out-of-order or misplaced pages +100. Legacy's audited Bright Lighting symbol adds +500 for every occurrence, including a seed-generated controller. Mineshafts, Nether Fortresses, Strongholds, and Villages add +100 for the fourth and every later occurrence. By project balance decision, Bastions, Trial Chambers, Ancient Cities, Shipwrecks, Ocean Ruins, Pillager Outposts, Ruined Portals, Nether Fossils, Buried Treasure, and Trail Ruins follow that same rule. The first three occurrences and all other audited Legacy feature symbols have no direct page cost. An authored structure may override its vanilla biome eligibility; every structure-page occurrence adds +100 if **any** selected biome is unsupported, even when another selected biome is compatible. Seed completion still prefers compatible biomes and therefore avoids that cost when it can. Other modern-only features remain neutral. The frozen score selects a deterministic, persisted provider plan through the Milestone 6 runtime. Registered handlers apply that plan only inside loaded Rivencraft Ages and remain subject to the documented scheduler and effect-specific safety bounds.
|
The baseline is explicitly zero for page-free and Link-Panel-only random Ages, and for authored Ages. Incomplete writing receives seed-based completion and a zero-point explanatory entry. Supported writing defects use the provisional Milestone 6 grammar costs: duplicate/conflicting single-choice pages +500, out-of-order or misplaced pages +100. Legacy's audited Bright Lighting symbol adds +500 for every occurrence, including a seed-generated controller. Mineshafts, Nether Fortresses, Strongholds, and Villages add +100 for the fourth and every later occurrence. By project balance decision, Bastions, Trial Chambers, Ancient Cities, Shipwrecks, Ocean Ruins, Pillager Outposts, Ruined Portals, Nether Fossils, Buried Treasure, and Trail Ruins follow that same rule. The first three occurrences and all other audited Legacy feature symbols have no direct page cost. An authored structure may override its vanilla biome eligibility; every structure-page occurrence adds +100 if **any** selected biome is unsupported, even when another selected biome is compatible. Seed completion still prefers compatible biomes and therefore avoids that cost when it can. Other modern-only features remain neutral. The frozen score selects a deterministic, persisted provider plan through the Milestone 6 runtime. Registered handlers apply that plan only inside loaded Rivencraft Ages and remain subject to the documented scheduler and effect-specific safety bounds.
|
||||||
@@ -12,4 +18,4 @@ without rewriting the authored breakdown or its saved base provider plan.
|
|||||||
|
|
||||||
Unknown or unsupported pages remain invalid for *new* Ages. Their absence from a later registry does not change an already saved score. Focused GameTests cover stable/defective writing, random baseline and bright lighting, repeated identity, save round-trip, and missing-symbol/old-save behavior.
|
Unknown or unsupported pages remain invalid for *new* Ages. Their absence from a later registry does not change an already saved score. Focused GameTests cover stable/defective writing, random baseline and bright lighting, repeated identity, save round-trip, and missing-symbol/old-save behavior.
|
||||||
|
|
||||||
`/rivencraft age instability` inspects the Age containing the command source; an optional dimension ID inspects an unloaded or different registered Age. It reports immutable base score, stability bonus, runtime penalty, and effective score. The pinned Legacy tree defines player-death and player-presence bonus classes but does not register them as default providers, so Rivencraft leaves player-driven modifiers disabled: bonus and penalty are zero and effective equals base. There is therefore no mutable bonus save to migrate in this release. No death, PvP event, player identity, or dimension transition can transfer stability between Ages. Any future opt-in mechanic must persist separately from `Instability`, define caps and reset behavior, and must not rewrite the generation plan or frozen provider plan.
|
`/rivencraft age instability` inspects the Age containing the command source; an optional dimension ID inspects an unloaded or different registered Age. It reports immutable authored base score, stability bonus, runtime penalty, profiled-block contribution, and difficulty-scaled effective score. The pinned Legacy tree defines player-death and player-presence bonus classes but does not register them as default providers, so Rivencraft leaves player-driven modifiers disabled: bonus and penalty are zero. The live block contribution may still change as chunks are profiled. There is no mutable player-bonus save to migrate in this release. No death, PvP event, player identity, or dimension transition can transfer stability between Ages. Any future opt-in player mechanic must persist separately from `Instability`, define caps and reset behavior, and must not rewrite the generation plan or frozen provider plan.
|
||||||
@@ -2,6 +2,13 @@
|
|||||||
|
|
||||||
This is the behavior contract used to implement Milestone 6. It is pinned to [Mystcraft Legacy source at commit `9bc8ddc`](https://github.com/Mystcraft/Mystcraft-Legacy/tree/9bc8ddc061845df0cd5ea47f4fdef773cff786ae). Values below are source defaults, not a claim that every historical config or server used them. Rivencraft now persists frozen writing-instability scores and provider plans and runs the implemented providers through a bounded, Age-scoped scheduler. The audit remains authoritative for Legacy behavior; the linked runtime documents identify modern adaptations and remaining gaps.
|
This is the behavior contract used to implement Milestone 6. It is pinned to [Mystcraft Legacy source at commit `9bc8ddc`](https://github.com/Mystcraft/Mystcraft-Legacy/tree/9bc8ddc061845df0cd5ea47f4fdef773cff786ae). Values below are source defaults, not a claim that every historical config or server used them. Rivencraft now persists frozen writing-instability scores and provider plans and runs the implemented providers through a bounded, Age-scoped scheduler. The audit remains authoritative for Legacy behavior; the linked runtime documents identify modern adaptations and remaining gaps.
|
||||||
|
|
||||||
|
Current new Age plans (schema 4) also implement Legacy's ordered period/phase
|
||||||
|
consumption and +100 for each modifier kind still pending after the last
|
||||||
|
celestial page. Repeated same-kind pages combine before consumption rather
|
||||||
|
than charging a replacement penalty. Older plans keep their saved score and
|
||||||
|
earlier stage-sorted time interpretation. The older #65 implementation notes
|
||||||
|
below are historical where they mention a generic misplaced-modifier cost.
|
||||||
|
|
||||||
## Score, grammar, and selection in Legacy
|
## Score, grammar, and selection in Legacy
|
||||||
|
|
||||||
Legacy's [`AgeController`](https://github.com/Mystcraft/Mystcraft-Legacy/blob/9bc8ddc061845df0cd5ea47f4fdef773cff786ae/src/main/java/com/xcompwiz/mystcraft/world/AgeController.java) calculates `debug + symbol + block + book/base + bonus`, then applies difficulty 0/1/2/3 multipliers of 0.25/0.5/1/1.75. Global instability is enabled by default; an Age also has its own enable flag. The unmodified score is not simply “number of bad pages.” Block profiling may delay a usable score: `blockinstability` is null until **more than** 400 Age chunks are profiled, and `getInstabilityScore()` returns zero until then. It is recalculated after additional profiling. [`ChunkProfiler`](https://github.com/Mystcraft/Mystcraft-Legacy/blob/9bc8ddc061845df0cd5ea47f4fdef773cff786ae/src/main/java/com/xcompwiz/mystcraft/world/profiling/ChunkProfiler.java) compares watched block abundance with a baseline, subtracting only positive excess above each baseline. The baseline is profiled or read from balance config; [`InstabilityDataCalculator`](https://github.com/Mystcraft/Mystcraft-Legacy/blob/9bc8ddc061845df0cd5ea47f4fdef773cff786ae/src/main/java/com/xcompwiz/mystcraft/world/profiling/InstabilityDataCalculator.java) sets its default tolerance to 1.05, and default `useconfigs=false`. Rivencraft's later [bounded implementation](LIVE_BLOCK_INSTABILITY.md) retains the threshold and tolerance, but not Legacy's spatial accessibility formula or unbounded profiling.
|
Legacy's [`AgeController`](https://github.com/Mystcraft/Mystcraft-Legacy/blob/9bc8ddc061845df0cd5ea47f4fdef773cff786ae/src/main/java/com/xcompwiz/mystcraft/world/AgeController.java) calculates `debug + symbol + block + book/base + bonus`, then applies difficulty 0/1/2/3 multipliers of 0.25/0.5/1/1.75. Global instability is enabled by default; an Age also has its own enable flag. The unmodified score is not simply “number of bad pages.” Block profiling may delay a usable score: `blockinstability` is null until **more than** 400 Age chunks are profiled, and `getInstabilityScore()` returns zero until then. It is recalculated after additional profiling. [`ChunkProfiler`](https://github.com/Mystcraft/Mystcraft-Legacy/blob/9bc8ddc061845df0cd5ea47f4fdef773cff786ae/src/main/java/com/xcompwiz/mystcraft/world/profiling/ChunkProfiler.java) compares watched block abundance with a baseline, subtracting only positive excess above each baseline. The baseline is profiled or read from balance config; [`InstabilityDataCalculator`](https://github.com/Mystcraft/Mystcraft-Legacy/blob/9bc8ddc061845df0cd5ea47f4fdef773cff786ae/src/main/java/com/xcompwiz/mystcraft/world/profiling/InstabilityDataCalculator.java) sets its default tolerance to 1.05, and default `useconfigs=false`. Rivencraft's later [bounded implementation](LIVE_BLOCK_INSTABILITY.md) retains the threshold and tolerance, but not Legacy's spatial accessibility formula or unbounded profiling.
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
# Milestone 6 acceptance review (issue #74)
|
||||||
|
|
||||||
|
Milestone 6 covers authored and live block instability, deterministic provider
|
||||||
|
selection, bounded Age effects, diagnostics, and migration. Its required issues
|
||||||
|
#63–#73 are closed. This document is the release gate for **0.6.0.0**, not a
|
||||||
|
claim of exact Mystcraft 1.12.2 parity or a claim that every optional-mod
|
||||||
|
combination was tested. The player reported an in-game pass for the 0.6.0.0
|
||||||
|
candidate on 2026-10-12; the broader manual matrix remains a future checklist.
|
||||||
|
|
||||||
|
## Scoring and writing
|
||||||
|
|
||||||
|
The [pinned Legacy audit](LEGACY_INSTABILITY_AUDIT.md) records source defaults
|
||||||
|
and the Rivencraft mapping. Authored score/breakdown and provider plan are
|
||||||
|
saved per Age; seed-completed missing branches cost zero. Extra single-choice
|
||||||
|
controllers cost +500, and out-of-order non-modifier pages cost +100.
|
||||||
|
Version-4 Age plans consume period/phase modifiers in written order; repeated
|
||||||
|
modifiers combine, while each kind left unconsumed after the last celestial
|
||||||
|
page costs +100. Bright Lighting costs +500 per occurrence. The fourth and
|
||||||
|
later occurrences of each of the four Legacy structure pages cost +100; the
|
||||||
|
same rule applies to named modern structure pages as a project balance
|
||||||
|
extension. Forcing a structure into an unsupported selected biome costs +100
|
||||||
|
per occurrence. Accelerated and Corrupted pages retain their audited costs.
|
||||||
|
Unknown or unrepresentable new writing remains a clear binding error, rather
|
||||||
|
than a silent no-op. See [score details](INSTABILITY_SCORE.md).
|
||||||
|
|
||||||
|
The [live block profile](LIVE_BLOCK_INSTABILITY.md) samples generated chunks
|
||||||
|
gradually and contributes only after the Age and Overworld-baseline thresholds.
|
||||||
|
It can change the effective score and active provider selection as new chunks
|
||||||
|
are profiled, without rewriting authored score or writing. Diamond storage
|
||||||
|
blocks are an intentional addition to Legacy's watched blocks. Ores, mineral
|
||||||
|
storage blocks, and source fluids supplied by installed mods are recognized
|
||||||
|
automatically through common tags and material names; those mods are **not**
|
||||||
|
Rivencraft dependencies. Exact weight overrides are optional configuration.
|
||||||
|
|
||||||
|
## Effects, safety, and diagnostics
|
||||||
|
|
||||||
|
The [runtime policy](INSTABILITY_RUNTIME.md) documents saved deck order,
|
||||||
|
provider costs/levels, per-Age cooldown, rotating Age admission, loaded-chunk
|
||||||
|
selection, and server-side action budgets. Defaults allow four Ages per tick,
|
||||||
|
two provider calls and one effect action per Age run, up to 16 loaded chunk
|
||||||
|
candidates, and a 20-tick per-Age cooldown. These caps and protected
|
||||||
|
spawn/portal/platform areas are modern safety adaptations, not Legacy's
|
||||||
|
unrestricted execution. Global, per-Age, provider, and world-mutation controls
|
||||||
|
suppress execution without rerolling a saved authored plan.
|
||||||
|
|
||||||
|
Implemented effect families include bounded status effects, four active
|
||||||
|
colored decay variants, lightning, explosions, meteors, ordinary burning,
|
||||||
|
and guarded erosion. Erosion is deliberately enabled by default for testing
|
||||||
|
although Legacy did not register its provider in the active deck. Dormant
|
||||||
|
Legacy black/green/yellow decay variants, global burning, and bedrock crumble
|
||||||
|
are **not** claimed as working. Potion targeting excludes Creative/Spectator
|
||||||
|
players; hostile buffs target hostile mobs. The [effect audit](LEGACY_INSTABILITY_AUDIT.md)
|
||||||
|
records those differences. [Operator diagnostics](INSTABILITY_DIAGNOSTICS.md)
|
||||||
|
report authored and live block contributions, selected providers, enabled
|
||||||
|
state, and budget suppression; raw score is not shown in book UI.
|
||||||
|
|
||||||
|
## Migration and validation
|
||||||
|
|
||||||
|
Pre-scoring Ages migrate to neutral instability. Score-only Ages receive a
|
||||||
|
deterministic saved provider plan. Existing plan-v3 Ages keep their old
|
||||||
|
stage-sorted celestial behavior and writing cost; new plans use schema 4.
|
||||||
|
Block profile schema 2 resets old samples once to capture modded materials,
|
||||||
|
and preserves unknown material IDs if an optional mod is later removed.
|
||||||
|
Missing symbol definitions do not reinterpret a saved authored score, but
|
||||||
|
removing required biome/worldgen registry entries may still prevent world
|
||||||
|
loading. General old-save compatibility is tracked separately in issue #60.
|
||||||
|
|
||||||
|
The [Milestone 6 regression record](MILESTONE_6_REGRESSION.md) distinguishes
|
||||||
|
the passing 0.5.8.10 focused archive, the later storage-stalled archive, the
|
||||||
|
0.5.8.18 build, and the player's reported in-game pass. Its expanded manual
|
||||||
|
matrix is a repeatable future checklist, **not** an individually signed-off
|
||||||
|
pass for every optional mod, multiplayer, restart, and hazard combination.
|
||||||
|
For this completion candidate, verify build, diff check, jar checksum, and a
|
||||||
|
fresh in-game pass before publication. Do not rerun the known stalling archive
|
||||||
|
as a release gate; hand any newly stalled GameTest scenario to the player.
|
||||||
|
The completion review also replaced repeated linear scans of saved active
|
||||||
|
symbol indices with one lookup map per call, and shares one effective provider
|
||||||
|
plan across the scheduler, hazard, and environmental lanes of an admitted Age
|
||||||
|
tick. Live-profile scoring within that plan also uses one sample evaluation
|
||||||
|
instead of two. These are
|
||||||
|
behavior-preserving cleanups; no provider rates or budgets were raised.
|
||||||
|
|
||||||
|
## NeoForge decision
|
||||||
|
|
||||||
|
The project currently develops against NeoForge **21.1.251** for Minecraft
|
||||||
|
1.21.1. The [official 21.1.259 changelog](https://maven.neoforged.net/releases/net/neoforged/neoforge/21.1.259/neoforge-21.1.259-changelog.txt)
|
||||||
|
lists mostly loader/Mixin dependency updates, a GUI slot-index API change,
|
||||||
|
and targeted fixes since 21.1.251. None addresses a known Rivencraft defect.
|
||||||
|
Upgrading the development and minimum runtime version during this acceptance
|
||||||
|
gate would force an additional optional-integration test pass for little clear
|
||||||
|
benefit. Keep 21.1.251 for 0.6.0.0; evaluate 21.1.259 in a separate later
|
||||||
|
slice with Create/Ponder, Immersive Portals, Dynamic Dimensions, JEI, and the
|
||||||
|
season/biome integrations. This is a release-risk judgment, not a claim that
|
||||||
|
21.1.259 is incompatible.
|
||||||
|
|
||||||
|
## Gate status
|
||||||
|
|
||||||
|
- Required implementation issues #63–#73: closed.
|
||||||
|
- Known unsupported effects and broader save compatibility: explicitly scoped
|
||||||
|
above, not counted as working Milestone 6 behavior.
|
||||||
|
- Candidate `./gradlew build --offline --no-daemon --quiet`: passed; the
|
||||||
|
compiler reported only existing GameTest helper deprecation warnings.
|
||||||
|
Local JAR SHA-256: `6eed828156dd1e2679b013c3c5bf8e692a20d9ab80ae27417b3cafe1c26422c7`.
|
||||||
|
- Player in-game acceptance of completion candidate: reported pass on
|
||||||
|
2026-10-12; individual optional-mod and multiplayer matrix rows were not
|
||||||
|
separately signed off.
|
||||||
|
- Gitea completion release, uploaded-jar checksum, #74 and milestone closure:
|
||||||
|
pending publication and verification.
|
||||||
+1
-1
@@ -36,7 +36,7 @@ mod_name=Rivencraft
|
|||||||
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
||||||
mod_license=LGPL-3.0-or-later
|
mod_license=LGPL-3.0-or-later
|
||||||
# The mod version. Rivencraft uses the x.y.z.b policy documented in README.md.
|
# The mod version. Rivencraft uses the x.y.z.b policy documented in README.md.
|
||||||
mod_version=0.5.8.18
|
mod_version=0.6.0.0
|
||||||
immersive_portals_version=6.0.7
|
immersive_portals_version=6.0.7
|
||||||
terrablender_version=4.1.0.8
|
terrablender_version=4.1.0.8
|
||||||
serene_seasons_version_id=B6nTjh4b
|
serene_seasons_version_id=B6nTjh4b
|
||||||
|
|||||||
@@ -289,8 +289,9 @@ public final class AgeBlockInstability {
|
|||||||
|
|
||||||
public static AgeInstabilityPlan effectivePlan(ServerLevel level, AgeDefinition age) {
|
public static AgeInstabilityPlan effectivePlan(ServerLevel level, AgeDefinition age) {
|
||||||
int blockScore = score(level, age);
|
int blockScore = score(level, age);
|
||||||
if (blockScore == 0 && RivencraftConfig.instabilityDifficulty() == 2) return age.instabilityPlan();
|
int difficulty = RivencraftConfig.instabilityDifficulty();
|
||||||
int combined = effectiveScore(level, age);
|
if (blockScore == 0 && difficulty == 2) return age.instabilityPlan();
|
||||||
|
int combined = difficultyScore((long) age.instability().score() + blockScore, difficulty);
|
||||||
AgeInstability effective = new AgeInstability(AgeInstability.CURRENT_VERSION, combined,
|
AgeInstability effective = new AgeInstability(AgeInstability.CURRENT_VERSION, combined,
|
||||||
java.util.List.of(new AgeInstability.Contribution("effective_block_profile", -1, combined,
|
java.util.List.of(new AgeInstability.Contribution("effective_block_profile", -1, combined,
|
||||||
"Saved writing score plus sampled excess blocks")));
|
"Saved writing score plus sampled excess blocks")));
|
||||||
|
|||||||
@@ -93,8 +93,9 @@ public final class AgeEnvironmentalProviders {
|
|||||||
return METEORS.equals(id) || BURNING.equals(id) || EROSION.equals(id);
|
return METEORS.equals(id) || BURNING.equals(id) || EROSION.equals(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tickPlanned(ServerLevel level, AgeDefinition age, long tick) {
|
static void tickPlanned(ServerLevel level, AgeDefinition age, long tick,
|
||||||
List<AgeInstabilityPlan.ProviderLevel> cards = AgeBlockInstability.effectivePlan(level, age).providers().stream()
|
List<AgeInstabilityPlan.ProviderLevel> providers) {
|
||||||
|
List<AgeInstabilityPlan.ProviderLevel> cards = providers.stream()
|
||||||
.filter(card -> isEnvironmental(card.id())).toList();
|
.filter(card -> isEnvironmental(card.id())).toList();
|
||||||
boolean optInErosion = RivencraftConfig.erosionEnabled()
|
boolean optInErosion = RivencraftConfig.erosionEnabled()
|
||||||
&& AgeBlockInstability.effectiveScore(level, age) >= InstabilityProviderCatalog.Deck.DESTRUCTIVE.entryCost();
|
&& AgeBlockInstability.effectiveScore(level, age) >= InstabilityProviderCatalog.Deck.DESTRUCTIVE.entryCost();
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
package net.dnorberg.rivencraft.book;
|
package net.dnorberg.rivencraft.book;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
import net.dnorberg.rivencraft.writing.OrderedWriting;
|
import net.dnorberg.rivencraft.writing.OrderedWriting;
|
||||||
import net.dnorberg.rivencraft.writing.SymbolReference;
|
import net.dnorberg.rivencraft.writing.SymbolReference;
|
||||||
import net.dnorberg.rivencraft.writing.SymbolSemantics;
|
import net.dnorberg.rivencraft.writing.SymbolSemantics;
|
||||||
@@ -44,11 +48,14 @@ public record AgeGenerationPlan(int version, long ageSeed, OrderedWriting writin
|
|||||||
if (version >= 3 && !symbols.isEmpty() && activeSymbolIndices.isEmpty()) {
|
if (version >= 3 && !symbols.isEmpty() && activeSymbolIndices.isEmpty()) {
|
||||||
throw new IllegalArgumentException("Version 3 Age plan has no active symbols");
|
throw new IllegalArgumentException("Version 3 Age plan has no active symbols");
|
||||||
}
|
}
|
||||||
if (activeSymbolIndices.stream().distinct().count() != activeSymbolIndices.size()) {
|
Set<Integer> resolvedIndices = new HashSet<>();
|
||||||
|
for (ResolvedSymbol symbol : symbols) resolvedIndices.add(symbol.index());
|
||||||
|
Set<Integer> seenActive = new HashSet<>();
|
||||||
|
for (int index : activeSymbolIndices) {
|
||||||
|
if (!seenActive.add(index)) {
|
||||||
throw new IllegalArgumentException("Age plan repeats an active symbol index");
|
throw new IllegalArgumentException("Age plan repeats an active symbol index");
|
||||||
}
|
}
|
||||||
for (int index : activeSymbolIndices) {
|
if (!resolvedIndices.contains(index)) {
|
||||||
if (symbols.stream().noneMatch(symbol -> symbol.index() == index)) {
|
|
||||||
throw new IllegalArgumentException("Active symbol is not in the saved writing: " + index);
|
throw new IllegalArgumentException("Active symbol is not in the saved writing: " + index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,8 +114,13 @@ public record AgeGenerationPlan(int version, long ageSeed, OrderedWriting writin
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<ResolvedSymbol> activeSymbols() {
|
public List<ResolvedSymbol> activeSymbols() {
|
||||||
return activeSymbolIndices.stream().map(index -> symbols.stream()
|
Map<Integer, ResolvedSymbol> byIndex = new HashMap<>(symbols.size());
|
||||||
.filter(symbol -> symbol.index() == index).findFirst().orElseThrow()).toList();
|
for (ResolvedSymbol symbol : symbols) byIndex.putIfAbsent(symbol.index(), symbol);
|
||||||
|
return activeSymbolIndices.stream().map(index -> {
|
||||||
|
ResolvedSymbol symbol = byIndex.get(index);
|
||||||
|
if (symbol == null) throw new IllegalStateException("Missing active symbol " + index);
|
||||||
|
return symbol;
|
||||||
|
}).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompoundTag save() {
|
public CompoundTag save() {
|
||||||
|
|||||||
@@ -55,9 +55,10 @@ public final class AgeHazardProviders {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Roll every selected hazard card each scheduler pass, independently of other providers. */
|
/** Roll every selected hazard card each scheduler pass, independently of other providers. */
|
||||||
static void tickPlanned(ServerLevel level, AgeDefinition age, long tick) {
|
static void tickPlanned(ServerLevel level, AgeDefinition age, long tick,
|
||||||
|
List<AgeInstabilityPlan.ProviderLevel> providers) {
|
||||||
if (!RivencraftConfig.hazardsEnabled()) return;
|
if (!RivencraftConfig.hazardsEnabled()) return;
|
||||||
List<AgeInstabilityPlan.ProviderLevel> hazards = AgeBlockInstability.effectivePlan(level, age).providers().stream()
|
List<AgeInstabilityPlan.ProviderLevel> hazards = providers.stream()
|
||||||
.filter(provider -> isHazard(provider.id())).toList();
|
.filter(provider -> isHazard(provider.id())).toList();
|
||||||
if (hazards.isEmpty()) return;
|
if (hazards.isEmpty()) return;
|
||||||
List<ChunkPos> chunks = loadedChunksNearPlayers(level, RivencraftConfig.hazardsMaxChunks());
|
List<ChunkPos> chunks = loadedChunksNearPlayers(level, RivencraftConfig.hazardsMaxChunks());
|
||||||
|
|||||||
@@ -55,16 +55,16 @@ public final class AgeInstabilityRuntime {
|
|||||||
if (!admitted(state.eligible, level.dimension().location(), tick / cooldown,
|
if (!admitted(state.eligible, level.dimension().location(), tick / cooldown,
|
||||||
RivencraftConfig.instabilityMaxAges())) return;
|
RivencraftConfig.instabilityMaxAges())) return;
|
||||||
AgeDefinition age = AgeRegistrySavedData.get(level).findByDimension(level.dimension().location()).orElse(null);
|
AgeDefinition age = AgeRegistrySavedData.get(level).findByDimension(level.dimension().location()).orElse(null);
|
||||||
if (age == null || AgeBlockInstability.effectivePlan(level, age).providers().isEmpty()
|
if (age == null || !AgeInstabilityControls.get(level).enabled(age.id())) return;
|
||||||
|| !AgeInstabilityControls.get(level).enabled(age.id())) return;
|
List<AgeInstabilityPlan.ProviderLevel> providers = AgeBlockInstability.effectivePlan(level, age).providers();
|
||||||
|
if (providers.isEmpty()) return;
|
||||||
// Legacy's per-chunk hazards must not wait for the rotating two-provider sample.
|
// Legacy's per-chunk hazards must not wait for the rotating two-provider sample.
|
||||||
AgeHazardProviders.tickPlanned(level, age, tick);
|
AgeHazardProviders.tickPlanned(level, age, tick, providers);
|
||||||
AgeEnvironmentalProviders.tickPlanned(level, age, tick);
|
AgeEnvironmentalProviders.tickPlanned(level, age, tick, providers);
|
||||||
List<ChunkPos> chunks = loadedChunksNearPlayers(level, RivencraftConfig.instabilityMaxChunks());
|
List<ChunkPos> chunks = loadedChunksNearPlayers(level, RivencraftConfig.instabilityMaxChunks());
|
||||||
if (chunks.isEmpty()) return;
|
if (chunks.isEmpty()) return;
|
||||||
Budget budget = new Budget(RivencraftConfig.instabilityMaxActions());
|
Budget budget = new Budget(RivencraftConfig.instabilityMaxActions());
|
||||||
Context context = new Context(level, age, chunks, tick, budget);
|
Context context = new Context(level, age, chunks, tick, budget);
|
||||||
List<AgeInstabilityPlan.ProviderLevel> providers = AgeBlockInstability.effectivePlan(level, age).providers();
|
|
||||||
int first = (int) Math.floorMod(tick / cooldown + age.seed(), providers.size());
|
int first = (int) Math.floorMod(tick / cooldown + age.seed(), providers.size());
|
||||||
int ran = 0;
|
int ran = 0;
|
||||||
for (int offset = 0; offset < providers.size() && ran < RivencraftConfig.instabilityMaxProviders()
|
for (int offset = 0; offset < providers.size() && ran < RivencraftConfig.instabilityMaxProviders()
|
||||||
|
|||||||
Reference in new issue
Block a user