mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
VScript: properly implement script command callback
Previously we did sq_pushroottable() and a subsequent sq_call() after compiling the text buffer, but this didn't work for code that was threaded, or using Get/SetNetVar* functions. The second issue was that the callback for the "script" command was ran in the main thread. Server script should always run in the server frame thread, the Set/GetNetVar* functions check thread id to retrieve the correct VM context, so running server script from the main thread ended up with Set/GetNetVar* functions retrieving the client VM context rather than server's, causing undefined behavior. Script commands are now queued to the server frame thread, ultimately fixing this bug. Also fixed a small bug with function 'sq_compilebuffer()'; it takes an extra argument but this wasn't taken into account in the SDK.
This commit is contained in:
parent
183a6e9c35
commit
1455017419
@ -10,6 +10,7 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
#include "core/stdafx.h"
|
||||
#include "common/protocol.h"
|
||||
#include "tier0/frametask.h"
|
||||
#include "tier1/cvar.h"
|
||||
#include "tier1/strtools.h"
|
||||
#include "engine/server/sv_main.h"
|
||||
@ -217,6 +218,17 @@ void CServer::BroadcastMessage(CNetMessage* const msg, const bool onlyActive, co
|
||||
//---------------------------------------------------------------------------------
|
||||
void CServer::FrameJob(double flFrameTime, bool bRunOverlays, bool bUpdateFrame)
|
||||
{
|
||||
for (IFrameTask* const& task : g_ServerTaskQueueList)
|
||||
{
|
||||
task->RunFrame();
|
||||
}
|
||||
|
||||
g_ServerTaskQueueList.erase(std::remove_if(g_ServerTaskQueueList.begin(),
|
||||
g_ServerTaskQueueList.end(), [](const IFrameTask* task)
|
||||
{
|
||||
return task->IsFinished();
|
||||
}), g_ServerTaskQueueList.end());
|
||||
|
||||
CServer__FrameJob(flFrameTime, bRunOverlays, bUpdateFrame);
|
||||
LiveAPISystem()->RunFrame();
|
||||
}
|
||||
@ -242,3 +254,6 @@ void VServer::Detour(const bool bAttach) const
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
CServer* g_pServer = nullptr;
|
||||
CClientExtended CServer::sm_ClientsExtended[MAX_PLAYERS];
|
||||
|
||||
std::list<IFrameTask*> g_ServerTaskQueueList;
|
||||
CFrameTask g_ServerTaskQueue;
|
||||
|
@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include "tier0/frametask.h"
|
||||
#include "tier1/NetAdr.h"
|
||||
#include "networksystem/pylon.h"
|
||||
#include "engine/client/client.h"
|
||||
@ -111,6 +112,9 @@ static_assert(sizeof(CServer) == 0x25264C0);
|
||||
|
||||
extern CServer* g_pServer;
|
||||
|
||||
extern std::list<IFrameTask*> g_ServerTaskQueueList;
|
||||
extern CFrameTask g_ServerTaskQueue;
|
||||
|
||||
extern ConVar sv_showconnecting;
|
||||
|
||||
extern ConVar sv_pylonVisibility;
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "engine/host_cmd.h"
|
||||
#include "engine/enginetrace.h"
|
||||
#ifndef CLIENT_DLL
|
||||
#include "engine/server/server.h"
|
||||
#include "engine/server/sv_main.h"
|
||||
#include "server/vengineserver_impl.h"
|
||||
#include "game/server/gameinterface.h"
|
||||
@ -116,8 +117,11 @@ bool CModAppSystemGroup::StaticCreate(CModAppSystemGroup* pModAppSystemGroup)
|
||||
}
|
||||
|
||||
g_TaskQueueList.push_back(&g_TaskQueue);
|
||||
g_bAppSystemInit = true;
|
||||
#ifndef CLIENT_DLL
|
||||
g_ServerTaskQueueList.push_back(&g_ServerTaskQueue);
|
||||
#endif // !CLIENT_DLL
|
||||
|
||||
g_bAppSystemInit = true;
|
||||
return CModAppSystemGroup__Create(pModAppSystemGroup);
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,18 @@ static void SQVM_ServerScript_f(const CCommand& args)
|
||||
{
|
||||
if (args.ArgC() >= 2)
|
||||
{
|
||||
const char* code = args.ArgS();
|
||||
|
||||
if (!ThreadInServerFrameThread())
|
||||
{
|
||||
const string scode(code);
|
||||
g_ServerTaskQueue.Dispatch([scode]()
|
||||
{
|
||||
Script_Execute(scode.c_str(), SQCONTEXT::SERVER);
|
||||
}, 0);
|
||||
return; // Only run in server frame thread.
|
||||
}
|
||||
|
||||
Script_Execute(args.ArgS(), SQCONTEXT::SERVER);
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,8 @@
|
||||
#include "public/iframetask.h"
|
||||
|
||||
//=============================================================================//
|
||||
// This class is set up to run before each frame (main thread).
|
||||
// Committed tasks are scheduled to execute after 'i' frames.
|
||||
// This class is set up to run before each frame, committed tasks are scheduled
|
||||
// to execute after 'i' frames.
|
||||
// ----------------------------------------------------------------------------
|
||||
// A use case for scheduling tasks in the main thread would be (for example)
|
||||
// performing a web request in a separate thread, and apply the results (such as
|
||||
|
@ -187,7 +187,7 @@ void sq_newtable(HSQUIRRELVM v);
|
||||
SQRESULT sq_newslot(HSQUIRRELVM v, SQInteger idx);
|
||||
SQRESULT sq_arrayappend(HSQUIRRELVM v, SQInteger idx);
|
||||
SQRESULT sq_pushstructure(HSQUIRRELVM v, const SQChar* name, const SQChar* member, const SQChar* codeclass1, const SQChar* codeclass2);
|
||||
SQRESULT sq_compilebuffer(HSQUIRRELVM v, SQBufState* bufferState, const SQChar* buffer, SQInteger context);
|
||||
SQRESULT sq_compilebuffer(HSQUIRRELVM v, SQBufState* bufferState, const SQChar* buffer, SQInteger context, SQBool raiseerror);
|
||||
SQRESULT sq_call(HSQUIRRELVM v, SQInteger params, SQBool retval, SQBool raiseerror);
|
||||
|
||||
SQRESULT sq_startconsttable(HSQUIRRELVM v);
|
||||
@ -228,7 +228,7 @@ inline void(*v_sq_newtable)(HSQUIRRELVM v);
|
||||
inline SQRESULT(*v_sq_newslot)(HSQUIRRELVM v, SQInteger idx);
|
||||
inline SQRESULT(*v_sq_arrayappend)(HSQUIRRELVM v, SQInteger idx);
|
||||
inline SQRESULT(*v_sq_pushstructure)(HSQUIRRELVM v, const SQChar* name, const SQChar* member, const SQChar* codeclass1, const SQChar* codeclass2);
|
||||
inline SQRESULT(*v_sq_compilebuffer)(HSQUIRRELVM v, SQBufState* bufferstate, const SQChar* buffer, SQInteger level);
|
||||
inline SQRESULT(*v_sq_compilebuffer)(HSQUIRRELVM v, SQBufState* bufferstate, const SQChar* buffer, SQInteger level, SQBool raiseerror);
|
||||
inline SQRESULT(*v_sq_call)(HSQUIRRELVM v, SQInteger params, SQBool retval, SQBool raiseerror);
|
||||
inline SQRESULT(*v_sq_get)(HSQUIRRELVM v, SQInteger idx);
|
||||
|
||||
|
@ -177,9 +177,9 @@ SQRESULT sq_pushstructure(HSQUIRRELVM v, const SQChar* name, const SQChar* membe
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
SQRESULT sq_compilebuffer(HSQUIRRELVM v, SQBufState* bufferState, const SQChar* buffer, SQInteger level)
|
||||
SQRESULT sq_compilebuffer(HSQUIRRELVM v, SQBufState* bufferState, const SQChar* buffer, SQInteger level, SQBool raiseerror)
|
||||
{
|
||||
return v_sq_compilebuffer(v, bufferState, buffer, level);
|
||||
return v_sq_compilebuffer(v, bufferState, buffer, level, raiseerror);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
|
@ -133,7 +133,11 @@ SQBool Script_PrecompileClientScripts(CSquirrelVM* vm)
|
||||
void Script_Execute(const SQChar* code, const SQCONTEXT context)
|
||||
{
|
||||
Assert(context != SQCONTEXT::NONE);
|
||||
Assert(ThreadInMainOrServerFrameThread());
|
||||
|
||||
if (context == SQCONTEXT::CLIENT || context == SQCONTEXT::UI)
|
||||
Assert(ThreadInMainThread());
|
||||
else if (context == SQCONTEXT::SERVER)
|
||||
Assert(ThreadInServerFrameThread());
|
||||
|
||||
CSquirrelVM* s = Script_GetScriptHandle(context);
|
||||
const char* const contextName = s_scriptContextNames[(int)context];
|
||||
@ -145,24 +149,29 @@ void Script_Execute(const SQChar* code, const SQCONTEXT context)
|
||||
}
|
||||
|
||||
HSQUIRRELVM v = s->GetVM();
|
||||
|
||||
if (!v)
|
||||
{
|
||||
Error(eDLL_T::ENGINE, NO_ERROR, "Attempted to run %s script while VM isn't initialized\n", contextName);
|
||||
return;
|
||||
}
|
||||
|
||||
SQBufState bufState = SQBufState(code);
|
||||
SQRESULT compileResult = sq_compilebuffer(v, &bufState, "console", -1);
|
||||
SQBufState bufState(code);
|
||||
|
||||
if (SQ_SUCCEEDED(compileResult))
|
||||
if (SQ_SUCCEEDED(sq_compilebuffer(v, &bufState, "unnamed", -1, SQTrue)))
|
||||
{
|
||||
sq_pushroottable(v);
|
||||
SQRESULT callResult = sq_call(v, 1, false, false);
|
||||
SQObject hScript;
|
||||
sq_getstackobj(v, -1, &hScript);
|
||||
|
||||
if (!SQ_SUCCEEDED(callResult))
|
||||
sq_addref(v, &hScript);
|
||||
sq_pop(v, 1);
|
||||
|
||||
if (s->ExecuteFunction((HSCRIPT)&hScript, NULL, 0, NULL, NULL) == SCRIPT_ERROR)
|
||||
{
|
||||
Error(eDLL_T::ENGINE, NO_ERROR, "Failed to execute %s script \"%s\"\n", contextName, code);
|
||||
}
|
||||
|
||||
sq_release(v, &hScript);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user