Shared client input and orbit camera
Ownership and data flow
Every game builds the same client/cl_input.c. There is no input-mode selector or per-game input source.
Pan, edge scrolling, relative mouse look, zoom, movement buttons, hover, and selection are available together.
cl_selection.c owns numbered groups and selection hints; cl_control_groups.c owns their append/reset helpers.
Game configs choose bindings and independent limits rather than an RTS/orbit mode.
flowchart LR
Config[Game and user config] --> Input[cl_input.c]
Input -->|clc_input: focus / view / move| Server[Server transport]
Server -->|ClientInput on player edict| Game[Authoritative game]
Game -->|playerState: focus XYZ, Euler, distance, lens| Client[Client snapshot interpolation]
Client --> Camera[One orbit view matrix]
The RTS player is an existing invisible controller edict. WC3/SC2 mirror the resolved focus into that controller's
origin, without adding a model, collision, or an extra network entity. WoW uses its visible player edict and publishes
its complete focus at the actor origin plus game-owned eye height. Camera sampling needs no target entity ID or
special client entity: playerState.vieworigin already communicates the resolved world-space point.
The client interpolates both complete focus samples, slerps their Euler-derived quaternions, and builds one
Matrix4_fromViewQuat camera. The WoW-only height override in Matrix4_getCameraMatrix is gone. Scripts, terrain
height, actor movement, camera target tracking, and lens defaults remain game-owned. WoW's legacy wrapped pitch
is converted inside its game module; the client speaks canonical Euler degrees only. Native angle origins and inverse
input mappings are documented in coordinates and camera angles.
Controller API and protocol
game_export.ClientInput(edict, INPUTCMD) replaces ClientSetCameraPosition, keeping the export count unchanged.
The tagged command carries only one operation. Transport accepts input only for a spawned client's assigned edict.
Malformed or truncated commands are diagnosed and terminate parsing of that packet.
| Action | Payload after action byte | Ownership |
|---|---|---|
BZ_INPUT_FOCUS |
2 float coordinates | RTS changes/clamps its focus; an actor-follow game ignores free panning |
BZ_INPUT_VIEW |
3 float Euler degrees, float distance | Game applies orbit angles/distance and its limits |
BZ_INPUT_MOVE |
byte direction bits, unsigned short milliseconds | Game moves its controller or actor |
clc_input is opcode 4. BZ_PROTOCOL_VERSION is 2: clients and servers must be upgraded together. The old
clc_camera_position opcode remains readable and maps to the focus operation. Existing game commands such as
select, smart, cast, and WoW's legacy text move remain game commands. This is not a new arbitrary-command
import/export callback.
Movement samples are capped at 250 ms. WoW retains its existing server-tick movement integration and consumes button state; RTS can integrate an invisible controller from a movement sample. Arrow/edge panning still predicts absolute focus. Look/zoom predict presentation samples without modifying the authoritative delta baseline. Prediction ends on a matching snapshot, after 250 ms without input, or when gameplay ownership is lost, so rejected commands cannot indefinitely hide server state. Menus, modals, map loads, and window focus loss cancel held controls and send one stop for an actor that was moving. Scripted camera UI ownership takes priority.
SC2's existing Galaxy camera state is shared across its client slots. Manual input now updates that same state, including preserved height offset; it cannot be overwritten by the next ordinary camera publication. Per-player Galaxy camera state remains separate future work.
Configuration
Defaults live in games/<game>/share/config.cfg, installed with make install-share.
| Setting | WC3 | SC2 | WoW |
|---|---|---|---|
cl_start_menu |
menu_main |
menu_main |
menu_login |
cl_selection_limit |
64 | 64 | 1 |
cl_same_type_select |
1 | 0 | 0 |
cl_group_focus |
1 | 1 | 0 |
cl_camera_scroll_speed |
1400 world units/s | 350 world units/s | 0 |
cl_camera_edge_scroll |
1 | 0 | 0 |
cl_camera_pan_plane |
0: terrain | 1: camera plane | 0 |
cl_camera_min_pitch / cl_camera_max_pitch |
-85 / -5 | -85 / -5 | -85 / -35 |
cl_hover_health_only |
1 | 1 | 0 |
cl_context_cursor |
0 | 0 | 1 |
cl_move_mouse (both buttons forward) |
0 | 0 | 1 |
cl_look_command (look-button click) |
empty | empty | interact |
All builds register +select, +attack, +smart, +pan, +look, +forward, +back, +moveleft, +moveright,
+camwest, +cameast, +camnorth, +camsouth, zoom, and group. Edge/arrow panning follows camera yaw.
Transient gameplay-window focus and modal ownership are documented in client windows; non-modal edit
controls must yield directional camera arrows to this binding path, while modal dialogs own input completely.
Mouse sensitivity defaults to cl_mouse_speed 0.18 degrees/pixel, click threshold to 10 pixels, and edge margin to
6 pixels. Existing zoom_speed, camera_min_distance, and camera_max_distance still apply. Server-authored
snapshot defaults determine initial angles/distance. To enable orbit drag in WC3, for example, bind an unused key
or mouse button to +look; its pan, groups, and edge settings continue working at the same time.
SC2's existing menu stub does not implement its configured menu_main; this change does not add that menu.
Saved-setting compatibility
The generic command cvar_alias old canonical declares a compatibility name for an already defined cvar. Game
configs declare aliases for wc3_camera_edge_scroll, wow_mouse_speed, wow_camera_min_pitch,
wow_camera_max_pitch, and wow_click_threshold. The engine contains no table of game-specific aliases.
Both names resolve to one cvar, so later user/autoexec/command-line settings win in their ordinary execution order,
and archived output uses only the canonical name. Existing values/flags created by early command-line settings are
migrated. Declarations are allowed only during startup config processing, before Cvar_EndConfig: migration can
retire an old cvar allocation, so it must precede modules retaining cvar pointers. Existing aliases remain readable
and writable during gameplay. Repeating an identical declaration is harmless; retargeting or declaring a new alias
after startup is rejected with a diagnostic. Alias declarations belong in shipped defaults, not generated user config.
Old saved wrapped pitch limits (values above 180) are converted once at startup to signed Euler limits,
with a diagnostic. WoW compatibility aliases swap minimum/maximum because negating pitch reverses the interval. No game-name lookup is needed for that mathematical conversion. cl_input_mode is obsolete.
Groups and target reconciliation
Group capacity follows cl_selection_limit; double-tap focus is independently controlled by cl_group_focus.
With capacity one, add fills an empty group and recall selects its target. WoW keeps its action-bar number binds;
users can opt into bind CTRL+1 "group assign 1" and bind ALT+1 "group 1" without changing the camera controls.
Clicks and group recalls share cl.selection. Wow_SelectEntity emits the existing svc_set_selection, so target
cycling, interaction, and rejected selections reconcile that cache. Game rules remain authoritative. Group membership
resets at map boundaries and is not pruned merely because a snapshot cannot currently see a member.
cl_same_type_select is a shared input policy switch enabled by WC3. Ctrl+click
or an unshifted second click on the same entity within 500 ms sends the clicked
entity plus renderer-visible viewport candidates through select ... sametype.
The client may pre-filter those candidates by snapshot class_id so unrelated
visible entities do not consume the bounded selection packet, but the game
module rechecks the anchor type, legal membership, and capacity authoritatively.
The candidate suffix is capped at 61 entries because the shared command tokenizer
accepts 64 tokens and select <anchor> sametype consumes three of them.
Shift-modified clicks stay on the existing selection/target-command path.
Minimap and context-click routing
Minimap interaction is shared by every client build, independently of cl_selection_limit. IN_SelectDown
checks the minimap before the world-selection HUD blocker; otherwise an authored texture behind the minimap
can swallow the click. IN_SelectUp ends the minimap drag before either single- or multi-selection returns.
Gameplay/menu/modal ownership still gates entry, and CL_ResetInput cancels drags when ownership changes.
Minimap camera input in client/cl_minimap.c and Smart input in client/cl_input.c call the mandatory
re.TraceMinimap export. A false trace result means no minimap hit;
an absent function pointer is an incomplete renderer API, not an optional feature. The renderer owns screen/world
conversion and the drawn minimap bounds; the client does not duplicate game layout geometry or inspect game names.
CL_SetCameraPosition predicts focus and writes clc_input with BZ_INPUT_FOCUS. Server transport delivers that
typed input to game_export.ClientInput. WC3 and SC2 resolve controller focus there; WoW deliberately ignores free
focus because its camera follows its actor. Availability of a shared minimap does not override that game policy.
Smart/context clicks resolve a minimap hit before the generic gameplay-HUD blocker. The WC3 and SC2 default configs
bind MOUSE2 to +smart, so a right-click over the minimap uses the traced world XY as the ordinary
smartpoint <x> <y> [queue] command; Shift therefore appends the same
queue suffix used by world-space Smart clicks. The minimap path does not entity-pick marker sprites, so visible
unit/building contacts remain positional references rather than direct Smart targets. It also does not call
CL_SetCameraPosition, keeping right-click orders independent from left-click camera focus. Other HUD pixels remain
blocked from Smart/world input.
Smart entity clicks preserve both available renderer results in the existing clc_stringcmd command:
smart <entity> <x> <y> [queue]. Without a point hit the command remains smart <entity> [queue].
The shared client does not classify bridges/destructables or choose attack versus movement. WC3 resolves its SLK
walkability and attack rules on the server and uses its existing queued-order/formation path for point movement;
see order queues. No new game-specific callback or snapshot field is required.
The client_input tests decode the focus packet, exercise selection capacities 1 and 64, drag/release, a missed
minimap trace, menu ownership, minimap right-click/Shift-right-click point orders through SDL bindings, generic-HUD
blocking, modal blocking, and Smart commands with/without traced points and Shift. A bounded diagnostic run
with +test 'client_input.*' +com_frame_limit 100 confirmed the focus channel before the old optional-callback
guards and duplicate release were removed. Investigative logs are not retained in source.
Order-marker media
CS_ORDER_MARKER (slot 11) carries the server-authored point-order model path. WC3 resolves TargetPointConfirm
from the active [Default] skin; the stock War3.mpq value is UI\Feedback\Confirmation\Confirmation.mdl.
The client precaches this resource in CL_PrepRefresh, replaces/releases it on late configstring changes, and releases
it in CL_ClearState. An empty slot explicitly disables marker drawing. A missing WC3 skin field or failed model
load is diagnosed; the client does not substitute a hardcoded game asset. WC3 test skin data includes the native key.
This adds meaning to a previously unused configstring slot without changing message framing; old clients do not
consume this new slot, so overriding marker art requires an updated client.
Evidence and verification
A bounded WoW run showed actor Z 38.718 while Wow_UpdateCamera published focus Z 0. The client compensated
using entity 0 plus 1.6 units. Publishing full XYZ from the game removes that implicit entity dependency and preserves
the same visual focus. Spawn and pending-teleport paths publish the same contract.
Coverage includes typed input round trips, every truncated payload length, invalid values, spawned-client gating, WoW actor focus/movement/orbit limits, pan surface selection, single-target groups, and modal movement release. Existing suites cover scripted cameras, native angle conversions, selection reconciliation, cvar alias lifecycle, and order-marker resource replacement. Dedicated tests install mandatory UI callbacks explicitly.
make -j4 openwarcraft3 openwow opensc2 install-share
make -j4 test TEST_JOBS=4
build/bin/openwarcraft3-tests -data build/tests +dedicated 1 +test 'client_input.*'
build/bin/openwow -data data/world-of-warcraft +set vid_hidden 1 +map 1 +com_frame_limit 80
This provides shared input source across game builds. Runtime game-module switching still requires broader ABI, filesystem, renderer, and session work. The architecture review records the original API assessment; remaining menu/session/renderer coupling is outside this input change.
See also: client camera, runtime config, and control groups.
Minimap diagnostic boundary
When a click appears ineffective, trace IN_SelectDown → CL_TryMinimapClick → clc_input/BZ_INPUT_FOCUS →
G_ClientInput → the next vieworigin snapshot before changing input dispatch. Key_Event queues the binding
before SCR_LayoutMouseEvent runs, so a later layout-event return alone does not prove that +select was lost.
A September 9 bounded Human02 replay on 4045cd0c confirmed a minimap hit, no_control=0, and the same focus
coordinates in the returned server snapshot. That revision's normal focused gameplay click could not reproduce
an input-order failure. Also check window focus, modal ownership, cinematic state, and the active MOUSE1 binding.
Regression coverage: client_input.minimap_sdl_click_drag_release_over_hud pushes SDL button/motion events
through CL_Input, the MOUSE1/MOUSE2 bindings, layout handling and Cbuf_Execute. It checks left-click focus
packets and drag/release, right-click smartpoint, Shift-right-click queued smartpoint, unchanged camera state for
Smart minimap clicks, non-minimap HUD blocking, and modal blocking.
wc3_api.controller_focus_updates_camera_and_respects_control
checks server application, camera-target release, bounds clamping and scripted ownership. Moving either selection
or Smart handling's generic HUD blocker ahead of its minimap trace makes the SDL regression fail.
WC3 mouse-edge cursor presentation and retail evidence: scroll cursors.