Skip to content

Commit

Permalink
Switch internal SM concept of frames to use Think (#1540)
Browse files Browse the repository at this point in the history
This has been asked for and debated in some form since Valve introduced
hibernation into the Source engine. The changes here are based on quite
a deep dive into the engine's frame/think logic (mainly in CS:GO which
has "legacy" hibernation and TF2 which has modern "frameless" ticking)
and all seem to be sane.

I think I've managed to maintain all the oddities around time keeping,
and the simulated bool (even though we don't really use it for anything)
should have a sane value. There is a slight behaviour change for
anything needing exact timings as we're now run earlier in the frame
before gpGlobals are updated, this should generally be fine but it might
affect some plugins such as bhop timers that are trying to be extremely
precise (often more precise than the underlying data they're using).

We'll probably want to add a native for plugins to detect if the server
is not completely simulating so they can opt out of work, but I think
defaulting to having things work like this makes more sense than adding
a 2nd set of per-frame forwards and natives (#540), and this makes
timers and any extension callbacks work automatically.
  • Loading branch information
asherkin committed Jul 19, 2021
1 parent 32d951e commit b383302
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 23 deletions.
47 changes: 28 additions & 19 deletions core/TimerSys.cpp
Expand Up @@ -35,6 +35,7 @@
#include "frame_hooks.h"
#include "ConVarManager.h"
#include "logic_bridge.h"
#include <bridge/include/IProviderCallbacks.h>

#define TIMER_MIN_ACCURACY 0.1

Expand Down Expand Up @@ -170,9 +171,8 @@ void ITimer::Initialize(ITimedEvent *pCallbacks, float fInterval, float fToExec,
TimerSystem::TimerSystem()
{
m_pMapTimer = NULL;
m_bHasMapTickedYet = false;
m_bHasMapSimulatedYet = false;
m_fLastTickedTime = 0.0f;
OnSourceModLevelEnd();
}

TimerSystem::~TimerSystem()
Expand Down Expand Up @@ -213,39 +213,48 @@ void TimerSystem::OnSourceModLevelEnd()
{
m_bHasMapTickedYet = false;
m_bHasMapSimulatedYet = false;
m_bWasSimulating = false;
m_uFramesAhead = 0;
}

void TimerSystem::GameFrame(bool simulating)
/* Think is called before gpGlobals is updated every frame, even if the server is hibernating */
void TimerSystem::Think(bool unused)
{
if (simulating && m_bHasMapTickedYet)
{
g_fUniversalTime += gpGlobals->curtime - m_fLastTickedTime;
if (!m_bHasMapSimulatedYet)
{
m_bHasMapSimulatedYet = true;
MapTimeLeftChanged();
}
}
else
{
m_uFramesAhead++;
bool simulating = m_bWasSimulating && m_uFramesAhead == 1;

if (m_bHasMapTickedYet) {
g_fUniversalTime += gpGlobals->realtime - m_fLastTickedTime;
} else {
g_fUniversalTime += gpGlobals->interval_per_tick;
}

m_fLastTickedTime = gpGlobals->curtime;
m_fLastTickedTime = gpGlobals->realtime;
m_bHasMapTickedYet = true;

if (g_fUniversalTime >= g_fTimerThink)
{
logicore.callbacks->OnThink(simulating);

if (g_fUniversalTime >= g_fTimerThink) {
RunFrame();

g_fTimerThink = CalcNextThink(g_fTimerThink, TIMER_MIN_ACCURACY);
}

RunFrameHooks(simulating);

if (m_pOnGameFrame->GetFunctionCount())
m_pOnGameFrame->Execute();
}

/* GameFrame is called after gpGlobals is updated, and may not be called when the server is hibernating */
void TimerSystem::GameFrame(bool simulating)
{
m_bWasSimulating = simulating;
m_uFramesAhead = 0;

if (simulating && !m_bHasMapSimulatedYet)
{
m_pOnGameFrame->Execute(NULL);
m_bHasMapSimulatedYet = true;
MapTimeLeftChanged();
}
}

Expand Down
3 changes: 3 additions & 0 deletions core/TimerSys.h
Expand Up @@ -82,6 +82,7 @@ class TimerSystem :
public:
void RunFrame();
void RemoveMapChangeTimers();
void Think(bool unused);
void GameFrame(bool simulating);
private:
List<ITimer *> m_SingleTimers;
Expand All @@ -92,6 +93,8 @@ class TimerSystem :
/* This is stuff for our manual ticking escapades. */
bool m_bHasMapTickedYet; /** Has the map ticked yet? */
bool m_bHasMapSimulatedYet; /** Has the map simulated yet? */
bool m_bWasSimulating; /** Was the last GameFrame simulating */
unsigned m_uFramesAhead; /** Number of frames Think is ahead of GameFrame */
float m_fLastTickedTime; /** Last time that the game currently gave
us while ticking.
*/
Expand Down
6 changes: 2 additions & 4 deletions core/sourcemod.cpp
Expand Up @@ -46,7 +46,6 @@
#include <amtl/os/am-path.h>
#include <bridge/include/IExtensionBridge.h>
#include <bridge/include/IScriptManager.h>
#include <bridge/include/IProviderCallbacks.h>
#include <bridge/include/ILogger.h>

SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, false, bool, const char *, const char *, const char *, const char *, bool, bool);
Expand Down Expand Up @@ -291,6 +290,7 @@ bool SourceModBase::InitializeSourceMod(char *error, size_t maxlength, bool late
void SourceModBase::StartSourceMod(bool late)
{
SH_ADD_HOOK(IServerGameDLL, LevelShutdown, gamedll, SH_MEMBER(this, &SourceModBase::LevelShutdown), false);
SH_ADD_HOOK(IServerGameDLL, Think, gamedll, SH_MEMBER(&g_Timers, &TimerSystem::Think), false);
SH_ADD_HOOK(IServerGameDLL, GameFrame, gamedll, SH_MEMBER(&g_Timers, &TimerSystem::GameFrame), false);

enginePatch = SH_GET_CALLCLASS(engine);
Expand Down Expand Up @@ -362,8 +362,6 @@ void SourceModBase::StartSourceMod(bool late)
{
g_pSourcePawn2->InstallWatchdogTimer(atoi(timeout) * 1000);
}

SH_ADD_HOOK(IServerGameDLL, Think, gamedll, SH_MEMBER(logicore.callbacks, &IProviderCallbacks::OnThink), false);
}

static bool g_LevelEndBarrier = false;
Expand Down Expand Up @@ -598,8 +596,8 @@ void SourceModBase::ShutdownServices()
}

SH_REMOVE_HOOK(IServerGameDLL, LevelShutdown, gamedll, SH_MEMBER(this, &SourceModBase::LevelShutdown), false);
SH_REMOVE_HOOK(IServerGameDLL, Think, gamedll, SH_MEMBER(&g_Timers, &TimerSystem::Think), false);
SH_REMOVE_HOOK(IServerGameDLL, GameFrame, gamedll, SH_MEMBER(&g_Timers, &TimerSystem::GameFrame), false);
SH_REMOVE_HOOK(IServerGameDLL, Think, gamedll, SH_MEMBER(logicore.callbacks, &IProviderCallbacks::OnThink), false);
}

void SourceModBase::LogMessage(IExtension *pExt, const char *format, ...)
Expand Down

0 comments on commit b383302

Please sign in to comment.