1717
1818#include < climits>
1919#include < fstream>
20- #include " TasTools/AutoJumpTool .hpp"
20+ #include " TasTools/JumpTool .hpp"
2121
2222Variable sar_tas_debug (" sar_tas_debug" , " 0" , 0 , 2 , " Debug TAS information. 0 - none, 1 - basic, 2 - all.\n " );
2323Variable sar_tas_dump_usercmd (" sar_tas_dump_usercmd" , " 0" , " Dump TAS-generated usercmds to a file.\n " );
@@ -136,6 +136,19 @@ TasPlayer::~TasPlayer() {
136136 // framebulkQueue[1].clear();
137137}
138138
139+ bool TasPlayer::IsUsingTools () const {
140+ if (!sar_tas_tools_enabled.GetBool ()) {
141+ return false ;
142+ }
143+
144+ if (sar_tas_tools_force.GetBool ()) {
145+ return true ;
146+ }
147+
148+ return (playbackInfo.slots [0 ].IsActive () && !playbackInfo.slots [0 ].IsRaw ())
149+ || (playbackInfo.slots [1 ].IsActive () && !playbackInfo.slots [1 ].IsRaw ());
150+ }
151+
139152void TasPlayer::Activate (TasPlaybackInfo info) {
140153
141154 if (!info.HasActiveSlot ()) return ;
@@ -154,8 +167,8 @@ void TasPlayer::Activate(TasPlaybackInfo info) {
154167 for (int slot = 0 ; slot < 2 ; ++slot) {
155168 playbackInfo.slots [slot].ClearGeneratedContent ();
156169
157- currentInputFramebulkIndex [slot] = 0 ;
158- currentToolsFramebulkIndex[slot] = 0 ;
170+ currentRequestRawFramebulkIndex [slot] = 0 ;
171+
159172 }
160173
161174 active = true ;
@@ -350,6 +363,29 @@ TasFramebulk TasPlayer::GetRawFramebulkAt(int slot, int tick, unsigned& cachedIn
350363 return playbackInfo.slots [slot].framebulks [cachedIndex];
351364}
352365
366+ TasFramebulk &TasPlayer::RequestProcessedFramebulkAt (int slot, int tick) {
367+ auto &processed = playbackInfo.slots [slot].processedFramebulks ;
368+ auto processedCount = processed.size ();
369+
370+ if (processedCount == 0 || processed.back ().tick < tick) {
371+ TasFramebulk fb = GetRawFramebulkAt (slot, tick, currentRequestRawFramebulkIndex[slot]);
372+ processed.push_back (fb);
373+ return processed.back ();
374+ }
375+
376+ // if it already exists, it should be near the end, as we usually request the newest ones
377+ for (int index = processedCount - 1 ; index >= 0 ; --index) {
378+ if (processed[index].tick == tick) {
379+ return processed[index];
380+ } else if (processed[index].tick < tick) {
381+ break ;
382+ }
383+ }
384+
385+ console->Warning (" TAS processed framebulk for tick %d not found! This should not happen!\n " , tick);
386+ return processed.back ();
387+ }
388+
353389TasPlayerInfo TasPlayer::GetPlayerInfo (int slot, void *player, CUserCmd *cmd, bool clientside) {
354390 TasPlayerInfo pi;
355391
@@ -427,7 +463,7 @@ TasPlayerInfo TasPlayer::GetPlayerInfo(int slot, void *player, CUserCmd *cmd, bo
427463
428464 // predict the result of autojump tool so other tools can react appropriately.
429465 FOR_TAS_SCRIPT_VERSIONS_SINCE (8 ) {
430- if (autoJumpTool[slot].GetCurrentParams (). enabled && autoJumpTool [slot].ShouldJump (pi)) {
466+ if (autoJumpTool[slot].WillJump (pi) || jumpTool [slot].WillJump (pi)) {
431467 pi.willBeGrounded = false ;
432468 }
433469 }
@@ -540,7 +576,7 @@ void TasPlayer::SaveProcessedFramebulks() {
540576 we assume the response time for our "virtual controller" to be
541577 non-existing and just let it parse inputs corresponding to given tick.
542578*/
543- void TasPlayer::FetchInputs (int slot, TasController *controller) {
579+ void TasPlayer::FetchInputs (int slot, TasController *controller, CUserCmd* cmd ) {
544580 // Slight hack! Input fetching (including SteamControllerMove) is
545581 // called through _Host_RunFrame_Input, which is called *before*
546582 // GameFrame (that being called via _Host_RunFrame_Server). Therefore,
@@ -549,36 +585,23 @@ void TasPlayer::FetchInputs(int slot, TasController *controller) {
549585 // said than done since the input fetching code is only run when the
550586 // client is connected, so to match the behaviour we'd probably need
551587 // to actually hook at _Host_RunFrame_Input or CL_Move.
552- int tick = currentTick + 1 ;
588+ int tasTick = currentTick + 1 ;
553589
554- TasFramebulk fb = GetRawFramebulkAt (slot, tick, currentInputFramebulkIndex[slot] );
590+ auto player = server-> GetPlayer (slot + 1 );
555591
556- int fbTick = fb.tick ;
557-
558- if (sar_tas_debug.GetInt () > 0 && fbTick == tick) {
559- console->Print (" %s\n " , fb.ToString ().c_str ());
592+ if (tasTick == 1 ) {
593+ SamplePreProcessedFramebulk (slot, 0 , player, cmd);
560594 }
561595
596+ TasFramebulk fb = SamplePreProcessedFramebulk (slot, tasTick, player, cmd);
597+
562598 controller->SetViewAnalog (fb.viewAnalog .x , fb.viewAnalog .y );
563599 controller->SetMoveAnalog (fb.moveAnalog .x , fb.moveAnalog .y );
564600 for (int i = 0 ; i < TAS_CONTROLLER_INPUT_COUNT; i++) {
565601 controller->SetButtonState ((TasControllerInput)i, fb.buttonStates [i]);
566602 }
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- }
603+ for (std::string cmd : fb.commands ) {
604+ controller->AddCommandToQueue (cmd);
582605 }
583606}
584607
@@ -591,6 +614,52 @@ static bool IsTaunting(ClientEnt *player) {
591614 return false ;
592615}
593616
617+ TasFramebulk TasPlayer::SamplePreProcessedFramebulk (int slot, int tasTick, void *player, CUserCmd *cmd) {
618+ TasFramebulk& fb = RequestProcessedFramebulkAt (slot, tasTick);
619+
620+ auto fbTick = fb.tick ;
621+ fb.tick = tasTick;
622+ bool framebulkUpdated = (fbTick == tasTick);
623+
624+ if (sar_tas_debug.GetInt () > 0 && framebulkUpdated) {
625+ console->Print (" (TAS: rawtick) %s\n " , fb.ToString ().c_str ());
626+ }
627+
628+ if (tasTick == 0 || !framebulkUpdated) {
629+ std::vector<std::string> emptyCommands;
630+ fb.commands = emptyCommands;
631+ }
632+
633+ if (!framebulkUpdated) {
634+ std::vector<TasToolCommand> emptyToolCmds;
635+ fb.toolCmds = emptyToolCmds;
636+ }
637+
638+ if (tasTick == 1 ) {
639+ // on tick 1, we'll run the commands from the bulk at tick 0 because
640+ // of the annoying off-by-one thing explained in FetchInputs
641+ TasFramebulk fb0 = GetRawFramebulkAt (slot, 0 );
642+
643+ if (fb0.tick == 0 ) {
644+ for (std::string cmd : fb0.commands ) {
645+ fb.commands .push_back (cmd);
646+ }
647+ }
648+ }
649+
650+ if (IsUsingTools ()) {
651+ UpdateTools (slot, fb, PRE_PROCESSING);
652+ auto pInfo = GetPlayerInfo (slot, server->GetPlayer (slot + 1 ), cmd);
653+ ApplyTools (fb, pInfo, PRE_PROCESSING);
654+
655+ if (sar_tas_debug.GetInt () > 0 ) {
656+ console->Print (" (TAS: pretick) %s\n " , fb.ToString ().c_str ());
657+ }
658+ }
659+
660+ return fb;
661+ }
662+
594663// special tools have to be parsed in input processing part.
595664// because of alternateticks, a pair of inputs are created and then executed at the same time,
596665// meaning that second tick in pair reads outdated info.
@@ -603,19 +672,9 @@ void TasPlayer::PostProcess(int slot, void *player, CUserCmd *cmd) {
603672 // every other way of getting time is incorrect due to alternateticks
604673 int tasTick = FetchCurrentPlayerTickBase (player) - startTick;
605674
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- }
675+ TasFramebulk& fb = RequestProcessedFramebulkAt (slot, tasTick);
618676
677+ UpdateTools (slot, fb, POST_PROCESSING);
619678 auto playerInfo = GetPlayerInfo (slot, player, cmd);
620679
621680 float orig_forward = cmd->forwardmove ;
@@ -634,21 +693,7 @@ void TasPlayer::PostProcess(int slot, void *player, CUserCmd *cmd) {
634693 return ;
635694 }
636695
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-
696+ ApplyTools (fb, playerInfo, POST_PROCESSING);
652697
653698 // make sure none of the framebulk is NaN
654699 if (std::isnan (fb.moveAnalog .x )) fb.moveAnalog .x = 0 ;
@@ -717,13 +762,9 @@ void TasPlayer::PostProcess(int slot, void *player, CUserCmd *cmd) {
717762 SE (player)->fieldOff <CUserCmd>(" m_hViewModel" , 8 ) /* m_LastCmd */ = *cmd;
718763 }
719764
720- // put processed framebulk in the list
721- if (fbTick != tasTick) {
722- std::vector<std::string> empty;
723- fb.commands = empty;
765+ if (sar_tas_debug.GetInt () > 0 ) {
766+ console->Print (" (TAS: posttick) %s\n " , fb.ToString ().c_str ());
724767 }
725- playbackInfo.slots [slot].processedFramebulks .push_back (fb);
726-
727768 tasPlayer->DumpUsercmd (slot, cmd, tasTick, " processed" );
728769}
729770
@@ -756,6 +797,48 @@ void TasPlayer::ApplyMoveAnalog(Vector moveAnalog, CUserCmd *cmd) {
756797 }
757798}
758799
800+ void TasPlayer::UpdateTools (int slot, const TasFramebulk &fb, TasToolProcessingType processType) {
801+ for (TasToolCommand cmd : fb.toolCmds ) {
802+ auto tool = TasTool::GetInstanceByName (slot, cmd.tool ->GetName ());
803+ if (!CanProcessTool (tool, processType)) continue ;
804+ tool->SetParams (cmd.params );
805+ }
806+ }
807+
808+ void TasPlayer::ApplyTools (TasFramebulk &fb, const TasPlayerInfo &pInfo, TasToolProcessingType processType) {
809+ int slot = pInfo.slot ;
810+
811+ FOR_TAS_SCRIPT_VERSIONS_UNTIL (2 ) {
812+ // use old "earliest first" ordering system (partially also present in TasTool::SetParams)
813+ for (TasTool *tool : TasTool::GetList (slot)) {
814+ if (!CanProcessTool (tool, processType)) continue ;
815+ tool->Apply (fb, pInfo);
816+ }
817+ return ;
818+ }
819+
820+ // use priority list for newer versions. technically all tools should be in the list
821+ for (std::string toolName : TasTool::priorityList) {
822+ auto tool = TasTool::GetInstanceByName (slot, toolName);
823+ if (!CanProcessTool (tool, processType)) continue ;
824+ tool->Apply (fb, pInfo);
825+ }
826+ }
827+
828+ bool TasPlayer::CanProcessTool (TasTool *tool, TasToolProcessingType processType) {
829+ if (tool == nullptr ) {
830+ return false ;
831+ }
832+
833+ int slot = tool->GetSlot ();
834+ FOR_TAS_SCRIPT_VERSIONS_UNTIL (8 ) {
835+ // old scripts process all tools in post processing
836+ return processType == POST_PROCESSING;
837+ }
838+
839+ return tool->CanProcess (processType);
840+ }
841+
759842void TasPlayer::DumpUsercmd (int slot, const CUserCmd *cmd, int tick, const char *source) {
760843 if (!sar_tas_dump_usercmd.GetBool ()) return ;
761844 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