Files
rivencraft/docs/LEGACY_INSTABILITY_AUDIT.md
T
2026-10-09 01:56:39 -04:00

16 KiB
Raw Blame History

Mystcraft Legacy instability audit (Milestone 6, issue #63)

This is a behavior contract for Milestone 6, not a completed runtime instability system. It compares the user-provided 0.13.7.06 class files at /home/dnorberg/Documents/mystcraft-src/mystcraft-1.12.2-0.13.7.06/ with Mystcraft Legacy source at commit 9bc8ddc. Values below are source defaults, not a claim that every historical config or server used them. Rivencraft 0.5.1.0 saves a provisional writing-instability score and repaired active-page order, but has no hazard loop yet.

Score, grammar, and selection in Legacy

Legacy's AgeController 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 compares watched block abundance with a baseline, subtracting only positive excess above each baseline. The baseline is profiled or read from balance config; InstabilityDataCalculator sets its default tolerance to 1.05, and default useconfigs=false. We should not copy its 400-chunk profiling delay or dynamic block scan into 1.21.1 without a separate performance case.

Input Verified Legacy behavior Rivencraft M6 interpretation
Missing required controller Terrain, biome controller, weather, and lighting are generated when missing. InstabilityData.missing.controller = 0; lighting explicitly adds zero. Missing pages themselves are not a penalty. Keep Age-seeded completion. No penalty solely for an omitted branch.
Repeated same symbol addSymbol increments a per-IAgeSymbol count and adds that symbol's instabilityModifier(count). This method is symbol-specific; there is no universal “duplicate = N” rule. Distinguish legal repeated features/materials from repeated single-choice pages. Do not add a flat duplicate penalty to every occurrence.
Extra/conflicting controller Registering a second terrain, biome, weather, or lighting controller adds InstabilityData.extra.controller = 500; the last registered controller replaces the prior one. This is separate from each symbol's own modifier. Eligible supported conflicts may become unstable rather than fatal in #65, with deterministic winner and a saved penalty.
Dangling modifier An unconsumed modifier contributes its dangling value; a default Modifier has dangling=100. Replacing a pending modifier charges the old one. clearModifiers() charges 20% (clearPercentage=0.20) of each pending value, with Java integer compound-assignment truncation. Record the ordered offending page and a reason; calculate a deterministic penalty from the consumed/leftover modifier semantics, not from GUI position alone.
Unsupported/unmatched page Legacy logs unmatched symbols during reconstruction and skips their logic; its AgeData.getBaseInstability() still has a TODO for calculating instability from pages. This is not a sound compatibility contract. Remains fatal for new books until semantic and runtime support exists; never silently erase the page or pretend its effect applied. Existing saved books retain their IDs.
Other penalties/bonuses AgeController.addInstability(int) is available to symbol logic. The base/book score is persisted as a short; Legacy's InstabilityBonusManager also has dynamic player death/behavior components. Persist a versioned score breakdown for Age writing and environmental contributions. Do not infer death/troll mechanics from the mere existence of Legacy classes.

Source for controller and modifier defaults: InstabilityData, AgeController. The local 0.13.7.06 Modifier.class confirms dangling_default=100. Legacy grammar ranks weight which missing symbols are selected; they are not a direct instability score or deck tier. See grammar audit and random weights.

InstabilityController rounds its working score toward zero to the current smallest registered positive provider cost (500 by default), then rebuilds active effects when that rounded score changes. Each seeded deck preserves its saved card order; new cards are shuffled with the Age seed. For each deck it subtracts the deck entry cost, then walks cards in order, paying each provider activation cost until the next card cannot be afforded. A provider's occurrences become its level. Deck costs are gates, not mutually exclusive severity bands: at high score multiple decks can contribute effects. Provider .enabled config can remove cards. The actual effect set cannot be inferred from a score alone without the saved deck orders and enabled providers (Deck, InstabilityManager).

Legacy deck Entry cost Examples of registered cards
basic 0 enemy regeneration/resistance, fatigue, hunger, poison, slowness, weakness
harsh 2,500 stronger/global potions, burning, lightning, wither
destructive 10,000 crumble, explosions, lightning, meteors, wither
eating 15,000 colored decay, blindness, nausea, wither
death 20,000 global blindness/nausea/wither, colored decay

These entry costs are from InstabilityData.initialize(), not guaranteed first-activation scores: each card also costs 500–5,000 depending on provider. A zero-cost basic deck still needs enough score to buy its first card. stability.* numbers in that file are provider activation costs, despite the misleading field name.

Effect inventory and modern policy

InstabilityData.initialize() is the authoritative list of registered providers. Some implemented classes are commented out there; their existence does not mean they occur normally. Each Legacy environmental effect is called for chunks that the Age's runtime processes, often with its own chance. No per-Age global work budget is visible in these effect classes.

Family Legacy behavior M6 disposition
Player and hostile-mob potions Local/global blindness, fatigue, hunger, nausea, poison, slowness, weakness, wither; hostile regeneration/resistance (EffectPotion). Implement in #67 with server-only, Age-scoped entity targeting and rate limits. No effects in preview/snapshot worlds.
Colored decay Blue, purple, red, white providers place advancing decay and extra-tick it; black provider is commented out. Decay handlers differ in spread/consumption (providers, handlers). Implement bounded, reversible-by-admin world mutation in #68; exact 1.12 spread/update rates require adaptation and tests. Exclude protected spawn/portal areas. Black/green/yellow are not proven active Legacy providers.
Lightning and explosions Random per-chunk strike/explosion attempts (EffectLightning, EffectExplosions). Implement in #69 with explicit block-damage and fire policy; no effect outside the Age.
Meteor, scorching Meteor entity spawn and sky-exposed entity ignition (EffectMeteor, EffectScorched). #70: modern meteor entity/impact needs an explicit damage budget; scorching can use modern fire/damage checks. Legacy meteor/roar.ogg is available.
Crumble and erosion Crumble maps blocks to weaker states; erosion spreads adjacent fluids and has extra lake-generation methods (EffectCrumble, EffectErosion). #70: bounded block palette and protected-area checks. Erosion's provider is commented out in Legacy registration: treat it as deliberate opt-in adaptation, not baseline parity.
Extra ticks Registered alongside blue/purple/red/white decay, targeting decay blocks (EffectExtraTicks). Implement only as a bounded decay helper in #68; do not accelerate arbitrary blocks/block entities.
Other dormant code Black decay, global burning, bedrock crumble, erosion provider are commented out. Defer unless a later issue explicitly requests them. An authored symbol requiring one must fail clearly until supported.

Assets in the supplied local tree: assets/mystcraft/textures/blocks/decay_{black,blue,green,purple,red,white,yellow}.png, corresponding decay block/item model JSON, and assets/mystcraft/sounds/entity/meteor/roar.ogg. Before importing, review attribution/license and fit modern 16×16 assets. Texture availability does not make an effect active. Use current Minecraft effects/sounds where adaptation is cleaner; no new artwork is needed for this audit.

Rivencraft boundary and proposed safety contract

WritingGrammar classifies INCOMPLETE as seeded completion and supported writing defects as accepted instability. AgeGenerationPlan.compile() freezes the original writing, repaired active-page indexes, and a provisional writing-instability score before world generation. #64 now persists a versioned score breakdown; saved provider/deck choice remains future work. The plan must not mutate an already-created Age when a data pack changes. Server-side settings may disable execution, but should not rewrite the saved authored score.

For #65, eligible-to-become-unstable means a known, supported page whose semantics and resulting world are still deterministic: repeated/conflicting single-choice pages and otherwise valid out-of-order or misplaced pages. The implementation retains every original page, uses the last single-choice page, sorts active categories, and saves 500 per extra/conflicting choice or 100 per misplaced page. These are provisional Rivencraft values, not a full port of every IAgeSymbol.instabilityModifier. Missing branches are explicitly classified as zero-cost seeded completion; legal repeated feature/material pages are not automatically errors. Remain fatal: missing/duplicate/misordered Link Panel, over-capacity book, unresolved or schema-v1 display-only symbol, unsupported weather/angle/runtime profile/biome/feature, unavailable required optional mod, and any combination the compiler cannot represent safely. Binder and unbound-book previews distinguish blocked writing, seeded completion, and accepted instability before binding; the server revalidates on output. This intentionally differs from Legacy's silent unmatched-symbol skip, universal controller cost assumptions, and modifier-specific dangling costs. A full symbol-specific Legacy penalty port remains a separate balance task, not a claim of parity in this slice.

Automated #65 checks cover classification, preserved input slots on rejection, and an accepted unstable preview. The live output/one-time Age creation check is assigned to in-game testing because the GameTest storage phase stalled when that transaction ran; the test world is not retained.

Issue #66 establishes a server-only scheduler with bounded per-Age actions, provider calls, levels, cooldowns, loaded-chunk candidates, and rotating active-Age admission. World-mutating handlers are off by default. No provider effects are registered yet; #67–#70 must enforce protected spawn/link platforms, portals, and provider-specific damage/spread limits before activation. These are Rivencraft safety adaptations, not Legacy defaults. Expose score breakdown, selected effects, enablement, and budget suppression in server diagnostics (#72). See runtime policy.

M6 owns scoring, selection, effects, controls, and regression. M7 still owns portal/transport and broader polish; M6 must not depend on new M7 structures. An illegal biome/terrain combination that is presently unrepresentable remains a generation failure until a separate, safe compiler path exists; instability must not be used to disguise absent worldgen support. Existing pre-release saves need explicit migration behavior in #73 rather than a silent rescore.

Verification and open questions

  • Inspected pinned source for AgeController, AgeData, GrammarTree, InstabilityData, InstabilityManager, InstabilityController, Deck, all active provider families, effect classes, ChunkProfiler, and InstabilityDataCalculator; checked matching local 0.13.7.06 class names and Modifier.dangling_default with javap.
  • The supplied extraction has no generated user balance config; registered defaults can be overridden or providers disabled. There is no single universal instability value for “wrong order” or “duplicate page.” IAgeSymbol.instabilityModifier(count) implementations and runtime configs determine the final Legacy value.
  • Exact Legacy visual/decay behavior, dynamic bonus registration, and full 1.12 block-baseline parity need focused checks when their slices are implemented. Do not label proposed modern caps as source facts.