Mining¶
Active resource extraction from asteroid-field sectors. Mining is the canonical inflow path for raw ore, the secondary path for precious_metals, and a trace inflow for quantum_shards in deep-asteroid sectors. The loop is built around a per-attempt harvest action (5 turns), a Mining Laser equipment requirement, and an Astral Mining Consortium licensing system that gates lawful extraction inside AM-claimed sectors.
This doc covers the mining loop in isolation. For commodity pricing and station-side trading, see trading.md. For the Quantum Shard chain that mining trace-feeds, see ../galaxy/quantum-resources.md.
Status: ✅ Shipped —
services/gameserver/src/services/mining_service.pyimplements the server-authoritative harvest and AM claim-license flow; the harvest and license endpoints are live inservices/gameserver/src/api/routes/mining.py(registered inservices/gameserver/src/api/api.py), and theClaimLicensemodel is inservices/gameserver/src/models/claim_license.py. Theasteroid_richness,depletion_pool, andhas_deep_asteroidskeys live inside the existingSector.resourcesJSONB and are derived on demand fromSector.resource_regenerationfor sectors that predate the mining extensions. The Frontier Coalition +5 unclaimed-asteroid rep hook (see Faction reputation hooks) is ✅ Implemented on LEG-97 tip / pending merge (0274a1b1/ PR #620 — open as of 2026-08-20, not yet onfeat/new-feature-development).
Where mining happens¶
Mining is restricted to sectors where Sector.type = ASTEROID_FIELD. No other sector type accepts a mining attempt; the harvest endpoint rejects with not_an_asteroid_field outside of that set.
ASTEROID_FIELD is one of six special types in the SectorType enum (models/sector.py). Per ../galaxy/generation.md, galaxy generation flags 15% of sectors as special types; the remaining 85% are STANDARD. Inside that 15% slice, ASTEROID_FIELD share is cluster-driven:
RESOURCE_RICHclusters: ~60% of special-type rolls land on ASTEROID_FIELD (the dominant special type in resource-rich clusters).- Default clusters: ~25% of special-type rolls (15% / 4 evenly across
ASTEROID_FIELD,RADIATION_ZONE,WARP_STORM,BLACK_HOLE). MILITARY_ZONEandFRONTIER_OUTPOSTclusters: 0% — those cluster profiles do not roll ASTEROID_FIELD.
Galaxy-wide, the realised asteroid-field density lands in the 3–10% of sectors band depending on the regional cluster mix. Players seeking high mining throughput are pushed toward RESOURCE_RICH cluster regions; see ../galaxy/generation.md#step-5--sectors and ../../SYSTEMS/bang-import-pipeline.md for the per-cluster distribution.
Equipment requirement¶
Mining requires a Mining Laser equipment slot (mining_laser key in EQUIPMENT_DEFINITIONS, services/gameserver/src/services/ship_upgrade_service.py). Without a Mining Laser equipped, the harvest endpoint rejects with no_mining_laser.
| Attribute | Value |
|---|---|
| Purchase cost (level 0) | 35,000 cr |
| Compatible ships | Cargo Hauler, Colony Ship, Defender |
| Effect | mining_efficiency: 1.5 (multiplier on raw yield) |
| Slot key | mining_laser |
| Removable | Yes (refunds 25% of base cost) |
Source: ../gameplay/ship-systems.md — equipment table.
Status: ✅ Shipped — the equipment definition lives in
services/gameserver/src/services/ship_upgrade_service.py:EQUIPMENT_DEFINITIONS["mining_laser"], and itsmining_efficiencymultiplier is consumed bymining_service.pyat harvest time via the laser-level efficiency ladder (_laser_efficiency_multiplier). The harvest endpoint rejects withno_mining_laserwhen no laser is fitted.
Mining Laser upgrade ladder¶
The base 35,000 cr Mining Laser ships at level 0. Three upgrade levels are purchasable at any Class 7+ Technology Port. Upgrades are applied to the equipped Mining Laser in place; uninstalling and re-installing the laser preserves the upgrade level.
| Level | Upgrade cost | Cumulative cost | Yield multiplier | Notes |
|---|---|---|---|---|
| 0 | — | 35,000 | 1.0× | Stock laser. |
| 1 | 50,000 | 85,000 | 1.25× | Standard upgrade. |
| 2 | 100,000 | 185,000 | 1.5× | Required for quantum_shards trace drops. |
| 3 | 200,000 | 385,000 | 2.0× | End-game; the ore-efficiency multiplier keeps scaling to 2.0×. precious_metals rare-drop rate does not scale further at L3 — it freezes at L2's 9% (ADR-0062 E-F3 ruling, 2026-08-04; see § Output). |
Status: ✅ Shipped — the ladder lives in
services/gameserver/src/services/ship_upgrade_service.py:MINING_LASER_LADDER["mining_laser_level"], kept deliberately separate from theUpgradeType-keyedUPGRADE_DEFINITIONSdict (a string key there would break the upgrade-type iterators). The level is alevel: intkey insideShip.equipment_slots["mining_laser"];mining_service.py:_laser_levelreads it and_laser_efficiency_multipliermaps it to the yield multiplier. ThePOST /mining/laser-upgradeendpoint raises the level viaShipUpgradeService.purchase_mining_laser_upgrade(the standard row-lock + credit-check purchase ritual).
The harvest action¶
A harvest is a single per-attempt action issued from the sector view of an ASTEROID_FIELD sector. Each attempt:
| Cost / output | Value |
|---|---|
| Turn cost | 5 turns |
| Credit cost | 0 cr |
| Real-time cooldown | None (turn cost is the throttle) |
| Output | ore primarily; precious_metals rare; quantum_shards trace |
| Ship state during attempt | Ship.status = MINING (stationary, vulnerable) |
Preconditions¶
- Player ship is in a sector where
Sector.type = ASTEROID_FIELD. - Ship has a Mining Laser equipped (
Ship.equipment_slots["mining_laser"]present). - Ship is undocked (in space, not at a station).
- Player has ≥ 5 turns.
- Cargo has ≥ 1 free unit.
- If the sector is AM-claimed and the player wants to avoid rep loss, a valid
ClaimLicensefor the sector is held (see Astral Mining Consortium claim licenses below).
Resolution¶
- Server reads the sector's
Sector.resources.asteroid_richness✅ Shipped — derived on demand fromSector.resource_regeneration(and persisted into the JSONB) bymining_service.py:_ensure_asteroid_richnesswhen the key is absent. - Set
Ship.status = MININGfor the duration of the resolution. The MINING status holds the ship in the sector and exposes it to PvP interrupts (see PvP interaction below). - Roll the ore band from the yield matrix below using
richness_tier × laser_level. - Apply the Mining Laser's
mining_efficiencymultiplier (1.0× / 1.25× / 1.5× / 2.0× by upgrade level). - Apply the planet-side Mining Engineers profession bonus if the player owns a planet in the same region with active Mining Engineers (+30% on
ore/fuelproduction — see../planets/professions.md). The mining bonus extends the same multiplier to direct asteroid extraction. 📐 Design-only — the colonist-professions system (professions.md) is 100% unbuilt;mining_service.pyhas zero profession references. - Apply the Trade Specialist profession bonus if the player owns a planet with active Trade Specialists (+25% credit-equivalent multiplier on raw ore sold from the same trade run; see
../planets/professions.md#trade-specialists). Cross-link only — the Trade Specialist bonus applies at sale, not at harvest. 📐 Design-only — same profession-system dependency as step 5. - Roll
precious_metalsrare drop: 5% per harvest at laser level 0; +2% per laser level, frozen at level 2's 9% — level 3 grants no further bump (ADR-0062 E-F3 ruling, 2026-08-04). - Roll
quantum_shardstrace drop: 1% per harvest, only ifSector.resources.has_deep_asteroids = true✅ Shipped and the Mining Laser is at level ≥ 2. - Decrement the sector's
Sector.resources.depletion_pool✅ Shipped by the rolled ore yield (precious metals and quantum shards do not count against the pool). - Add commodities to player cargo, deduct turns, set
Ship.status = IN_SPACE, emit the realtime event. - Apply faction-rep deltas (see Faction reputation hooks below).
Yield matrix¶
Base ore yield per harvest, by sector richness tier × Mining Laser level. Richness tier is read from the sector's asteroid_richness JSONB field; tier 5 sectors are rare and concentrated in RESOURCE_RICH clusters.
| Richness tier | Laser L0 (1.0×) | Laser L1 (1.25×) | Laser L2 (1.5×) | Laser L3 (2.0×) |
|---|---|---|---|---|
| 1 (sparse) | 2–4 ore | 3–5 ore | 3–6 ore | 4–8 ore |
| 2 | 4–8 ore | 5–10 ore | 6–12 ore | 8–16 ore |
| 3 (typical) | 6–12 ore | 8–15 ore | 9–18 ore | 12–24 ore |
| 4 | 10–18 ore | 13–23 ore | 15–27 ore | 20–36 ore |
| 5 (deep core) | 15–25 ore | 19–31 ore | 23–38 ore | 30–50 ore |
The min-of-band is a hard floor — see Asteroid depletion for the per-sector depletion modifier. Even at maximum depletion, the sector returns at least 1 ore per attempt.
Status: ✅ Shipped — the yield matrix is
mining_service.py:_YIELD_MATRIX, indexed by(richness_tier, laser_level)with the bands above copied verbatim; the harvest rolls the band, applies the laser-efficiency and depletion multipliers, and enforces the 1-ore floor.
asteroid_richness derivation¶
Sector.resources.asteroid_richness is computed from Sector.resource_regeneration (a float 0.0–1.0 already on the Sector model) and stored as a JSONB sub-object. mining_service.py:_ensure_asteroid_richness performs this derivation lazily — on the first harvest or license purchase in a sector whose resources lacks the key — and persists the result, so sectors generated before the mining extensions backfill on demand:
resource_regeneration |
Tier | Name | Yield band (L0 laser) | Cooldown |
|---|---|---|---|---|
| ≥ 0.9 | 5 | Abundant | 15–25 | None |
| 0.6–0.89 | 4 | Rich | 10–18 | None |
| 0.3–0.59 | 3 | Moderate | 6–12 | None |
| < 0.3 | 1 | Depleted | 0–1 | 24h between harvests |
Stored shape:
"asteroid_richness": {
"richness": "moderate",
"richness_tier": 3,
"yield_band": [6, 12],
"harvest_cooldown_hours": null
}
Computed at Sector creation (worldgen or import from sw2102-bang); does not change during the game session. The yield-matrix lookup above multiplies richness_tier × laser_level for the harvest-time band; this stored representation is the canonical source of richness_tier.
Commodity output¶
Mining produces only raw materials — the loop never emits finished goods. Equipment, fuel, and other refined commodities are out of scope for direct mining yield.
| Commodity | Drop | Rate | Notes |
|---|---|---|---|
ore |
Primary | 100% | Always rolled per the yield matrix. The canonical commodity sold to Class 1, Class 4, Class 5, Class 6 stations and AM-flagged refineries. Price band 15–45 cr/unit per trading.md. |
precious_metals |
Secondary | 5% per harvest at L0; +2% per laser level, frozen at L2's 9% | Rare drop on top of the base ore yield. Rate does not scale further at Laser L3 (ADR-0062 E-F3 ruling, 2026-08-04). Yield 1–3 units per drop. Price band 80–180 cr/unit (services/gameserver/src/core/commodity_economy.py), slotted between equipment and exotic_technology. |
quantum_shards |
Trace | 1% per harvest | Only in sectors with Sector.resources.has_deep_asteroids = true and a Mining Laser at level ≥ 2. Yield 1 shard per drop. Cross-feed into the ../galaxy/quantum-resources.md chain — note that the primary Quantum Shard source remains nebula harvesting; the asteroid drop is a small secondary inflow that complements the dedicated Quantum Field Harvester loop. |
Status: ✅ Shipped —
mining_service.pyemits all three commodities:oreper the yield matrix,precious_metalson the 5%+2%/level rare roll frozen at Laser L2's 9% (Laser L3 grants no further precious-metals bump, per ADR-0062 E-F3 ruling, 2026-08-04), andquantum_shardson the 1% trace roll gated byhas_deep_asteroidsand laser level ≥ 2. Each drop is clamped to free cargo space.precious_metalsis priced at 80–180 cr/unit incommodity_economy.py;has_deep_asteroidsis aSector.resourcesJSONB key (defaultfalse, set by worldgen/import for deep-asteroid sectors).
Astral Mining Consortium claim licenses¶
Sectors flagged as AM-claimed (the sector's dominant Faction row has Faction.faction_type == MINING per ADR-0033; Sector.controlling_faction is a String(50) snake-case faction code — permanently canonical, not merely "at launch": the previously proposed Sector.controlling_faction_id FK alternative is formally dropped, not deferred, per DECISIONS.md's controlling-faction-fk-debt entry (Max carte blanche, 2026-08-07), which formalizes this footnote's reasoning as a real ruling rather than an untracked punch-list item) require a claim license to mine without rep penalty. AM controls a portion of RESOURCE_RICH clusters in Federation and Border zones — exact share is regional and tracked in ../gameplay/factions-and-teams.md.
License model¶
| Field | Value |
|---|---|
| Model | ClaimLicense ✅ Shipped (services/gameserver/src/models/claim_license.py) |
| Issuer | Any AM-flagged refinery station (Class 1 mining or Class 5 collection with AM controlling-faction tag) |
| Scope | Single sector, single player |
| Duration | 24 real-time hours per purchase, renewable at 80% of base cost |
| Daily fee | 500 cr × richness_tier |
| Stack | One active license per (player, sector) pair |
| Persistence | Survives logout, expires on the timer |
Schema — services/gameserver/src/models/claim_license.py (table claim_licenses):
| Column | Type | Constraints | Notes |
|---|---|---|---|
id |
UUID | PK | |
player_id |
UUID | FK Player.id, not null |
License holder |
region_id |
UUID | FK regions.id, nullable |
Compound sector identity (region + sector); nullable for region-less sectors, where the unique constraint distinguishes by sector_number alone |
sector_number |
Integer | not null | Region-scoped sector |
faction_code |
String(50) | not null | Issuing faction (always astral_mining_consortium at launch) |
purchased_at |
DateTime | not null | UTC timestamp of purchase |
expires_at |
DateTime | not null | purchased_at + 24h |
cost_paid_cr |
Integer | not null | 500 × richness_tier |
is_active |
Boolean (computed) | — | expires_at > NOW() |
UNIQUE on (player_id, region_id, sector_number) ensures one active license per (player, sector) at a time. Renewal: a fresh row is inserted on each renewal at 80% of base (400 cr × richness_tier); the prior row's expires_at is honoured for any overlap window.
License cost by richness tier¶
| Richness tier | Fee per 24h |
|---|---|
| 1 (sparse) | 500 cr |
| 2 | 1,000 cr |
| 3 (typical) | 1,500 cr |
| 4 | 2,000 cr |
| 5 (deep core) | 2,500 cr |
Licenses are paid up-front; partial-day refunds are not granted on cancellation. The license row is created at any AM-flagged station via a single docked transaction; the player does not need to be in the licensed sector to purchase.
License effects¶
- Licensed mining in an AM-claimed sector: harvest proceeds normally, no rep penalty, +1 AM rep per harvest (stacks with the base mining rep gain — see Faction reputation hooks).
- Unlicensed mining in an AM-claimed sector: harvest proceeds, but −10 AM rep per extraction. Repeated unlicensed extraction trips the AM faction enforcement path (NPC patrol response in
MILITARY_ZONEoverlap clusters). - Mining in unclaimed asteroid sectors: no license required, no AM rep penalty. Frontier-zone unclaimed asteroids additionally grant a small Frontier Coalition rep boost — see hooks below.
Status: ✅ Shipped — the
ClaimLicensemodel and thePOST /mining/licensepurchase/renewal endpoint are live (mining_service.py:purchase_license, charging500 cr × richness_tieron a new license and 80% of that on renewal). Licensed mining grants +2 AM rep, unlicensed extraction in AM space applies the −10 AM rep penalty, and unclaimed asteroids grant the base +1 AM tick — all wired inmining_service.py:harvest. The repeated-unlicensed AM enforcement (NPC patrol response) is ✅ Shipped on feat tip (d6cf8cd9/ PR #627 MERGED): provisional implementation defaultAM_UNLICENSED_ENFORCEMENT_THRESHOLD=3unlicensed AM harvests in a rolling 24h window → LAW_ENFORCEMENT /route_engagement(unlicensed_am_mining)with MILITARY_ZONE LE relocate fallback; soft no-op when no in-jurisdiction LE is available (N=3/24h pending balance ratification — not settled FEATURES balance).
Asteroid depletion¶
Each ASTEROID_FIELD sector carries a Sector.resources.depletion_pool — a counter that decrements per harvest and self-replenishes on a real-time timer. Depletion modulates yield band but never floors it to zero.
| Depletion state | Pool consumed | Yield modifier | Replenishment |
|---|---|---|---|
| Fresh | 0% | 1.0× (full band) | n/a |
| Light | < 5% per attempt | 1.0× (full band) | Auto-replenish in 24h real time after last harvest |
| Moderate | 5–50% | 0.75× | Auto-replenish in 24h real time |
| Heavy | 50–90% | 0.5× | Auto-replenish requires 7 real-time days |
| Exhausted | > 90% | Floor — minimum 1 ore per attempt regardless of band | 7 real-time days to fresh |
The hard floor is enforced server-side: if the rolled band would return zero (after multipliers and depletion), the harvest emits 1 ore and the depletion pool is unchanged for that attempt. This guarantees a non-zero feedback signal and prevents stuck "sector worthless" states.
Pool size scales with richness tier: tier 1 = 100 units, tier 5 = 500 units. The pool is decremented by the rolled ore yield; precious metals and quantum shards do not count against the pool.
Status: ✅ Shipped — the
depletion_poolJSONB key, the tier-scaled pool size (tier × 100), the per-harvest decrement by rolled ore yield, the Fresh/Moderate/Heavy yield modifiers (1.0× / 0.75× / 0.5×), the 1-ore hard floor, and the real-time auto-replenishment timer (24h Light/Moderate / 7-day Heavy recovery via_depletion_replenish_hours+_maybe_replenish_depletion_pool) are implemented inmining_service.py(PR #216, merged 2026-08-05).
Faction reputation hooks¶
All AM deltas applied via services/gameserver/src/services/faction_service.py:apply_faction_rep_delta against FactionType.MINING, in the same transaction as the harvest, per the contract documented in ../gameplay/factions-and-teams.md#reputation-triggers.
| Action | Faction | Delta | Notes |
|---|---|---|---|
| Mine in any asteroid sector with Mining Laser equipped | Astral Mining Consortium | +1 / harvest | Base AM rep tick — applies in all asteroid sectors regardless of claim status. |
| Mine in AM-claimed sector with valid license | Astral Mining Consortium | +1 / harvest | Stacks with the base tick: licensed mining in AM-claimed space yields +2 AM rep / harvest. |
| Mine in AM-claimed sector without license | Astral Mining Consortium | −10 / extraction | Per-attempt penalty (✅ Shipped). Repeated incidents trip AM enforcement — ✅ Shipped on feat tip (d6cf8cd9 / PR #627 MERGED); provisional N=3 unlicensed AM harvests / rolling 24h → route_engagement(unlicensed_am_mining) (implementation default pending balance ratification). |
| Mine in Frontier-zone unclaimed asteroid | Frontier Coalition | +5 / harvest | ✅ Implemented on LEG-97 tip / pending merge (0274a1b1 / PR #620 — open as of 2026-08-20, not yet on feat/new-feature-development). mining_service.resolve_harvest applies FC_REP_UNCLAIMED_FRONTIER = 5 via apply_faction_rep_delta(..., FactionType.INDEPENDENTS, faction_name='Frontier Coalition') only for FRONTIER-zone unclaimed harvests (AM-claimed / non-FRONTIER → 0; null zone_id → no FC, intentional). Seeded Independents row named Frontier Coalition — not a missing faction. |
Sell raw ore to AM-flagged refinery |
Astral Mining Consortium | +2 / 5,000 cr of trade volume | Already in ../gameplay/factions-and-teams.md#reputation-triggers; listed here for completeness. |
Buy a ClaimLicense at any AM-flagged station |
Astral Mining Consortium | +15 / purchase | Single-shot reputation boost on license-fee payment (matches ../gameplay/factions-and-teams.md#reputation-triggers). |
Reputation deltas are written inside the same transaction as the harvest commodity grant; a failed harvest does not move the rep counters.
Status: 🚧 Partial — all Astral Mining Consortium hooks (the +1 base tick, the +2 licensed total, the −10 unlicensed penalty, and the +15 license-purchase boost) are wired in
mining_service.pyon feat tip. The Frontier Coalition +5 unclaimed-asteroid hook is ✅ Implemented on LEG-97 tip / pending merge (0274a1b1/ PR #620 — open as of 2026-08-20, not yet onfeat/new-feature-development); the old “awaits a Frontier Coalition faction row” premise is stale (seeded Independents named Frontier Coalition). Repeated-unlicensed AM NPC patrol enforcement is ✅ Shipped on feat tip (d6cf8cd9/ PR #627 MERGED): provisionalAM_UNLICENSED_ENFORCEMENT_THRESHOLD=3/ 24h → LAW_ENFORCEMENT /route_engagement(unlicensed_am_mining)with MILITARY_ZONE LE relocate fallback; soft no-op if no in-jurisdiction LE (N=3/24h implementation default pending balance ratification).
PvP interaction¶
While Ship.status = MINING, the ship is stationary and visible in the sector for the duration of the harvest action. Any other player in the sector can attack a mining ship; the mining ship cannot maneuver or flee until the action resolves.
Interrupt behaviour¶
If a mining ship is attacked or otherwise interrupted (sector evacuation, warp gate collapse, etc.) before the harvest resolves:
- The harvest cancels — no commodities are awarded.
- 50% of the 5-turn cost is refunded (2 turns returned to
Player.turns; the other 3 are sunk). - The sector's
depletion_poolis not decremented for the cancelled attempt. - The Mining Laser is undamaged; the player can re-attempt as soon as the threat resolves.
Ship.statusresets toIN_SPACE(orIN_COMBATif the interrupt was a successful PvP attack).
The 50% refund is the design lever that disincentivises grief-interrupting cheap harvests while still penalising the miner for poor situational awareness.
Status: ✅ Shipped (WO-MINING-ASYNC-HARVEST + WO-MINING-PVP-INTERRUPT) — harvest is genuinely async now: start persists a
MiningHarvestrow (PENDING, snapshottinglaser_level/richness_tier/license state so resolve/interrupt aren't affected by later equipment or license changes) withresolves_atset, turns are prepaid at start (HARVEST_TURN_COST = 5), and yields apply only when the scheduler sweep completes it (COMPLETED).mining_service.interrupt_pending_for_shipcancels an in-flightPENDINGrow toINTERRUPTEDand refundsfloor(turns_spent / 2), wired at the combat-hit call sites incombat_service.py. At most one in-flight harvest per ship (partial-unique constraint onstatus='PENDING', enforced in the Alembic migration since SQLAlchemy can't express a partialUniqueConstraintportably). Matches the interrupt-behaviour steps above exactly — this was previously the target spec, now it's the shipped mechanism.
Player-facing affordances¶
- ✅ Empty-state
⛏️ HARVESTin anASTEROID_FIELDsector view (GameDashboard.tsxplanetary-harvest-btn→handleHarvest→POST /api/v1/mining/harvest; origin/feat46bce720). Label is HARVEST, not Mine. Renders only whenplanetsInSector.length === 0 && stationsInSector.length === 0; any planet or station hides the control. Whileflying, the same empty row shows🛑 HALT ▸instead (GameDashboard.solarRowStateMachine.test.tsx). Post-clickHARVEST FAILEDalerts viaHARVEST_GATE_COPY(no_mining_laser,must_be_undocked,cargo_full, …) remain ✅. - 📐 Pre-click grey-out with reason text (no laser, docked, cargo full, unlicensed) — Design-only. The HARVEST control
disabledonly forhelmBusy || harvestBusy; gate copy is post-click, not a greyed control. - 📐 One-click Buy License CTA on the sector harvest control — Design-only. Unlicensed success shows
am_rep_delta < 0warning copy only. License purchase/renew lives on SpaceDockMiningVenue.tsx(Claim License card), not the HARVEST button. Do not conflate MiningVenue with the sector harvest affordance. - When a compatible ship lacks
equipment_slots.mining_laser, the mining venue also exposes an Install Mining Laser CTA (catalog 35,000 cr viashipUpgradeAPI.installEquipment('mining_laser')) — ✅ Implemented on LEG-109 tip / pending merge (229d2579/ PR #622 — open as of 2026-08-20, not yet onfeat/new-feature-development). ModuleGrid mining-family ladder UI remains 📐 Design-only (out of scope for this CTA). - Yield preview (band for current sector × laser level × richness tier before committing the 5 turns) — GS band+ETA server side ✅ Shipped on tip
46bce720(LEG-427 / PR #714 —depletion_band_for_consumed_fraction+build_asteroid_depletion_readouton sector/player reads). PC yield-preview UI (LEG-424 route + LEG-425/428 PC surface) remains 🚧 tip-pending — tip PC has no harvest-preview UI; do not read this bullet as PC-Shipped. - Depletion overlay (four-step Fresh / Light / Moderate / Heavy indicator + real-time replenishment countdown when Heavy) — GS depletion band+ETA ✅ Shipped on tip
46bce720(LEG-427 / PR #714). PC depletion overlay (LEG-425/428 PC overlay consumer) remains 🚧 tip-pending — tip PC has zero depletion-overlay consumer; depletion math remains ✅ Shipped — do not conflate pool math with a live overlay. - ARIA market-intelligence overlay surfaces nearest AM-flagged refinery for ore offload, with current buy price — GS GET
/mining/nearest-am-refinery✅ Shipped on tip46bce720(PR #715 squash / LEG-430;api/routes/mining.py:204→MiningService.find_nearest_am_refinery; honest emptyfound: falsewhen none reachable). Player-client AM ore overlay still 🚧 tip-pending (LEG-432/#439) — tip PC has zero nearest-AM-refinery consumer — do not read this bullet as overlay-Shipped. - License panel at AM-flagged stations lists the player's active and recently expired licenses with one-click renewal — 🚧 tip-pending (same tip
46bce720; invent LEG-435/#442 GET license list + LEG-436/#443 panel UI). Tip has POST/mining/licenseonly and MiningVenue purchase/renew CTA only — no GET license list / active+expired panel — do not read this bullet as tip-Shipped. - Notifications fire on harvest success (with yield),
precious_metalsrare drop,quantum_shardstrace drop, and license expiry warning (1h before) — ✅ tip-PRESENT on feat tip935e557d3ancestor (#946 MERGED / LEG-2737/#2738): GSmining_service.pyemitsmining_harvest_notificationsubtypes (harvest_success,precious_metals,quantum_shards) +mining_license_expiry_warning; PCminingWsNotifications.ts+WebSocketContext.tsx:1042-1054toast wiring +spacedockVenueBus.tsAstral Mining deep-link on expiry; VitestminingWsNotifications.test.ts+WebSocketContext.miningNotifications.test.tsx. Depletion-state transitions on bookmarked sectors remain deferred until a sector-bookmark substrate exists (honesty — do not invent bookmarks).
Anti-cheat / safety¶
- The harvest endpoint is server-authoritative and atomic; commodity grants and turn deductions occur in the same transaction.
- Yield rolls use a cryptographically secure RNG, matching the standard set in
../galaxy/generation.mdfor Genesis device rolls. - The
MININGstatus row takeswith_for_updateon the ship to serialise concurrent attempt requests from a single ship. - License-validity checks are read-locked against
ClaimLicenserows; expiry timestamps are server-clock authoritative. - Per-sector
depletion_pooldecrements are atomic with the commodity grant; a failed grant rolls back the pool decrement.
Source map¶
| Topic | Path |
|---|---|
| Mining service | services/gameserver/src/services/mining_service.py |
| Sector asteroid extensions | services/gameserver/src/models/sector.py — the Sector.resources JSONB carries asteroid_richness, depletion_pool, and has_deep_asteroids (derived on demand by mining_service.py:_ensure_asteroid_richness) |
| Claim license model | services/gameserver/src/models/claim_license.py |
| Mining Laser equipment | services/gameserver/src/services/ship_upgrade_service.py:EQUIPMENT_DEFINITIONS["mining_laser"] |
| Mining Laser upgrade ladder | services/gameserver/src/services/ship_upgrade_service.py:MINING_LASER_LADDER["mining_laser_level"] |
MINING ship status |
services/gameserver/src/models/ship.py:ShipStatus.MINING |
| Faction rep hook | services/gameserver/src/services/faction_service.py:apply_faction_rep_delta — AM deltas on feat tip; Frontier Coalition +5 FRONTIER-unclaimed via mining_service.FC_REP_UNCLAIMED_FRONTIER + faction_name='Frontier Coalition' — ✅ Implemented on LEG-97 tip / pending merge (0274a1b1 / PR #620) |
| AM-flagged refinery sale | services/gameserver/src/services/trading_service.py |
| Mining API | services/gameserver/src/api/routes/mining.py (registered in services/gameserver/src/api/api.py) |
Related¶
./trading.md— ore pricing, AM-flagged refinery sale path.../galaxy/generation.md—ASTEROID_FIELDdensity and cluster-type distribution.../galaxy/quantum-resources.md— primary Quantum Shard chain; mining is a trace secondary inflow.../gameplay/ship-systems.md— Mining Laser equipment definition and upgrade architecture.../gameplay/factions-and-teams.md— Astral Mining Consortium reputation contract.../planets/professions.md— Mining Engineers and Trade Specialists profession bonuses.../../DATA_MODELS/galaxy.md—Sector.resourcesJSONB schema; the target asteroid-mining keys extend this.