Repository navigation
POV demos: the red crosshair opens when the player runs, as it did live (follows #1187) - #1238
Merged
Merged
Conversation
The call site at client+0x2cda9, the scans that find it and the hook that answers it move out of spectator_crosshair_gap.rs into crosshair_gap.rs, so a second answer (a POV demo's, next commit) can share them. The spectator's arithmetic, its table and its speed stay where they were; the table is read by spectator_crosshair_gap::ready() before spectator_crosshair routes the red crosshair, as the scan read it before. Behaviour is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1187) DoD's style-0 crosshair widens its gap above 105 u/s from a velocity that only HUD_WeaponsPostThink writes, from the client's own prediction, which does not run in demo playback; so a POV demo held the resting gap however fast its recorder ran. While a POV demo plays, crosshair_gap now also points the gap call at its hook, and pov_crosshair_gap runs the game's own gap function with the view's simvel (the recorded velocity) in that slot for the one call, putting the old value back after it. cl_dynamic_xhair, the fixed grenade and rocket gap, the weapon table and the easing stay the game's. Live play and HLTV demos are unchanged. verify_spectator_crosshair_offsets.py gains check 12: the velocity is the vector the spread function takes the length of, and one function writes it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One hook-log line per change (a POV demo starts or stops), not per frame, so a user's log shows the crosshair fix was in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Release note: New: with
cl_xhair_style 0andcl_dynamic_xhair 1, a POV demo's red crosshair now opens while the player runs and closes when he stops, as it did when he played.Follows #1187 (#308). No issue to close.
Why a POV demo's crosshair never moved
Checked in
client.dll(both installs, byte-identical), not taken from #1187. The gap functionclient+0x2cfb0has three inputs:client+0x223b0), set by theCurWeaponmessageg_iUser3 == 2(client+0xe88dc) or the sandbag flag (client+0xea46c), both written atclient+0x311bc/+0x311d9from the player's entity stateclient+0x1080f8.textisclient+0x12656, insideHUD_WeaponsPostThink(client+0x12360), which copies the predicted player's velocity in. Prediction does not run in demo playback, so it stays at whatever it was (zero in a fresh game)So only the speed term was missing, which is what #1187 saw (5 px over the whole rush).
What changes
client+0x2cda9), the scans that find it and the hook that answers it move out ofspectator_crosshair_gap.rsinto a newcrosshair_gap.rs, so a second answer can share them. The spectator's table, arithmetic and speed stay inspectator_crosshair_gap.rs(ready()reads the table beforespectator_crosshairroutes the red draw, as the scan did before). No behaviour change;git diff --color-movedshows it.pov_crosshair_gap.rs. While a POV demo plays (pov_demo_playing: playing back and not an HLTV demo),crosshair_gappoints the gap call at its hook; in the demo's own view (g_iUser10) the hook puts the view's velocity intoclient+0x1080f8for one call of the game's own gap function and puts the old value back after it. The velocity isref_params_s.simvelas the engine hands it toV_CalcRefdeffor the main view (nowpubinengine.rs), kept in three atomics; the hook reads atomics only. Everything else is the game's code:cl_dynamic_xhair, the fixed 4 px for grenades and rockets, the weapon table, the 3D speed, the 3 px a frame easing and the x87 precision.ds_debug_statusgets aPOV demo red crosshair: gap N px, the recorder at N u/sline while a POV demo plays.tools/verify_spectator_crosshair_offsets.pyreads both Rust files and gains check 12:VELOCITY_ATis the vector the spread function pushes for its length call, exactly one writer stores its x, y, z (client+0x12656), and every other use only pushes it. All checks pass on PRE and POST.docs/goldsrc_crosshair.md§2 (the POV paragraph rewritten),docs/dodstudio_commands.md("Stock settings the hook extends":cl_dynamic_xhair),lib.rsmodule list.Decisions
cl_dynamic_xhair, which the game's function still reads (0keeps the fixed 4 px, measured).simvel, the predicted velocity the recorder's crosshair read live (and the one his view bob moves with). Measured inwsod25_grp2_h1_warchyld(PRE, a temporary log in a probe build, not committed): on 1,290 of the 1,371 frames whosesimorgsat exactly on a recorded frame's,simvelwas that frame's recorded value to 0.02 u/s; the other frames are between recorded ones, where the engine interpolates. 3D, as the game's: a fall widens it too (the HLTV side stays horizontal, HLTV first-person view draws POV's red crosshair, its gap following the watched player (#308) #1187).client+0x1080f8see what they saw before, and the change is confined to the crosshair.patch::write_code_bytes; the two users' wishes are two atomics, sospectator_crosshairturning its side off in a POV demo no longer fights the POV side.Test done
In-game, Brain account,
game_probe.py(game behind,game_lock), this branch's release DLL (local\test-povxhair\dodstudio_goldsrc_hooks.dll) against a dev build of 1369693 (local\test-povxhair\dev-13696938\),cl_xhair_style 0,cl_dynamic_xhair 1, clips recorded by HLAE at 5 fps (a frame is 0.2 s of demo time), each frame scanned for the right arm's distance from the centre (±1 px; a 4 px gap reads 4-5).wsod25_grp2_h1_warchyld(K43) from 13:27:35 on the viewdemo bar, 389 frames: dev 4-6 px on every frame; this branch 5 at rest, 25-26 running (157 frames), easing 3 px a frame between, 4 on a stick grenade.cl_dynamic_xhair 0: 4-5 px in both builds.../path, 370 frames: the same (dev 4-6; this branch 5 / 25-26 / grenade 4;cl_dynamic_xhair 04-5).wsod25_grp2_h1_hltvfrom 13:26:00, in-eye on warchyld,ds_spec_match_pov 1), POV vs HLTV frame by frame (frames matched by picture, the crosshair left out): this branch within 3 px of HLTV on 213 of 252 frames (PRE) and 232 of 265 (POST); dev on 111 of 252 and 113 of 263. The misses are the ease-in/ease-out frames a few frames apart and the frames HLTV's Spectator crosshair follows POV's rules (weapon switch, airborne, sprint, prone transitions, snipers, melee, undeployed MG, reload) #310 rule hides that the POV demo shows (pre-existing).ktps8w11sf-gorilla_gskill_m2_armory_h2(Gorilla, BAR) from 5:15:00: 7-8 at rest, 27-28 running;ktps8w10qf-element_soul_m1_saints_h2(element, Garand and a K98) from 8:14:00: 25 running with the Garand, 34 with the K98, 4 on a grenade;ktps8w5-db_thunder_thunder2_h2(deeebeee, MP40) from 7:56:00: 27-28 running. All as the weapon table says.local\review-images\povxhair\(pre_pov_before_after_hltv.mp4,_0.25x,post_*,*_running_*.png,*_resting_*.png,*_grenade_*.png, per-frame gaps in*_gaps.tsv).ds_debug_statusprinted thePOV demo red crosshair:line, and the gaps were the same (5 rest, 25-26 running, 4 on the grenade).cargo test -p goldsrc-hooks --target i686-pc-windows-msvc: 1110 passed. Clippy clean on i686 and host (1.98.1), fmt clean. Commit 1 alone also builds, passes the tests (1108) and the offsets check.Needs a look
commands/poll.rs,engine.rs), R&D: show the overview map in POV demos (part of #897) #906 and R&D: a restyled health readout drawn by the hook (part of #849) #882 (engine.rs,lib.rs). Small, separate hunks; whichever merges second may need a trivial rebase.🤖 Generated with Claude Code