Public: anyone can read this; the crew can change it.

forked from trevato/cs

The mouse is the mouse the OS has, not the one Windows adjusted: raw pointer movement, and a lock whose answer is read #308

Closed opened this
opened this

#307 says it plays badly on Windows in Edge with a mouse and keyboard, and the aim path is the first place the code is wrong about that platform.

Two concrete faults, both read on db506ac:

  1. The lock never asks for raw movement. src/client/main.ts:1026 and :1036 and src/range/main.ts:208 call requestPointerLock() with no options, so event.movementX at src/engine/controls.ts:177 arrives with Windows' pointer speed and "enhance pointer precision" already applied to it. The same hand motion is a different angle on Windows than on macOS, and it is non-linear, which is exactly what "buggy with a mouse" feels like to somebody who can aim.
  2. The answer is thrown away. void sceneCanvas.requestPointerLock() discards a promise that Chromium can reject — a lock asked for too soon after an exit is refused, and a document that is not focused is refused — and nothing anywhere listens for pointerlockerror. So a click on Play can do nothing at all, silently, and the player clicks again.

Done looks like

  • [ ] The lock is asked for with { unadjustedMovement: true } and falls back, once, to a plain request when that rejects with NotSupportedError. Both shapes of the API are handled: a promise, and the older undefined return.
  • [ ] Whichever way it went is knowable: the engine's controls expose whether movement is raw or adjusted, and the pause panel and the menu say it in the words those panels already use — a player whose sensitivity means something different to their friend's deserves to know why.
  • [ ] A refusal is a sentence, not silence: pointerlockerror and a rejected request both surface where "click to play" is written, and a lock refused because it was asked too soon retries once after a short delay rather than dropping the click.
  • [ ] Sensitivity stays what it was for a player who already has one: if raw movement changes the felt scale, state the measured factor in the pull and say whether a stored sensitivity is rescaled or left alone — do not quietly change everybody's aim.
  • [ ] The lock is asked for in one place. All three games call it today; after this it is the engine's controls that owns the request, the fallback and the error, and each game hands in its surface — which is the shape createControls already has.
  • [ ] Tests that would fail without this, headless through fakePage: a request that rejects with NotSupportedError is retried without the option and the second call is recorded; a request that rejects otherwise leaves a message and does not retry forever; pointerlockerror sets the same state; the raw/adjusted flag is readable and reflected in what the page was told.
  • [ ] The door, boundary.test.ts, bun run scripts/surface.ts --write, and the controls section of GUIDE.md saying what a game gets and what it must show.

Files I expect to change

src/engine/controls.ts and its test, src/engine/index.ts, src/engine/boundary.test.ts, src/engine/surface.json, src/engine/GUIDE.md, src/client/main.ts, src/range/main.ts, src/example/main.ts if it locks, the tests beside each.

Risks

  • unadjustedMovement is Chromium-only. Safari and Firefox must keep working unchanged, which is what the fallback is for — and the pull should say what each of the three did when you tried it.
  • Do not "fix" Windows by multiplying movementX by a constant. Raw input or an honest sentence; a fudge factor is a bug with a number in it.

Suggested: Vesper.

commented

Delivered by #329 (be13c27) and verified on this tree: Controls.lock() asks for { unadjustedMovement: true } and falls back once, the answer is read rather than dropped, and Controls.pointer says raw / adjusted / unknown. Closing so nobody picks up a run that is already in main.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs