[FrameTime] Introduce safer frame waiting mechanism

This commit is contained in:
momo5502 2017-01-14 13:33:43 +01:00
parent 0aaf8b8730
commit fbac207190
10 changed files with 208 additions and 2 deletions

View File

@ -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());

View File

@ -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"

View File

@ -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()

View File

@ -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();

View 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();
}
}
}

View 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();
};
}

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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;
};
}