feat: career-mode handler surface (tech / kc / sci / contracts / launch / actiongroups)#86
Open
jonpepler wants to merge 7 commits into
Open
feat: career-mode handler surface (tech / kc / sci / contracts / launch / actiongroups)#86jonpepler wants to merge 7 commits into
jonpepler wants to merge 7 commits into
Conversation
10 tasks
55e728c to
e037a4a
Compare
e037a4a to
08138b1
Compare
Six new handlers and ~50 new data keys covering more of the
career-mode UI surface — useful for external dashboards that
want to mirror or drive R&D, KSC, science, contracts, and
launch/recovery from outside Flight:
TechTreeDataLinkHandler tech.nodes / tech.unlock / …
KscDataLinkHandler kc.facilities / kc.crewRoster …
ScienceInstrumentsDataLinkHandler sci.instruments / sci.breakdown
ContractsDataLinkHandler contracts.active / accept / …
LaunchDataLinkHandler ksp.launch / recover / revert*
ActionGroupBindingsDataLinkHandler f.ag.bindings
- All action keys are AlwaysEvaluable = true so they're invokable
from Space Center / Editor / Tracking Station, not just Flight.
Each handler refuses internally when the underlying state isn't
ready (e.g. ksp.launch refuses outside SC/Editor or with an
active vessel).
- Contracts:
* Contract IDs serialise as JSON strings (`"id": "12345..."`)
not numbers, because they often exceed 2^53 and silently lose
precision in JS.
* Parameter rows include the parameter's runtime type plus the
well-known typed fields (minAltitude/maxAltitude/body/situation/
partName) so consumers can render proper progress bars without
name-string heuristics.
- Launch: VesselCrewManifest is always built from the .craft,
even unmanned. Passing null NREs inside setStartupNewVessel and
leaves Flight half-initialised (frozen HUD, sentinel values).
Rebuilt the schema + openapi to pick up the tech.*, kc.*, sci.*, contracts.*, ksp.* and f.ag.bindings entries. `tools/generate-openapi.ts` also now omits the `sourceFile` field from the committed `docs/api-schema.json`. The field is populated with an absolute path from the build machine, so it differs between contributors and produces a large per-rebuild diff on the committed artifact. Nothing in the docs site consumes it; keeping it out of the committed JSON keeps the review surface focused on actual schema changes. The field is still present on the in-memory entries the generator works with.
08138b1 to
2e8d48d
Compare
ebb43ad to
ba6ff93
Compare
Both additions live-verified against a running KSP install.
tech.nodes (TechTreeDataLinkHandler)
Full tech tree — every node with id, title, description,
scienceCost, state, parents, parts. AlwaysEvaluable=true so
the career UI can read it from the Space Center scene without
an active vessel.
- Description parses out of tree.GetTreeConfigNode()'s RDNode
entries and routes through Localizer.Format, so #autoLOC
tokens resolve to readable English (e.g. "How hard can Rocket
Science be anyway?" for Basic Rocketry).
- Parts list builds from PartLoader.LoadedPartsList, grouping
by AvailablePart.TechRequired. Per-part payload: name
(internal id), title (display), manufacturer, category
(PartCategories enum name), entryCost, purchased
(PartTechAvailable + PartModelPurchased).
- Parents read directly from ProtoRDNode.parents (elements
are ProtoRDNode, not wrapped — ProtoRDNode lacks a `.parent`
field).
- Two session-stable indexes (_descriptionsByTech,
_partsByTech) built lazily on the first call and reused for
the lifetime of the KSP process; payload itself sticky-cached
during the IsTransientLoadingState() window the way
unlockedIds / affordable already are.
kc.facilityLevels (KscDataLinkHandler)
Each facility entry now carries currentLevelText +
nextLevelText — multi-line descriptions matching the bullet-
point block KSP's upgrade dialog renders. nextLevelText is
the empty string when the facility is already at max tier.
Pulls from UpgradeableFacility.GetLevelText(int lvl) on the
facilityRefs[0] instance via
ScenarioUpgradeableFacilities.protoUpgradeables, same access
path the existing upgradeFunds lookup uses. Wrapped in
try/catch matching the surrounding style — best-effort, empty
strings when refs unavailable (e.g. before SC scene loaded).
Curls verified against the live Telemachus instance:
tech.nodes
→ hundreds of nodes, descriptions resolved, parts list with
manufacturer + category + entryCost + purchased per part.
kc.facilityLevels
→ admin currentLevelText starts at tier 1 wording, vab at
level 2 / max 2 returns nextLevelText "" (correctly empty
at max).
Builds clean under dotnet 10 against the bundled Assembly-CSharp.
KSP restart required to pick up the new TelemetryAPI entries.
ba6ff93 to
f23f7f2
Compare
Adds three TelemetryAPIs under a new StrategiesDataLinkHandler: strategies.all (read), strategies.activate[id, factor] (action), and strategies.deactivate[id] (action). The handler bypasses Administration.Instance — a MonoBehaviour live only while the Admin Building dialog is open — by replicating Strategy.CanBeActivated / Activate / Deactivate against StrategySystem, GameVariables, ScenarioUpgradeableFacilities, Funding, Reputation, ResearchAndDevelopment, and Planetarium so the operations work from any scene. Reflection is used only to write Strategy's private `isActive` and `dateActivated` fields; everything else is a public API call. strategies.all exposes a sidecar `effectiveCostReputation` per strategy that mirrors Reputation.addReputation_granular's per-unit walk against GameVariables.reputationSubtraction so consumers see the real rep loss on activate (the nominal 14.5 charged near rep cap can deduct ~27). Adds CurrencyModifiers — a one-liner wrapper around GameEvents.Modifiers.OnCurrencyModifierQuery — and uses it to bake sidecar effective-cost fields onto existing endpoints so consumers see the post-strategy charge alongside the nominal: - kc.facilityLevels.upgradeFundsEffective (StructureConstruction) - kc.savedShips.requiresFundsEffective (VesselRollout) - tech.nodes.scienceCostEffective (RnDTechResearch) - tech.nodes.parts[].entryCostEffective (RnDPartPurchase) README, OpenAPI, and JSON schema updated to match.
The career-mode handler rows leaked internal KSP and Telemachus
terminology into user-facing descriptions. Reword so the readme
reads as an overview and the OpenAPI schema remains the technical
reference:
- Drop CurrencyModifierQuery / TransactionReasons references
from tech.nodes, kc.facilityLevels, kc.savedShips — say
"nominal and strategy-modified" instead.
- Replace AlwaysEvaluable phrasing with "Callable from any
game scene" in the tech.* section blurb.
- Trim kc.crewRoster row from a field list to a summary.
- Promote strategies.* to its own section with a one-line note
on the in-any-scene activate / deactivate path.
- Split the trailing WIP sci.* / career.* / comm.* table into
a dedicated career.* and comm.* section (now both
production-ready), drop the duplicate sci.* rows that
overlapped the existing sci.* section, and remove the WIP tag.
- Move f.ag.bindings into the f.* action-groups table rather
than its own one-line subsection.
The trailing "…" suggested an open-ended enumeration but the four scene names listed are the complete set. Use an explicit "or" instead so readers don't go hunting for the missing ones.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Six new handlers + ~50 data keys covering more of the career-mode UI surface - useful for external dashboards that want to mirror or drive R&D, KSC, science, contracts, and launch/recovery from outside Flight. All new keys are
AlwaysEvaluable = trueso they're queryable from Space Center / Editor / Tracking Station.Builds on #84 (now merged) for the
AlwaysEvaluableattribute to take effect on the new action keys.The branch has grown since the first review - a Strategies / Administration Building handler and a
CurrencyModifierQuery-aware effective-cost layer have been added on top of the original six handlers. See "Updates since first review" below.New handlers
TechTreeDataLinkHandlertech.nodes,tech.unlock[id],tech.unlockedIds,tech.unlockedPartCount,tech.affordableKscDataLinkHandlerkc.facilityLevels,kc.upgradeFacility[name],kc.crewRoster,kc.scene,kc.savedShips,kc.partsAvailableScienceInstrumentsDataLinkHandlersci.instruments,sci.experimentBreakdown,sci.experiments,sci.canRecoverTotal,sci.canTransmitTotal,sci.deploy[partFlightId, experimentId],sci.transmit[partFlightId]ContractsDataLinkHandlercontracts.active,contracts.offered,contracts.completedRecent,contracts.accept[id],contracts.decline[id],contracts.cancel[id]LaunchDataLinkHandlerksp.launch[ship, facility, site, crew],ksp.recover,ksp.revertToLaunch,ksp.revertToEditor[vab|sph],ksp.toSpaceCenter,ksp.toTrackingStation,ksp.canRevert*ActionGroupBindingsDataLinkHandlerf.ag.bindings(which parts/modules/actions are bound to each AG)StrategiesDataLinkHandlerstrategies.all,strategies.activate[id, factor],strategies.deactivate[id]Design notes
Contracts long ids as strings. Contract IDs frequently exceed 2^53 and silently lose precision when serialised as JSON numbers.
contracts.*rows emit"id": "<digits>"and the action handlers accept both string and number forms for back-compat. Contract parameter rows include the parameter's runtime type plus typed fields (parameterType/minAltitude/maxAltitude/body/situation/partName) so consumers can render real progress bars without name-string heuristics.Action handlers fire from any scene.
ksp.launchrefuses outside SC/Editor;ksp.revertToLaunchrefuses outside Flight;tech.unlockrefuses unaffordably-priced nodes; etc. - internal state checks, not blanket scene gates.ksp.launchalways builds aVesselCrewManifesteven for unmanned launches. Passing null NREs insidesetStartupNewVesseland leaves Flight half-initialised (frozen HUD, sentinel values).Recovery / crash / flight-log keys are NOT in this PR - they're separately submitted as event-driven snapshot handlers in #85.
Updates since first review
Two follow-on areas have been added in the same career-mode surface. Both ride this branch.
Tech-tree enrichment
tech.nodesrows now include:description- parsed from the tech tree's underlyingRDNodeconfig and run throughLocalizer.Formatso it matches what KSP shows in the R&D dialog.parts- one entry perAvailablePartassigned to the node, withname/title/manufacturer/category/entryCost/purchased. Built once per game-load by indexingPartLoader.LoadedPartsListbyTechRequired.kc.facilityLevelsrows now includecurrentLevelTextandnextLevelText(fromUpgradeableFacility.GetLevelText(int)) so consumers can show KSP's multi-line upgrade dialog text without mirroring it locally.Strategies / Administration Building
A new
StrategiesDataLinkHandleradds three keys:strategies.all,strategies.activate[id, factor],strategies.deactivate[id]. The interesting bit is that all three work from any scene.KSP's
Strategy.CanBeActivated/Activate/DeactivatedereferenceAdministration.Instance, aMonoBehaviourthat's only alive while the Admin Building dialog is open. That makes the stock methods unusable from a remote dashboard when the player isn't in front of the dialog. The handler replicates each check and mutation against publicly-accessible state (StrategySystem,GameVariables,ScenarioUpgradeableFacilities,Funding,Reputation,ResearchAndDevelopment,Planetarium) so the same operations work from Space Center, Editor, Flight, or Tracking Station. Reflection is used only to writeStrategy.isActiveandStrategy.dateActivated; everything else is a public API call. Behaviour matches stock KSP 1.12.The replica section is cleanly fenced inside the handler with header/footer comments so the boundary against stock behaviour is obvious to anyone reading the file.
Currency-modifier effective costs
KSP's
{Funding, ResearchAndDevelopment, Reputation}.Add*methods apply the mutation before firingGameEvents.Modifiers.OnCurrencyModifierQuery, so consumers can't adjust the deduction by listening - they have to pre-query the modifier separately. Without that step, dashboards showing the nominal cost of a launch / facility upgrade / tech purchase will be wrong whenever a strategy is active (e.g. Aggressive Negotiations advertises -1.5% on launches and R&D purchases, and -0.05% on facility construction).A new
CurrencyModifiershelper wraps the pre-query in a one-liner. Existing rows now include a sidecar field next to each cost:kc.facilityLevels.upgradeFundsupgradeFundsEffectiveStructureConstructionkc.savedShips.requiresFundsrequiresFundsEffectiveVesselRollouttech.nodes.scienceCostscienceCostEffectiveRnDTechResearchtech.nodes.parts[].entryCostentryCostEffectiveRnDPartPurchasestrategies.allalso includeseffectiveCostReputationper strategy, which mirrorsReputation.addReputation_granular's per-unit walk againstGameVariables.reputationSubtraction. The rep curve makes losses near the cap cost meaningfully more than at zero - a 14.5-nominal cost can deduct ~27 at 976 rep, so the nominal figure on its own is misleading.Validation
Tested locally on KSP 1.12.x with a custom HTTP/WS dashboard, in a career save:
tech.nodeslisted every node + its unlocked state from the Space Center;tech.unlock[experimentalRocketry]deducted science and unlocked the node in the R&D UI on the next tick. Per-nodedescriptionandpartsmatched the in-game R&D dialog content.kc.facilityLevelsreturned the expected building list with current/max upgrade levels andcurrentLevelText/nextLevelTextmatching the in-game upgrade dialog;kc.upgradeFacility[LaunchPad]deducted funds and started the upgrade animation.kc.crewRosterexposed the full kerbal roster including experience and current assignment fields.contracts.activerows includedparameterTypeplus the typed fields for the well-known parameter classes (altitude, body, situation, part). Long contract IDs round-tripped correctly as strings.ksp.launch[Stayputnik X, VAB, LaunchPad, Jeb;Bill]launched the saved craft with the named crew aboard. Refuses outside SC/Editor as designed.ksp.*scene actions worked correctly.f.ag.bindingsreturned one row per (action, group) pair on a vessel with multiple custom bindings.strategies.allreturned from the Space Center without the Admin dialog being open; the currently-active strategy showedisActive: true / canDeactivate: trueand every other strategy was blocked by the stock 1-active-limit gate with the localised human-readable reason. Round-tripped deactivate + activate end-to-end (Aggressive Negotiations → off, Fundraising Campaign → on at factor 0.05); change was reflected immediately in bothstrategies.alland the in-game Admin Building dialog. Per-strategyeffectiveCostReputationmatched the post-curve deduction observed on real activation to within ~1 unit.effective.