aboutsummaryrefslogtreecommitdiff
path: root/NorthstarDLL/squirrel
diff options
context:
space:
mode:
Diffstat (limited to 'NorthstarDLL/squirrel')
-rw-r--r--NorthstarDLL/squirrel/squirrel.cpp795
-rw-r--r--NorthstarDLL/squirrel/squirrel.h493
-rw-r--r--NorthstarDLL/squirrel/squirrelautobind.cpp21
-rw-r--r--NorthstarDLL/squirrel/squirrelautobind.h76
-rw-r--r--NorthstarDLL/squirrel/squirrelclasstypes.h242
-rw-r--r--NorthstarDLL/squirrel/squirreldatatypes.h495
6 files changed, 2122 insertions, 0 deletions
diff --git a/NorthstarDLL/squirrel/squirrel.cpp b/NorthstarDLL/squirrel/squirrel.cpp
new file mode 100644
index 00000000..8dd93ed0
--- /dev/null
+++ b/NorthstarDLL/squirrel/squirrel.cpp
@@ -0,0 +1,795 @@
+#include "pch.h"
+#include "squirrel.h"
+#include "core/convar/concommand.h"
+#include "mods/modmanager.h"
+#include "dedicated/dedicated.h"
+#include "engine/r2engine.h"
+#include "core/tier0.h"
+
+#include <any>
+
+AUTOHOOK_INIT()
+
+std::shared_ptr<ColoredLogger> getSquirrelLoggerByContext(ScriptContext context)
+{
+ switch (context)
+ {
+ case ScriptContext::UI:
+ return NS::log::SCRIPT_UI;
+ case ScriptContext::CLIENT:
+ return NS::log::SCRIPT_CL;
+ case ScriptContext::SERVER:
+ return NS::log::SCRIPT_SV;
+ default:
+ throw std::runtime_error("getSquirrelLoggerByContext called with invalid context");
+ return nullptr;
+ }
+}
+
+namespace NS::log
+{
+ template <ScriptContext context> std::shared_ptr<spdlog::logger> squirrel_logger()
+ {
+ // Switch statements can't be constexpr afaik
+ // clang-format off
+ if constexpr (context == ScriptContext::UI) { return SCRIPT_UI; }
+ if constexpr (context == ScriptContext::CLIENT) { return SCRIPT_CL; }
+ if constexpr (context == ScriptContext::SERVER) { return SCRIPT_SV; }
+ // clang-format on
+ }
+}; // namespace NS::log
+
+const char* GetContextName(ScriptContext context)
+{
+ switch (context)
+ {
+ case ScriptContext::CLIENT:
+ return "CLIENT";
+ case ScriptContext::SERVER:
+ return "SERVER";
+ case ScriptContext::UI:
+ return "UI";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char* GetContextName_Short(ScriptContext context)
+{
+ switch (context)
+ {
+ case ScriptContext::CLIENT:
+ return "CL";
+ case ScriptContext::SERVER:
+ return "SV";
+ case ScriptContext::UI:
+ return "UI";
+ default:
+ return "??";
+ }
+}
+
+eSQReturnType SQReturnTypeFromString(const char* pReturnType)
+{
+ static const std::map<std::string, eSQReturnType> sqReturnTypeNameToString = {
+ {"bool", eSQReturnType::Boolean},
+ {"float", eSQReturnType::Float},
+ {"vector", eSQReturnType::Vector},
+ {"int", eSQReturnType::Integer},
+ {"entity", eSQReturnType::Entity},
+ {"string", eSQReturnType::String},
+ {"array", eSQReturnType::Arrays},
+ {"asset", eSQReturnType::Asset},
+ {"table", eSQReturnType::Table}};
+
+ if (sqReturnTypeNameToString.find(pReturnType) != sqReturnTypeNameToString.end())
+ return sqReturnTypeNameToString.at(pReturnType);
+ else
+ return eSQReturnType::Default; // previous default value
+}
+
+ScriptContext ScriptContextFromString(std::string string)
+{
+ if (string == "UI")
+ return ScriptContext::UI;
+ if (string == "CLIENT")
+ return ScriptContext::CLIENT;
+ if (string == "SERVER")
+ return ScriptContext::SERVER;
+ else
+ return ScriptContext::INVALID;
+}
+
+const char* SQTypeNameFromID(int type)
+{
+ switch (type)
+ {
+ case OT_ASSET:
+ return "asset";
+ case OT_INTEGER:
+ return "int";
+ case OT_BOOL:
+ return "bool";
+ case SQOBJECT_NUMERIC:
+ return "float or int";
+ case OT_NULL:
+ return "null";
+ case OT_VECTOR:
+ return "vector";
+ case 0:
+ return "var";
+ case OT_USERDATA:
+ return "userdata";
+ case OT_FLOAT:
+ return "float";
+ case OT_STRING:
+ return "string";
+ case OT_ARRAY:
+ return "array";
+ case 0x8000200:
+ return "function";
+ case 0x8100000:
+ return "structdef";
+ case OT_THREAD:
+ return "thread";
+ case OT_FUNCPROTO:
+ return "function";
+ case OT_CLAAS:
+ return "class";
+ case OT_WEAKREF:
+ return "weakref";
+ case 0x8080000:
+ return "unimplemented function";
+ case 0x8200000:
+ return "struct instance";
+ case OT_TABLE:
+ return "table";
+ case 0xA008000:
+ return "instance";
+ case OT_ENTITY:
+ return "entity";
+ }
+ return "";
+}
+
+// Allows for generating squirrelmessages from plugins.
+// Not used in this version, but will be used later
+void AsyncCall_External(ScriptContext context, const char* func_name, SquirrelMessage_External_Pop function)
+{
+ SquirrelMessage message {};
+ message.functionName = func_name;
+ message.isExternal = true;
+ message.externalFunc = function;
+ switch (context)
+ {
+ case ScriptContext::CLIENT:
+ g_pSquirrel<ScriptContext::CLIENT>->messageBuffer->push(message);
+ break;
+ case ScriptContext::SERVER:
+ g_pSquirrel<ScriptContext::SERVER>->messageBuffer->push(message);
+ break;
+ case ScriptContext::UI:
+ g_pSquirrel<ScriptContext::UI>->messageBuffer->push(message);
+ break;
+ }
+}
+
+// needed to define implementations for squirrelmanager outside of squirrel.h without compiler errors
+template class SquirrelManager<ScriptContext::SERVER>;
+template class SquirrelManager<ScriptContext::CLIENT>;
+template class SquirrelManager<ScriptContext::UI>;
+
+template <ScriptContext context> void SquirrelManager<context>::VMCreated(CSquirrelVM* newSqvm)
+{
+ m_pSQVM = newSqvm;
+
+ for (SQFuncRegistration* funcReg : m_funcRegistrations)
+ {
+ spdlog::info("Registering {} function {}", GetContextName(context), funcReg->squirrelFuncName);
+ RegisterSquirrelFunc(m_pSQVM, funcReg, 1);
+ }
+
+ for (auto& pair : g_pModManager->m_DependencyConstants)
+ {
+ bool bWasFound = false;
+
+ for (Mod& dependency : g_pModManager->m_LoadedMods)
+ {
+ if (!dependency.m_bEnabled)
+ continue;
+
+ if (dependency.Name == pair.second)
+ {
+ bWasFound = true;
+ break;
+ }
+ }
+
+ defconst(m_pSQVM, pair.first.c_str(), bWasFound);
+ }
+ g_pSquirrel<context>->messageBuffer = new SquirrelMessageBuffer();
+}
+
+template <ScriptContext context> void SquirrelManager<context>::VMDestroyed()
+{
+ m_pSQVM = nullptr;
+}
+
+template <ScriptContext context> void SquirrelManager<context>::ExecuteCode(const char* pCode)
+{
+ if (!m_pSQVM || !m_pSQVM->sqvm)
+ {
+ spdlog::error("Cannot execute code, {} squirrel vm is not initialised", GetContextName(context));
+ return;
+ }
+
+ spdlog::info("Executing {} script code {} ", GetContextName(context), pCode);
+
+ std::string strCode(pCode);
+ CompileBufferState bufferState = CompileBufferState(strCode);
+
+ SQRESULT compileResult = compilebuffer(&bufferState, "console");
+ spdlog::info("sq_compilebuffer returned {}", PrintSQRESULT.at(compileResult));
+
+ if (compileResult != SQRESULT_ERROR)
+ {
+ pushroottable(m_pSQVM->sqvm);
+ SQRESULT callResult = _call(m_pSQVM->sqvm, 0);
+ spdlog::info("sq_call returned {}", PrintSQRESULT.at(callResult));
+ }
+}
+
+template <ScriptContext context> void SquirrelManager<context>::AddFuncRegistration(
+ std::string returnType, std::string name, std::string argTypes, std::string helpText, SQFunction func)
+{
+ SQFuncRegistration* reg = new SQFuncRegistration;
+
+ reg->squirrelFuncName = new char[name.size() + 1];
+ strcpy((char*)reg->squirrelFuncName, name.c_str());
+ reg->cppFuncName = reg->squirrelFuncName;
+
+ reg->helpText = new char[helpText.size() + 1];
+ strcpy((char*)reg->helpText, helpText.c_str());
+
+ reg->returnTypeString = new char[returnType.size() + 1];
+ strcpy((char*)reg->returnTypeString, returnType.c_str());
+ reg->returnType = SQReturnTypeFromString(returnType.c_str());
+
+ reg->argTypes = new char[argTypes.size() + 1];
+ strcpy((char*)reg->argTypes, argTypes.c_str());
+
+ reg->funcPtr = func;
+
+ m_funcRegistrations.push_back(reg);
+}
+
+template <ScriptContext context> SQRESULT SquirrelManager<context>::setupfunc(const SQChar* funcname)
+{
+ pushroottable(m_pSQVM->sqvm);
+ pushstring(m_pSQVM->sqvm, funcname, -1);
+
+ SQRESULT result = get(m_pSQVM->sqvm, -2);
+ if (result != SQRESULT_ERROR)
+ pushroottable(m_pSQVM->sqvm);
+
+ return result;
+}
+
+template <ScriptContext context> void SquirrelManager<context>::AddFuncOverride(std::string name, SQFunction func)
+{
+ m_funcOverrides[name] = func;
+}
+
+// hooks
+bool IsUIVM(ScriptContext context, HSquirrelVM* pSqvm)
+{
+ return context != ScriptContext::SERVER && g_pSquirrel<ScriptContext::UI>->m_pSQVM &&
+ g_pSquirrel<ScriptContext::UI>->m_pSQVM->sqvm == pSqvm;
+}
+
+template <ScriptContext context> void* (*__fastcall sq_compiler_create)(HSquirrelVM* sqvm, void* a2, void* a3, SQBool bShouldThrowError);
+template <ScriptContext context> void* __fastcall sq_compiler_createHook(HSquirrelVM* sqvm, void* a2, void* a3, SQBool bShouldThrowError)
+{
+ // store whether errors generated from this compile should be fatal
+ if (IsUIVM(context, sqvm))
+ g_pSquirrel<ScriptContext::UI>->m_bFatalCompilationErrors = bShouldThrowError;
+ else
+ g_pSquirrel<context>->m_bFatalCompilationErrors = bShouldThrowError;
+
+ return sq_compiler_create<context>(sqvm, a2, a3, bShouldThrowError);
+}
+
+template <ScriptContext context> SQInteger (*SQPrint)(HSquirrelVM* sqvm, const char* fmt);
+template <ScriptContext context> SQInteger SQPrintHook(HSquirrelVM* sqvm, const char* fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+
+ SQChar buf[1024];
+ int charsWritten = vsnprintf_s(buf, _TRUNCATE, fmt, va);
+
+ if (charsWritten > 0)
+ {
+ if (buf[charsWritten - 1] == '\n')
+ buf[charsWritten - 1] = '\0';
+ g_pSquirrel<context>->logger->info("{}", buf);
+ }
+
+ va_end(va);
+ return 0;
+}
+
+template <ScriptContext context> CSquirrelVM* (*__fastcall CreateNewVM)(void* a1, ScriptContext realContext);
+template <ScriptContext context> CSquirrelVM* __fastcall CreateNewVMHook(void* a1, ScriptContext realContext)
+{
+ CSquirrelVM* sqvm = CreateNewVM<context>(a1, realContext);
+ if (realContext == ScriptContext::UI)
+ g_pSquirrel<ScriptContext::UI>->VMCreated(sqvm);
+ else
+ g_pSquirrel<context>->VMCreated(sqvm);
+
+ spdlog::info("CreateNewVM {} {}", GetContextName(realContext), (void*)sqvm);
+ return sqvm;
+}
+
+template <ScriptContext context> void (*__fastcall DestroyVM)(void* a1, CSquirrelVM* sqvm);
+template <ScriptContext context> void __fastcall DestroyVMHook(void* a1, CSquirrelVM* sqvm)
+{
+ ScriptContext realContext = context; // ui and client use the same function so we use this for prints
+ if (IsUIVM(context, sqvm->sqvm))
+ {
+ realContext = ScriptContext::UI;
+ g_pSquirrel<ScriptContext::UI>->VMDestroyed();
+ DestroyVM<ScriptContext::CLIENT>(a1, sqvm); // If we pass UI here it crashes
+ }
+ else
+ {
+ g_pSquirrel<context>->VMDestroyed();
+ DestroyVM<context>(a1, sqvm);
+ }
+
+ spdlog::info("DestroyVM {} {}", GetContextName(realContext), (void*)sqvm);
+}
+
+template <ScriptContext context>
+void (*__fastcall SQCompileError)(HSquirrelVM* sqvm, const char* error, const char* file, int line, int column);
+template <ScriptContext context>
+void __fastcall ScriptCompileErrorHook(HSquirrelVM* sqvm, const char* error, const char* file, int line, int column)
+{
+ bool bIsFatalError = g_pSquirrel<context>->m_bFatalCompilationErrors;
+ ScriptContext realContext = context; // ui and client use the same function so we use this for prints
+ if (IsUIVM(context, sqvm))
+ {
+ realContext = ScriptContext::UI;
+ bIsFatalError = g_pSquirrel<ScriptContext::UI>->m_bFatalCompilationErrors;
+ }
+
+ auto logger = getSquirrelLoggerByContext(realContext);
+
+ logger->error("COMPILE ERROR {}", error);
+ logger->error("{} line [{}] column [{}]", file, line, column);
+
+ // use disconnect to display an error message for the compile error, but only if the compilation error was fatal
+ // todo, we could get this from sqvm itself probably, rather than hooking sq_compiler_create
+ if (bIsFatalError)
+ {
+ // kill dedicated server if we hit this
+ if (IsDedicatedServer())
+ {
+ // flush the logger before we exit so debug things get saved to log file
+ logger->flush();
+ exit(EXIT_FAILURE);
+ }
+ else
+ {
+ R2::Cbuf_AddText(
+ R2::Cbuf_GetCurrentPlayer(),
+ fmt::format("disconnect \"Encountered {} script compilation error, see console for details.\"", GetContextName(realContext))
+ .c_str(),
+ R2::cmd_source_t::kCommandSrcCode);
+
+ // likely temp: show console so user can see any errors, as error message wont display if ui is dead
+ // maybe we could disable all mods other than the coremods and try a reload before doing this?
+ // could also maybe do some vgui bullshit to show something visually rather than console
+ if (realContext == ScriptContext::UI)
+ R2::Cbuf_AddText(R2::Cbuf_GetCurrentPlayer(), "showconsole", R2::cmd_source_t::kCommandSrcCode);
+ }
+ }
+
+ // dont call the original function since it kills game lol
+}
+
+template <ScriptContext context>
+int64_t (*__fastcall RegisterSquirrelFunction)(CSquirrelVM* sqvm, SQFuncRegistration* funcReg, char unknown);
+template <ScriptContext context>
+int64_t __fastcall RegisterSquirrelFunctionHook(CSquirrelVM* sqvm, SQFuncRegistration* funcReg, char unknown)
+{
+ if (IsUIVM(context, sqvm->sqvm))
+ {
+ if (g_pSquirrel<ScriptContext::UI>->m_funcOverrides.count(funcReg->squirrelFuncName))
+ {
+ g_pSquirrel<ScriptContext::UI>->m_funcOriginals[funcReg->squirrelFuncName] = funcReg->funcPtr;
+ funcReg->funcPtr = g_pSquirrel<ScriptContext::UI>->m_funcOverrides[funcReg->squirrelFuncName];
+ spdlog::info("Replacing {} in UI", std::string(funcReg->squirrelFuncName));
+ }
+
+ return g_pSquirrel<ScriptContext::UI>->RegisterSquirrelFunc(sqvm, funcReg, unknown);
+ }
+
+ if (g_pSquirrel<context>->m_funcOverrides.find(funcReg->squirrelFuncName) != g_pSquirrel<context>->m_funcOverrides.end())
+ {
+ g_pSquirrel<context>->m_funcOriginals[funcReg->squirrelFuncName] = funcReg->funcPtr;
+ funcReg->funcPtr = g_pSquirrel<context>->m_funcOverrides[funcReg->squirrelFuncName];
+ spdlog::info("Replacing {} in Client", std::string(funcReg->squirrelFuncName));
+ }
+
+ return g_pSquirrel<context>->RegisterSquirrelFunc(sqvm, funcReg, unknown);
+}
+
+template <ScriptContext context> bool (*__fastcall CallScriptInitCallback)(void* sqvm, const char* callback);
+template <ScriptContext context> bool __fastcall CallScriptInitCallbackHook(void* sqvm, const char* callback)
+{
+ ScriptContext realContext = context;
+ bool bShouldCallCustomCallbacks = true;
+
+ if (context == ScriptContext::CLIENT)
+ {
+ if (!strcmp(callback, "UICodeCallback_UIInit"))
+ realContext = ScriptContext::UI;
+ else if (strcmp(callback, "ClientCodeCallback_MapSpawn"))
+ bShouldCallCustomCallbacks = false;
+ }
+ else if (context == ScriptContext::SERVER)
+ bShouldCallCustomCallbacks = !strcmp(callback, "CodeCallback_MapSpawn");
+
+ if (bShouldCallCustomCallbacks)
+ {
+ for (Mod mod : g_pModManager->m_LoadedMods)
+ {
+ if (!mod.m_bEnabled)
+ continue;
+
+ for (ModScript script : mod.Scripts)
+ {
+ for (ModScriptCallback modCallback : script.Callbacks)
+ {
+ if (modCallback.Context == realContext && modCallback.BeforeCallback.length())
+ {
+ spdlog::info("Running custom {} script callback \"{}\"", GetContextName(realContext), modCallback.BeforeCallback);
+ CallScriptInitCallback<context>(sqvm, modCallback.BeforeCallback.c_str());
+ }
+ }
+ }
+ }
+ }
+
+ spdlog::info("{} CodeCallback {} called", GetContextName(realContext), callback);
+ if (!bShouldCallCustomCallbacks)
+ spdlog::info("Not executing custom callbacks for CodeCallback {}", callback);
+ bool ret = CallScriptInitCallback<context>(sqvm, callback);
+
+ // run after callbacks
+ if (bShouldCallCustomCallbacks)
+ {
+ for (Mod mod : g_pModManager->m_LoadedMods)
+ {
+ if (!mod.m_bEnabled)
+ continue;
+
+ for (ModScript script : mod.Scripts)
+ {
+ for (ModScriptCallback modCallback : script.Callbacks)
+ {
+ if (modCallback.Context == realContext && modCallback.AfterCallback.length())
+ {
+ spdlog::info("Running custom {} script callback \"{}\"", GetContextName(realContext), modCallback.AfterCallback);
+ CallScriptInitCallback<context>(sqvm, modCallback.AfterCallback.c_str());
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+template <ScriptContext context> void ConCommand_script(const CCommand& args)
+{
+ g_pSquirrel<context>->ExecuteCode(args.ArgS());
+}
+
+// literal class type that wraps a constant expression string
+template <size_t N> struct TemplateStringLiteral
+{
+ constexpr TemplateStringLiteral(const char (&str)[N])
+ {
+ std::copy_n(str, N, value);
+ }
+
+ char value[N];
+};
+
+template <ScriptContext context, TemplateStringLiteral funcName> SQRESULT SQ_StubbedFunc(HSquirrelVM* sqvm)
+{
+ spdlog::info("Blocking call to stubbed function {} in {}", funcName.value, GetContextName(context));
+ return SQRESULT_NULL;
+}
+
+template <ScriptContext context> void StubUnsafeSQFuncs()
+{
+ if (!Tier0::CommandLine()->CheckParm("-allowunsafesqfuncs"))
+ {
+ g_pSquirrel<context>->AddFuncOverride("DevTextBufferWrite", SQ_StubbedFunc<context, "DevTextBufferWrite">);
+ g_pSquirrel<context>->AddFuncOverride("DevTextBufferClear", SQ_StubbedFunc<context, "DevTextBufferClear">);
+ g_pSquirrel<context>->AddFuncOverride("DevTextBufferDumpToFile", SQ_StubbedFunc<context, "DevTextBufferDumpToFile">);
+ g_pSquirrel<context>->AddFuncOverride("Dev_CommandLineAddParam", SQ_StubbedFunc<context, "Dev_CommandLineAddParam">);
+ g_pSquirrel<context>->AddFuncOverride("DevP4Checkout", SQ_StubbedFunc<context, "DevP4Checkout">);
+ g_pSquirrel<context>->AddFuncOverride("DevP4Add", SQ_StubbedFunc<context, "DevP4Add">);
+ }
+}
+
+template <ScriptContext context> void SquirrelManager<context>::ProcessMessageBuffer()
+{
+ while (std::optional<SquirrelMessage> maybeMessage = messageBuffer->pop())
+ {
+ SquirrelMessage message = maybeMessage.value();
+
+ SQObject functionobj {};
+ int result = sq_getfunction(m_pSQVM->sqvm, message.functionName.c_str(), &functionobj, 0);
+ if (result != 0) // This func returns 0 on success for some reason
+ {
+ NS::log::squirrel_logger<context>()->error(
+ "ProcessMessageBuffer was unable to find function with name '{}'. Is it global?", message.functionName);
+ continue;
+ }
+
+ pushobject(m_pSQVM->sqvm, &functionobj); // Push the function object
+ pushroottable(m_pSQVM->sqvm);
+
+ if (message.isExternal)
+ {
+ message.externalFunc(m_pSQVM->sqvm);
+ }
+ else
+ {
+ for (auto& v : message.args)
+ {
+ // Execute lambda to push arg to stack
+ v();
+ }
+ }
+
+ _call(m_pSQVM->sqvm, message.args.size());
+ }
+}
+
+ADD_SQFUNC(
+ "string",
+ NSGetCurrentModName,
+ "",
+ "Returns the mod name of the script running this function",
+ ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)
+{
+ int depth = g_pSquirrel<context>->getinteger(sqvm, 1);
+ if (auto mod = g_pSquirrel<context>->getcallingmod(sqvm, depth); mod == nullptr)
+ {
+ g_pSquirrel<context>->raiseerror(sqvm, "NSGetModName was called from a non-mod script. This shouldn't be possible");
+ return SQRESULT_ERROR;
+ }
+ else
+ {
+ g_pSquirrel<context>->pushstring(sqvm, mod->Name.c_str());
+ }
+ return SQRESULT_NOTNULL;
+}
+
+ADD_SQFUNC(
+ "string",
+ NSGetCallingModName,
+ "int depth = 0",
+ "Returns the mod name of the script running this function",
+ ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)
+{
+ int depth = g_pSquirrel<context>->getinteger(sqvm, 1);
+ if (auto mod = g_pSquirrel<context>->getcallingmod(sqvm, depth); mod == nullptr)
+ {
+ g_pSquirrel<context>->pushstring(sqvm, "Unknown");
+ }
+ else
+ {
+ g_pSquirrel<context>->pushstring(sqvm, mod->Name.c_str());
+ }
+ return SQRESULT_NOTNULL;
+}
+
+ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrel, ConCommand, (CModule module))
+{
+ AUTOHOOK_DISPATCH_MODULE(client.dll)
+
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_defconst = module.Offset(0x12120).As<sq_defconstType>();
+ g_pSquirrel<ScriptContext::UI>->__sq_defconst = g_pSquirrel<ScriptContext::CLIENT>->__sq_defconst;
+
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_compilebuffer = module.Offset(0x3110).As<sq_compilebufferType>();
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_pushroottable = module.Offset(0x5860).As<sq_pushroottableType>();
+ g_pSquirrel<ScriptContext::UI>->__sq_compilebuffer = g_pSquirrel<ScriptContext::CLIENT>->__sq_compilebuffer;
+ g_pSquirrel<ScriptContext::UI>->__sq_pushroottable = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushroottable;
+
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_call = module.Offset(0x8650).As<sq_callType>();
+ g_pSquirrel<ScriptContext::UI>->__sq_call = g_pSquirrel<ScriptContext::CLIENT>->__sq_call;
+
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_newarray = module.Offset(0x39F0).As<sq_newarrayType>();
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_arrayappend = module.Offset(0x3C70).As<sq_arrayappendType>();
+ g_pSquirrel<ScriptContext::UI>->__sq_newarray = g_pSquirrel<ScriptContext::CLIENT>->__sq_newarray;
+ g_pSquirrel<ScriptContext::UI>->__sq_arrayappend = g_pSquirrel<ScriptContext::CLIENT>->__sq_arrayappend;
+
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_newtable = module.Offset(0x3960).As<sq_newtableType>();
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_newslot = module.Offset(0x70B0).As<sq_newslotType>();
+ g_pSquirrel<ScriptContext::UI>->__sq_newtable = g_pSquirrel<ScriptContext::CLIENT>->__sq_newtable;
+ g_pSquirrel<ScriptContext::UI>->__sq_newslot = g_pSquirrel<ScriptContext::CLIENT>->__sq_newslot;
+
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_pushstring = module.Offset(0x3440).As<sq_pushstringType>();
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_pushinteger = module.Offset(0x36A0).As<sq_pushintegerType>();
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_pushfloat = module.Offset(0x3800).As<sq_pushfloatType>();
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_pushbool = module.Offset(0x3710).As<sq_pushboolType>();
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_pushasset = module.Offset(0x3560).As<sq_pushassetType>();
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_pushvector = module.Offset(0x3780).As<sq_pushvectorType>();
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_pushobject = module.Offset(0x83D0).As<sq_pushobjectType>();
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_raiseerror = module.Offset(0x8470).As<sq_raiseerrorType>();
+ g_pSquirrel<ScriptContext::UI>->__sq_pushstring = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushstring;
+ g_pSquirrel<ScriptContext::UI>->__sq_pushinteger = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushinteger;
+ g_pSquirrel<ScriptContext::UI>->__sq_pushfloat = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushfloat;
+ g_pSquirrel<ScriptContext::UI>->__sq_pushbool = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushbool;
+ g_pSquirrel<ScriptContext::UI>->__sq_pushvector = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushvector;
+ g_pSquirrel<ScriptContext::UI>->__sq_pushasset = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushasset;
+ g_pSquirrel<ScriptContext::UI>->__sq_pushobject = g_pSquirrel<ScriptContext::CLIENT>->__sq_pushobject;
+ g_pSquirrel<ScriptContext::UI>->__sq_raiseerror = g_pSquirrel<ScriptContext::CLIENT>->__sq_raiseerror;
+
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_getstring = module.Offset(0x60C0).As<sq_getstringType>();
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_getinteger = module.Offset(0x60E0).As<sq_getintegerType>();
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_getfloat = module.Offset(0x6100).As<sq_getfloatType>();
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_getbool = module.Offset(0x6130).As<sq_getboolType>();
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_get = module.Offset(0x7C30).As<sq_getType>();
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_getasset = module.Offset(0x6010).As<sq_getassetType>();
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_getuserdata = module.Offset(0x63D0).As<sq_getuserdataType>();
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_getvector = module.Offset(0x6140).As<sq_getvectorType>();
+ g_pSquirrel<ScriptContext::UI>->__sq_getstring = g_pSquirrel<ScriptContext::CLIENT>->__sq_getstring;
+ g_pSquirrel<ScriptContext::UI>->__sq_getinteger = g_pSquirrel<ScriptContext::CLIENT>->__sq_getinteger;
+ g_pSquirrel<ScriptContext::UI>->__sq_getfloat = g_pSquirrel<ScriptContext::CLIENT>->__sq_getfloat;
+ g_pSquirrel<ScriptContext::UI>->__sq_getbool = g_pSquirrel<ScriptContext::CLIENT>->__sq_getbool;
+ g_pSquirrel<ScriptContext::UI>->__sq_get = g_pSquirrel<ScriptContext::CLIENT>->__sq_get;
+ g_pSquirrel<ScriptContext::UI>->__sq_getasset = g_pSquirrel<ScriptContext::CLIENT>->__sq_getasset;
+ g_pSquirrel<ScriptContext::UI>->__sq_getuserdata = g_pSquirrel<ScriptContext::CLIENT>->__sq_getuserdata;
+ g_pSquirrel<ScriptContext::UI>->__sq_getvector = g_pSquirrel<ScriptContext::CLIENT>->__sq_getvector;
+ g_pSquirrel<ScriptContext::UI>->__sq_getthisentity = g_pSquirrel<ScriptContext::CLIENT>->__sq_getthisentity;
+ g_pSquirrel<ScriptContext::UI>->__sq_getobject = g_pSquirrel<ScriptContext::CLIENT>->__sq_getobject;
+
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_createuserdata = module.Offset(0x38D0).As<sq_createuserdataType>();
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_setuserdatatypeid = module.Offset(0x6490).As<sq_setuserdatatypeidType>();
+ g_pSquirrel<ScriptContext::UI>->__sq_createuserdata = g_pSquirrel<ScriptContext::CLIENT>->__sq_createuserdata;
+ g_pSquirrel<ScriptContext::UI>->__sq_setuserdatatypeid = g_pSquirrel<ScriptContext::CLIENT>->__sq_setuserdatatypeid;
+
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_GetEntityConstant_CBaseEntity = module.Offset(0x3E49B0).As<sq_GetEntityConstantType>();
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_getentityfrominstance = module.Offset(0x114F0).As<sq_getentityfrominstanceType>();
+ g_pSquirrel<ScriptContext::UI>->__sq_GetEntityConstant_CBaseEntity =
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_GetEntityConstant_CBaseEntity;
+ g_pSquirrel<ScriptContext::UI>->__sq_getentityfrominstance = g_pSquirrel<ScriptContext::CLIENT>->__sq_getentityfrominstance;
+
+ // Message buffer stuff
+ g_pSquirrel<ScriptContext::UI>->messageBuffer = g_pSquirrel<ScriptContext::CLIENT>->messageBuffer;
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_getfunction = module.Offset(0x572FB0).As<sq_getfunctionType>();
+ g_pSquirrel<ScriptContext::UI>->__sq_getfunction = g_pSquirrel<ScriptContext::CLIENT>->__sq_getfunction;
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_stackinfos = module.Offset(0x35970).As<sq_stackinfosType>();
+ g_pSquirrel<ScriptContext::UI>->__sq_stackinfos = g_pSquirrel<ScriptContext::CLIENT>->__sq_stackinfos;
+
+ MAKEHOOK(
+ module.Offset(0x108E0),
+ &RegisterSquirrelFunctionHook<ScriptContext::CLIENT>,
+ &g_pSquirrel<ScriptContext::CLIENT>->RegisterSquirrelFunc);
+ g_pSquirrel<ScriptContext::UI>->RegisterSquirrelFunc = g_pSquirrel<ScriptContext::CLIENT>->RegisterSquirrelFunc;
+
+ g_pSquirrel<ScriptContext::CLIENT>->logger = NS::log::SCRIPT_CL;
+ g_pSquirrel<ScriptContext::UI>->logger = NS::log::SCRIPT_UI;
+
+ // uiscript_reset concommand: don't loop forever if compilation fails
+ module.Offset(0x3C6E4C).NOP(6);
+
+ MAKEHOOK(module.Offset(0x8AD0), &sq_compiler_createHook<ScriptContext::CLIENT>, &sq_compiler_create<ScriptContext::CLIENT>);
+
+ MAKEHOOK(module.Offset(0x12B00), &SQPrintHook<ScriptContext::CLIENT>, &SQPrint<ScriptContext::CLIENT>);
+ MAKEHOOK(module.Offset(0x12BA0), &SQPrintHook<ScriptContext::UI>, &SQPrint<ScriptContext::UI>);
+
+ MAKEHOOK(module.Offset(0x26130), &CreateNewVMHook<ScriptContext::CLIENT>, &CreateNewVM<ScriptContext::CLIENT>);
+ MAKEHOOK(module.Offset(0x26E70), &DestroyVMHook<ScriptContext::CLIENT>, &DestroyVM<ScriptContext::CLIENT>);
+ MAKEHOOK(module.Offset(0x79A50), &ScriptCompileErrorHook<ScriptContext::CLIENT>, &SQCompileError<ScriptContext::CLIENT>);
+
+ MAKEHOOK(module.Offset(0x10190), &CallScriptInitCallbackHook<ScriptContext::CLIENT>, &CallScriptInitCallback<ScriptContext::CLIENT>);
+
+ RegisterConCommand("script_client", ConCommand_script<ScriptContext::CLIENT>, "Executes script code on the client vm", FCVAR_CLIENTDLL);
+ RegisterConCommand("script_ui", ConCommand_script<ScriptContext::UI>, "Executes script code on the ui vm", FCVAR_CLIENTDLL);
+
+ StubUnsafeSQFuncs<ScriptContext::CLIENT>();
+ StubUnsafeSQFuncs<ScriptContext::UI>();
+
+ g_pSquirrel<ScriptContext::CLIENT>->__sq_getfunction = module.Offset(0x6CB0).As<sq_getfunctionType>();
+ g_pSquirrel<ScriptContext::UI>->__sq_getfunction = g_pSquirrel<ScriptContext::CLIENT>->__sq_getfunction;
+}
+
+ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrel, ConCommand, (CModule module))
+{
+ AUTOHOOK_DISPATCH_MODULE(server.dll)
+
+ g_pSquirrel<ScriptContext::SERVER>->__sq_defconst = module.Offset(0x1F550).As<sq_defconstType>();
+
+ g_pSquirrel<ScriptContext::SERVER>->__sq_compilebuffer = module.Offset(0x3110).As<sq_compilebufferType>();
+ g_pSquirrel<ScriptContext::SERVER>->__sq_pushroottable = module.Offset(0x5840).As<sq_pushroottableType>();
+ g_pSquirrel<ScriptContext::SERVER>->__sq_call = module.Offset(0x8620).As<sq_callType>();
+
+ g_pSquirrel<ScriptContext::SERVER>->__sq_newarray = module.Offset(0x39F0).As<sq_newarrayType>();
+ g_pSquirrel<ScriptContext::SERVER>->__sq_arrayappend = module.Offset(0x3C70).As<sq_arrayappendType>();
+
+ g_pSquirrel<ScriptContext::SERVER>->__sq_newtable = module.Offset(0x3960).As<sq_newtableType>();
+ g_pSquirrel<ScriptContext::SERVER>->__sq_newslot = module.Offset(0x7080).As<sq_newslotType>();
+
+ g_pSquirrel<ScriptContext::SERVER>->__sq_pushstring = module.Offset(0x3440).As<sq_pushstringType>();
+ g_pSquirrel<ScriptContext::SERVER>->__sq_pushinteger = module.Offset(0x36A0).As<sq_pushintegerType>();
+ g_pSquirrel<ScriptContext::SERVER>->__sq_pushfloat = module.Offset(0x3800).As<sq_pushfloatType>();
+ g_pSquirrel<ScriptContext::SERVER>->__sq_pushbool = module.Offset(0x3710).As<sq_pushboolType>();
+ g_pSquirrel<ScriptContext::SERVER>->__sq_pushasset = module.Offset(0x3560).As<sq_pushassetType>();
+ g_pSquirrel<ScriptContext::SERVER>->__sq_pushvector = module.Offset(0x3780).As<sq_pushvectorType>();
+ g_pSquirrel<ScriptContext::SERVER>->__sq_pushobject = module.Offset(0x83A0).As<sq_pushobjectType>();
+
+ g_pSquirrel<ScriptContext::SERVER>->__sq_raiseerror = module.Offset(0x8440).As<sq_raiseerrorType>();
+
+ g_pSquirrel<ScriptContext::SERVER>->__sq_getstring = module.Offset(0x60A0).As<sq_getstringType>();
+ g_pSquirrel<ScriptContext::SERVER>->__sq_getinteger = module.Offset(0x60C0).As<sq_getintegerType>();
+ g_pSquirrel<ScriptContext::SERVER>->__sq_getfloat = module.Offset(0x60E0).As<sq_getfloatType>();
+ g_pSquirrel<ScriptContext::SERVER>->__sq_getbool = module.Offset(0x6110).As<sq_getboolType>();
+ g_pSquirrel<ScriptContext::SERVER>->__sq_getasset = module.Offset(0x5FF0).As<sq_getassetType>();
+ g_pSquirrel<ScriptContext::SERVER>->__sq_getuserdata = module.Offset(0x63B0).As<sq_getuserdataType>();
+ g_pSquirrel<ScriptContext::SERVER>->__sq_getvector = module.Offset(0x6120).As<sq_getvectorType>();
+ g_pSquirrel<ScriptContext::SERVER>->__sq_get = module.Offset(0x7C00).As<sq_getType>();
+
+ g_pSquirrel<ScriptContext::SERVER>->__sq_getthisentity = module.Offset(0x203B0).As<sq_getthisentityType>();
+ g_pSquirrel<ScriptContext::SERVER>->__sq_getobject = module.Offset(0x6140).As<sq_getobjectType>();
+
+ g_pSquirrel<ScriptContext::SERVER>->__sq_createuserdata = module.Offset(0x38D0).As<sq_createuserdataType>();
+ g_pSquirrel<ScriptContext::SERVER>->__sq_setuserdatatypeid = module.Offset(0x6470).As<sq_setuserdatatypeidType>();
+
+ g_pSquirrel<ScriptContext::SERVER>->__sq_GetEntityConstant_CBaseEntity = module.Offset(0x418AF0).As<sq_GetEntityConstantType>();
+ g_pSquirrel<ScriptContext::SERVER>->__sq_getentityfrominstance = module.Offset(0x1E920).As<sq_getentityfrominstanceType>();
+
+ g_pSquirrel<ScriptContext::SERVER>->logger = NS::log::SCRIPT_SV;
+ // Message buffer stuff
+ g_pSquirrel<ScriptContext::SERVER>->__sq_getfunction = module.Offset(0x6C85).As<sq_getfunctionType>();
+ g_pSquirrel<ScriptContext::SERVER>->__sq_stackinfos = module.Offset(0x35920).As<sq_stackinfosType>();
+
+ MAKEHOOK(
+ module.Offset(0x1DD10),
+ &RegisterSquirrelFunctionHook<ScriptContext::SERVER>,
+ &g_pSquirrel<ScriptContext::SERVER>->RegisterSquirrelFunc);
+
+ MAKEHOOK(module.Offset(0x8AA0), &sq_compiler_createHook<ScriptContext::SERVER>, &sq_compiler_create<ScriptContext::SERVER>);
+
+ MAKEHOOK(module.Offset(0x1FE90), &SQPrintHook<ScriptContext::SERVER>, &SQPrint<ScriptContext::SERVER>);
+ MAKEHOOK(module.Offset(0x260E0), &CreateNewVMHook<ScriptContext::SERVER>, &CreateNewVM<ScriptContext::SERVER>);
+ MAKEHOOK(module.Offset(0x26E20), &DestroyVMHook<ScriptContext::SERVER>, &DestroyVM<ScriptContext::SERVER>);
+ MAKEHOOK(module.Offset(0x799E0), &ScriptCompileErrorHook<ScriptContext::SERVER>, &SQCompileError<ScriptContext::SERVER>);
+ MAKEHOOK(module.Offset(0x1D5C0), &CallScriptInitCallbackHook<ScriptContext::SERVER>, &CallScriptInitCallback<ScriptContext::SERVER>);
+
+ // FCVAR_CHEAT and FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS allows clients to execute this, but since it's unsafe we only allow it when cheats
+ // are enabled for script_client and script_ui, we don't use cheats, so clients can execute them on themselves all they want
+ RegisterConCommand(
+ "script",
+ ConCommand_script<ScriptContext::SERVER>,
+ "Executes script code on the server vm",
+ FCVAR_GAMEDLL | FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS | FCVAR_CHEAT);
+
+ StubUnsafeSQFuncs<ScriptContext::SERVER>();
+}
+
+void InitialiseSquirrelManagers()
+{
+ g_pSquirrel<ScriptContext::CLIENT> = new SquirrelManager<ScriptContext::CLIENT>;
+ g_pSquirrel<ScriptContext::UI> = new SquirrelManager<ScriptContext::UI>;
+ g_pSquirrel<ScriptContext::SERVER> = new SquirrelManager<ScriptContext::SERVER>;
+}
diff --git a/NorthstarDLL/squirrel/squirrel.h b/NorthstarDLL/squirrel/squirrel.h
new file mode 100644
index 00000000..82825b3c
--- /dev/null
+++ b/NorthstarDLL/squirrel/squirrel.h
@@ -0,0 +1,493 @@
+#pragma once
+
+#include "squirrelclasstypes.h"
+#include "squirrelautobind.h"
+#include "core/math/vector.h"
+#include "mods/modmanager.h"
+
+// stolen from ttf2sdk: sqvm types
+typedef float SQFloat;
+typedef long SQInteger;
+typedef unsigned long SQUnsignedInteger;
+typedef char SQChar;
+typedef SQUnsignedInteger SQBool;
+
+static constexpr int operator&(ScriptContext first, ScriptContext second)
+{
+ return first == second;
+}
+
+static constexpr int operator&(int first, ScriptContext second)
+{
+ return first & (1 << static_cast<int>(second));
+}
+
+static constexpr int operator|(ScriptContext first, ScriptContext second)
+{
+ return (1 << static_cast<int>(first)) + (1 << static_cast<int>(second));
+}
+
+static constexpr int operator|(int first, ScriptContext second)
+{
+ return first + (1 << static_cast<int>(second));
+}
+
+const char* GetContextName(ScriptContext context);
+const char* GetContextName_Short(ScriptContext context);
+eSQReturnType SQReturnTypeFromString(const char* pReturnType);
+const char* SQTypeNameFromID(const int iTypeId);
+
+ScriptContext ScriptContextFromString(std::string string);
+
+namespace NS::log
+{
+ template <ScriptContext context> std::shared_ptr<spdlog::logger> squirrel_logger();
+}; // namespace NS::log
+
+void schedule_call_external(ScriptContext context, const char* func_name, SquirrelMessage_External_Pop function);
+
+// This base class means that only the templated functions have to be rebuilt for each template instance
+// Cuts down on compile time by ~5 seconds
+class SquirrelManagerBase
+{
+ protected:
+ std::vector<SQFuncRegistration*> m_funcRegistrations;
+
+ public:
+ CSquirrelVM* m_pSQVM;
+ std::map<std::string, SQFunction> m_funcOverrides = {};
+ std::map<std::string, SQFunction> m_funcOriginals = {};
+
+ bool m_bFatalCompilationErrors = false;
+
+ std::shared_ptr<spdlog::logger> logger;
+
+#pragma region SQVM funcs
+ RegisterSquirrelFuncType RegisterSquirrelFunc;
+ sq_defconstType __sq_defconst;
+
+ sq_compilebufferType __sq_compilebuffer;
+ sq_callType __sq_call;
+ sq_raiseerrorType __sq_raiseerror;
+
+ sq_newarrayType __sq_newarray;
+ sq_arrayappendType __sq_arrayappend;
+
+ sq_newtableType __sq_newtable;
+ sq_newslotType __sq_newslot;
+
+ sq_pushroottableType __sq_pushroottable;
+ sq_pushstringType __sq_pushstring;
+ sq_pushintegerType __sq_pushinteger;
+ sq_pushfloatType __sq_pushfloat;
+ sq_pushboolType __sq_pushbool;
+ sq_pushassetType __sq_pushasset;
+ sq_pushvectorType __sq_pushvector;
+ sq_pushobjectType __sq_pushobject;
+
+ sq_getstringType __sq_getstring;
+ sq_getintegerType __sq_getinteger;
+ sq_getfloatType __sq_getfloat;
+ sq_getboolType __sq_getbool;
+ sq_getType __sq_get;
+ sq_getassetType __sq_getasset;
+ sq_getuserdataType __sq_getuserdata;
+ sq_getvectorType __sq_getvector;
+ sq_getthisentityType __sq_getthisentity;
+ sq_getobjectType __sq_getobject;
+
+ sq_stackinfosType __sq_stackinfos;
+
+ sq_createuserdataType __sq_createuserdata;
+ sq_setuserdatatypeidType __sq_setuserdatatypeid;
+ sq_getfunctionType __sq_getfunction;
+
+ sq_getentityfrominstanceType __sq_getentityfrominstance;
+ sq_GetEntityConstantType __sq_GetEntityConstant_CBaseEntity;
+
+#pragma endregion
+
+#pragma region SQVM func wrappers
+ inline void defconst(CSquirrelVM* sqvm, const SQChar* pName, int nValue)
+ {
+ __sq_defconst(sqvm, pName, nValue);
+ }
+
+ inline SQRESULT
+ compilebuffer(CompileBufferState* bufferState, const SQChar* bufferName = "unnamedbuffer", const SQBool bShouldThrowError = false)
+ {
+ return __sq_compilebuffer(m_pSQVM->sqvm, bufferState, bufferName, -1, bShouldThrowError);
+ }
+
+ inline SQRESULT _call(HSquirrelVM* sqvm, const SQInteger args)
+ {
+ return __sq_call(sqvm, args + 1, false, false);
+ }
+
+ inline SQInteger raiseerror(HSquirrelVM* sqvm, const SQChar* sError)
+ {
+ return __sq_raiseerror(sqvm, sError);
+ }
+
+ inline void newarray(HSquirrelVM* sqvm, const SQInteger stackpos = 0)
+ {
+ __sq_newarray(sqvm, stackpos);
+ }
+
+ inline SQRESULT arrayappend(HSquirrelVM* sqvm, const SQInteger stackpos)
+ {
+ return __sq_arrayappend(sqvm, stackpos);
+ }
+
+ inline SQRESULT newtable(HSquirrelVM* sqvm)
+ {
+ return __sq_newtable(sqvm);
+ }
+
+ inline SQRESULT newslot(HSquirrelVM* sqvm, SQInteger idx, SQBool bStatic)
+ {
+ return __sq_newslot(sqvm, idx, bStatic);
+ }
+
+ inline void pushroottable(HSquirrelVM* sqvm)
+ {
+ __sq_pushroottable(sqvm);
+ }
+
+ inline void pushstring(HSquirrelVM* sqvm, const SQChar* sVal, int length = -1)
+ {
+ __sq_pushstring(sqvm, sVal, length);
+ }
+
+ inline void pushinteger(HSquirrelVM* sqvm, const SQInteger iVal)
+ {
+ __sq_pushinteger(sqvm, iVal);
+ }
+
+ inline void pushfloat(HSquirrelVM* sqvm, const SQFloat flVal)
+ {
+ __sq_pushfloat(sqvm, flVal);
+ }
+
+ inline void pushbool(HSquirrelVM* sqvm, const SQBool bVal)
+ {
+ __sq_pushbool(sqvm, bVal);
+ }
+
+ inline void pushasset(HSquirrelVM* sqvm, const SQChar* sVal, int length = -1)
+ {
+ __sq_pushasset(sqvm, sVal, length);
+ }
+
+ inline void pushvector(HSquirrelVM* sqvm, const Vector3 pVal)
+ {
+ __sq_pushvector(sqvm, *(float**)&pVal);
+ }
+
+ inline void pushobject(HSquirrelVM* sqvm, SQObject* obj)
+ {
+ __sq_pushobject(sqvm, obj);
+ }
+
+ inline const SQChar* getstring(HSquirrelVM* sqvm, const SQInteger stackpos)
+ {
+ return __sq_getstring(sqvm, stackpos);
+ }
+
+ inline SQInteger getinteger(HSquirrelVM* sqvm, const SQInteger stackpos)
+ {
+ return __sq_getinteger(sqvm, stackpos);
+ }
+
+ inline SQFloat getfloat(HSquirrelVM* sqvm, const SQInteger stackpos)
+ {
+ return __sq_getfloat(sqvm, stackpos);
+ }
+
+ inline SQBool getbool(HSquirrelVM* sqvm, const SQInteger stackpos)
+ {
+ return __sq_getbool(sqvm, stackpos);
+ }
+
+ inline SQRESULT get(HSquirrelVM* sqvm, const SQInteger stackpos)
+ {
+ return __sq_get(sqvm, stackpos);
+ }
+
+ inline Vector3 getvector(HSquirrelVM* sqvm, const SQInteger stackpos)
+ {
+ float* pRet = __sq_getvector(sqvm, stackpos);
+ return *(Vector3*)&pRet;
+ }
+
+ inline int sq_getfunction(HSquirrelVM* sqvm, const char* name, SQObject* returnObj, const char* signature)
+ {
+ return __sq_getfunction(sqvm, name, returnObj, signature);
+ }
+
+ inline SQRESULT getasset(HSquirrelVM* sqvm, const SQInteger stackpos, const char** result)
+ {
+ return __sq_getasset(sqvm, stackpos, result);
+ }
+
+ inline long long sq_stackinfos(HSquirrelVM* sqvm, int level, SQStackInfos& out)
+ {
+ return __sq_stackinfos(sqvm, level, &out, sqvm->_callstacksize);
+ }
+
+ inline Mod* getcallingmod(HSquirrelVM* sqvm, int depth = 0)
+ {
+ SQStackInfos stackInfo {};
+ if (1 + depth >= sqvm->_callstacksize)
+ {
+ return nullptr;
+ }
+ sq_stackinfos(sqvm, 1 + depth, stackInfo);
+ std::string sourceName = stackInfo._sourceName;
+ std::replace(sourceName.begin(), sourceName.end(), '/', '\\');
+ std::string filename = "scripts\\vscripts\\" + sourceName;
+ if (auto res = g_pModManager->m_ModFiles.find(filename); res != g_pModManager->m_ModFiles.end())
+ {
+ return res->second.m_pOwningMod;
+ }
+ return nullptr;
+ }
+ template <typename T> inline SQRESULT getuserdata(HSquirrelVM* sqvm, const SQInteger stackpos, T* data, uint64_t* typeId)
+ {
+ return __sq_getuserdata(sqvm, stackpos, (void**)data, typeId); // this sometimes crashes idk
+ }
+
+ template <typename T> inline T* createuserdata(HSquirrelVM* sqvm, SQInteger size)
+ {
+ void* ret = __sq_createuserdata(sqvm, size);
+ memset(ret, 0, size);
+ return (T*)ret;
+ }
+
+ inline SQRESULT setuserdatatypeid(HSquirrelVM* sqvm, const SQInteger stackpos, uint64_t typeId)
+ {
+ return __sq_setuserdatatypeid(sqvm, stackpos, typeId);
+ }
+
+ template <typename T> inline SQBool getthisentity(HSquirrelVM* sqvm, T* ppEntity)
+ {
+ return __sq_getentity(sqvm, (void**)ppEntity);
+ }
+
+ template <typename T> inline T* getentity(HSquirrelVM* sqvm, SQInteger iStackPos)
+ {
+ SQObject obj;
+ __sq_getobject(sqvm, iStackPos, &obj);
+
+ // there are entity constants for other types, but seemingly CBaseEntity's is the only one needed
+ return (T*)__sq_getentityfrominstance(m_pSQVM, &obj, __sq_GetEntityConstant_CBaseEntity());
+ }
+#pragma endregion
+};
+
+template <ScriptContext context> class SquirrelManager : public virtual SquirrelManagerBase
+{
+ public:
+#pragma region MessageBuffer
+ SquirrelMessageBuffer* messageBuffer;
+
+ template <typename... Args> SquirrelMessage AsyncCall(std::string funcname, Args... args)
+ {
+ // This function schedules a call to be executed on the next frame
+ // This is useful for things like threads and plugins, which do not run on the main thread
+ FunctionVector functionVector;
+ SqRecurseArgs<context>(functionVector, args...);
+ SquirrelMessage message = {funcname, functionVector};
+ messageBuffer->push(message);
+ return message;
+ }
+
+ SquirrelMessage AsyncCall(std::string funcname)
+ {
+ // This function schedules a call to be executed on the next frame
+ // This is useful for things like threads and plugins, which do not run on the main thread
+ FunctionVector functionVector = {};
+ SquirrelMessage message = {funcname, functionVector};
+ messageBuffer->push(message);
+ return message;
+ }
+
+ SQRESULT Call(const char* funcname)
+ {
+ // Warning!
+ // This function assumes the squirrel VM is stopped/blocked at the moment of call
+ // Calling this function while the VM is running is likely to result in a crash due to stack destruction
+ // If you want to call into squirrel asynchronously, use `AsyncCall` instead
+
+ if (!m_pSQVM || !m_pSQVM->sqvm)
+ {
+ spdlog::error(
+ "{} was called on context {} while VM was not initialized. This will crash", __FUNCTION__, GetContextName(context));
+ }
+
+ SQObject functionobj {};
+ int result = sq_getfunction(m_pSQVM->sqvm, funcname, &functionobj, 0);
+ if (result != 0) // This func returns 0 on success for some reason
+ {
+ NS::log::squirrel_logger<context>()->error("Call was unable to find function with name '{}'. Is it global?", funcname);
+ return SQRESULT_ERROR;
+ }
+ pushobject(m_pSQVM->sqvm, &functionobj); // Push the function object
+ pushroottable(m_pSQVM->sqvm); // Push root table
+ return _call(m_pSQVM->sqvm, 0);
+ }
+
+ template <typename... Args> SQRESULT Call(const char* funcname, Args... args)
+ {
+ // Warning!
+ // This function assumes the squirrel VM is stopped/blocked at the moment of call
+ // Calling this function while the VM is running is likely to result in a crash due to stack destruction
+ // If you want to call into squirrel asynchronously, use `schedule_call` instead
+ if (!m_pSQVM || !m_pSQVM->sqvm)
+ {
+ spdlog::error(
+ "{} was called on context {} while VM was not initialized. This will crash", __FUNCTION__, GetContextName(context));
+ }
+ SQObject functionobj {};
+ int result = sq_getfunction(m_pSQVM->sqvm, funcname, &functionobj, 0);
+ if (result != 0) // This func returns 0 on success for some reason
+ {
+ NS::log::squirrel_logger<context>()->error("Call was unable to find function with name '{}'. Is it global?", funcname);
+ return SQRESULT_ERROR;
+ }
+ pushobject(m_pSQVM->sqvm, &functionobj); // Push the function object
+ pushroottable(m_pSQVM->sqvm); // Push root table
+
+ FunctionVector functionVector;
+ SqRecurseArgs<context>(functionVector, args...);
+
+ for (auto& v : functionVector)
+ {
+ // Execute lambda to push arg to stack
+ v();
+ }
+
+ return _call(m_pSQVM->sqvm, functionVector.size());
+ }
+
+#pragma endregion
+
+ public:
+ SquirrelManager()
+ {
+ m_pSQVM = nullptr;
+ }
+
+ void VMCreated(CSquirrelVM* newSqvm);
+ void VMDestroyed();
+ void ExecuteCode(const char* code);
+ void AddFuncRegistration(std::string returnType, std::string name, std::string argTypes, std::string helpText, SQFunction func);
+ SQRESULT setupfunc(const SQChar* funcname);
+ void AddFuncOverride(std::string name, SQFunction func);
+ void ProcessMessageBuffer();
+};
+
+template <ScriptContext context> SquirrelManager<context>* g_pSquirrel;
+
+void InitialiseSquirrelManagers();
+
+/*
+ Beware all ye who enter below.
+ This place is not a place of honor... no highly esteemed deed is commemorated here... nothing valued is here.
+ What is here was dangerous and repulsive to us. This message is a warning about danger.
+*/
+
+#pragma region MessageBuffer templates
+
+// Clang-formatting makes this whole thing unreadable
+// clang-format off
+
+#ifndef MessageBufferFuncs
+#define MessageBufferFuncs
+// Bools
+template <ScriptContext context, typename T>
+requires std::convertible_to<T, bool> && (!std::is_floating_point_v<T>) && (!std::convertible_to<T, std::string>) && (!std::convertible_to<T, int>)
+inline VoidFunction SQMessageBufferPushArg(T& arg) {
+ return [arg]{ g_pSquirrel<context>->pushbool(g_pSquirrel<context>->m_pSQVM->sqvm, static_cast<bool>(arg)); };
+}
+// Vectors
+template <ScriptContext context>
+inline VoidFunction SQMessageBufferPushArg(Vector3& arg) {
+ return [arg]{ g_pSquirrel<context>->pushvector(g_pSquirrel<context>->m_pSQVM->sqvm, arg); };
+}
+// Vectors
+template <ScriptContext context>
+inline VoidFunction SQMessageBufferPushArg(SQObject* arg) {
+ return [arg]{ g_pSquirrel<context>->pushSQObject(g_pSquirrel<context>->m_pSQVM->sqvm, arg); };
+}
+// Ints
+template <ScriptContext context, typename T>
+requires std::convertible_to<T, int> && (!std::is_floating_point_v<T>)
+inline VoidFunction SQMessageBufferPushArg(T& arg) {
+ return [arg]{ g_pSquirrel<context>->pushinteger(g_pSquirrel<context>->m_pSQVM->sqvm, static_cast<int>(arg)); };
+}
+// Floats
+template <ScriptContext context, typename T>
+requires std::convertible_to<T, float> && (std::is_floating_point_v<T>)
+inline VoidFunction SQMessageBufferPushArg(T& arg) {
+ return [arg]{ g_pSquirrel<context>->pushfloat(g_pSquirrel<context>->m_pSQVM->sqvm, static_cast<float>(arg)); };
+}
+// Strings
+template <ScriptContext context, typename T>
+requires (std::convertible_to<T, std::string> || std::is_constructible_v<std::string, T>)
+inline VoidFunction SQMessageBufferPushArg(T& arg) {
+ auto converted = std::string(arg);
+ return [converted]{ g_pSquirrel<context>->pushstring(g_pSquirrel<context>->m_pSQVM->sqvm, converted.c_str(), converted.length()); };
+}
+// Assets
+template <ScriptContext context>
+inline VoidFunction SQMessageBufferPushArg(SquirrelAsset& arg) {
+ return [arg]{ g_pSquirrel<context>->pushasset(g_pSquirrel<context>->m_pSQVM->sqvm, arg.path.c_str(), arg.path.length()); };
+}
+// Maps
+template <ScriptContext context, typename T>
+requires is_iterable<T>
+inline VoidFunction SQMessageBufferPushArg(T& arg) {
+ FunctionVector localv = {};
+ localv.push_back([]{g_pSquirrel<context>->newarray(g_pSquirrel<context>->m_pSQVM->sqvm, 0);});
+
+ for (const auto& item : arg) {
+ localv.push_back(SQMessageBufferPushArg<context>(item));
+ localv.push_back([]{g_pSquirrel<context>->arrayappend(g_pSquirrel<context>->m_pSQVM->sqvm, -2);});
+ }
+
+ return [localv] { for (auto& func : localv) { func(); } };
+}
+// Vectors
+template <ScriptContext context, typename T>
+requires is_map<T>
+inline VoidFunction SQMessageBufferPushArg(T& map) {
+ FunctionVector localv = {};
+ localv.push_back([]{g_pSquirrel<context>->newtable(g_pSquirrel<context>->m_pSQVM->sqvm);});
+
+ for (const auto& item : map) {
+ localv.push_back(SQMessageBufferPushArg<context>(item.first));
+ localv.push_back(SQMessageBufferPushArg<context>(item.second));
+ localv.push_back([]{g_pSquirrel<context>->newslot(g_pSquirrel<context>->m_pSQVM->sqvm, -3, false);});
+ }
+
+ return [localv]{ for (auto& func : localv) { func(); } };
+}
+
+template <ScriptContext context, typename T>
+inline void SqRecurseArgs(FunctionVector& v, T& arg) {
+ v.push_back(SQMessageBufferPushArg<context>(arg));
+}
+
+// This function is separated from the PushArg function so as to not generate too many template instances
+// This is the main function responsible for unrolling the argument pack
+template <ScriptContext context, typename T, typename... Args>
+inline void SqRecurseArgs(FunctionVector& v, T& arg, Args... args) {
+ v.push_back(SQMessageBufferPushArg<context>(arg));
+ SqRecurseArgs<context>(v, args...);
+}
+
+// clang-format on
+#endif
+
+#pragma endregion
diff --git a/NorthstarDLL/squirrel/squirrelautobind.cpp b/NorthstarDLL/squirrel/squirrelautobind.cpp
new file mode 100644
index 00000000..d5f42477
--- /dev/null
+++ b/NorthstarDLL/squirrel/squirrelautobind.cpp
@@ -0,0 +1,21 @@
+#include "pch.h"
+#include "squirrelautobind.h"
+
+SquirrelAutoBindContainer* g_pSqAutoBindContainer;
+
+ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrelAutoBind, ClientSquirrel, (CModule module))
+{
+ spdlog::info("ClientSquirrelAutoBInd AutoBindFuncsVectorsize {}", g_pSqAutoBindContainer->clientSqAutoBindFuncs.size());
+ for (auto& autoBindFunc : g_pSqAutoBindContainer->clientSqAutoBindFuncs)
+ {
+ autoBindFunc();
+ }
+}
+
+ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrelAutoBind, ServerSquirrel, (CModule module))
+{
+ for (auto& autoBindFunc : g_pSqAutoBindContainer->serverSqAutoBindFuncs)
+ {
+ autoBindFunc();
+ }
+}
diff --git a/NorthstarDLL/squirrel/squirrelautobind.h b/NorthstarDLL/squirrel/squirrelautobind.h
new file mode 100644
index 00000000..865479a1
--- /dev/null
+++ b/NorthstarDLL/squirrel/squirrelautobind.h
@@ -0,0 +1,76 @@
+#pragma once
+#include <vector>
+
+typedef void (*SqAutoBindFunc)();
+
+class SquirrelAutoBindContainer
+{
+ public:
+ std::vector<std::function<void()>> clientSqAutoBindFuncs;
+ std::vector<std::function<void()>> serverSqAutoBindFuncs;
+};
+
+extern SquirrelAutoBindContainer* g_pSqAutoBindContainer;
+
+class __squirrelautobind;
+
+#define ADD_SQFUNC(returnType, funcName, argTypes, helpText, runOnContext) \
+ template <ScriptContext context> SQRESULT CONCAT2(Script_, funcName)(HSquirrelVM * sqvm); \
+ namespace \
+ { \
+ __squirrelautobind CONCAT2(__squirrelautobind, __LINE__)( \
+ []() \
+ { \
+ if constexpr (runOnContext & ScriptContext::UI) \
+ g_pSquirrel<ScriptContext::UI>->AddFuncRegistration( \
+ returnType, __STR(funcName), argTypes, helpText, CONCAT2(Script_, funcName) < ScriptContext::UI >); \
+ if constexpr (runOnContext & ScriptContext::CLIENT) \
+ g_pSquirrel<ScriptContext::CLIENT>->AddFuncRegistration( \
+ returnType, __STR(funcName), argTypes, helpText, CONCAT2(Script_, funcName) < ScriptContext::CLIENT >); \
+ }, \
+ []() \
+ { \
+ if constexpr (runOnContext & ScriptContext::SERVER) \
+ g_pSquirrel<ScriptContext::SERVER>->AddFuncRegistration( \
+ returnType, __STR(funcName), argTypes, helpText, CONCAT2(Script_, funcName) < ScriptContext::SERVER >); \
+ }); \
+ } \
+ template <ScriptContext context> SQRESULT CONCAT2(Script_, funcName)(HSquirrelVM * sqvm)
+
+#define REPLACE_SQFUNC(funcName, runOnContext) \
+ template <ScriptContext context> SQRESULT CONCAT2(Script_, funcName)(HSquirrelVM * sqvm); \
+ namespace \
+ { \
+ __squirrelautobind CONCAT2(__squirrelautobind, __LINE__)( \
+ []() \
+ { \
+ if constexpr (runOnContext & ScriptContext::UI) \
+ g_pSquirrel<ScriptContext::UI>->AddFuncOverride(__STR(funcName), CONCAT2(Script_, funcName) < ScriptContext::UI >); \
+ if constexpr (runOnContext & ScriptContext::CLIENT) \
+ g_pSquirrel<ScriptContext::CLIENT>->AddFuncOverride( \
+ __STR(funcName), CONCAT2(Script_, funcName) < ScriptContext::CLIENT >); \
+ }, \
+ []() \
+ { \
+ if constexpr (runOnContext & ScriptContext::SERVER) \
+ g_pSquirrel<ScriptContext::SERVER>->AddFuncOverride( \
+ __STR(funcName), CONCAT2(Script_, funcName) < ScriptContext::SERVER >); \
+ }); \
+ } \
+ template <ScriptContext context> SQRESULT CONCAT2(Script_, funcName)(HSquirrelVM * sqvm)
+
+class __squirrelautobind
+{
+ public:
+ __squirrelautobind() = delete;
+
+ __squirrelautobind(std::function<void()> clientAutoBindFunc, std::function<void()> serverAutoBindFunc)
+ {
+ // Bit hacky but we can't initialise this normally since this gets run automatically on load
+ if (g_pSqAutoBindContainer == nullptr)
+ g_pSqAutoBindContainer = new SquirrelAutoBindContainer();
+
+ g_pSqAutoBindContainer->clientSqAutoBindFuncs.push_back(clientAutoBindFunc);
+ g_pSqAutoBindContainer->serverSqAutoBindFuncs.push_back(serverAutoBindFunc);
+ }
+};
diff --git a/NorthstarDLL/squirrel/squirrelclasstypes.h b/NorthstarDLL/squirrel/squirrelclasstypes.h
new file mode 100644
index 00000000..efc80f15
--- /dev/null
+++ b/NorthstarDLL/squirrel/squirrelclasstypes.h
@@ -0,0 +1,242 @@
+#pragma once
+#include "pch.h"
+#include "squirreldatatypes.h"
+
+#include <queue>
+
+enum SQRESULT : SQInteger
+{
+ SQRESULT_ERROR = -1,
+ SQRESULT_NULL = 0,
+ SQRESULT_NOTNULL = 1,
+};
+
+typedef SQRESULT (*SQFunction)(HSquirrelVM* sqvm);
+
+enum class eSQReturnType
+{
+ Float = 0x1,
+ Vector = 0x3,
+ Integer = 0x5,
+ Boolean = 0x6,
+ Entity = 0xD,
+ String = 0x21,
+ Default = 0x20,
+ Arrays = 0x25,
+ Asset = 0x28,
+ Table = 0x26,
+};
+
+const std::map<SQRESULT, const char*> PrintSQRESULT = {
+ {SQRESULT::SQRESULT_ERROR, "SQRESULT_ERROR"},
+ {SQRESULT::SQRESULT_NULL, "SQRESULT_NULL"},
+ {SQRESULT::SQRESULT_NOTNULL, "SQRESULT_NOTNULL"}};
+
+struct CompileBufferState
+{
+ const SQChar* buffer;
+ const SQChar* bufferPlusLength;
+ const SQChar* bufferAgain;
+
+ CompileBufferState(const std::string& code)
+ {
+ buffer = code.c_str();
+ bufferPlusLength = code.c_str() + code.size();
+ bufferAgain = code.c_str();
+ }
+};
+
+struct SQFuncRegistration
+{
+ const char* squirrelFuncName;
+ const char* cppFuncName;
+ const char* helpText;
+ const char* returnTypeString;
+ const char* argTypes;
+ uint32_t unknown1;
+ uint32_t devLevel;
+ const char* shortNameMaybe;
+ uint32_t unknown2;
+ eSQReturnType returnType;
+ uint32_t* externalBufferPointer;
+ uint64_t externalBufferSize;
+ uint64_t unknown3;
+ uint64_t unknown4;
+ SQFunction funcPtr;
+
+ SQFuncRegistration()
+ {
+ memset(this, 0, sizeof(SQFuncRegistration));
+ this->returnType = eSQReturnType::Default;
+ }
+};
+
+enum class ScriptContext : int
+{
+ INVALID = -1,
+ SERVER,
+ CLIENT,
+ UI,
+};
+
+typedef std::vector<std::function<void()>> FunctionVector;
+typedef std::function<void()> VoidFunction;
+
+// clang-format off
+template <typename T>
+concept is_map =
+ // Simple maps
+ std::same_as<T, std::map<typename T::key_type, typename T::mapped_type, typename T::key_compare, typename T::allocator_type>> ||
+ std::same_as<T, std::unordered_map<typename T::key_type, typename T::mapped_type, typename T::hasher, typename T::key_equal, typename T::allocator_type>> ||
+
+ // Nested maps
+ std::same_as <
+ std::map<typename T::key_type, typename T::mapped_type, typename T::key_compare, typename T::allocator_type>,
+ std::map<typename T::key_type, typename T::mapped_type, typename T::key_compare, typename T::allocator_type>
+ > ||
+ std::same_as <
+ std::unordered_map<typename T::key_type, typename T::mapped_type, typename T::key_compare, typename T::allocator_type>,
+ std::unordered_map<typename T::key_type, typename T::mapped_type, typename T::key_compare, typename T::allocator_type>
+ > ||
+ std::same_as <
+ std::unordered_map<typename T::key_type, typename T::mapped_type, typename T::key_compare, typename T::allocator_type>,
+ std::map<typename T::key_type, typename T::mapped_type, typename T::key_compare, typename T::allocator_type>
+ > ||
+ std::same_as <
+ std::map<typename T::key_type, typename T::mapped_type, typename T::key_compare, typename T::allocator_type>,
+ std::unordered_map<typename T::key_type, typename T::mapped_type, typename T::key_compare, typename T::allocator_type>
+ >
+;
+
+template<typename T>
+concept is_iterable = requires(std::ranges::range_value_t<T> x)
+{
+ x.begin(); // must have `x.begin()`
+ x.end(); // and `x.end()`
+};
+
+// clang-format on
+
+typedef void (*SquirrelMessage_External_Pop)(HSquirrelVM* sqvm);
+typedef void (*sq_schedule_call_externalType)(ScriptContext context, const char* funcname, SquirrelMessage_External_Pop function);
+
+class SquirrelMessage
+{
+ public:
+ std::string functionName;
+ FunctionVector args;
+ bool isExternal = false;
+ SquirrelMessage_External_Pop externalFunc = NULL;
+};
+
+class SquirrelMessageBuffer
+{
+
+ private:
+ std::queue<SquirrelMessage> messages = {};
+
+ public:
+ std::mutex mutex;
+ std::optional<SquirrelMessage> pop()
+ {
+ std::lock_guard<std::mutex> guard(mutex);
+ if (!messages.empty())
+ {
+ auto message = messages.front();
+ messages.pop();
+ return message;
+ }
+ else
+ {
+ return std::nullopt;
+ }
+ }
+
+ void unwind()
+ {
+ auto maybeMessage = this->pop();
+ if (!maybeMessage)
+ {
+ spdlog::error("Plugin tried consuming SquirrelMessage while buffer was empty");
+ return;
+ }
+ auto message = maybeMessage.value();
+ for (auto& v : message.args)
+ {
+ // Execute lambda to push arg to stack
+ v();
+ }
+ }
+
+ void push(SquirrelMessage message)
+ {
+ std::lock_guard<std::mutex> guard(mutex);
+ messages.push(message);
+ }
+};
+
+// Super simple wrapper class to allow pushing Assets via call
+class SquirrelAsset
+{
+ public:
+ std::string path;
+ SquirrelAsset(std::string path) : path(path) {};
+};
+
+#pragma region TypeDefs
+
+// core sqvm funcs
+typedef int64_t (*RegisterSquirrelFuncType)(CSquirrelVM* sqvm, SQFuncRegistration* funcReg, char unknown);
+typedef void (*sq_defconstType)(CSquirrelVM* sqvm, const SQChar* name, int value);
+
+typedef SQRESULT (*sq_compilebufferType)(
+ HSquirrelVM* sqvm, CompileBufferState* compileBuffer, const char* file, int a1, SQBool bShouldThrowError);
+typedef SQRESULT (*sq_callType)(HSquirrelVM* sqvm, SQInteger iArgs, SQBool bShouldReturn, SQBool bThrowError);
+typedef SQInteger (*sq_raiseerrorType)(HSquirrelVM* sqvm, const SQChar* pError);
+
+// sq stack array funcs
+typedef void (*sq_newarrayType)(HSquirrelVM* sqvm, SQInteger iStackpos);
+typedef SQRESULT (*sq_arrayappendType)(HSquirrelVM* sqvm, SQInteger iStackpos);
+
+// sq table funcs
+typedef SQRESULT (*sq_newtableType)(HSquirrelVM* sqvm);
+typedef SQRESULT (*sq_newslotType)(HSquirrelVM* sqvm, SQInteger idx, SQBool bStatic);
+
+// sq stack push funcs
+typedef void (*sq_pushroottableType)(HSquirrelVM* sqvm);
+typedef void (*sq_pushstringType)(HSquirrelVM* sqvm, const SQChar* pStr, SQInteger iLength);
+typedef void (*sq_pushintegerType)(HSquirrelVM* sqvm, SQInteger i);
+typedef void (*sq_pushfloatType)(HSquirrelVM* sqvm, SQFloat f);
+typedef void (*sq_pushboolType)(HSquirrelVM* sqvm, SQBool b);
+typedef void (*sq_pushassetType)(HSquirrelVM* sqvm, const SQChar* str, SQInteger iLength);
+typedef void (*sq_pushvectorType)(HSquirrelVM* sqvm, const SQFloat* pVec);
+typedef void (*sq_pushobjectType)(HSquirrelVM* sqvm, SQObject* pVec);
+
+// sq stack get funcs
+typedef const SQChar* (*sq_getstringType)(HSquirrelVM* sqvm, SQInteger iStackpos);
+typedef SQInteger (*sq_getintegerType)(HSquirrelVM* sqvm, SQInteger iStackpos);
+typedef SQFloat (*sq_getfloatType)(HSquirrelVM*, SQInteger iStackpos);
+typedef SQBool (*sq_getboolType)(HSquirrelVM*, SQInteger iStackpos);
+typedef SQRESULT (*sq_getType)(HSquirrelVM* sqvm, SQInteger iStackpos);
+typedef SQRESULT (*sq_getassetType)(HSquirrelVM* sqvm, SQInteger iStackpos, const char** pResult);
+typedef SQRESULT (*sq_getuserdataType)(HSquirrelVM* sqvm, SQInteger iStackpos, void** pData, uint64_t* pTypeId);
+typedef SQFloat* (*sq_getvectorType)(HSquirrelVM* sqvm, SQInteger iStackpos);
+typedef SQBool (*sq_getthisentityType)(HSquirrelVM*, void** ppEntity);
+typedef void (*sq_getobjectType)(HSquirrelVM*, SQInteger iStackPos, SQObject* pOutObj);
+
+typedef long long (*sq_stackinfosType)(HSquirrelVM* sqvm, int iLevel, SQStackInfos* pOutObj, int iCallStackSize);
+
+// sq stack userpointer funcs
+typedef void* (*sq_createuserdataType)(HSquirrelVM* sqvm, SQInteger iSize);
+typedef SQRESULT (*sq_setuserdatatypeidType)(HSquirrelVM* sqvm, SQInteger iStackpos, uint64_t iTypeId);
+
+// sq misc entity funcs
+typedef void* (*sq_getentityfrominstanceType)(CSquirrelVM* sqvm, SQObject* pInstance, char** ppEntityConstant);
+typedef char** (*sq_GetEntityConstantType)();
+
+typedef int (*sq_getfunctionType)(HSquirrelVM* sqvm, const char* name, SQObject* returnObj, const char* signature);
+
+#pragma endregion
+
+// These "external" versions of the types are for plugins
+typedef int64_t (*RegisterSquirrelFuncType_External)(ScriptContext context, SQFuncRegistration* funcReg, char unknown);
diff --git a/NorthstarDLL/squirrel/squirreldatatypes.h b/NorthstarDLL/squirrel/squirreldatatypes.h
new file mode 100644
index 00000000..b5025f6b
--- /dev/null
+++ b/NorthstarDLL/squirrel/squirreldatatypes.h
@@ -0,0 +1,495 @@
+#pragma once
+/*
+ This file has been generated by IDA.
+ It contains local type definitions from
+ the type library 'server.dll'
+*/
+
+struct HSquirrelVM;
+struct CallInfo;
+struct SQTable;
+struct SQString;
+struct SQFunctionProto;
+struct SQClosure;
+struct SQSharedState;
+struct StringTable;
+struct SQStructInstance;
+struct SQStructDef;
+struct SQNativeClosure;
+struct SQArray;
+struct tableNode;
+struct SQUserData;
+
+typedef void (*releasehookType)(void* val, int size);
+
+// stolen from ttf2sdk: sqvm types
+typedef float SQFloat;
+typedef long SQInteger;
+typedef unsigned long SQUnsignedInteger;
+typedef char SQChar;
+typedef SQUnsignedInteger SQBool;
+
+/* 127 */
+enum SQObjectType : int
+{
+ _RT_NULL = 0x1,
+ _RT_INTEGER = 0x2,
+ _RT_FLOAT = 0x4,
+ _RT_BOOL = 0x8,
+ _RT_STRING = 0x10,
+ _RT_TABLE = 0x20,
+ _RT_ARRAY = 0x40,
+ _RT_USERDATA = 0x80,
+ _RT_CLOSURE = 0x100,
+ _RT_NATIVECLOSURE = 0x200,
+ _RT_GENERATOR = 0x400,
+ OT_USERPOINTER = 0x800,
+ _RT_USERPOINTER = 0x800,
+ _RT_THREAD = 0x1000,
+ _RT_FUNCPROTO = 0x2000,
+ _RT_CLASS = 0x4000,
+ _RT_INSTANCE = 0x8000,
+ _RT_WEAKREF = 0x10000,
+ OT_VECTOR = 0x40000,
+ SQOBJECT_CANBEFALSE = 0x1000000,
+ OT_NULL = 0x1000001,
+ OT_BOOL = 0x1000008,
+ SQOBJECT_DELEGABLE = 0x2000000,
+ SQOBJECT_NUMERIC = 0x4000000,
+ OT_INTEGER = 0x5000002,
+ OT_FLOAT = 0x5000004,
+ SQOBJECT_REF_COUNTED = 0x8000000,
+ OT_STRING = 0x8000010,
+ OT_ARRAY = 0x8000040,
+ OT_CLOSURE = 0x8000100,
+ OT_NATIVECLOSURE = 0x8000200,
+ OT_ASSET = 0x8000400,
+ OT_THREAD = 0x8001000,
+ OT_FUNCPROTO = 0x8002000,
+ OT_CLAAS = 0x8004000,
+ OT_STRUCT = 0x8200000,
+ OT_WEAKREF = 0x8010000,
+ OT_TABLE = 0xA000020,
+ OT_USERDATA = 0xA000080,
+ OT_INSTANCE = 0xA008000,
+ OT_ENTITY = 0xA400000,
+};
+
+/* 156 */
+union SQObjectValue
+{
+ SQString* asString;
+ SQTable* asTable;
+ SQClosure* asClosure;
+ SQFunctionProto* asFuncProto;
+ SQStructDef* asStructDef;
+ long long as64Integer;
+ SQNativeClosure* asNativeClosure;
+ SQArray* asArray;
+ HSquirrelVM* asThread;
+ float asFloat;
+ int asInteger;
+ SQUserData* asUserdata;
+ SQStructInstance* asStructInstance;
+};
+
+/* 160 */
+struct SQVector
+{
+ SQObjectType _Type;
+ float x;
+ float y;
+ float z;
+};
+
+/* 128 */
+struct SQObject
+{
+ SQObjectType _Type;
+ int structNumber;
+ SQObjectValue _VAL;
+};
+
+/* 138 */
+struct alignas(8) SQString
+{
+ void* vftable;
+ int uiRef;
+ int padding;
+ SQString* _next_maybe;
+ SQSharedState* sharedState;
+ int length;
+ unsigned char gap_24[4];
+ char _hash[8];
+ char _val[1];
+};
+
+/* 137 */
+struct alignas(8) SQTable
+{
+ void* vftable;
+ unsigned char gap_08[4];
+ int uiRef;
+ unsigned char gap_10[8];
+ void* pointer_18;
+ void* pointer_20;
+ void* _sharedState;
+ long long field_30;
+ tableNode* _nodes;
+ int _numOfNodes;
+ int size;
+ int field_48;
+ int _usedNodes;
+ unsigned char _gap_50[20];
+ int field_64;
+ unsigned char _gap_68[80];
+};
+
+/* 140 */
+struct alignas(8) SQClosure
+{
+ void* vftable;
+ unsigned char gap_08[4];
+ int uiRef;
+ void* pointer_10;
+ void* pointer_18;
+ void* pointer_20;
+ void* sharedState;
+ SQObject obj_30;
+ SQObject _function;
+ SQObject* _outervalues;
+ unsigned char gap_58[8];
+ unsigned char gap_60[96];
+ SQObject* objectPointer_C0;
+ unsigned char gap_C8[16];
+};
+
+/* 139 */
+struct alignas(8) SQFunctionProto
+{
+ void* vftable;
+ unsigned char gap_08[4];
+ int uiRef;
+ unsigned char gap_10[8];
+ void* pointer_18;
+ void* pointer_20;
+ void* sharedState;
+ void* pointer_30;
+ SQObjectType _fileNameType;
+ SQString* _fileName;
+ SQObjectType _funcNameType;
+ SQString* _funcName;
+ SQObject obj_58;
+ unsigned char gap_68[12];
+ int _stacksize;
+ unsigned char gap_78[48];
+ int nParameters;
+ unsigned char gap_AC[60];
+ int nDefaultParams;
+ unsigned char gap_EC[200];
+};
+
+/* 152 */
+struct SQStructDef
+{
+ void* vtable;
+ int uiRef;
+ unsigned char padding_C[4];
+ unsigned char unknown[24];
+ SQSharedState* sharedState;
+ SQObjectType _nameType;
+ SQString* _name;
+ unsigned char gap_38[16];
+ SQObjectType _variableNamesType;
+ SQTable* _variableNames;
+ unsigned char gap_[32];
+};
+
+/* 157 */
+struct alignas(8) SQNativeClosure
+{
+ void* vftable;
+ int uiRef;
+ unsigned char gap_C[4];
+ long long value_10;
+ long long value_18;
+ long long value_20;
+ SQSharedState* sharedState;
+ char unknown_30;
+ unsigned char padding_34[7];
+ long long value_38;
+ long long value_40;
+ long long value_48;
+ long long value_50;
+ long long value_58;
+ SQObjectType _nameType;
+ SQString* _name;
+ long long value_70;
+ long long value_78;
+ unsigned char justInCaseGap_80[300];
+};
+
+/* 162 */
+struct SQArray
+{
+ void* vftable;
+ int uiRef;
+ unsigned char gap_24[36];
+ SQObject* _values;
+ int _usedSlots;
+ int _allocated;
+};
+
+/* 129 */
+struct alignas(8) HSquirrelVM
+{
+ void* vftable;
+ int uiRef;
+ unsigned char gap_8[12];
+ void* _toString;
+ void* _roottable_pointer;
+ void* pointer_28;
+ CallInfo* ci;
+ CallInfo* _callstack;
+ int _callstacksize;
+ int _stackbase;
+ SQObject* _stackOfCurrentFunction;
+ SQSharedState* sharedState;
+ void* pointer_58;
+ void* pointer_60;
+ int _top;
+ SQObject* _stack;
+ unsigned char gap_78[8];
+ SQObject* _vargvstack;
+ unsigned char gap_88[8];
+ SQObject temp_reg;
+ unsigned char gapA0[8];
+ void* pointer_A8;
+ unsigned char gap_B0[8];
+ SQObject _roottable_object;
+ SQObject _lasterror;
+ SQObject _errorHandler;
+ long long field_E8;
+ int traps;
+ unsigned char gap_F4[12];
+ int _nnativecalls;
+ int _suspended;
+ int _suspended_root;
+ int _unk;
+ int _suspended_target;
+ int trapAmount;
+ int _suspend_varargs;
+ int unknown_field_11C;
+ SQObject object_120;
+};
+
+/* 150 */
+struct SQStructInstance
+{
+ void* vftable;
+ __int32 uiRef;
+ BYTE gap_C[4];
+ __int64 unknown_10;
+ void* pointer_18;
+ __int64 unknown_20;
+ SQSharedState* _sharedState;
+ unsigned int size;
+ BYTE gap_34[4];
+ SQObject data[1]; // This struct is dynamically sized, so this size is unknown
+};
+
+/* 148 */
+struct SQSharedState
+{
+ unsigned char gap_0[72];
+ void* unknown;
+ unsigned char gap_50[16344];
+ SQObjectType _unknownTableType00;
+ long long _unknownTableValue00;
+ unsigned char gap_4038[16];
+ StringTable* _stringTable;
+ unsigned char gap_4050[32];
+ SQObjectType _unknownTableType0;
+ long long _unknownTableValue0;
+ SQObjectType _unknownObjectType1;
+ long long _unknownObjectValue1;
+ unsigned char gap_4090[8];
+ SQObjectType _unknownArrayType2;
+ long long _unknownArrayValue2;
+ SQObjectType _gobalsArrayType;
+ SQStructInstance* _globalsArray;
+ unsigned char gap_40B8[16];
+ SQObjectType _nativeClosuresType;
+ SQTable* _nativeClosures;
+ SQObjectType _typedConstantsType;
+ SQTable* _typedConstants;
+ SQObjectType _untypedConstantsType;
+ SQTable* _untypedConstants;
+ SQObjectType _globalsMaybeType;
+ SQTable* _globals;
+ SQObjectType _functionsType;
+ SQTable* _functions;
+ SQObjectType _structsType;
+ SQTable* _structs;
+ SQObjectType _typeDefsType;
+ SQTable* _typeDefs;
+ SQObjectType unknownTableType;
+ SQTable* unknownTable;
+ SQObjectType _squirrelFilesType;
+ SQTable* _squirrelFiles;
+ unsigned char gap_4158[80];
+ SQObjectType _nativeClosures2Type;
+ SQTable* _nativeClosures2;
+ SQObjectType _entityTypesMaybeType;
+ SQTable* _entityTypesMaybe;
+ SQObjectType unknownTable2Type;
+ SQTable* unknownTable2;
+ unsigned char gap_41D8[72];
+ SQObjectType _compilerKeywordsType;
+ SQTable* _compilerKeywords;
+ HSquirrelVM* _currentThreadMaybe;
+ unsigned char gap_4238[8];
+ SQObjectType unknownTable3Type;
+ SQTable* unknownTable3;
+ unsigned char gap_4250[16];
+ SQObjectType unknownThreadType;
+ SQTable* unknownThread;
+ SQObjectType _tableNativeFunctionsType;
+ SQTable* _tableNativeFunctions;
+ SQObjectType _unknownTableType4;
+ long long _unknownObjectValue4;
+ SQObjectType _unknownObjectType5;
+ long long _unknownObjectValue5;
+ SQObjectType _unknownObjectType6;
+ long long _unknownObjectValue6;
+ SQObjectType _unknownObjectType7;
+ long long _unknownObjectValue7;
+ SQObjectType _unknownObjectType8;
+ long long _unknownObjectValue8;
+ SQObjectType _unknownObjectType9;
+ long long _unknownObjectValue9;
+ SQObjectType _unknownObjectType10;
+ long long _unknownObjectValue10;
+ SQObjectType _unknownObjectType11;
+ long long _unknownObjectValue11;
+ SQObjectType _unknownObjectType12;
+ long long _unknownObjectValue12;
+ SQObjectType _unknownObjectType13;
+ long long _unknownObjectValue13;
+ SQObjectType _unknownObjectType14;
+ long long _unknownObjectValue14;
+ SQObjectType _unknownObjectType15;
+ long long _unknownObjectValue15;
+ unsigned char gap_4340[16];
+ void* printFunction;
+ unsigned char gap_4358[16];
+ void* logEntityFunction;
+ unsigned char gap_4370[40];
+ SQObjectType _waitStringType;
+ SQString* _waitStringValue;
+ SQObjectType _SpinOffAndWaitForStringType;
+ SQString* _SpinOffAndWaitForStringValue;
+ SQObjectType _SpinOffAndWaitForSoloStringType;
+ SQString* _SpinOffAndWaitForSoloStringValue;
+ SQObjectType _SpinOffStringType;
+ SQString* _SpinOffStringValue;
+ SQObjectType _SpinOffDelayedStringType;
+ SQString* _SpinOffDelayedStringValue;
+ unsigned char gap_43E8[8];
+ bool enableDebugInfo; // functionality stripped
+ unsigned char gap_43F1[23];
+};
+
+/* 165 */
+struct tableNode
+{
+ SQObject val;
+ SQObject key;
+ tableNode* next;
+};
+
+/* 136 */
+struct alignas(8) CallInfo
+{
+ long long ip;
+ SQObject* _literals;
+ SQObject obj10;
+ SQObject closure;
+ int _etraps[4];
+ int _root;
+ short _vargs_size;
+ short _vargs_base;
+ unsigned char gap[16];
+};
+
+/* 149 */
+struct StringTable
+{
+ unsigned char gap_0[12];
+ int _numofslots;
+ unsigned char gap_10[200];
+};
+
+/* 141 */
+struct alignas(8) SQStackInfos
+{
+ char* _name;
+ char* _sourceName;
+ int _line;
+};
+
+/* 151 */
+struct alignas(4) SQInstruction
+{
+ int op;
+ int arg1;
+ int output;
+ short arg2;
+ short arg3;
+};
+
+/* 154 */
+struct SQLexer
+{
+ unsigned char gap_0[112];
+};
+
+/* 153 */
+struct SQCompiler
+{
+ unsigned char gap_0[4];
+ int _token;
+ unsigned char gap_8[8];
+ SQObject object_10;
+ SQLexer lexer;
+ unsigned char gap_90[752];
+ HSquirrelVM* sqvm;
+ unsigned char gap_288[8];
+};
+
+/* 155 */
+struct CSquirrelVM
+{
+ unsigned char gap_0[8];
+ HSquirrelVM* sqvm;
+ unsigned char gap_10[44];
+ int loadEnumFromFileMaybe;
+ unsigned char gap_40[200];
+};
+
+struct SQUserData
+{
+ void* vftable;
+ int uiRef;
+ char gap_12[4];
+ long long unknown_10;
+ long long unknown_18;
+ long long unknown_20;
+ long long sharedState;
+ long long unknown_30;
+ int size;
+ char padding1[4];
+ releasehookType releaseHook;
+ long long typeId;
+ char data[1];
+};