r5sdk/r5dev/squirrel/sqscript.cpp
Kawe Mazidjatari 656b0be3ec Improve threading (work in progress)
* Only run '_DownloadPlaylists_f()' in the main thread, schedule for next frame if we aren't in the main thread. (this should fix crash cases related to disconnecting from the game).

* Locked read/write to CBrowser members (thread for obtaining the server list is detached, but once the 'slow' post operation in this thread is complete, mutex lock is acquired (locking the render thread if the browser is active) to set the string members of CBrowser, this operation is very fast as we only set the string and the color after the http post operation (this never caused a crash, but the behavior without any lock mechanism is technically undefined regardless).

* Obtain the host name dynamically from the ConVar 'pylon_matchmaking_hostname' (atomic operation). Initial approach was deleting the whole master server pointer just to construct a new httpclient object..
2022-08-27 18:57:56 +02:00

322 lines
16 KiB
C++

//=============================================================================//
//
// Purpose: Script VM
//
//=============================================================================//
#include "core/stdafx.h"
#include "tier0/frametask.h"
#include "tier1/cvar.h"
#include "squirrel/sqapi.h"
#include "squirrel/sqinit.h"
#include "squirrel/sqscript.h"
//---------------------------------------------------------------------------------
// Purpose: registers and exposes code functions to target context
// Input : *sqvm -
// *szName -
// *szHelpString -
// *szRetValType -
// *szArgTypes -
// *pFunction -
//---------------------------------------------------------------------------------
SQRESULT Script_RegisterFunction(CSquirrelVM* pSquirrelVM, const SQChar* szScriptName,const SQChar* szNativeName,
const SQChar* szHelpString, const SQChar* szRetValType, const SQChar* szArgTypes, void* pFunction)
{
ScriptFunctionBinding_t* sqFunc = new ScriptFunctionBinding_t();
sqFunc->m_szScriptName = szScriptName;
sqFunc->m_szNativeName = szNativeName;
sqFunc->m_szHelpString = szHelpString;
sqFunc->m_szRetValType = szRetValType;
sqFunc->m_szArgTypes = szArgTypes;
sqFunc->m_pFunction = pFunction;
SQRESULT results = v_Script_RegisterFunction(pSquirrelVM, sqFunc, 1);
delete sqFunc;
return results;
}
#ifndef CLIENT_DLL
//---------------------------------------------------------------------------------
// Purpose: registers script functions in SERVER context
// Input : *sqvm -
//---------------------------------------------------------------------------------
void Script_RegisterServerFunctions(CSquirrelVM* pSquirrelVM)
{
Script_RegisterFunction(pSquirrelVM, "SDKNativeTest", "Script_SDKNativeTest", "Native SERVER test function", "void", "", &VSquirrel::SHARED::SDKNativeTest);
Script_RegisterFunction(pSquirrelVM, "GetSDKVersion", "Script_GetSDKVersion", "Gets the SDK version as a string", "string", "", &VSquirrel::SHARED::GetSDKVersion);
Script_RegisterFunction(pSquirrelVM, "GetNumHumanPlayers", "Script_GetNumHumanPlayers", "Gets the number of human players on the server", "int", "", &VSquirrel::SERVER::GetNumHumanPlayers);
Script_RegisterFunction(pSquirrelVM, "GetNumFakeClients", "Script_GetNumFakeClients", "Gets the number of bot players on the server", "int", "", &VSquirrel::SERVER::GetNumFakeClients);
Script_RegisterFunction(pSquirrelVM, "GetAvailableMaps", "Script_GetAvailableMaps", "Gets an array of all available maps", "array< string >", "", &VSquirrel::SHARED::GetAvailableMaps);
Script_RegisterFunction(pSquirrelVM, "GetAvailablePlaylists", "Script_GetAvailablePlaylists", "Gets an array of all available playlists", "array< string >", "", &VSquirrel::SHARED::GetAvailablePlaylists);
Script_RegisterFunction(pSquirrelVM, "ShutdownHostGame", "Script_ShutdownHostGame", "Shuts down the local host game", "void", "", &VSquirrel::SHARED::ShutdownHostGame);
}
#endif // !CLIENT_DLL
#ifndef DEDICATED
//---------------------------------------------------------------------------------
// Purpose: registers script functions in CLIENT context
// Input : *sqvm -
//---------------------------------------------------------------------------------
void Script_RegisterClientFunctions(CSquirrelVM* pSquirrelVM)
{
Script_RegisterFunction(pSquirrelVM, "SDKNativeTest", "Script_SDKNativeTest", "Native CLIENT test function", "void", "", &VSquirrel::SHARED::SDKNativeTest);
Script_RegisterFunction(pSquirrelVM, "GetSDKVersion", "Script_GetSDKVersion", "Gets the SDK version as a string", "string", "", &VSquirrel::SHARED::GetSDKVersion);
Script_RegisterFunction(pSquirrelVM, "GetAvailableMaps", "Script_GetAvailableMaps", "Gets an array of all available maps", "array< string >", "", &VSquirrel::SHARED::GetAvailableMaps);
Script_RegisterFunction(pSquirrelVM, "GetAvailablePlaylists", "Script_GetAvailablePlaylists", "Gets an array of all available playlists", "array< string >", "", &VSquirrel::SHARED::GetAvailablePlaylists);
Script_RegisterFunction(pSquirrelVM, "ShutdownHostGame", "Script_ShutdownHostGame", "Shuts down the local host game", "void", "", &VSquirrel::SHARED::ShutdownHostGame);
}
//---------------------------------------------------------------------------------
// Purpose: registers script functions in UI context
// Input : *sqvm -
//---------------------------------------------------------------------------------
void Script_RegisterUIFunctions(CSquirrelVM* pSquirrelVM)
{
Script_RegisterFunction(pSquirrelVM, "SDKNativeTest", "Script_SDKNativeTest", "Native UI test function", "void", "", &VSquirrel::SHARED::SDKNativeTest);
Script_RegisterFunction(pSquirrelVM, "RefreshServerList", "Script_RefreshServerList", "Refreshes the public server list and returns the count", "int", "", &VSquirrel::UI::RefreshServerCount);
// Functions for retrieving server browser data
Script_RegisterFunction(pSquirrelVM, "GetServerName", "Script_GetServerName", "Gets the name of the server at the specified index of the server list", "string", "int", &VSquirrel::UI::GetServerName);
Script_RegisterFunction(pSquirrelVM, "GetServerDescription", "Script_GetServerDescription", "Gets the description of the server at the specified index of the server list", "string", "int", &VSquirrel::UI::GetServerDescription);
Script_RegisterFunction(pSquirrelVM, "GetServerMap", "Script_GetServerMap", "Gets the map of the server at the specified index of the server list", "string", "int", &VSquirrel::UI::GetServerMap);
Script_RegisterFunction(pSquirrelVM, "GetServerPlaylist", "Script_GetServerPlaylist", "Gets the playlist of the server at the specified index of the server list", "string", "int", &VSquirrel::UI::GetServerPlaylist);
Script_RegisterFunction(pSquirrelVM, "GetServerCurrentPlayers", "Script_GetServerCurrentPlayers", "Gets the current player count of the server at the specified index of the server list", "int", "int", &VSquirrel::UI::GetServerCurrentPlayers);
Script_RegisterFunction(pSquirrelVM, "GetServerMaxPlayers", "Script_GetServerMaxPlayers", "Gets the max player count of the server at the specified index of the server list", "int", "int", &VSquirrel::UI::GetServerMaxPlayers);
Script_RegisterFunction(pSquirrelVM, "GetServerCount", "Script_GetServerCount", "Gets the number of public servers", "int", "", &VSquirrel::UI::GetServerCount);
// Misc main menu functions
Script_RegisterFunction(pSquirrelVM, "GetSDKVersion", "Script_GetSDKVersion", "Gets the SDK version as a string", "string", "", &VSquirrel::SHARED::GetSDKVersion);
Script_RegisterFunction(pSquirrelVM, "GetPromoData", "Script_GetPromoData", "Gets promo data for specified slot type", "string", "int", &VSquirrel::UI::GetPromoData);
// Functions for connecting to servers
Script_RegisterFunction(pSquirrelVM, "CreateServer", "Script_CreateServer", "Start server with the specified settings", "void", "string, string, string, string, int", &VSquirrel::UI::CreateServerFromMenu);
Script_RegisterFunction(pSquirrelVM, "SetEncKeyAndConnect", "Script_SetEncKeyAndConnect", "Set the encryption key to that of the specified server and connects to it", "void", "int", &VSquirrel::UI::SetEncKeyAndConnect);
Script_RegisterFunction(pSquirrelVM, "JoinPrivateServerFromMenu", "Script_JoinPrivateServerFromMenu", "Joins private server by token", "void", "string", &VSquirrel::UI::JoinPrivateServerFromMenu);
Script_RegisterFunction(pSquirrelVM, "GetPrivateServerMessage", "Script_GetPrivateServerMessage", "Gets private server join status message", "string", "string", &VSquirrel::UI::GetPrivateServerMessage);
Script_RegisterFunction(pSquirrelVM, "ConnectToIPFromMenu", "Script_ConnectToIPFromMenu", "Joins server by ip address and encryption key", "void", "string, string", &VSquirrel::UI::ConnectToIPFromMenu);
Script_RegisterFunction(pSquirrelVM, "GetAvailableMaps", "Script_GetAvailableMaps", "Gets an array of all available maps", "array< string >", "", &VSquirrel::SHARED::GetAvailableMaps);
Script_RegisterFunction(pSquirrelVM, "GetAvailablePlaylists", "Script_GetAvailablePlaylists", "Gets an array of all available playlists", "array< string >", "", &VSquirrel::SHARED::GetAvailablePlaylists);
Script_RegisterFunction(pSquirrelVM, "ShutdownHostGame", "Script_ShutdownHostGame", "Shuts down the local host game", "void", "", &VSquirrel::SHARED::ShutdownHostGame);
}
//---------------------------------------------------------------------------------
// Purpose: Initialize all CLIENT/UI global structs and register SDK (CLIENT/UI) script functions
// Input : *sqvm -
// context - (1 = CLIENT 2 = UI)
//---------------------------------------------------------------------------------
SQRESULT Script_InitializeCLGlobalStructs(CSquirrelVM* pSquirrelVM, SQCONTEXT context)
{
SQRESULT results = v_Script_InitializeCLGlobalStructs(pSquirrelVM, context);
if (context == SQCONTEXT::CLIENT)
Script_RegisterClientFunctions(g_pClientScript.GetValue<CSquirrelVM*>());
if (context == SQCONTEXT::UI)
Script_RegisterUIFunctions(g_pUIScript.GetValue<CSquirrelVM*>());
return results;
}
#endif // !DEDICATED
#ifndef CLIENT_DLL
//---------------------------------------------------------------------------------
// Purpose: Initialize all SERVER global structs and register SDK (SERVER) script functions
// Input : *sqvm -
//---------------------------------------------------------------------------------
void Script_InitializeSVGlobalStructs(CSquirrelVM* pSquirrelVM)
{
v_Script_InitializeSVGlobalStructs(pSquirrelVM);
Script_RegisterServerFunctions(g_pServerScript.GetValue<CSquirrelVM*>());
}
//---------------------------------------------------------------------------------
// Purpose: Creates the SERVER Squirrel VM
// Output : True on success, false on failure
//---------------------------------------------------------------------------------
SQBool Script_CreateServerVM()
{
SQBool results = v_Script_CreateServerVM();
if (results)
DevMsg(eDLL_T::SERVER, "Created SERVER VM: '%p'\n", g_pServerScript.GetValue<CSquirrelVM*>());
else
Error(eDLL_T::SERVER, true, "Failed to create SERVER VM\n");
return results;
}
#endif // !CLIENT_DLL
#ifndef DEDICATED
//---------------------------------------------------------------------------------
// Purpose: Creates the CLIENT Squirrel VM
// Input : *hlclient -
// Output : True on success, false on failure
//---------------------------------------------------------------------------------
SQBool Script_CreateClientVM(CHLClient* hlclient)
{
SQBool results = v_Script_CreateClientVM(hlclient);
if (results)
DevMsg(eDLL_T::CLIENT, "Created CLIENT VM: '%p'\n", g_pClientScript.GetValue<CSquirrelVM*>());
else
Error(eDLL_T::CLIENT, true, "Failed to create CLIENT VM\n");
return results;
}
//---------------------------------------------------------------------------------
// Purpose: Creates the UI Squirrel VM
// Output : True on success, false on failure
//---------------------------------------------------------------------------------
SQBool Script_CreateUIVM()
{
SQBool results = v_Script_CreateUIVM();
if (results)
DevMsg(eDLL_T::UI, "Created UI VM: '%p'\n", g_pUIScript.GetValue<CSquirrelVM*>());
else
Error(eDLL_T::UI, true, "Failed to create UI VM\n");
return results;
}
#endif // !DEDICATED
//---------------------------------------------------------------------------------
// Purpose: Returns the script VM pointer by context
// Input : context -
// Output : SQVM*
//---------------------------------------------------------------------------------
CSquirrelVM* Script_GetContextObject(SQCONTEXT context)
{
switch (context)
{
#ifndef CLIENT_DLL
case SQCONTEXT::SERVER:
return g_pServerScript.GetValue<CSquirrelVM*>();
#endif // !CLIENT_DLL
#ifndef DEDICATED
case SQCONTEXT::CLIENT:
return g_pClientScript.GetValue<CSquirrelVM*>();
case SQCONTEXT::UI:
return g_pUIScript.GetValue<CSquirrelVM*>();
#endif // !DEDICATED
default:
return nullptr;
}
}
//---------------------------------------------------------------------------------
// Purpose: prints the global include file the compiler loads for loading scripts
// Input : *szRsonName -
//---------------------------------------------------------------------------------
SQInteger Script_LoadRson(const SQChar* szRsonName)
{
if (sq_showrsonloading->GetBool())
{
DevMsg(eDLL_T::ENGINE, "\n");
DevMsg(eDLL_T::ENGINE, "______________________________________________________________\n");
DevMsg(eDLL_T::ENGINE, "] RSON ]------------------------------------------------------\n");
DevMsg(eDLL_T::ENGINE, "] PATH: '%s'\n", szRsonName);
DevMsg(eDLL_T::ENGINE, "--------------------------------------------------------------\n");
DevMsg(eDLL_T::ENGINE, "\n");
}
return v_Script_LoadRson(szRsonName);
}
//---------------------------------------------------------------------------------
// Purpose: prints the scripts the compiler loads from global include to be compiled
// Input : *sqvm -
// *szScriptPath -
// *szScriptName -
// nFlag -
//---------------------------------------------------------------------------------
SQBool Script_LoadScript(HSQUIRRELVM v, const SQChar* szScriptPath, const SQChar* szScriptName, SQInteger nFlag)
{
if (sq_showscriptloading->GetBool())
{
DevMsg(eDLL_T::ENGINE, "Loading script: '%s'\n", szScriptName);
}
///////////////////////////////////////////////////////////////////////////////
return v_Script_LoadScript(v, szScriptPath, szScriptName, nFlag);
}
//---------------------------------------------------------------------------------
// Purpose: Compiles and executes input code on target VM by context
// Input : *code -
// context -
//---------------------------------------------------------------------------------
void Script_Execute(const SQChar* code, SQCONTEXT context)
{
if (!ThreadInMainThread())
{
g_TaskScheduler->Dispatch([code, context]()
{
string scode(code);
Script_Execute(scode.c_str(), context);
}, 0);
return; // Only run in main thread.
}
CSquirrelVM* script = Script_GetContextObject(context);
if (!script)
{
Error(eDLL_T::ENGINE, false, "Attempted to run %s script with no handle to script context\n", SQVM_GetContextName(context));
return;
}
HSQUIRRELVM v = script->GetVM();
if (!v)
{
Error(eDLL_T::ENGINE, false, "Attempted to run %s script while VM isn't initialized\n", SQVM_GetContextName(context));
return;
}
SQRESULT compileResult{};
SQBufState bufState = SQBufState(code);
compileResult = sq_compilebuffer(v, &bufState, "console", -1);
if (compileResult >= 0)
{
sq_pushroottable(v);
SQRESULT callResult = sq_call(v, 1, false, false);
}
}
//---------------------------------------------------------------------------------
void SQScript_Attach()
{
#ifndef DEDICATED
DetourAttach((LPVOID*)&v_Script_InitializeCLGlobalStructs, &Script_InitializeCLGlobalStructs);
#endif // !DEDICATED
#ifndef CLIENT_DLL
DetourAttach((LPVOID*)&v_Script_InitializeSVGlobalStructs, &Script_InitializeSVGlobalStructs);
DetourAttach((LPVOID*)&v_Script_CreateServerVM, &Script_CreateServerVM);
#endif // !CLIENT_DLL
#ifndef DEDICATED
DetourAttach((LPVOID*)&v_Script_CreateClientVM, &Script_CreateClientVM);
DetourAttach((LPVOID*)&v_Script_CreateUIVM, &Script_CreateUIVM);
#endif // !DEDICATED
DetourAttach((LPVOID*)&v_Script_LoadRson, &Script_LoadRson);
DetourAttach((LPVOID*)&v_Script_LoadScript, &Script_LoadScript);
}
//---------------------------------------------------------------------------------
void SQScript_Detach()
{
#ifndef DEDICATED
DetourDetach((LPVOID*)&v_Script_InitializeCLGlobalStructs, &Script_InitializeCLGlobalStructs);
#endif // !DEDICATED
#ifndef CLIENT_DLL
DetourDetach((LPVOID*)&v_Script_InitializeSVGlobalStructs, &Script_InitializeSVGlobalStructs);
DetourDetach((LPVOID*)&v_Script_CreateServerVM, &Script_CreateServerVM);
#endif // !CLIENT_DLL
#ifndef DEDICATED
DetourDetach((LPVOID*)&v_Script_CreateClientVM, &Script_CreateClientVM);
DetourDetach((LPVOID*)&v_Script_CreateUIVM, &Script_CreateUIVM);
#endif // !DEDICATED
DetourDetach((LPVOID*)&v_Script_LoadRson, &Script_LoadRson);
DetourDetach((LPVOID*)&v_Script_LoadScript, &Script_LoadScript);
}