Separate scripted input from controller state (#326)
Give DM_KEYS and DM_CTL their own buttons, press latch and stick axes, merged with player 1's keyboard/touch and physical controller state. Controller updates no longer replace scripted holds, and script release preserves physical input. Include the scripted path in shake detection. Exercise the real scripted publisher with controller updates during its hold, including repeated reads after the press latch is consumed. Cover release, short presses, handheld axes and player isolation, and update the input documentation. Co-authored-by: v0lumehi <aljic.zijad@gmail.com>
This commit is contained in:
1 parent
d3bbb3a6e5
commit
583ae42c59
7 files changed
+69
-14
No files matched your search
@@ -96,7 +96,7 @@ Patterns that worked: print every call of one function with its arguments; stop
|
||||
|
||||
## Scripted input and thread scheduling
|
||||
|
||||
- `DM_KEYS="<seconds>:<button>[+button][~hold],…"` presses buttons from host start (`hle_hid.c`). `<seconds>:<button>*<count>/<interval>` repeats a press, e.g. `42:A*170/2` presses A every 2 s from 42 s on, 170 times; at most 1,024 steps after expansion. Steps run in time order, so repeated groups interleave (`f17400:Left~0.3*120/500,f17480:A*120/500,f17560:Left~0.3*120/500,f17640:A*120/500` = Left, A, Left, A every 500 frames: Fire Emblem: Three Houses' confirm dialogs default to No, and this cycle confirms Yes whether a dialog is open or not when it starts; A, Left, A loops forever once it starts on an open dialog); a list that mixes seconds and frame numbers keeps its listed order. A long run through a game's story is then a single switch, such as Metroid Dread's title, new game and story recap. `LLeft` / `LUp` / `LRight` / `LDown` push the left stick fully (Super Mario 3D World moves on its map by stick only); `Shake` shakes the controller for the hold time, at least one whole shake (its six-axis sensor); combos such as `L+R~0.5` press buttons together. Scripted presses are player 1's (`dm_pad_set`), as is the keyboard. An `f` before the time counts presented frames instead of seconds (`f2500:A`, repeat intervals in frames too): with another heavy job running, seconds drift away from the game's progress and a route stops lining up; frame numbers do not. Breath of the Wild from its title menu into the autosave: `DM_KEYS=f2500:A,f2800:A,f3100:A,f3400:A` (control at frame ~3900).
|
||||
- `DM_KEYS="<seconds>:<button>[+button][~hold],…"` presses buttons from host start (`hle_hid.c`). `<seconds>:<button>*<count>/<interval>` repeats a press, e.g. `42:A*170/2` presses A every 2 s from 42 s on, 170 times; at most 1,024 steps after expansion. Steps run in time order, so repeated groups interleave (`f17400:Left~0.3*120/500,f17480:A*120/500,f17560:Left~0.3*120/500,f17640:A*120/500` = Left, A, Left, A every 500 frames: Fire Emblem: Three Houses' confirm dialogs default to No, and this cycle confirms Yes whether a dialog is open or not when it starts; A, Left, A loops forever once it starts on an open dialog); a list that mixes seconds and frame numbers keeps its listed order. A long run through a game's story is then a single switch, such as Metroid Dread's title, new game and story recap. `LLeft` / `LUp` / `LRight` / `LDown` push the left stick fully (Super Mario 3D World moves on its map by stick only); `Shake` shakes the controller for the hold time, at least one whole shake (its six-axis sensor); combos such as `L+R~0.5` press buttons together. Scripted presses are player 1's, as is the keyboard, on a path of their own that is ORed with the keyboard and player 1's controller (`script_set`): a connected controller (a DualShock 4 publishes its sticks all the time) no longer overwrites them. An `f` before the time counts presented frames instead of seconds (`f2500:A`, repeat intervals in frames too): with another heavy job running, seconds drift away from the game's progress and a route stops lining up; frame numbers do not. Breath of the Wild from its title menu into the autosave: `DM_KEYS=f2500:A,f2800:A,f3100:A,f3400:A` (control at frame ~3900).
|
||||
- `DM_CTL=<file>` (Mac) drives a running game step by step instead: the host polls the file every 100 ms and acts on each line appended to it (`hle_hid.c` `ctl_main`). A line is a press as in `DM_KEYS` (`A`, `LDown~0.2`, `L+R~0.5`), or `shot <path>` (the next presented frame as PNG), `passdump <n>` (the next n frames as `DM_NVN_PASSDUMP`), `texdump <path with %d>` (the next frame's colour targets as `DM_NVN_TEXDUMP`), `texraw <serial> <path>` (level 0 of the host texture behind a serial, raw, with a `width height format row` header), `texlist <NVN target>` (registered textures of that target, `2` = 3D, with storage address, size and block-linear layout), `memdump <address> <bytes> <path>` (guest memory to a file: a buffer texture's storage from its `B` line holds what the GPU wrote), `peek <address> <count>` (floats to the log), `cvarf` / `cvari <main offset> <value>` (an Unreal console variable's game- and render-thread copies, for a `TAutoConsoleVariable` found as for `hle_fpslock.c`), or `env KEY=VALUE` (for switches read at use). Look, press, look again: menus and loads whose timing moves between runs need no blind timed script. A shell helper that appends a line and waits for a shot to be written keeps the loop short (DRAGON QUEST VII Reimagined's work folder: `run/ctl.sh` with `wait S` / `snap NAME`, and route scripts on top of it into the opening cutscene).
|
||||
- Frame numbers are a stable way to aim dumps (`DM_NVN_PASSDUMP=<frame>`, `DM_NVN_SHOT=…`) at the same scene in another run: the per-frame NVN call counts (`[nvn] frame N: … calls`) matched between runs of the same script. Times in seconds drift with load.
|
||||
- **A route through a 3D level** (Captain Toad: Treasure Tracker's opening course, where Toad has to find his way up a diorama to the star): screenshots alone took a dozen runs and still lost Toad behind the scenery, and turning the camera mid-route made the same input land differently each run. What worked: read the player's position every frame with a probe and plan from the stage's own data. The guest heap is laid out the same in every run, so the player actor is at the same address. `DM_PROBE=main:1fb2e0@*x0+48+8` (`PlayerActor::control`, the actor's pose at +0x48, the position 8 bytes into it, as `al::getTrans` reads it) prints x, y (height), z as three floats per frame. The stage map (`StageData/<stage>.szs`, a BYML) lists every actor with its `Translate`, including the goal and the rail the guide character walks. Stick directions are camera-relative: on the default camera two directions together (`LUp+LRight`) move along one world axis. Hold against walls where possible, since a wall stops the player at the same place in every run; the route then reproduced in 5 of 5 runs.
|
||||
|
||||
@@ -128,6 +128,7 @@ One line per symptom in the [subsystem references](../reference/): what you see
|
||||
- SDK 20 calls that reach a service; `nfp`'s `AttachActivateEvent` / `AttachDeactivateEvent` crashed in the SDK at `sdk+0x3f2a78`. → [SDK 20 six-axis sensor and amiibo (`nfp`) calls](../reference/services.md#sdk-20-six-axis-sensor-and-amiibo-nfp-calls)
|
||||
- abort in `nn::sm::Initialize` under `nn::hid::VibrationNode::Update`. → [Vibration values flushed by the SDK's vibration graph](../reference/services.md#vibration-values-flushed-by-the-sdks-vibration-graph)
|
||||
- `DM_RUMBLE_LOG=1` shows the game's startup handle request answered with none, then every vibration value sent to handle 0 and reaching no output for the whole session, although a controller with haptics (a DualSense on the Mac) is connected. A game that rumbles would stay silent. → [Vibration handles before a controller is reported](../reference/services.md#vibration-handles-before-a-controller-is-reported)
|
||||
- with a controller connected (a DualShock 4 here), `DM_KEYS` and `DM_CTL` presses are logged (`DM_KEYS: … press …`) but the game does not react to them, or only now and then; without the controller the same script works. → [Scripted presses on a path of their own](../reference/services.md#scripted-presses-on-a-path-of-their-own)
|
||||
- taps in handheld mode do nothing, though the game reads the touch screen (the title's PRESS ANY BUTTON, the menus' cards). → [Touch gestures recognized from the touch points](../reference/services.md#touch-gestures-recognized-from-the-touch-points)
|
||||
- the first boot aborted in `hid::GetMouseState` (the SDK's mouse resource was never initialized). → [Mouse, keyboard and key code maps](../reference/services.md#mouse-keyboard-and-key-code-maps)
|
||||
- the first boot crashed in `hid::detail::SetPalmaBoostMode`, a null read at `sdk+0x238eb0` (the hid service never opened). → [Poké Ball Plus (Palma)](../reference/services.md#poké-ball-plus-palma)
|
||||
|
||||
@@ -250,6 +250,14 @@ Most service calls can be found before they crash, before the first boot or the
|
||||
- **Fix (resolved):** the handles no longer wait for a routed output: with rumble on and an output sink installed (`dm_haptics_enabled`), a connected npad style gets its two handles; `vib_send` checks for an output at each send (`hle_hid.c`). No sink, rumble off or a disconnected npad still get none, and `IsVibrationPermitted` still follows the routed outputs. `DM_RUMBLE_LOG=1` logs the handle requests and, once a second, the values sent, those with no routed output and the peak amplitude. Each input value is counted separately, including mixed batches; a mutex protects the diagnostic totals across game threads. `t_hid` covers cached startup handles delivering a nonzero value after routing, mixed-batch diagnostics and concurrent sends; `t_hid_tsan` checks those sends for data races.
|
||||
- **Found on:** [Unicorn Overlord](../../dm/games/010069401ADB8000/README.md) (the Asian release, `010054B01AD92000`), in the log only: the game has no rumble of its own (it sends amplitude 0 throughout), so nothing could be felt before or after. Not yet checked on a game that rumbles.
|
||||
|
||||
### Scripted presses on a path of their own
|
||||
|
||||
- **Symptom:** with a controller connected (a DualShock 4 here), `DM_KEYS` and `DM_CTL` presses are logged (`DM_KEYS: … press …`) but the game does not react to them, or only now and then; without the controller the same script works.
|
||||
- **Cause:** scripted presses went through player 1's controller path (`dm_pad_set`). A connected controller publishes its whole state there on every change, replacing scripted holds and sticks; the press latch preserves only the first read. Script release also cleared the physical controller's state.
|
||||
- **Fix (resolved):** `DM_KEYS` and `DM_CTL` set buttons, a press latch and sticks of their own (`script_set` in `hle_hid.c`), ORed with the keyboard / touch path and player 1's controller when the game reads player 1; for the sticks, the one deflected more wins. Shake includes the scripted path too.
|
||||
- **Check:** `dm/host/tests/t_hid.c` (`t_script`) runs the scripted publisher with a controller update during the hold: buttons survive repeated reads after the latch is consumed, the scripted stick wins over drift, shake continues, and release preserves physical input. Short presses are latched once, all four axes reach the handheld npad, and player 2 stays separate.
|
||||
- **Found on:** Batman: Arkham Knight (`0100ACD0163D0000`), reported in [PR #326](https://github.com/seedds/nxnative/pull/326) while driving the opening route with `DM_KEYS` and a DualShock 4. Reproduced independently in the host HID test without game files; its game profile is not in this checkout.
|
||||
|
||||
### Touch gestures recognized from the touch points
|
||||
|
||||
- **Symptom:** taps in handheld mode do nothing, though the game reads the touch screen (the title's PRESS ANY BUTTON, the menus' cards).
|
||||
|
||||
Reference in new issue
Block a user