Filter verbs: confirm yes|no + appraise id:<guid> - #14
Draft
Awful-Waffle-Rofl wants to merge 1 commit into
Draft
Conversation
Two gaps blocked automated testing of ACE server actions that ask a question or that act on the last-appraised object. confirm yes|no -------------- An ACE server asks a yes/no question with GameEventConfirmationRequest (game event 0x0274 inside the 0xF7B0 container). Only GameActionConfirmationResponse (game action 0x0275) answers it, and only the client can send that: in the ACE source, ConfirmationManager.HandleResponse has no caller for a normal response other than that game action. So a harness could neither answer a confirmation nor even see that one was outstanding. Decal cannot send a raw network message. Verified by reflection over the whole Decal surface (Decal 3.0, Decal.Adapter 2.9.8.3): HooksWrapper is a fixed list of client-function hooks with no send; INetworkFilter2 exposes only inbound DispatchServer/DispatchClient; INetService exposes only Decal/Filter/FilterVB/ Hooks; IMessageFactory.CreateMessage parses an observed buffer rather than sending; and messages.xml defines only F657, F7B1 and F7DE as outbound, with 0x0275 absent from the F7B1 switch. The answer therefore comes from the client itself, by posting a mouse click at the confirmation panel's Yes or No button - the same mechanism FastQuit already uses for the quit box and AutoRetryLogin for login boxes. ClickNo is added to PostMessageTools as the mirror of the existing ClickYes, at the +80 x offset the sibling KeyTestApp/PostMsgs.cs already uses. Whether the server-driven panel sits at the same screen position as those client-local boxes is unverified until this runs against a live client, so the verb also accepts "at:X,Y" to click an explicit point and "force" to click with nothing outstanding, which separates a detection failure from a missed click. Detection: Confirmer watches ServerDispatch for events 0x0274 and 0x0276 and publishes the outstanding request three ways - heartbeat fields (file version 1.6, read optionally so older parsers still work), a "confirmation" section in the dumpstate snapshot, and chatlog records with source "confirmation". The answer record names the confirmation type, context and text it answered, so a test can prove which dialog it answered. ACE only sends ConfirmationDone on the 30 second abort, never after a normal answer, so ConfirmationDone arriving after an answer is direct evidence the click did not land. That is documented as the pass/fail rule. appraise id:<guid> ------------------ The existing appraise resolves names through the world object list, so it cannot reach an item in the player's pack. That forced rigs to race a freshly spawned item and made an already-acted-on item impossible to re-aim. The id: form skips resolution and sends the identify request for the guid given, accepting decimal, unsigned decimal and 0x hex (guids are unsigned on the wire but Decal's object id is a signed Int32, so all three spellings of the same object parse). Verification ------------ Solution and project build clean. tools/confirm-verb-smoke.ps1 covers routing, argument parsing, guid parsing, the state machine and the chatlog records with no live client, and passes 24 of 24 checks. The click landing on the panel and the server accepting the answer are NOT verified and cannot be without a live client; the PR carries a deploy-and-live-verify checklist. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TmAUeyy5smytFsieYapeuK
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.
Why
Two gaps found while vetting the ACE test harness (ACE draft PR #287) blocked automated testing outright.
1. Nothing could answer a server confirmation dialog. An ACE server asks a yes/no question with
GameEventConfirmationRequest, game event0x0274inside the0xF7B0game-event container. The only thing that answers it isGameActionConfirmationResponse, game action0x0275, and only the client can send that: in the ACE source,ConfirmationManager.HandleResponsehas no caller for a normal (non-timeout) response other than that game action. Tinkering, augmentations, skill and attribute changes, allegiance and fellowship offers all pass through it. Worse, a harness could not even see that a question was outstanding - the only record was the ACE server's own log, which a remote or CI run does not have.2.
appraisecould not reach inventory./usewith <sourceGuid>works server-side and drives real tinkering, but it takes its target from the server's last-appraised object. The filter'sappraiseresolves names through the world object list, so it cannot see an item in the player's pack. That forced rigs into a fragile race - spawn a fresh item, pollitemkeysuntil the guid the audit reported appeared, fire immediately - and an item that had already been tinkered could never be re-aimed at all.What this adds
confirm yes/confirm yconfirm no/confirm nconfirm yes at:X,Yconfirm yes forceappraise id:<guid>0xhex, no resolution and no ambiguityBoth are reachable over the launcher channel and typed in game as
/tf ..., and both are in/tf help.The mechanism for
0x0275, and how it was determinedDecal cannot send a raw or arbitrary network message. Established by reflection over the entire Decal surface (Decal 3.0,
Decal.Adapter2.9.8.3), not by assumption:Decal.Adapter.Wrappers.HooksWrapper- whatCoreManager.Current.Actionsis, and what everything outbound in this filter already goes through - is a fixed list of client-function hooks:UseItem,RequestId,CastSpell,TradeAccept,SpellTabAddand so on. There is noSend, noSendMessage, and nothing confirmation related.Decal.Interop.Net.INetworkFilter2exposes onlyDispatchServer/DispatchClient/Initialize/Terminate. Those are inbound observation callbacks; there is no outbound injection point.Decal.Interop.Net.INetServiceexposes onlyDecal/Filter/FilterVB/Hooks.Decal.Interop.Net.IMessageFactory.CreateMessagebuilds a message object for parsing an observed buffer. It puts nothing on the wire.C:\Program Files (x86)\Decal 3.0\messages.xml, the authority for message shapes, marks exactly three messagesdirection="outbound":F657,F7B1,F7DE. TheF7B1(ordered game action) switch lists actions0x0005, 0x0010, 0x0019, 0x001A, 0x001B, 0x0036, 0x0044-0x0048, 0x004A, 0x00CD, 0x019C, 0x019D, 0x01A1, 0x01E3, 0x01E4-0x0275is not among them, so Decal has no schema for the response either.Option (i) in the brief is therefore closed, with source evidence. Option (ii) is what shipped: the answer comes from the client itself, by operating the client's own confirmation panel. This filter already does exactly that for the client's other yes/no boxes -
FastQuitanswers the quit box withPostMessageTools.ClickYes,AutoRetryLogindismisses login boxes withClickOK. Those postWM_MOUSEMOVE/WM_LBUTTONDOWN/WM_LBUTTONUPat a position derived from the client window rect, at three vertical offsets that were tuned against real clients (see the comments inShared\PostMessageTools.cs: "800x600 +32 works, +33 does not ... 1600x1200 +31 works").ClickNois added here as the mirror ofClickYes, at the+80x offset that the siblingKeyTestApp\PostMsgs.cshas always used for its ownClickNo.With nothing outstanding, a bare
confirm yesdeliberately does nothing: a stray click in the 3D window selects or attacks whatever is under it, which would silently corrupt the test that asked for the confirmation.forceis the explicit override.Detecting an outstanding confirmation
ConfirmerwatchesServerDispatchfor0xF7B0and switches on the event id. Decal'smessages.xmlparses both cases already, so no raw byte work is needed:0x0274->type(theConfirmationType),number(the context id),text0x0276->unknown(the type; ACE writes the type there),number(the context)State is published three ways:
game_<pid>.txt, file version bumped 1.5 -> 1.6, compat prefix still"1". New fieldsConfirmationState(none/outstanding/answered/aborted/expired),ConfirmationType,ConfirmationContext,ConfirmationText(flattened to one line, capped at 200 chars so it cannot corrupt the line-oriented file),ConfirmationAnswer. Read back with the same optional-read helpers the 1.5 fields use, so an older heartbeat still parses during a rolling upgrade.dumpstategains aconfirmationsection with the full untruncated text and the request timestamp.chatlog_<pid>.jsonlgainsConfirmationRequest,ConfirmationAnswerandConfirmationDonerecords, all with"source": "confirmation". The answer record names theconfirmationType,contextandtextof the dialog it answered, so a test can prove which dialog it answered rather than assuming.A request that is never answered and whose abort is missed expires locally after 30 s (
ConfirmationManager.confirmationTimeout), so a polling test cannot hang on a staleoutstanding.Reading the outcome: silence is success. ACE sends
GameEventConfirmationDoneonly fromConfirmationManager.EnqueueAbort, i.e. the 30 second timeout - never after a normal answer. So an answer followed by silence and then the effect means the click landed; an answer followed byConfirmationDone(stateaborted, usually with "You waited too long to answer the question!") means it did not.Verified
MSBuild.exe ThwargFilter.csproj /t:Rebuild /p:Configuration=Debug "/p:SolutionDir=C:\Users\danie\source\repos\ThwargLauncher\ThwargLauncher\"- clean, 0 errors. Only pre-existing warnings, both in files this PR does not touch.MSBuild.exe ThwargLauncher.sln /t:Build /p:Configuration=Debug- whole solution clean, 0 errors.ThwargFilter.dllsmoke-loads underC:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe(image runtimev2.0.50727).tools\confirm-verb-smoke.ps1(new, built on the existingtools\filter-smoke.ps1fixture): 24 of 24 checks pass. It covers the heartbeat contract, the cold state machine, thedumpstatesection, guid parsing forappraise id:(signed decimal, unsigned decimal aboveint.MaxValue,0xhex below and above0x7FFFFFFF, and rejection of garbage),at:X,Yparsing, and the launcher command routing end to end - proved by the filter log line the confirm branch emits, with a control showing that an unrouted verb instead falls through to the executor and fails, so the routing result is not vacuous.var, no LINQ, noasync; the new file is listed in the csproj's explicit<Compile>list.Select-String -Pattern "[\u2013\u2014]").NOT verified, pending deploy
Stated plainly rather than implied away:
ClickYes/ClickNooffsets were tuned against the client's own message boxes (quit, login). Whether the server-driven confirmation panel (0x0274) is drawn at the same screen position is a hypothesis, not a measured fact. It cannot be settled without a live client.0x0274/0x0276field names come from Decal'smessages.xmland the field order from the ACE source; both are read evidence, but no live message has been observed through this code.appraise id:moves the server's last-appraised object. The parsing is unit-tested;Actions.RequestIdon an inventory guid is not.Nothing here was deployed.
C:\Program Files (x86)\Thwargle Games\ThwargLauncher\was not touched.Deploy and live-verify checklist
For whoever deploys (needs a client restart, so schedule it):
ThwargFilter.dllintoC:\Program Files (x86)\Thwargle Games\ThwargLauncher\and restart the client.game_<pid>.txtreadsFileVersion:1.6and that the launcher still lists the game (it must, the compat prefix is unchanged)./augmentationor a tinker attempt is easy - and confirm within 30 s that:game_<pid>.txtshowsConfirmationState:outstandingwith a plausibleConfirmationType,ConfirmationContextandConfirmationText;chatlog_<pid>.jsonlgained a"source":"confirmation","type":"ConfirmationRequest"line.If detection fails but the dialog is visible on screen, the fault is the
0x0274parse, not the click.confirm yes. Expect aConfirmationAnswerrecord with"outcome":"clicked"naming that dialog, and then the action to complete.ConfirmationDonearrives and state goesaborted(usually with "You waited too long to answer the question!"), the click missed. Then:confirm yes forcewhile no dialog is up to confirm clicks are being posted at all;confirm yes at:X,Y(client-window-relative pixels);at:works, fold the corrected offsets intoClickYes/ClickNoinShared\PostMessageTools.csas a follow-up.confirm no. Repeat step 4 withconfirm noon a cancellable dialog and confirm the action does not happen. The No button's+80offset is the least evidenced constant in the change.appraise id:. Put a known item in the pack, read its guid fromitemkeys, sendappraise id:0x<guid>, then run the ACE admin command that consumes the last-appraised object (/usewith <sourceGuid>against it) and confirm it hit the intended item. Then re-aim at the same item a second time - that is the case the old race could never do.TESTING_CHANNEL.mdsection 14 with what was actually measured.Draft on purpose. Do not merge before step 4 and step 7 have been run against a live client.