@@ -154,8 +154,8 @@ void TasPlayer::Activate(TasPlaybackInfo info) {
154154 for (int slot = 0 ; slot < 2 ; ++slot) {
155155 playbackInfo.slots [slot].ClearGeneratedContent ();
156156
157- currentInputFramebulkIndex [slot] = 0 ;
158- currentToolsFramebulkIndex[slot] = 0 ;
157+ currentRequestRawFramebulkIndex [slot] = 0 ;
158+
159159 }
160160
161161 active = true ;
@@ -350,6 +350,28 @@ TasFramebulk TasPlayer::GetRawFramebulkAt(int slot, int tick, unsigned& cachedIn
350350 return playbackInfo.slots [slot].framebulks [cachedIndex];
351351}
352352
353+ TasFramebulk &TasPlayer::RequestProcessedFramebulkAt (int slot, int tick) {
354+ auto &processed = playbackInfo.slots [slot].processedFramebulks ;
355+ auto processedCount = processed.size ();
356+
357+ if (processedCount == 0 || processed.back ().tick < tick) {
358+ TasFramebulk fb = GetRawFramebulkAt (slot, tick, currentRequestRawFramebulkIndex[slot]);
359+ processed.push_back (fb);
360+ return processed.back ();
361+ }
362+
363+ // if it already exists, it should be near the end, as we usually request the newest ones
364+ for (int index = processedCount - 1 ; index >= 0 ; --index) {
365+ if (processed[index].tick == tick) {
366+ return processed[index];
367+ } else if (processed[index].tick < tick) {
368+ break ;
369+ }
370+ }
371+
372+ console->Warning (" TAS processed framebulk for tick %d not found! This should not happen!\n " , tick);
373+ }
374+
353375TasPlayerInfo TasPlayer::GetPlayerInfo (int slot, void *player, CUserCmd *cmd, bool clientside) {
354376 TasPlayerInfo pi;
355377
@@ -540,7 +562,7 @@ void TasPlayer::SaveProcessedFramebulks() {
540562 we assume the response time for our "virtual controller" to be
541563 non-existing and just let it parse inputs corresponding to given tick.
542564*/
543- void TasPlayer::FetchInputs (int slot, TasController *controller) {
565+ void TasPlayer::FetchInputs (int slot, TasController *controller, CUserCmd* cmd ) {
544566 // Slight hack! Input fetching (including SteamControllerMove) is
545567 // called through _Host_RunFrame_Input, which is called *before*
546568 // GameFrame (that being called via _Host_RunFrame_Server). Therefore,
@@ -549,36 +571,23 @@ void TasPlayer::FetchInputs(int slot, TasController *controller) {
549571 // said than done since the input fetching code is only run when the
550572 // client is connected, so to match the behaviour we'd probably need
551573 // to actually hook at _Host_RunFrame_Input or CL_Move.
552- int tick = currentTick + 1 ;
553-
554- TasFramebulk fb = GetRawFramebulkAt (slot, tick, currentInputFramebulkIndex[slot]);
574+ int tasTick = currentTick + 1 ;
555575
556- int fbTick = fb. tick ;
576+ auto player = server-> GetPlayer (slot + 1 ) ;
557577
558- if (sar_tas_debug. GetInt () > 0 && fbTick == tick ) {
559- console-> Print ( " %s \n " , fb. ToString (). c_str () );
578+ if (tasTick == 1 ) {
579+ SamplePreProcessedFramebulk (slot, 0 , player, cmd );
560580 }
561581
582+ TasFramebulk fb = SamplePreProcessedFramebulk (slot, tasTick, player, cmd);
583+
562584 controller->SetViewAnalog (fb.viewAnalog .x , fb.viewAnalog .y );
563585 controller->SetMoveAnalog (fb.moveAnalog .x , fb.moveAnalog .y );
564586 for (int i = 0 ; i < TAS_CONTROLLER_INPUT_COUNT; i++) {
565587 controller->SetButtonState ((TasControllerInput)i, fb.buttonStates [i]);
566588 }
567-
568- if (tick == 1 ) {
569- // on tick 1, we'll run the commands from the bulk at tick 0 because
570- // of the annoying off-by-one thing explained above
571- TasFramebulk fb0 = GetRawFramebulkAt (slot, 0 );
572- for (std::string cmd : fb0.commands ) {
573- controller->AddCommandToQueue (cmd);
574- }
575- }
576-
577- // add commands only for tick when framebulk is placed. Don't preserve it to other ticks.
578- if (tick == fbTick) {
579- for (std::string cmd : fb.commands ) {
580- controller->AddCommandToQueue (cmd);
581- }
589+ for (std::string cmd : fb.commands ) {
590+ controller->AddCommandToQueue (cmd);
582591 }
583592}
584593
@@ -591,6 +600,52 @@ static bool IsTaunting(ClientEnt *player) {
591600 return false ;
592601}
593602
603+ TasFramebulk TasPlayer::SamplePreProcessedFramebulk (int slot, int tasTick, void *player, CUserCmd *cmd) {
604+
605+ auto pInfo = GetPlayerInfo (slot, server->GetPlayer (slot + 1 ), cmd);
606+ TasFramebulk& fb = RequestProcessedFramebulkAt (slot, tasTick);
607+
608+ auto fbTick = fb.tick ;
609+ fb.tick = tasTick;
610+ bool framebulkUpdated = (fbTick == tasTick);
611+
612+ if (sar_tas_debug.GetInt () > 0 && framebulkUpdated) {
613+ console->Print (" (TAS: rawtick) %s\n " , fb.ToString ().c_str ());
614+ }
615+
616+ if (tasTick == 0 || !framebulkUpdated) {
617+ std::vector<std::string> emptyCommands;
618+ fb.commands = emptyCommands;
619+ }
620+
621+ if (!framebulkUpdated) {
622+ std::vector<TasToolCommand> emptyToolCmds;
623+ fb.toolCmds = emptyToolCmds;
624+ }
625+
626+ if (tasTick == 1 ) {
627+ // on tick 1, we'll run the commands from the bulk at tick 0 because
628+ // of the annoying off-by-one thing explained in FetchInputs
629+ TasFramebulk fb0 = GetRawFramebulkAt (slot, 0 );
630+
631+ if (fb0.tick == 0 ) {
632+ for (std::string cmd : fb0.commands ) {
633+ fb.commands .push_back (cmd);
634+ }
635+ }
636+ }
637+
638+ if (IsUsingTools ()) {
639+ ApplyTools (fb, pInfo, PRE_PROCESSING);
640+
641+ if (sar_tas_debug.GetInt () > 0 ) {
642+ console->Print (" (TAS: pretick) %s\n " , fb.ToString ().c_str ());
643+ }
644+ }
645+
646+ return fb;
647+ }
648+
594649// special tools have to be parsed in input processing part.
595650// because of alternateticks, a pair of inputs are created and then executed at the same time,
596651// meaning that second tick in pair reads outdated info.
@@ -603,18 +658,7 @@ void TasPlayer::PostProcess(int slot, void *player, CUserCmd *cmd) {
603658 // every other way of getting time is incorrect due to alternateticks
604659 int tasTick = FetchCurrentPlayerTickBase (player) - startTick;
605660
606- TasFramebulk fb = GetRawFramebulkAt (slot, tasTick, currentToolsFramebulkIndex[slot]);
607-
608- // update all tools that needs to be updated
609- auto fbTick = fb.tick ;
610- fb.tick = tasTick;
611- if (fbTick == tasTick) {
612- for (TasToolCommand cmd : fb.toolCmds ) {
613- auto tool = TasTool::GetInstanceByName (slot, cmd.tool ->GetName ());
614- if (tool == nullptr ) continue ;
615- tool->SetParams (cmd.params );
616- }
617- }
661+ TasFramebulk& fb = RequestProcessedFramebulkAt (slot, tasTick);
618662
619663 auto playerInfo = GetPlayerInfo (slot, player, cmd);
620664
@@ -634,21 +678,7 @@ void TasPlayer::PostProcess(int slot, void *player, CUserCmd *cmd) {
634678 return ;
635679 }
636680
637- // applying tools
638- if (playbackInfo.slots [slot].header .version >= 3 ) {
639- // use priority list for newer versions. technically all tools should be in the list
640- for (std::string toolName : TasTool::priorityList) {
641- auto tool = TasTool::GetInstanceByName (slot, toolName);
642- if (tool == nullptr ) continue ;
643- tool->Apply (fb, playerInfo);
644- }
645- } else {
646- // use old "earliest first" ordering system (partially also present in TasTool::SetParams)
647- for (TasTool *tool : TasTool::GetList (slot)) {
648- tool->Apply (fb, playerInfo);
649- }
650- }
651-
681+ ApplyTools (fb, playerInfo, POST_PROCESSING);
652682
653683 // make sure none of the framebulk is NaN
654684 if (std::isnan (fb.moveAnalog .x )) fb.moveAnalog .x = 0 ;
@@ -717,13 +747,9 @@ void TasPlayer::PostProcess(int slot, void *player, CUserCmd *cmd) {
717747 SE (player)->fieldOff <CUserCmd>(" m_hViewModel" , 8 ) /* m_LastCmd */ = *cmd;
718748 }
719749
720- // put processed framebulk in the list
721- if (fbTick != tasTick) {
722- std::vector<std::string> empty;
723- fb.commands = empty;
750+ if (sar_tas_debug.GetInt () > 0 ) {
751+ console->Print (" (TAS: posttick) %s\n " , fb.ToString ().c_str ());
724752 }
725- playbackInfo.slots [slot].processedFramebulks .push_back (fb);
726-
727753 tasPlayer->DumpUsercmd (slot, cmd, tasTick, " processed" );
728754}
729755
@@ -756,6 +782,46 @@ void TasPlayer::ApplyMoveAnalog(Vector moveAnalog, CUserCmd *cmd) {
756782 }
757783}
758784
785+ void TasPlayer::ApplyTools (TasFramebulk &fb, const TasPlayerInfo &pInfo, TasToolProcessingType processType) {
786+ int slot = pInfo.slot ;
787+
788+ for (TasToolCommand cmd : fb.toolCmds ) {
789+ auto tool = TasTool::GetInstanceByName (slot, cmd.tool ->GetName ());
790+ if (!CanProcessTool (tool, processType)) continue ;
791+ tool->SetParams (cmd.params );
792+ }
793+
794+ FOR_TAS_SCRIPT_VERSIONS_UNTIL (2 ) {
795+ // use old "earliest first" ordering system (partially also present in TasTool::SetParams)
796+ for (TasTool *tool : TasTool::GetList (slot)) {
797+ if (!CanProcessTool (tool, processType)) continue ;
798+ tool->Apply (fb, pInfo);
799+ }
800+ return ;
801+ }
802+
803+ // use priority list for newer versions. technically all tools should be in the list
804+ for (std::string toolName : TasTool::priorityList) {
805+ auto tool = TasTool::GetInstanceByName (slot, toolName);
806+ if (!CanProcessTool (tool, processType)) continue ;
807+ tool->Apply (fb, pInfo);
808+ }
809+ }
810+
811+ bool TasPlayer::CanProcessTool (TasTool *tool, TasToolProcessingType processType) {
812+ if (tool == nullptr ) {
813+ return false ;
814+ }
815+
816+ int slot = tool->GetSlot ();
817+ FOR_TAS_SCRIPT_VERSIONS_UNTIL (8 ) {
818+ // old scripts process all tools in post processing
819+ return processType == POST_PROCESSING;
820+ }
821+
822+ return tool->CanProcess (processType);
823+ }
824+
759825void TasPlayer::DumpUsercmd (int slot, const CUserCmd *cmd, int tick, const char *source) {
760826 if (!sar_tas_dump_usercmd.GetBool ()) return ;
761827 std::string str = Utils::ssprintf (" %s,%d,%.6f,%.6f,%08X,%.6f,%.6f,%.6f" , source, tick, cmd->forwardmove , cmd->sidemove , cmd->buttons , cmd->viewangles .x , cmd->viewangles .y , cmd->viewangles .z );
0 commit comments