aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmma Miler <emma.pi@protonmail.com>2022-11-21 00:38:42 +0100
committerGitHub <noreply@github.com>2022-11-20 23:38:42 +0000
commit9e469ac28b610ecb8bce3e6c279660fae78861cf (patch)
tree57274f3be03cb160e42af7ed939274a2a028dbf0
parent70b71ba3d3ad7121c6aabe55271542f55abe4008 (diff)
downloadNorthstarLauncher-9e469ac28b610ecb8bce3e6c279660fae78861cf.tar.gz
NorthstarLauncher-9e469ac28b610ecb8bce3e6c279660fae78861cf.zip
Squirrel bridge v3 (#310)
* Initial * Move squirrelmanager to virtual base class * Implement changes from code review * Formatting * Update squirrel.cpp * Formatting shit * Fix filters * Fix up Use new squirrel autobind syntax Move from `std::vector` to `std::queue` for message buffer Add `NSTestMessageBuffer` * Update squirrel.cpp * Update squirrel.h * Remove inline virtual final because this is stupid I probably had a bit of a brain fart when this was written * Moved to running ProcessMessages in-engine * Remove TestMessageBuffer * Formatting * Rename pushSQObject -> pushobject * Rename some stuff * Update squirrel.h * Formattting * Remove unneeded global access * Oops
-rw-r--r--NorthstarDLL/NorthstarDLL.vcxproj1
-rw-r--r--NorthstarDLL/NorthstarDLL.vcxproj.filters3
-rw-r--r--NorthstarDLL/clientchathooks.cpp35
-rw-r--r--NorthstarDLL/hoststate.cpp11
-rw-r--r--NorthstarDLL/scriptservertoclientstringcommand.cpp10
-rw-r--r--NorthstarDLL/serverchathooks.cpp20
-rw-r--r--NorthstarDLL/squirrel.cpp83
-rw-r--r--NorthstarDLL/squirrel.h354
-rw-r--r--NorthstarDLL/squirrelclasstypes.h239
-rw-r--r--NorthstarDLL/squirreldatatypes.h19
10 files changed, 595 insertions, 180 deletions
diff --git a/NorthstarDLL/NorthstarDLL.vcxproj b/NorthstarDLL/NorthstarDLL.vcxproj
index c1532157..98e3e182 100644
--- a/NorthstarDLL/NorthstarDLL.vcxproj
+++ b/NorthstarDLL/NorthstarDLL.vcxproj
@@ -123,6 +123,7 @@
<ClInclude Include="crashhandler.h" />
<ClInclude Include="keyvalues.h" />
<ClInclude Include="loghooks.h" />
+ <ClInclude Include="squirrelclasstypes.h" />
<ClInclude Include="squirreldatatypes.h" />
<ClInclude Include="limits.h" />
<ClInclude Include="maxplayers.h" />
diff --git a/NorthstarDLL/NorthstarDLL.vcxproj.filters b/NorthstarDLL/NorthstarDLL.vcxproj.filters
index 8ff5f461..7e13234e 100644
--- a/NorthstarDLL/NorthstarDLL.vcxproj.filters
+++ b/NorthstarDLL/NorthstarDLL.vcxproj.filters
@@ -1499,6 +1499,9 @@
</ClInclude>
<ClInclude Include="loghooks.h">
<Filter>Header Files\Console</Filter>
+ </ClInclude>
+ <ClInclude Include="squirrelclasstypes.h">
+ <Filter>Header Files\Squirrel</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
diff --git a/NorthstarDLL/clientchathooks.cpp b/NorthstarDLL/clientchathooks.cpp
index 1a25c897..847b5eb1 100644
--- a/NorthstarDLL/clientchathooks.cpp
+++ b/NorthstarDLL/clientchathooks.cpp
@@ -17,29 +17,22 @@ void, __fastcall, (void* self, const char* message, int inboxId, bool isTeam, bo
if (self != *CHudChat::allHuds)
return;
- if (g_pSquirrel<ScriptContext::CLIENT>->setupfunc("CHudChat_ProcessMessageStartThread") != SQRESULT_ERROR)
- {
- int senderId = inboxId & CUSTOM_MESSAGE_INDEX_MASK;
- bool isAnonymous = senderId == 0;
- bool isCustom = isAnonymous || (inboxId & CUSTOM_MESSAGE_INDEX_BIT);
-
- // Type is set to 0 for non-custom messages, custom messages have a type encoded as the first byte
- int type = 0;
- const char* payload = message;
- if (isCustom)
- {
- type = message[0];
- payload = message + 1;
- }
+ int senderId = inboxId & CUSTOM_MESSAGE_INDEX_MASK;
+ bool isAnonymous = senderId == 0;
+ bool isCustom = isAnonymous || (inboxId & CUSTOM_MESSAGE_INDEX_BIT);
- g_pSquirrel<ScriptContext::CLIENT>->pushinteger(g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM->sqvm, (int)senderId - 1);
- g_pSquirrel<ScriptContext::CLIENT>->pushstring(g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM->sqvm, payload);
- g_pSquirrel<ScriptContext::CLIENT>->pushbool(g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM->sqvm, isTeam);
- g_pSquirrel<ScriptContext::CLIENT>->pushbool(g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM->sqvm, isDead);
- g_pSquirrel<ScriptContext::CLIENT>->pushinteger(g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM->sqvm, type);
- g_pSquirrel<ScriptContext::CLIENT>->call(g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM->sqvm, 5);
+ // Type is set to 0 for non-custom messages, custom messages have a type encoded as the first byte
+ int type = 0;
+ const char* payload = message;
+ if (isCustom)
+ {
+ type = message[0];
+ payload = message + 1;
}
- else
+
+ SQRESULT result = g_pSquirrel<ScriptContext::CLIENT>->Call(
+ "CHudChat_ProcessMessageStartThread", static_cast<int>(senderId) - 1, payload, isTeam, isDead, type);
+ if (result == SQRESULT_ERROR)
for (CHudChat* hud = *CHudChat::allHuds; hud != NULL; hud = hud->next)
CHudChat__AddGameLine(hud, message, inboxId, isTeam, isDead);
}
diff --git a/NorthstarDLL/hoststate.cpp b/NorthstarDLL/hoststate.cpp
index c36af20c..24e4bf63 100644
--- a/NorthstarDLL/hoststate.cpp
+++ b/NorthstarDLL/hoststate.cpp
@@ -7,6 +7,7 @@
#include "tier0.h"
#include "r2engine.h"
#include "limits.h"
+#include "squirrel.h"
AUTOHOOK_INIT()
@@ -102,6 +103,16 @@ void, __fastcall, (CHostState* self, double flCurrentTime, float flFrameTime))
// update limits for frame
g_pServerLimits->RunFrame(flCurrentTime, flFrameTime);
}
+
+ // Run Squirrel message buffer
+ if (g_pSquirrel<ScriptContext::UI>->m_pSQVM != nullptr && g_pSquirrel<ScriptContext::UI>->m_pSQVM->sqvm != nullptr)
+ g_pSquirrel<ScriptContext::UI>->ProcessMessageBuffer();
+
+ if (g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM != nullptr && g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM->sqvm != nullptr)
+ g_pSquirrel<ScriptContext::CLIENT>->ProcessMessageBuffer();
+
+ if (g_pSquirrel<ScriptContext::SERVER>->m_pSQVM != nullptr && g_pSquirrel<ScriptContext::SERVER>->m_pSQVM->sqvm != nullptr)
+ g_pSquirrel<ScriptContext::SERVER>->ProcessMessageBuffer();
}
ON_DLL_LOAD_RELIESON("engine.dll", HostState, ConVar, (CModule module))
diff --git a/NorthstarDLL/scriptservertoclientstringcommand.cpp b/NorthstarDLL/scriptservertoclientstringcommand.cpp
index 5c116973..ac19c3af 100644
--- a/NorthstarDLL/scriptservertoclientstringcommand.cpp
+++ b/NorthstarDLL/scriptservertoclientstringcommand.cpp
@@ -5,14 +5,8 @@
void ConCommand_ns_script_servertoclientstringcommand(const CCommand& arg)
{
- if (g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM &&
- g_pSquirrel<ScriptContext::CLIENT>->setupfunc("NSClientCodeCallback_RecievedServerToClientStringCommand") != SQRESULT_ERROR)
- {
- g_pSquirrel<ScriptContext::CLIENT>->pushstring(g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM->sqvm, arg.ArgS());
- g_pSquirrel<ScriptContext::CLIENT>->call(
- g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM->sqvm,
- 1); // todo: doesn't throw or log errors from within this, probably not great behaviour
- }
+ if (g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM)
+ g_pSquirrel<ScriptContext::CLIENT>->Call("NSClientCodeCallback_RecievedServerToClientStringCommand", arg.ArgS());
}
ON_DLL_LOAD_CLIENT_RELIESON("client.dll", ScriptServerToClientStringCommand, ClientSquirrel, (CModule module))
diff --git a/NorthstarDLL/serverchathooks.cpp b/NorthstarDLL/serverchathooks.cpp
index d97f1ebf..e4e67e05 100644
--- a/NorthstarDLL/serverchathooks.cpp
+++ b/NorthstarDLL/serverchathooks.cpp
@@ -37,7 +37,7 @@ void(__fastcall* MessageWriteBool)(bool bValue);
bool bShouldCallSayTextHook = false;
// clang-format off
AUTOHOOK(_CServerGameDLL__OnReceivedSayTextMessage, server.dll + 0x1595C0,
-void, __fastcall, (CServerGameDLL* self, unsigned int nSenderPlayerIndex, const char* text, bool isTeam))
+void, __fastcall, (CServerGameDLL* self, unsigned int senderPlayerId, const char* text, bool isTeam))
// clang-format on
{
// MiniHook doesn't allow calling the base function outside of anywhere but the hook function.
@@ -45,23 +45,19 @@ void, __fastcall, (CServerGameDLL* self, unsigned int nSenderPlayerIndex, const
if (bShouldCallSayTextHook)
{
bShouldCallSayTextHook = false;
- _CServerGameDLL__OnReceivedSayTextMessage(self, nSenderPlayerIndex, text, isTeam);
+ _CServerGameDLL__OnReceivedSayTextMessage(self, senderPlayerId, text, isTeam);
return;
}
// check chat ratelimits
- if (!g_pServerLimits->CheckChatLimits(&R2::g_pClientArray[nSenderPlayerIndex - 1]))
+ if (!g_pServerLimits->CheckChatLimits(&R2::g_pClientArray[senderPlayerId - 1]))
return;
- if (g_pSquirrel<ScriptContext::SERVER>->setupfunc("CServerGameDLL_ProcessMessageStartThread") != SQRESULT_ERROR)
- {
- g_pSquirrel<ScriptContext::SERVER>->pushinteger(g_pSquirrel<ScriptContext::SERVER>->m_pSQVM->sqvm, (int)nSenderPlayerIndex - 1);
- g_pSquirrel<ScriptContext::SERVER>->pushstring(g_pSquirrel<ScriptContext::SERVER>->m_pSQVM->sqvm, text);
- g_pSquirrel<ScriptContext::SERVER>->pushbool(g_pSquirrel<ScriptContext::SERVER>->m_pSQVM->sqvm, isTeam);
- g_pSquirrel<ScriptContext::SERVER>->call(g_pSquirrel<ScriptContext::SERVER>->m_pSQVM->sqvm, 3);
- }
- else
- _CServerGameDLL__OnReceivedSayTextMessage(self, nSenderPlayerIndex, text, isTeam);
+ SQRESULT result = g_pSquirrel<ScriptContext::SERVER>->Call(
+ "CServerGameDLL_ProcessMessageStartThread", static_cast<int>(senderPlayerId) - 1, text, isTeam);
+
+ if (result == SQRESULT_ERROR)
+ _CServerGameDLL__OnReceivedSayTextMessage(self, senderPlayerId, text, isTeam);
}
void ChatSendMessage(unsigned int playerIndex, const char* text, bool isTeam)
diff --git a/NorthstarDLL/squirrel.cpp b/NorthstarDLL/squirrel.cpp
index c90b2d7f..673e2098 100644
--- a/NorthstarDLL/squirrel.cpp
+++ b/NorthstarDLL/squirrel.cpp
@@ -6,6 +6,8 @@
#include "r2engine.h"
#include "tier0.h"
+#include <any>
+
AUTOHOOK_INIT()
std::shared_ptr<ColoredLogger> getSquirrelLoggerByContext(ScriptContext context)
@@ -138,6 +140,25 @@ const char* SQTypeNameFromID(int type)
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);
+ case ScriptContext::SERVER:
+ g_pSquirrel<ScriptContext::SERVER>->messageBuffer->push(message);
+ case ScriptContext::UI:
+ g_pSquirrel<ScriptContext::UI>->messageBuffer->push(message);
+ }
+}
+
// needed to define implementations for squirrelmanager outside of squirrel.h without compiler errors
template class SquirrelManager<ScriptContext::SERVER>;
template class SquirrelManager<ScriptContext::CLIENT>;
@@ -156,6 +177,7 @@ template <ScriptContext context> void SquirrelManager<context>::VMCreated(CSquir
for (auto& pair : g_pModManager->m_DependencyConstants)
{
bool bWasFound = false;
+
for (Mod& dependency : g_pModManager->m_LoadedMods)
{
if (!dependency.m_bEnabled)
@@ -170,6 +192,7 @@ template <ScriptContext context> void SquirrelManager<context>::VMCreated(CSquir
defconst(m_pSQVM, pair.first.c_str(), bWasFound);
}
+ g_pSquirrel<context>->messageBuffer = new SquirrelMessageBuffer();
}
template <ScriptContext context> void SquirrelManager<context>::VMDestroyed()
@@ -196,7 +219,7 @@ template <ScriptContext context> void SquirrelManager<context>::ExecuteCode(cons
if (compileResult != SQRESULT_ERROR)
{
pushroottable(m_pSQVM->sqvm);
- SQRESULT callResult = call(m_pSQVM->sqvm, 0);
+ SQRESULT callResult = _call(m_pSQVM->sqvm, 0);
spdlog::info("sq_call returned {}", PrintSQRESULT.at(callResult));
}
}
@@ -304,7 +327,10 @@ template <ScriptContext context> void __fastcall DestroyVMHook(void* a1, HSquirr
g_pSquirrel<ScriptContext::UI>->VMDestroyed();
}
else
+ {
+ g_pSquirrel<context>->m_pSQVM = nullptr; // Fixes a race-like bug
DestroyVM<context>(a1, sqvm);
+ }
spdlog::info("DestroyVM {} {}", GetContextName(realContext), (void*)sqvm);
}
@@ -482,6 +508,44 @@ template <ScriptContext context> void StubUnsafeSQFuncs()
}
}
+template <ScriptContext context> void SquirrelManager<context>::ProcessMessageBuffer()
+{
+ auto maybeMessage = messageBuffer->pop();
+ if (!maybeMessage)
+ {
+ return;
+ }
+
+ 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
+ {
+ spdlog::error(
+ "ProcessMessageBuffer was unable to find function with name '{}' on {}. Is it global?",
+ message.functionName,
+ GetContextName(context));
+ return;
+ }
+ 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());
+}
+
ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrel, ConCommand, (CModule module))
{
AUTOHOOK_DISPATCH_MODULE(client.dll)
@@ -516,6 +580,7 @@ ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrel, ConCommand, (CModule module))
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;
@@ -523,6 +588,7 @@ ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrel, ConCommand, (CModule module))
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>();
@@ -533,8 +599,6 @@ ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrel, ConCommand, (CModule module))
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::CLIENT>->__sq_getthisentity = module.Offset(0x12F80).As<sq_getthisentityType>();
- g_pSquirrel<ScriptContext::CLIENT>->__sq_getobject = module.Offset(0x6160).As<sq_getobjectType>();
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;
@@ -557,6 +621,11 @@ ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrel, ConCommand, (CModule module))
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;
+
MAKEHOOK(
module.Offset(0x108E0),
&RegisterSquirrelFunctionHook<ScriptContext::CLIENT>,
@@ -585,6 +654,9 @@ ON_DLL_LOAD_RELIESON("client.dll", ClientSquirrel, ConCommand, (CModule module))
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))
@@ -611,6 +683,8 @@ ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrel, ConCommand, (CModule module))
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>();
@@ -621,6 +695,7 @@ ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrel, ConCommand, (CModule module))
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>();
@@ -631,6 +706,8 @@ ON_DLL_LOAD_RELIESON("server.dll", ServerSquirrel, ConCommand, (CModule module))
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>();
MAKEHOOK(
module.Offset(0x1DD10),
diff --git a/NorthstarDLL/squirrel.h b/NorthstarDLL/squirrel.h
index 6142b157..626d4f12 100644
--- a/NorthstarDLL/squirrel.h
+++ b/NorthstarDLL/squirrel.h
@@ -1,6 +1,6 @@
#pragma once
-#include "squirreldatatypes.h"
+#include "squirrelclasstypes.h"
#include "squirrelautobind.h"
#include "vector.h"
@@ -11,78 +11,6 @@ typedef unsigned long SQUnsignedInteger;
typedef char SQChar;
typedef SQUnsignedInteger SQBool;
-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_ERROR, "SQRESULT_ERROR"}, {SQRESULT_NULL, "SQRESULT_NULL"}, {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
-{
- SERVER,
- CLIENT,
- UI,
-};
-
static constexpr int operator&(ScriptContext first, ScriptContext second)
{
return first == second;
@@ -108,55 +36,13 @@ const char* GetContextName_Short(ScriptContext context);
eSQReturnType SQReturnTypeFromString(const char* pReturnType);
const char* SQTypeNameFromID(const int iTypeId);
-// 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);
-
-// 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);
-
-// 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)();
-
-template <ScriptContext context> class SquirrelManager
+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
{
- private:
+ protected:
std::vector<SQFuncRegistration*> m_funcRegistrations;
public:
@@ -189,6 +75,7 @@ template <ScriptContext context> class SquirrelManager
sq_pushboolType __sq_pushbool;
sq_pushassetType __sq_pushasset;
sq_pushvectorType __sq_pushvector;
+ sq_pushobjectType __sq_pushobject;
sq_getstringType __sq_getstring;
sq_getintegerType __sq_getinteger;
@@ -203,20 +90,12 @@ template <ScriptContext context> class SquirrelManager
sq_createuserdataType __sq_createuserdata;
sq_setuserdatatypeidType __sq_setuserdatatypeid;
+ sq_getfunctionType __sq_getfunction;
sq_getentityfrominstanceType __sq_getentityfrominstance;
sq_GetEntityConstantType __sq_GetEntityConstant_CBaseEntity;
-#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);
+#pragma endregion
#pragma region SQVM func wrappers
inline void defconst(CSquirrelVM* sqvm, const SQChar* pName, int nValue)
@@ -230,7 +109,7 @@ template <ScriptContext context> class SquirrelManager
return __sq_compilebuffer(m_pSQVM->sqvm, bufferState, bufferName, -1, bShouldThrowError);
}
- inline SQRESULT call(HSquirrelVM* sqvm, const SQInteger args)
+ inline SQRESULT _call(HSquirrelVM* sqvm, const SQInteger args)
{
return __sq_call(sqvm, args + 1, false, false);
}
@@ -295,6 +174,11 @@ template <ScriptContext context> class SquirrelManager
__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);
@@ -326,6 +210,11 @@ template <ScriptContext context> class SquirrelManager
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);
@@ -364,4 +253,205 @@ template <ScriptContext context> class SquirrelManager
#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
+ {
+ 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
+ {
+ 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;
+
+/*
+ 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/squirrelclasstypes.h b/NorthstarDLL/squirrelclasstypes.h
new file mode 100644
index 00000000..e26bc8d0
--- /dev/null
+++ b/NorthstarDLL/squirrelclasstypes.h
@@ -0,0 +1,239 @@
+#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
+{
+ 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);
+
+// 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/squirreldatatypes.h b/NorthstarDLL/squirreldatatypes.h
index 818ce2a4..e9f88d08 100644
--- a/NorthstarDLL/squirreldatatypes.h
+++ b/NorthstarDLL/squirreldatatypes.h
@@ -22,6 +22,13 @@ 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
{
@@ -83,6 +90,7 @@ union SQObjectValue
float asFloat;
int asInteger;
SQUserData* asUserdata;
+ SQStructInstance* asStructInstance;
};
/* 160 */
@@ -279,12 +287,15 @@ struct alignas(8) HSquirrelVM
struct SQStructInstance
{
void* vftable;
- unsigned char gap_8[16];
+ __int32 uiRef;
+ BYTE gap_C[4];
+ __int64 unknown_10;
void* pointer_18;
- unsigned char gap_20[8];
+ __int64 unknown_20;
SQSharedState* _sharedState;
- unsigned char gap[8];
- SQObject data[20];
+ unsigned int size;
+ BYTE gap_34[4];
+ SQObject data[1]; // This struct is dynamically sized, so this size is unknown
};
/* 148 */