Repository navigation
feat: sync away team assignments - #295
Conversation
Add SyncConfig::Type::AwayAssignments as the first enumerator (mirrors Jobs/Missions/Traits naming), a corresponding away_assignments member (default false, matching every other sync data flag), and its SyncOptions row so TOML loading, per-target overrides, runtime.vars write-back and JSON type serialization all pick it up automatically. config.cc needs no changes since everything iterates SyncOptions. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Add processors::away_assignments_list (EntityGroup 111, AwayAssignmentUserListResponse) and processors::away_assignment_instance (EntityGroup 113, bare AwayAssignmentInstance), following the same two-processors-plus-shared-helper split used for entity_slots / entity_slots_data + single_slot_locked, since no entity group carries a single-instance wrapper here. away_assignment_event() builds the sync event for one instance and only emits it when a hash of its observable fields changes, tracked in the new trackers::away_assignment_states map (mirrors slot_states). The list processor also prunes ids that drop out of the response and emits a collected_away_assignment event for each, mirroring how jobs() prunes completed jobs. Wire both entity groups into HandleEntityGroup's dispatcher as the first (alphabetical) block, gated by the new away_assignments option. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
jobs(): add JOBTYPE_AWAYASSIGNMENT so away-team jobs flow through the existing job/completed_job events with an "aid" pointing at the instance, in the same style as the other job-type cases. officers(): surface Officer.awayAssignmentInstanceId as "away_assignment_id" on the officer event. The officer de-dup state is a struct local to officers() (kept separate from the shared RankLevelShardsState used by forbidden_tech) so this extra field doesn't change forbidden tech's comparison at all, while still causing an officer to re-emit if only their away assignment changes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Add the away_assignments key as the first entry of the [sync] "Subgroup: Data" block in all 10 localized example config files, matching the existing comment/blank-line rhythm of the other data keys. da/de/es/fr/nl/ru translations follow each file's own missions line pattern and are best-effort; tlh follows the file's established "Do you want Daq sync X De'?" template. The unsuffixed pointer file and [sync.targets.*] example blocks are intentionally left untouched, matching how every other sync data key (e.g. traits) is handled there. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
officerIds is a protobuf map, whose iteration order does not match slot order (observed e.g. 2=.., 0=.., 1=..). Sort by slot index before emitting officer_ids so the array, and the de-dup hash built from it, are stable run to run. Remove the AwayAssignmentsInstance (group 113) processor and dispatch case: it was never observed on the wire during probing (not at login, not on opening the screen, not on collect), so its payload shape is unverified and we should not ship unverified parsing. AwayAssignmentsList (group 111) is the only observed source of away assignment sync data and remains the single caller of away_assignment_event(), whose comment now states plainly that the caller must hold trackers::away_assignment_states_mtx. Also runs clang-format over the touched line ranges in config.h and sync.cc to keep the branch's own added/changed lines clean. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
netniV
left a comment
There was a problem hiding this comment.
Changes look good. @lightbull-stfc may want to weigh in on whether we need anything more or compatibility for Spocks Club.
lightbull-stfc
left a comment
There was a problem hiding this comment.
Let's explore if there's a way to emit updates to away_assignments at a point when an assignment is started (in addition to when the whole list is refreshed) to avoid using officer updates for that.
| {"level", officer.level()}, | ||
| {"shard_count", officer.shardcount()}}); | ||
| {"shard_count", officer.shardcount()}, | ||
| {"away_assignment_id", officer.awayassignmentinstanceid()}}); |
There was a problem hiding this comment.
This shifts the burden to the sync target to piece back the data together. Slot order is unavailable in this hook and is thus lost. I would prefer to remove it and emit a started assignment through other means.
| {"duration", instance.parameters().duration()}, | ||
| {"rarity", instance.rarity()}}; | ||
|
|
||
| const auto state_value = static_cast<size_t>(std::hash<json>{}(event)); |
There was a problem hiding this comment.
Redundant static_cast
| @@ -1446,7 +1548,8 @@ static void officers(std::unique_ptr<std::string>&& bytes) | |||
| std::scoped_lock lk(officer_states_mtx); | |||
|
|
|||
| for (const auto& officer : response.officers()) { | |||
| const RankLevelShardsState officer_state{officer.rankindex(), officer.level(), officer.shardcount()}; | |||
| const OfficerState officer_state{officer.rankindex(), officer.level(), officer.shardcount(), | |||
| officer.awayassignmentinstanceid()}; | |||
There was a problem hiding this comment.
I would prefer not to have Away Assignments mixed with officers. For users to benefit from this feature addition, they'd currently have to enable away_assignments, officers, and jobs options in the config.
Address sync maintainer review feedback on PR STFC-Mod#295: - Revert processors::officers() to be byte-identical to dev. Away assignment status stays off officer events; slot order isn't available in that hook, so instead of losing it we emit assignment starts through a dedicated event (see below). - Drop a redundant static_cast in away_assignment_event(): std::hash already returns size_t, matching away_assignment_states's value type. - Restore processors::away_assignment_instance for EntityGroup 113 (AwayAssignmentsInstance), parsed as a bare AwayAssignmentInstance and routed through the existing away_assignment_event()/ away_assignment_states_mtx so list and instance updates share one de-dup state and the slot-ordered officer_ids. Live capture against the Windows client confirms group 113 fires when an assignment is started: first at state=1 (READYTOSTART) with officerIds already populated and an empty jobUuid, then ~1.5s later at state=2 (INPROGRESS) with jobUuid set. Wired into HandleEntityGroup's dispatcher right after the existing AwayAssignmentsList case, gated by the same away_assignments option. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Thanks for the review — all addressed in 6bd1893:
The PR description is updated with the details. Fork CI is green on this exact commit (Windows, both macOS archs, package, config validation); the workflows here still need a maintainer's approval to run. |
netniV
left a comment
There was a problem hiding this comment.
Thanks for the update. Unfortunately, there are now conflicts with this branch. Could you resolve those so myself and @lightbull-stfc can review once more.
…signments # Conflicts: # mods/src/config.h # mods/src/patches/parts/sync.cc Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Conflicts resolved by merging the current |
|
I have approved, but I'll let @lightbull-stfc have the final say. |
…signments Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Client build 267 (2026-09-29) delivers battle result headers as entity group 219 (BattleResultHeadersResponse) and owned ships as group 251 (PlayerShipsResponse) instead of the Json group. Implement the 219 processor and add a 251 processor, sharing helpers with the Json handlers. Add the client's EntityGroup values 242-252, and declare only the proto fields that are read. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…assignments Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…signments Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Summary
Adds Away Team data to the sync feature so consumers can tell which officers are currently away and until when. Today
processors::jobs()skipsJOBTYPE_AWAYASSIGNMENTand the dispatcher ignores theAwayAssignments*entity groups, so a crew-planning tool will happily recommend an officer who is off on a 2-day away mission.Everything is additive, read-only, reuses the existing
ProcessResultInternaldispatcher (no new hooks), and is gated like every other data type.What changes
New sync option
away_assignments([sync]and per-target, same semantics/defaults as the other data flags) → new data typeaway_assignment. It is one new row inSyncOptions, soconfig.ccneeds no changes.away_assignmentevents, built by one shared helper (away_assignment_event) so both sources share the same de-dup state and slot-orderedofficer_ids:AwayAssignmentsList(111,AwayAssignmentUserListResponse) — sent when the Away Teams screen is opened and after collecting a mission.AwayAssignmentsInstance(113, a bareAwayAssignmentInstance) — sent when an assignment is started: first withstate=1(READYTOSTART), officers already chosen and an emptyjob_uuid, then ~1.5s later withstate=2(INPROGRESS) andjob_uuidset.Example event:
{"type":"away_assignment","aid":2814298639311718967,"template_id":4186662802,"state":2, "officer_ids":[3479870516,440622131,2520801863],"job_uuid":"60f7…264b","duration":43200,"rarity":2}officer_idsis emitted in slot order (the proto map iterates in arbitrary order). Events are only emitted when the instance's observable state changed since it was last sent. When an instance disappears from the list a{"type":"collected_away_assignment","aid":…}event is sent and its state is pruned, mirroringcompleted_job.jobevents now includeJOBTYPE_AWAYASSIGNMENTwith{"aid": …}, sostart_time/durationflow through the existingjob/completed_jobevents (gated by the existingjobsoption) for the instance with the matchingaid.All 10 localized example configs get the new key (first entry of
Subgroup: Data, alphabetical). The da/de/es/fr/nl/ru comments are best-effort translations in each file's existing style — corrections welcome.Officer events (
processors::officers()) are untouched by this PR — away-assignment status is not mixed into them. A prior revision added anaway_assignment_idfield there, but slot order isn't available in that hook, so instead of shipping an event that loses it, assignment starts are now reported directly via group 113 above.Verified against the live client (Windows, probe build, 2026-09-20)
AwayAssignmentUserListResponseand re-serialises byte-exact;officerIdsis slot index → officer id. Sent when the Away Teams screen is opened and in the response to collecting a mission (collected instance appears once with state 5, then drops out →collected_away_assignment). Lists were always complete, so pruning is safe.AwayAssignmentInstanceand re-serialises byte-exact (285/285 and 317/317 bytes on the two captures); cross-parsing the same bytes asAwayAssignmentUserListResponseyields 0 instances, confirming it is not a wrapped list. It fires when an assignment is started, twice: state=1 (READYTOSTART) withofficerIdsalready populated and an emptyjobUuid, then ~1.5s later state=2 (INPROGRESS) withjobUuidset — the second one arrives in the same server response as the Jobs and Officers groups. It was not seen at login, when opening the Away Teams screen, or on collect (those use the 111 list).[sync], inherited per target, and written tocommunity_patch_runtime.varslike the other data flags (it goes through the sameSyncOptionsloop).Consumers only need
away_assignmentsto know who is away (the state-2 event arrives at the moment an assignment starts); enablejobsas well for the exactstart_time/duration, from thejobevent whoseaidmatches.Note:
aidis an int64 above 2^53, emitted as a JSON number like the other ids in the sync stream; JavaScript consumers should be aware of precision.Checks
build-win,build-mac (arm64),build-mac (x86_64),package-mac,Validate example configs— all green.git diff --checkclean, LF only, clang-format applied to the changed lines only.version.h, orxmake-requires.lock.🤖 Generated with Claude Code