[FrameTime] Introduce safer frame waiting mechanism
This commit is contained in:
parent
0aaf8b8730
commit
fbac207190
@ -55,6 +55,7 @@ namespace Components
|
||||
Loader::Register(new Discovery());
|
||||
Loader::Register(new Exception());
|
||||
Loader::Register(new FastFiles());
|
||||
Loader::Register(new FrameTime());
|
||||
Loader::Register(new Gametypes());
|
||||
Loader::Register(new Materials());
|
||||
Loader::Register(new Threading());
|
||||
|
@ -67,6 +67,7 @@ namespace Components
|
||||
#include "Modules\Discovery.hpp"
|
||||
#include "Modules\Exception.hpp"
|
||||
#include "Modules\FastFiles.hpp"
|
||||
#include "Modules\FrameTime.hpp"
|
||||
#include "Modules\Gametypes.hpp"
|
||||
#include "Modules\Materials.hpp"
|
||||
#include "Modules\Singleton.hpp"
|
||||
|
@ -9,7 +9,14 @@ namespace Components
|
||||
|
||||
bool Dedicated::IsEnabled()
|
||||
{
|
||||
return Flags::HasFlag("dedicated");
|
||||
static Utils::Value<bool> flag;
|
||||
|
||||
if (!flag.isValid())
|
||||
{
|
||||
flag.set(Flags::HasFlag("dedicated"));
|
||||
}
|
||||
|
||||
return flag.get();
|
||||
}
|
||||
|
||||
void Dedicated::InitDedicatedServer()
|
||||
|
@ -20,6 +20,7 @@ namespace Components
|
||||
Var() : dvar(0) {};
|
||||
Var(const Var &obj) { this->dvar = obj.dvar; };
|
||||
Var(Game::dvar_t* _dvar) : dvar(_dvar) {};
|
||||
Var(DWORD ppdvar) : Var(*reinterpret_cast<Game::dvar_t**>(ppdvar)) {};
|
||||
Var(std::string dvarName);
|
||||
|
||||
template<typename T> T get();
|
||||
|
131
src/Components/Modules/FrameTime.cpp
Normal file
131
src/Components/Modules/FrameTime.cpp
Normal file
@ -0,0 +1,131 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
void FrameTime::NetSleep(int msec)
|
||||
{
|
||||
if (msec < 0) msec = 0;
|
||||
|
||||
fd_set fdr;
|
||||
FD_ZERO(&fdr);
|
||||
|
||||
SOCKET highestfd = INVALID_SOCKET;
|
||||
if (*Game::ip_socket != INVALID_SOCKET)
|
||||
{
|
||||
FD_SET(*Game::ip_socket, &fdr);
|
||||
highestfd = *Game::ip_socket;
|
||||
}
|
||||
|
||||
if (highestfd == INVALID_SOCKET)
|
||||
{
|
||||
// windows ain't happy when select is called without valid FDs
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(msec));
|
||||
return;
|
||||
}
|
||||
|
||||
timeval timeout;
|
||||
timeout.tv_sec = msec / 1000;
|
||||
timeout.tv_usec = (msec % 1000) * 1000;
|
||||
|
||||
int retval = select(highestfd + 1, &fdr, NULL, NULL, &timeout);
|
||||
|
||||
if (retval == SOCKET_ERROR)
|
||||
{
|
||||
Logger::Print("Warning: select() syscall failed: %s\n", Game::NET_ErrorString());
|
||||
}
|
||||
else if (retval > 0)
|
||||
{
|
||||
// process packets
|
||||
if (Dvar::Var(0x1AD7934).get<bool>()) // com_sv_running
|
||||
{
|
||||
Utils::Hook::Call<void()>(0x458160)();
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils::Hook::Call<void()>(0x49F0B0)();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FrameTime::SVFrameWaitFunc()
|
||||
{
|
||||
int sv_residualTime = *reinterpret_cast<int*>(0x2089E14);
|
||||
|
||||
if (sv_residualTime < 50)
|
||||
{
|
||||
FrameTime::NetSleep(50 - sv_residualTime);
|
||||
}
|
||||
}
|
||||
|
||||
void __declspec(naked) FrameTime::SVFrameWaitStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
pushad
|
||||
call FrameTime::SVFrameWaitFunc
|
||||
popad
|
||||
|
||||
push 4CD420h
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
int FrameTime::ComTimeVal(int minMsec)
|
||||
{
|
||||
int timeVal = Game::Sys_Milliseconds() - *reinterpret_cast<uint32_t*>(0x1AD8F3C); // com_frameTime
|
||||
return (timeVal >= minMsec ? 0 : minMsec - timeVal);
|
||||
}
|
||||
|
||||
uint32_t FrameTime::ComFrameWait(int minMsec)
|
||||
{
|
||||
int timeVal;
|
||||
|
||||
do
|
||||
{
|
||||
timeVal = FrameTime::ComTimeVal(minMsec);
|
||||
FrameTime::NetSleep(timeVal < 1 ? 0 : timeVal - 1);
|
||||
} while (FrameTime::ComTimeVal(minMsec));
|
||||
|
||||
uint32_t lastTime = *Game::com_frameTime;
|
||||
Utils::Hook::Call<void()>(0x43D140)(); // Com_EventLoop
|
||||
*Game::com_frameTime = Game::Sys_Milliseconds();
|
||||
|
||||
return *Game::com_frameTime - lastTime;
|
||||
}
|
||||
|
||||
void __declspec(naked) FrameTime::ComFrameWaitStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
push ecx
|
||||
pushad
|
||||
|
||||
push edi
|
||||
call FrameTime::ComFrameWait
|
||||
add esp, 4
|
||||
|
||||
mov[esp + 20h], eax
|
||||
popad
|
||||
pop eax
|
||||
mov ecx, eax
|
||||
|
||||
mov edx, 1AD7934h // com_sv_running
|
||||
cmp byte ptr[edx + 10h], 0
|
||||
|
||||
push 47DDC1h
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
FrameTime::FrameTime()
|
||||
{
|
||||
if (Dedicated::IsEnabled())
|
||||
{
|
||||
Utils::Hook(0x4BAAAD, FrameTime::SVFrameWaitStub, HOOK_CALL).install()->quick();
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils::Hook(0x47DD80, FrameTime::ComFrameWaitStub, HOOK_JUMP).install()->quick();
|
||||
}
|
||||
}
|
||||
}
|
22
src/Components/Modules/FrameTime.hpp
Normal file
22
src/Components/Modules/FrameTime.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
namespace Components
|
||||
{
|
||||
class FrameTime : public Component
|
||||
{
|
||||
public:
|
||||
FrameTime();
|
||||
|
||||
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
|
||||
const char* getName() { return "FrameTime"; };
|
||||
#endif
|
||||
|
||||
private:
|
||||
static void SVFrameWaitStub();
|
||||
static void SVFrameWaitFunc();
|
||||
|
||||
static void NetSleep(int msec);
|
||||
|
||||
static int ComTimeVal(int minMsec);
|
||||
static uint32_t ComFrameWait(int minMsec);
|
||||
static void ComFrameWaitStub();
|
||||
};
|
||||
}
|
@ -630,7 +630,14 @@ namespace Components
|
||||
|
||||
bool ZoneBuilder::IsEnabled()
|
||||
{
|
||||
return (Flags::HasFlag("zonebuilder") && !Dedicated::IsEnabled());
|
||||
static Utils::Value<bool> flag;
|
||||
|
||||
if (!flag.isValid())
|
||||
{
|
||||
flag.set(Flags::HasFlag("zonebuilder"));
|
||||
}
|
||||
|
||||
return (flag.get() && !Dedicated::IsEnabled());
|
||||
}
|
||||
|
||||
void ZoneBuilder::BeginAssetTrace(std::string zone)
|
||||
|
@ -154,6 +154,7 @@ namespace Game
|
||||
|
||||
NET_AdrToString_t NET_AdrToString = (NET_AdrToString_t)0x469880;
|
||||
NET_CompareAdr_t NET_CompareAdr = (NET_CompareAdr_t)0x4D0AA0;
|
||||
NET_ErrorString_t NET_ErrorString = (NET_ErrorString_t)0x4E7720;
|
||||
NET_Init_t NET_Init = (NET_Init_t)0x491860;
|
||||
NET_IsLocalAddress_t NET_IsLocalAddress = (NET_IsLocalAddress_t)0x402BD0;
|
||||
NET_StringToAdr_t NET_StringToAdr = (NET_StringToAdr_t)0x409010;
|
||||
@ -304,6 +305,8 @@ namespace Game
|
||||
|
||||
SOCKET* ip_socket = (SOCKET*)0x64A3008;
|
||||
|
||||
uint32_t* com_frameTime = (uint32_t*)0x1AD8F3C;
|
||||
|
||||
SafeArea* safeArea = (SafeArea*)0xA15F3C;
|
||||
|
||||
SpawnVar* spawnVars = (SpawnVar*)0x1A83DE8;
|
||||
|
@ -380,6 +380,9 @@ namespace Game
|
||||
typedef bool(__cdecl * NET_CompareAdr_t)(netadr_t a, netadr_t b);
|
||||
extern NET_CompareAdr_t NET_CompareAdr;
|
||||
|
||||
typedef const char* (__cdecl * NET_ErrorString_t)();
|
||||
extern NET_ErrorString_t NET_ErrorString;
|
||||
|
||||
typedef void(__cdecl * NET_Init_t)();
|
||||
extern NET_Init_t NET_Init;
|
||||
|
||||
@ -646,6 +649,8 @@ namespace Game
|
||||
extern netadr_t* connectedHost;
|
||||
extern SOCKET* ip_socket;
|
||||
|
||||
extern uint32_t* com_frameTime;
|
||||
|
||||
extern SafeArea* safeArea;
|
||||
|
||||
extern SpawnVar* spawnVars;
|
||||
|
@ -52,4 +52,32 @@ namespace Utils
|
||||
private:
|
||||
std::vector<Slot<T>> slots;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Value
|
||||
{
|
||||
public:
|
||||
Value() : hasValue(false) {}
|
||||
Value(T _value) { this->set(_value); }
|
||||
|
||||
void set(T _value)
|
||||
{
|
||||
this->value = _value;
|
||||
this->hasValue = true;
|
||||
}
|
||||
|
||||
bool isValid()
|
||||
{
|
||||
return this->hasValue;
|
||||
}
|
||||
|
||||
T get()
|
||||
{
|
||||
return this->value;
|
||||
}
|
||||
|
||||
private:
|
||||
bool hasValue;
|
||||
T value;
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user