Repository navigation
Protect and recover Kirshara queues after target or address changes #287
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
Guffawaffle
wants to merge
14
commits into
STFC-Mod:dev
Choose a base branch
from
Guffawaffle:feature/faster-queue-recovery
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
d6c10b7
Guard existing IL2CPP class lookup when metadata is unavailable
Guffawaffle 48009dd
Merge remote-tracking branch 'guffawaffle/fix/il2cpp-class-lookup' in…
Guffawaffle 46f144d
Recover stalled queues through guarded native planner paths
Guffawaffle aded064
Align queue recovery PR with upstream dev
Guffawaffle 4d857ad
Complete queue config examples and reuse class lookup helper
Guffawaffle a75a479
Merge remote-tracking branch 'origin/dev' into feature/faster-queue-r…
Guffawaffle ff75bb5
Separate queue hook installation from runtime recovery preferences
Guffawaffle c60b6f8
Remove obsolete feature-derived queue installation helper
Guffawaffle 80b927b
Name queue compatibility checks and failed detours
Guffawaffle b999cdc
Keep missing queue field types safe while logging
Guffawaffle 3e608d8
Align queue recovery PR with current upstream dev
Guffawaffle 579ab2a
Protect Kirshara queues from another ship changing the reported address
Guffawaffle d7929a6
Merge branch 'dev' into feature/faster-queue-recovery
Guffawaffle eddd65b
Merge remote-tracking branch 'origin/dev' into feature/faster-queue-r…
Guffawaffle File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| # Faster Queue Recovery | ||
|
|
||
| When a queued target disappears while a course request is outstanding, the game can remove that target before its failed response arrives. Native failure handling then attempts the removal again. Since it did not remove the front entry this time, it can leave the next target waiting for the watchdog. | ||
|
|
||
| Enable the opt-in recovery preference: | ||
|
|
||
| ```toml | ||
| [control] | ||
| faster_queue_recovery = true | ||
| ``` | ||
|
|
||
| Following #313, default-enabled `[patches].actionqueuerecoveryhooks` independently controls installation. Changing that installation switch requires a restart. Installed callbacks check the current `control.faster_queue_recovery` and `control.queue_enabled` preferences; changing TOML still requires reload/restart. | ||
|
|
||
| The feature also respects `control.queue_enabled`. Hooks are supported on Windows x64 when the required method signatures and queue layout are compatible. Other platforms do not install these hooks; incompatible Windows layouts log an unavailable message and retain native behavior. | ||
|
|
||
| ## Behavior | ||
|
|
||
| The adapter records the latest engagement attempt for up to eight fleets, using weak queue identities and full 64-bit target IDs. A failed, non-recall course response may request native planning only when: | ||
|
|
||
| - The response belongs to the same queue and latest target/attempt, observed within 30 seconds. | ||
| - The queue was engaging on entry to the response and native processing cleared that flag. | ||
| - A different front target remains, and the failed target is absent from every inspected queue. | ||
| - The native retry decision was false. Ordinary retries are unchanged. | ||
|
|
||
| The request record is consumed once. The native planner selects and validates the next target. The mod does not force ship state, clear engagement flags, change retry counts, or remove targets. If another fleet still queues the target, the native cross-fleet removal path remains responsible. | ||
|
|
||
| Queue storage is bounded and validated. Unknown layouts/content are ineligible. Expired/replaced requests release their weak handles; native session cleanup clears all records. Engage, Retry and Course discard pending recovery records when they observe disabled behavior. Both Toggle Queue transitions also clear them, so an off/on toggle cannot reuse authorization from before the toggle. A native attempt returning skip/stop cancels only its own record, preserving newer reentrant attempts. | ||
|
|
||
| There is no watchdog hook, frame scan, timer, background worker, or per-engagement logging. Queue inspection occurs in existing callbacks; all-queue inspection runs only for a potentially eligible failed response. Response matching is not a server-issued request ID, so delayed same-target responses remain an interoperability limitation. | ||
|
|
||
| ## Native integration | ||
|
|
||
| One detour owns each method. Methods are resolved through their complete managed signatures. Installation validates the event and queue field layouts and the engagement result's integer representation. SPUD builds the trampolines using its existing instruction decoder. Addresses and relocation-dependent instruction bytes are not pinned to a client release. | ||
|
|
||
| The hooked methods are `TryPlanPathAndEngageTarget`, `ShouldRetryFailedSetCourse`, `OnSetCourseResponseEventHandler` and `StopWatchdogAndClearAllQueues`. These checks establish binding compatibility; changes to the native retry/planner behavior still require update review and runtime testing. | ||
|
|
||
| The course event is a 24-byte value type, with fleet ID at 0, success/recall at 8/9 and boxed target at 16. Metadata field offsets include the boxed object header. The native retry handler is called synchronously inside the course handler; returning true selects its existing planner branch. | ||
|
|
||
| The older off-screen Kir'Shara combat-completion repair was fixed by Scopely. That workaround and its `kirshara_queue_repair` setting have already been removed; neither is a prerequisite for Faster Queue Recovery. This feature addresses only the separate unavailable-target/course-response race described above. | ||
|
|
||
| ## Validation | ||
|
|
||
| Run `tests/run-action-queue.ps1` on Windows, or compile `tests/action_queue.cc` with a C++23 compiler and `-Imods/src`. Tests use the production policy and request store with fake weak handles. They cover one-shot recovery, reordered/still-present targets, cross-fleet rejection, stale attempts, replaced/collected queues, expiry, reentrant cancellation, bounded capacity and cleanup. They do not model game ABI or network scheduling. | ||
|
|
||
| The prototype produced two observed handoffs with the next target attempted 1–2 ms later and successful responses within 388–532 ms. Those timings are observations, not a latency guarantee. The final adapter requires its own smoke test: normal queued combat; removal of the outstanding target; removal before the first successful course response; queue clear/rebuild; recall; and session restart. Group-wave behavior needs additional coverage when available. |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| # Kir'Shara queue address guard | ||
|
|
||
| Moving or recalling another ship in a different system can temporarily make a | ||
| deployed ship's native player address point at its starbase. A subsequent native | ||
| player-state event then treats that ship's same-system queued targets as foreign | ||
| and clears its queue. This failure was also reproduced by a tester with the mod | ||
| DLL disabled. | ||
|
|
||
| The default-on guard substitutes the ship's current owned deployment address only | ||
| inside the native queue address comparison during a player-state event. It requires | ||
| a validated nonempty queue belonging to that ship and slot, a local deployment | ||
| whose fleet ID matches, no recall or removal, and an active non-warping ship state. | ||
| The reported address must be a planet in another system, and the current target | ||
| must match the deployment's full galaxy/system/planet/instance address. | ||
|
|
||
| The native comparison still checks every target. A missing target, a genuinely | ||
| foreign address, or unknown metadata retains native behavior. The guard writes no | ||
| fleet fields or queue contents and retains no address between events. Explicit | ||
| clearing, recalling the queued ship itself, and genuine system changes remain | ||
| native. This is independent of Faster Queue Recovery and Thin Queue Protection. | ||
|
|
||
| `[control] queue_address_guard = false` disables correction. The separate | ||
| default-on `[patches] queueaddressguardhooks` switch controls hook installation; | ||
| changing it requires a restart. Disabling the queue also disables correction. | ||
|
|
||
| The implementation is shared by Windows and macOS and contains no capture logger, | ||
| polling loop, or diagnostic hooks. It owns four native methods: the player-state | ||
| event handler, the queue address predicate, the player address getter, and the | ||
| deployment lookup. Their Windows x64 client 271 native overwrite windows were | ||
| measured against the matching installed client. Current Mac native hook fit and | ||
| gameplay validation have not been established; successful builds alone do not | ||
| establish those properties. | ||
|
|
||
| Run `tests/run-action-queue.ps1` for local regression checks. Gameplay validation | ||
| must use the clean build: queue A's same-system hostiles, move/recall B in another | ||
| system, and allow A to engage its next target. Then recall A and check its queue | ||
| clears normally. The prior instrumented build passed both controls, and reproduced | ||
| the raw address fault even with fleet observation polling disabled. |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| # Thin Queue Protection | ||
|
|
||
| Restores the active guard from `v2.1.0-guffa.10` alongside Faster Queue Recovery. | ||
| This is separate from the retired Kir'shara combat-completion repair. | ||
|
|
||
| ```toml | ||
| [control] | ||
| thin_queue_protection = true | ||
| ``` | ||
|
|
||
| Enabled by default on Windows x64. Also respects `control.queue_enabled`. The old | ||
| `advanced.queue.thin_queue_protection` value is used when the new control key is | ||
| absent, preserving an explicit opt-out. Default-enabled `[patches].thinqueueprotectionhooks` | ||
| controls installation independently. Installed callbacks check the current feature and queue | ||
| preferences. Changing the installation switch requires restart; TOML changes require | ||
| reload/restart. | ||
|
|
||
| The native planner (`DoPlanPathAndEngageTarget`) and watchdog (`HandleStall`) run | ||
| first. If they removed an exact prefix of targets, left a nonempty unchanged | ||
| suffix, and left the same fleet idle without an active engagement, the guard | ||
| rechecks that state and asks the native `TryPlanPathAndEngageTarget` to resume. | ||
| Pending/last-target latches must be absent or refer only to removed targets. | ||
| Reordering, replacement, truncation, or a latch naming a surviving target rejects | ||
| recovery. An unchanged queue is never enough evidence to retry. | ||
|
|
||
| On disposal, a fleet must be explicitly destroyed with removal reason `Destroyed` | ||
| (1). If native handling leaves that target at an inactive queue head, the guard | ||
| rechecks it and calls native `ProcessQueue(target, false)`. It does not force | ||
| immediate selection, remove arbitrary targets, or edit queue/engagement state. | ||
|
|
||
| Faster Queue Recovery still owns the failed-course-response path. The restored | ||
| guard uses three different detours and calls the existing native engage entry, | ||
| so Faster Queue Recovery can observe those requests normally. The nested planner | ||
| and watchdog paths cannot replay a successfully engaged target because the outer | ||
| guard sees the resulting active engagement. Queue identity is checked again after | ||
| native calls. All three guard hooks must install before any guard action is enabled. | ||
|
|
||
| Methods, field offsets/types and enum return representations are checked before | ||
| installation; incompatible layouts keep native behavior. This port currently | ||
| installs only on Windows x64. macOS runtime/ABI validation is not claimed. | ||
|
|
||
| Successful actions log `[ThinQueueProtection] resume` or | ||
| `[ThinQueueProtection] process-destroyed-head`; startup logs `ready=true` or an | ||
| unavailable warning. There is no per-frame scan or background polling. | ||
|
|
||
| Run `tests/run-action-queue.ps1` for both recovery-policy suites. The restored | ||
| release tests cover destroyed-head filtering, prefix removal, multiple removed | ||
| targets, latch safety, queue replacement/reordering and postcondition changes. | ||
| They do not establish game ABI or live wave behavior. Runtime verification still | ||
| requires a new build to be deployed and a wave test after restart. |
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
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
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
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
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
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
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
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
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
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
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
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
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a purpose to having three hook methods? Would not all these be desired to be enabled together?