Skip to content

Commit

Permalink
capture: Add _bxt_cap_skip_non_gameplay_frames (#55)
Browse files Browse the repository at this point in the history
* add bxt_cap_skip_non_gameplay_frames

* changes suggested
  • Loading branch information
khanghugo authored Oct 28, 2023
1 parent a9d61c0 commit 377b63e
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 1 deletion.
34 changes: 33 additions & 1 deletion src/hooks/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ pub static ClientDLL_DrawTransparentTriangles: Pointer<unsafe extern "C" fn()> =
null_mut(),
);
pub static cl: Pointer<*mut client_state_t> = Pointer::empty(b"cl\0");
pub static cl_stats: Pointer<*mut [i32; 32]> = Pointer::empty(
// Not a real symbol name.
b"cl_stats\0",
);
pub static cl_viewent: Pointer<*mut cl_entity_s> = Pointer::empty(
// Not a real symbol name.
b"cl_viewent\0",
Expand All @@ -154,6 +158,10 @@ pub static cl_viewent_viewmodel: Pointer<*mut cl_entity_s_viewmodel> = Pointer::
b"cl_viewent_viewmodel\0",
);
pub static cls: Pointer<*mut client_static_s> = Pointer::empty(b"cls\0");
pub static cls_demoframecount: Pointer<*mut c_int> = Pointer::empty(
// Not a real symbol name.
b"cls_demoframecount\0",
);
pub static cls_demos: Pointer<*mut client_static_s_demos> = Pointer::empty(
// Not a real symbol name.
b"cls_demos\0",
Expand Down Expand Up @@ -878,9 +886,11 @@ static POINTERS: &[&dyn PointerTrait] = &[
&ClientDLL_Init,
&ClientDLL_DrawTransparentTriangles,
&cl,
&cl_stats,
&cl_viewent,
&cl_viewent_viewmodel,
&cls,
&cls_demoframecount,
&cls_demos,
&Cmd_AddMallocCommand,
&Cmd_Argc,
Expand Down Expand Up @@ -1341,8 +1351,10 @@ unsafe fn find_pointers(marker: MainThreadMarker) {
pointer.set(marker, ptr);
}

cl_stats.set(marker, cl.offset(marker, 174892));
cl_viewent.set(marker, cl.offset(marker, 1717500));
cl_viewent_viewmodel.set(marker, cl_viewent.offset(marker, 2888));
cls_demoframecount.set(marker, cls.offset(marker, 16776));
cls_demos.set(marker, cls.offset(marker, 15960));
frametime_remainder.set(marker, CL_Move.by_offset(marker, 452));
idum.set(marker, ran1.by_offset(marker, 2));
Expand Down Expand Up @@ -1410,6 +1422,13 @@ pub unsafe fn find_pointers(marker: MainThreadMarker, base: *mut c_void, size: u
_ => (),
}

let ptr = &CL_PlayDemo_f;
match ptr.pattern_index(marker) {
// 8684
Some(0) => cls_demoframecount.set(marker, ptr.by_offset(marker, 735)),
_ => (),
}

let ptr = &Cmd_AddMallocCommand;
match ptr.pattern_index(marker) {
// 6153
Expand Down Expand Up @@ -1605,6 +1624,15 @@ pub unsafe fn find_pointers(marker: MainThreadMarker, base: *mut c_void, size: u
_ => (),
}

let ptr = &R_DrawViewModel;
match ptr.pattern_index(marker) {
// 8684
Some(0) => {
cl_stats.set(marker, ptr.by_offset(marker, 129));
}
_ => (),
}

let ptr = &R_SetFrustum;
match ptr.pattern_index(marker) {
// 6153
Expand Down Expand Up @@ -2171,7 +2199,9 @@ pub mod exported {
};

if rv != 0 {
capture::time_passed(marker);
if capture_skip_non_gameplay::should_record_current_frame(marker) {
capture::time_passed(marker);
}

tas_optimizer::update_client_connection_condition(marker);
tas_optimizer::maybe_receive_messages_from_remote_server(marker);
Expand All @@ -2189,6 +2219,8 @@ pub mod exported {
abort_on_panic(move || {
let marker = MainThreadMarker::new();

capture_skip_non_gameplay::on_cl_disconnect(marker);

if !capture_video_per_demo::on_cl_disconnect(marker) {
capture::on_cl_disconnect(marker);
}
Expand Down
88 changes: 88 additions & 0 deletions src/modules/capture_skip_non_gameplay.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//! bxt_cap_skip_non_gameplay_frames.
use super::cvars::CVar;
use super::{capture, Module};
use crate::hooks::engine;
use crate::utils::*;

pub struct CaptureSkipNonGameplay;
impl Module for CaptureSkipNonGameplay {
fn name(&self) -> &'static str {
"Video capture (skipping non-gameplay frames)"
}

fn description(&self) -> &'static str {
"Skipping loading or broken frames."
}

fn is_enabled(&self, marker: MainThreadMarker) -> bool {
capture::Capture.is_enabled(marker)
&& engine::cls.is_set(marker)
&& engine::cl_stats.is_set(marker)
&& engine::cls_demoframecount.is_set(marker)
&& engine::cls_demos.is_set(marker)
}

fn cvars(&self) -> &'static [&'static super::cvars::CVar] {
static CVARS: &[&CVar] = &[&BXT_CAP_SKIP_NON_GAMEPLAY_FRAMES];
CVARS
}
}

static BXT_CAP_SKIP_NON_GAMEPLAY_FRAMES: CVar = CVar::new(
b"bxt_cap_skip_non_gameplay_frames\0",
b"0\0",
"\
Skipping recording non-gameplay frames such as main menu, loading screen, or demo load.
Set to `0` to disable. Set to `1` to enable.
Any values higher than `1` will be the extra 'gameplay' frames being skipped. \
For example, `2` means one extra gameplay frame skipped during capture.",
);

static FRAMES_SKIPPED: MainThreadCell<u32> = MainThreadCell::new(0);

pub unsafe fn should_record_current_frame(marker: MainThreadMarker) -> bool {
if !CaptureSkipNonGameplay.is_enabled(marker) {
return true;
}

let skip_frames = BXT_CAP_SKIP_NON_GAMEPLAY_FRAMES.as_u64(marker);

if skip_frames == 0 {
return true;
}

if (&*engine::cls.get(marker)).state != 5 {
// If state is not 5, skip frame.
// State 4 is still loading.
return false;
}

// demoplayback is updated to 1 after state 4 is done.
// The current implementation will skip all the frames until some viewmodel values are set.
if (&*engine::cls_demos.get(marker)).demoplayback == 1 {
// For some reasons, the "true" first frame will be catched in this condition
// despite having no viewmodel shown in demo.
// So it does technically capture all non-loading frames.
if (*engine::cl_stats.get(marker))[2] == 0 {
// Fallback when there is no viewmodel assigned ever. Happens when no weapons are picked
// up. Frame 7 is guaranteed to be the "start" frame most of the time.
if *engine::cls_demoframecount.get(marker) < 7 {
return false;
}
}

// Alternative use of the cvar to skip multiple starting frames.
if FRAMES_SKIPPED.get(marker) + 1 < skip_frames as u32 {
FRAMES_SKIPPED.set(marker, FRAMES_SKIPPED.get(marker) + 1);
return false;
}
}

true
}

pub fn on_cl_disconnect(marker: MainThreadMarker) {
FRAMES_SKIPPED.set(marker, 0);
}
2 changes: 2 additions & 0 deletions src/modules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub mod cvars;
use cvars::CVar;

pub mod capture;
pub mod capture_skip_non_gameplay;
pub mod capture_video_per_demo;
pub mod comment_overflow_fix;
pub mod demo_playback;
Expand Down Expand Up @@ -84,6 +85,7 @@ pub trait Module: Sync {
/// All modules.
pub static MODULES: &[&dyn Module] = &[
&capture::Capture,
&capture_skip_non_gameplay::CaptureSkipNonGameplay,
&capture_video_per_demo::CaptureVideoPerDemo,
&commands::Commands,
&comment_overflow_fix::CommentOverflowFix,
Expand Down

0 comments on commit 377b63e

Please sign in to comment.