Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
ea3ac4b
feat(control): nearby reports the window pixel of each character
Mosch0512 Sep 28, 2026
6554ea9
fix(editor): the real pointer no longer blocks injected keys and clicks
Mosch0512 Sep 28, 2026
693e180
feat(tools): chat scenario; shared meeting and trading helpers
Mosch0512 Sep 28, 2026
491f46e
feat: npc-shop scenario; the NPC shop and repair buttons in the socket
Mosch0512 Sep 28, 2026
d643c18
feat: repair scenario; repair prices in the socket's state
Mosch0512 Sep 28, 2026
2ad4ec1
feat(tools): equip-all-slots scenario
Mosch0512 Sep 28, 2026
67aa66a
feat: party scenario; party events and the command window's buttons
Mosch0512 Sep 28, 2026
eeda5bf
feat(tools): trade-inventory-full scenario
Mosch0512 Sep 28, 2026
abca0ae
feat: personal-shop scenario; personal shops in the socket
Mosch0512 Sep 28, 2026
6701977
docs: the new scenarios, accounts and socket fields; fewer screenshots
Mosch0512 Sep 28, 2026
ae88e9e
style: clang-format the changed lines
Mosch0512 Sep 28, 2026
a616bad
feat(tools): the test list stays usable while a run goes on, and show…
Mosch0512 Sep 29, 2026
267a9be
fix(tools): personal-shop trades between the quest accounts' Dark Lords
Mosch0512 Sep 29, 2026
0d87259
feat: quest scenarios for the class changes, hero status and the combo
Mosch0512 Sep 29, 2026
0e26693
style: clang-format the changed lines
Mosch0512 Sep 29, 2026
d933256
feat(tools): the quest scenarios go straight to Marlon
Mosch0512 Sep 29, 2026
67e2094
feat(tools): each test category checks and unchecks its own tests
Mosch0512 Sep 29, 2026
073e2bd
fix: review of the in-game test scenarios (part 2)
Mosch0512 Sep 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 37 additions & 9 deletions docs/control-socket.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ Error codes: `bad_request`, `unknown_command`, `wrong_scene`, `busy`,
| `hotkey` (`key`) | press one game key for a frame: `esc`, `i`, `home`, `f1`, … |
| `click-ui` (`x`, `y`, `button`) | click a window pixel (`left` by default) |
| `type` (`text`) | type text into the text field that has the focus, as the keyboard's text input does, e.g. an amount into the trade's zen box; `not_open` when no field has the focus |
| `ui` | the open item windows by name (`message_box` while a dialog waits for Enter or Esc), and the window pixels of named elements: `trade.confirm`, `trade.zen` |
| `slot-pixel` (`grid`, `slot`) | the window pixel of a slot's square: `inventory` and `equipment` (the slot numbers `state` reports), `trade`, `trade_partner`, `storage`, `mix`; `not_open` while that window is closed, `bad_request` for a slot the grid does not have |
| `ui` | the open windows by name — `inventory`, `inventory_extension`, `character`, `trade`, `storage`, `storage_extension`, `mix`, `npc_shop`, `lucky_item`, `chat_input`, `party`, `command`, `my_shop`, `purchase_shop`, `npc_quest` (the quest dialog of Sebina, Marlon and Devin), and `message_box` while a dialog waits for Enter or Esc — and the window pixels of the named elements that are shown: `trade.confirm`, `trade.zen`, `inventory.repair`, `inventory.my_shop`, `npc_shop.repair`, `npc_shop.repair_all`, `my_shop.title`, `my_shop.open`, `my_shop.close`, `command.trade`, `command.purchase`, `command.party`, `npc_quest.answer.0` and on (the rows of the quest dialog's answers), `npc_quest.complete`, `npc_quest.close`, `character.stat.strength` (`agility`, `vitality`, `energy`, `command`: the character window's "+" buttons, while there are level-up points) |
| `slot-pixel` (`grid`, `slot`) | the window pixel of a slot's square: `inventory` and `equipment` (the slot numbers `state` reports), `trade`, `trade_partner`, `storage`, `mix`, `npc_shop`, and the personal shops `my_shop` and `purchase_shop` (slots 204 and up, as `state` and the server number them); `not_open` while that window is closed, `bad_request` for a slot the grid does not have |
| `login` (`account`, `password`, `server`) | server selection, credentials, character list |
| `select-char` (`name` or `slot`) | enter the world with that character |
| `logout`, `quit` | back to the character list; close the client |
Expand All @@ -96,15 +96,41 @@ character name, class, level, experience, zen, HP/mana/SD/AG with their
maxima, map number and name, position, alive flag, safe-zone flag, current
target, the skills the character owns, equipment, inventory, buffs, party,
the open trade (partner, both offers with their items and zen, both confirm buttons and `my_confirm_wait`, the frames until my button takes clicks again, or `null`) and
`nearby`. An item carries `slot`, `name`, `level`, `durability`, and its
`width` and `height` in inventory squares; one that covers several squares is
listed once for each of them.
`nearby`. An item carries `slot`, `name`, `level`, `durability`,
`max_durability`, and its `width` and `height` in inventory squares; one that
covers several squares is listed once for each of them. While a click would
repair it (the inventory's repair mode, or an NPC that repairs), a worn item
also carries `repair_price`, and while an NPC shop is open an inventory item
carries `sell_price`: both as the item's tooltip shows them.

The shops: `npc_shop` is the open NPC shop (`repair_shop`, `tax_rate`,
`repair_all_price` at an NPC that repairs, and its goods with `price`, tax
included) or `null`; `my_shop` is the player's own personal shop (`open`, the
`title` in its title field, its goods with `price`), `purchase_shop` the
personal shop the player looks into (`seller`, `title`, goods with `price`) or
`null`. `repair_mode` says whether a click repairs instead of picking up.

For the quests and their rewards: `class_name` (e.g. `Blade Knight`; `class`
is the client's number, e.g. 1 Dark Knight, 8 Blade Knight, 12 Blade Master),
`level_up_points`, `stats` (`strength`, `agility`, `vitality`, `energy`,
`command`, without bonuses), `combo` (the Blade Knight's combo from Marlon),
`quests` (the seven legacy quests with `index`, `name` and `state`:
`active`, `complete`, `not_started`, `none`, or `unknown` for a value outside
these, as in the `quest` events), and `npc_quest`, the quest
dialog on screen (`quest`, `page`, `text`, `need_zen`, and its `answers`, each
with its `text` and `action`: `next` turns the page, `accept` takes the quest,
`complete` hands it in, `close` ends the talk) or `null`.

Each `nearby` object carries `id`, `kind`, `name`, `position`, and a player,
monster or NPC also `alive`, `level` and `hp_percent`. `hp_percent` is a
percentage of full health (`100` is untouched) and is `null` when the server
has not told the client that object's health — a threshold test has to allow
for the null rather than read it as zero.
for the null rather than read it as zero. A player, monster or NPC also
carries `pixel`, the window pixel `{x, y}` at the middle of the box the mouse
picks it by, or `null` while it is not drawn: `click-ui` there talks to an NPC
or targets a player the way a player's click does. The pixel is taken from the
last rendered frame, so it lags a moving object by a frame, and a window drawn
over it catches the click instead.

### Synthetic input

Expand All @@ -130,7 +156,7 @@ release frame has run; a second injection while one is in flight answers
an observation of the act slot, not a claim on it. The sequence follows
*rendered* frames, so an injection sent to a client that is not rendering (the
occluded-window case below) answers `timeout` and is dropped rather than
delivered late. Not covered: typing text (`say` sends chat), key chords, drags.
delivered late. Text goes into the focused text field with `type`. Not covered: key chords (Shift+L, Ctrl+Q), drags.

## Events

Expand All @@ -147,7 +173,8 @@ strictly increasing `seq`, a UTC `time` and its own fields:
| `map` | `map`, `map_name`, `position` |
| `scene` | `scene` |
| `view_enter` / `view_leave` | `object` |
| `party` | `change`, `name` |
| `quest` | `change`: `state` with the legacy `quest` and its new `state` (`active`, `complete`, `not_started`, `none`, `unknown`), or `reward` with the character's `name`, the `reward` (`level_up_points`, `second_class`, `points_per_level`, `combo`, `third_class`), its `amount` (none for the two class changes, whose new class is `class`) and the character's `class` afterwards; the client records the rewards of every player in view, so a script matches its own `name` |
| `party` | `change` (`invited` with the inviter's `name`; `list` with the leader's `name` after every change of the members; `left`; `result` with `result` — `failed`, `denied`, `full`, `user_left`, `other_party`, `left`, `opposing_gens`, `battle_zone`, `battle_zone_off` — when an invitation formed no party) |
| `trade` | `change` (`requested`, `opened`, `refused`, `unavailable`, `partner_confirm`, `closed`), `name` for a request or an opened trade, `state` (`checked`, `unchecked`, `reset`; `unknown` for a value outside the protocol) for the partner's button, `result` (`completed`, `cancelled`, `inventory_full`, `request_cancelled`, `reinforced_item`) when it closes; `refused` also on the asked side, when a window that forbids trading is open and the client says no by itself |
| `disconnect` | `reason` |
| `error` | `command`, `error`, `message` |
Expand Down Expand Up @@ -180,11 +207,12 @@ The event recorders are one-line calls named `App::Control::Events::Record…`,
sitting at the end of the packet receive functions in
`src/source/Network/Server/WSclient.cpp` (hits, deaths, experience, stats,
chat, whisper, drops appearing and vanishing, view enter/leave, party changes,
invitations and answers, quest states and rewards,
trade steps, logout) plus the scene and map watcher in `App/Control/ControlServer.cpp`.
When one of those functions is rewritten:

1. `rg -c 'App::Control::Events::' src/source/Network/Server/WSclient.cpp` —
the count is 27; a lower one means a tap was dropped. Compare it against
the count is 32; a lower one means a tap was dropped. Compare it against
`git show upstream/main:…` when the number itself is in doubt: the count
is a smoke test, the list above is the contract.
2. Re-run the live checks that cover the dropped tap (a fight records `hit`,
Expand Down
Loading
Loading