aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2021-07-21 00:51:32 +0100
committerBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2021-07-21 00:51:32 +0100
commit93fe64e04ed766727634e5b5f6906f6461a70711 (patch)
tree467f58533ddf4c139f896166894bde8213fd34c8
parent958d03d2817e312c8eb70234f1c65e4bcbded716 (diff)
downloadNorthstarLauncher-93fe64e04ed766727634e5b5f6906f6461a70711.tar.gz
NorthstarLauncher-93fe64e04ed766727634e5b5f6906f6461a70711.zip
add eval commands and script compile error hook
-rw-r--r--NorthstarDedicatedTest/modmanager.cpp54
-rw-r--r--NorthstarDedicatedTest/modmanager.h5
-rw-r--r--NorthstarDedicatedTest/sourceconsole.cpp1
-rw-r--r--NorthstarDedicatedTest/squirrel.cpp130
-rw-r--r--NorthstarDedicatedTest/squirrel.h83
5 files changed, 237 insertions, 36 deletions
diff --git a/NorthstarDedicatedTest/modmanager.cpp b/NorthstarDedicatedTest/modmanager.cpp
index 72998347..a45d2b2a 100644
--- a/NorthstarDedicatedTest/modmanager.cpp
+++ b/NorthstarDedicatedTest/modmanager.cpp
@@ -106,20 +106,44 @@ Mod::Mod(fs::path modDir, char* jsonBuf)
script->Path = scriptObj["Path"].GetString();
script->RsonRunOn = scriptObj["RunOn"].GetString();
- // callbacks
- for (auto iterator = scriptObj.MemberBegin(); iterator != scriptObj.MemberEnd(); iterator++)
+ if (scriptObj.HasMember("ServerCallback") && scriptObj["ServerCallback"].IsObject())
{
- if (!iterator->name.IsString() || !iterator->value.IsObject())
- continue;
+ ModScriptCallback* callback = new ModScriptCallback;
+ callback->Context = SERVER;
+
+ if (scriptObj["ServerCallback"].HasMember("Before") && scriptObj["ServerCallback"]["Before"].IsString())
+ callback->BeforeCallback = scriptObj["ServerCallback"]["Before"].GetString();
+ if (scriptObj["ServerCallback"].HasMember("After") && scriptObj["ServerCallback"]["After"].IsString())
+ callback->AfterCallback = scriptObj["ServerCallback"]["After"].GetString();
+
+ script->Callbacks.push_back(callback);
+ }
+
+ if (scriptObj.HasMember("ClientCallback") && scriptObj["ClientCallback"].IsObject())
+ {
ModScriptCallback* callback = new ModScriptCallback;
- callback->HookedCodeCallback = iterator->name.GetString();
+ callback->Context = CLIENT;
- if (iterator->value.HasMember("Before") && iterator->value["Before"].IsString())
- callback->BeforeCallback = iterator->value["Before"].GetString();
+ if (scriptObj["ClientCallback"].HasMember("Before") && scriptObj["ClientCallback"]["Before"].IsString())
+ callback->BeforeCallback = scriptObj["ClientCallback"]["Before"].GetString();
- if (iterator->value.HasMember("After") && iterator->value["After"].IsString())
- callback->AfterCallback = iterator->value["After"].GetString();
+ if (scriptObj["ClientCallback"].HasMember("After") && scriptObj["ClientCallback"]["After"].IsString())
+ callback->AfterCallback = scriptObj["ClientCallback"]["After"].GetString();
+
+ script->Callbacks.push_back(callback);
+ }
+
+ if (scriptObj.HasMember("UICallback") && scriptObj["UICallback"].IsObject())
+ {
+ ModScriptCallback* callback = new ModScriptCallback;
+ callback->Context = UI;
+
+ if (scriptObj["UICallback"].HasMember("Before") && scriptObj["UICallback"]["Before"].IsString())
+ callback->BeforeCallback = scriptObj["UICallback"]["Before"].GetString();
+
+ if (scriptObj["UICallback"].HasMember("After") && scriptObj["UICallback"]["After"].IsString())
+ callback->AfterCallback = scriptObj["UICallback"]["After"].GetString();
script->Callbacks.push_back(callback);
}
@@ -128,6 +152,12 @@ Mod::Mod(fs::path modDir, char* jsonBuf)
}
}
+ // vpk stuff
+ if (fs::exists(modDir / "vpk"))
+ for (fs::directory_entry file : fs::directory_iterator(modDir / "vpk"))
+ if (fs::is_regular_file(file) && file.path().extension() == "vpk")
+ Vpks.push_back(file.path().string());
+
wasReadSuccessfully = true;
}
@@ -138,6 +168,8 @@ ModManager::ModManager()
void ModManager::LoadMods()
{
+ // this needs better support for reloads
+
std::vector<fs::path> modDirs;
// get mod directories
@@ -179,8 +211,10 @@ void ModManager::LoadMods()
for (Mod* mod : loadedMods)
{
+ // for reloads, this is sorta barebones, when we have a good findconvar method, we could probably reset flags and stuff on preexisting convars
+ // potentially it might also be good to unregister convars when they get removed on a reload, but unsure if necessary
for (ModConVar* convar : mod->ConVars)
- if (g_CustomConvars.find(convar->Name) == g_CustomConvars.end()) // make sure convar isn't registered yet
+ if (g_CustomConvars.find(convar->Name) == g_CustomConvars.end()) // make sure convar isn't registered yet, unsure if necessary but idk what behaviour is for defining same convar multiple times
RegisterConVar(convar->Name.c_str(), convar->DefaultValue.c_str(), convar->Flags, convar->HelpString.c_str());
}
diff --git a/NorthstarDedicatedTest/modmanager.h b/NorthstarDedicatedTest/modmanager.h
index f2501bed..c5ddb09f 100644
--- a/NorthstarDedicatedTest/modmanager.h
+++ b/NorthstarDedicatedTest/modmanager.h
@@ -19,7 +19,10 @@ public:
class ModScriptCallback
{
public:
- std::string HookedCodeCallback;
+ // would've liked to make it possible to hook arbitrary codecallbacks, but couldn't find a function that calls some ui ones
+ //std::string HookedCodeCallback;
+
+ Context Context;
// called before the codecallback is executed
std::string BeforeCallback;
diff --git a/NorthstarDedicatedTest/sourceconsole.cpp b/NorthstarDedicatedTest/sourceconsole.cpp
index 88d038df..b6e69568 100644
--- a/NorthstarDedicatedTest/sourceconsole.cpp
+++ b/NorthstarDedicatedTest/sourceconsole.cpp
@@ -51,6 +51,7 @@ void InitialiseSourceConsole(HMODULE baseAddress)
{
g_SourceGameConsole = new SourceInterface<CGameConsole>("client.dll", "GameConsole004");
RegisterConCommand("toggleconsole", ConCommand_toggleconsole, "toggles the console", FCVAR_NONE);
+ RegisterConVar("ns_test", "yeah", 0, "fuck");
}
diff --git a/NorthstarDedicatedTest/squirrel.cpp b/NorthstarDedicatedTest/squirrel.cpp
index 90d16e89..27b997ab 100644
--- a/NorthstarDedicatedTest/squirrel.cpp
+++ b/NorthstarDedicatedTest/squirrel.cpp
@@ -3,6 +3,7 @@
#include "hooks.h"
#include "hookutils.h"
#include "sigscanning.h"
+#include "concommand.h"
#include <iostream>
// hook forward declarations
@@ -12,10 +13,31 @@ SQPrintType UISQPrint;
SQPrintType ServerSQPrint;
template<Context context> SQInteger SQPrintHook(void* sqvm, char* fmt, ...);
-typedef void* (*CreateNewVMType)(void* a1, Context contextArg);
+typedef void*(*CreateNewVMType)(void* a1, Context contextArg);
CreateNewVMType ClientCreateNewVM; // only need a client one since ui doesn't have its own func for this
CreateNewVMType ServerCreateNewVM;
-void* CreateNewVMHook(void* a1, Context contextArg);
+template<Context context> void* CreateNewVMHook(void* a1, Context contextArg);
+
+typedef void(*DestroyVMType)(void* a1, void* sqvm);
+DestroyVMType ClientDestroyVM; // only need a client one since ui doesn't have its own func for this
+DestroyVMType ServerDestroyVM;
+template<Context context> void DestroyVMHook(void* a1, void* sqvm);
+
+typedef void (*SQCompileErrorType)(void* sqvm, const char* error, const char* file, int line, int column);
+SQCompileErrorType ClientSQCompileError; // only need a client one since ui doesn't have its own func for this
+SQCompileErrorType ServerSQCompileError;
+template<Context context> void SQCompileErrorHook(void* sqvm, const char* error, const char* file, int line, int column);
+
+sq_compilebufferType ClientSq_compilebuffer;
+sq_compilebufferType ServerSq_compilebuffer;
+
+sq_pushroottableType ClientSq_pushroottable;
+sq_pushroottableType ServerSq_pushroottable;
+
+sq_callType ClientSq_call;
+sq_callType ServerSq_call;
+
+template<Context context> void ExecuteCodeCommand(const CCommand& args);
// inits
SquirrelManager<CLIENT>* g_ClientSquirrelManager;
@@ -29,13 +51,21 @@ void InitialiseClientSquirrel(HMODULE baseAddress)
// client inits
g_ClientSquirrelManager = new SquirrelManager<CLIENT>();
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x12B00, &SQPrintHook<CLIENT>, reinterpret_cast<LPVOID*>(&ClientSQPrint)); // client print function
+ RegisterConCommand("script_client", ExecuteCodeCommand<CLIENT>, "Executes script code in the client vm", FCVAR_CLIENTDLL);
// ui inits
g_UISquirrelManager = new SquirrelManager<UI>();
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x12BA0, &SQPrintHook<UI>, reinterpret_cast<LPVOID*>(&UISQPrint)); // ui print function
+ RegisterConCommand("script_ui", ExecuteCodeCommand<UI>, "Executes script code in the client vm", FCVAR_CLIENTDLL);
+
+ // inits for both client and ui, since they share some functions
+ ClientSq_compilebuffer = (sq_compilebufferType)((char*)baseAddress + 0x3110);
+ ClientSq_pushroottable = (sq_pushroottableType)((char*)baseAddress + 0x5860);
+ ClientSq_call = (sq_callType)((char*)baseAddress + 0x8650);
- // hooks for both client and ui, since they share some functions
- ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x26130, &CreateNewVMHook, reinterpret_cast<LPVOID*>(&ClientCreateNewVM)); // client createnewvm function
+ ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x26130, &CreateNewVMHook<CLIENT>, reinterpret_cast<LPVOID*>(&ClientCreateNewVM)); // client createnewvm function
+ ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x26E70, &DestroyVMHook<CLIENT>, reinterpret_cast<LPVOID*>(&ClientDestroyVM)); // client destroyvm function
+ ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x79A50, &SQCompileErrorHook<CLIENT>, reinterpret_cast<LPVOID*>(&ClientSQCompileError)); // client compileerror function
}
void InitialiseServerSquirrel(HMODULE baseAddress)
@@ -43,8 +73,17 @@ void InitialiseServerSquirrel(HMODULE baseAddress)
g_ServerSquirrelManager = new SquirrelManager<SERVER>();
HookEnabler hook;
+
+ ServerSq_compilebuffer = (sq_compilebufferType)((char*)baseAddress + 0x3110);
+ ServerSq_pushroottable = (sq_pushroottableType)((char*)baseAddress + 0x5840);
+ ServerSq_call = (sq_callType)((char*)baseAddress + 0x8620);
+
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x1FE90, &SQPrintHook<SERVER>, reinterpret_cast<LPVOID*>(&ServerSQPrint)); // server print function
- ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x260E0, &CreateNewVMHook, reinterpret_cast<LPVOID*>(&ServerCreateNewVM)); // server createnewvm function
+ ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x260E0, &CreateNewVMHook<SERVER>, reinterpret_cast<LPVOID*>(&ServerCreateNewVM)); // server createnewvm function
+ ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x26E20, &DestroyVMHook<SERVER>, reinterpret_cast<LPVOID*>(&ServerDestroyVM)); // server destroyvm function
+ ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x799E0, &SQCompileErrorHook<SERVER>, reinterpret_cast<LPVOID*>(&ServerSQCompileError)); // server compileerror function
+
+ RegisterConCommand("script", ExecuteCodeCommand<SERVER>, "Executes script code in the server vm", FCVAR_GAMEDLL);
}
// hooks
@@ -59,7 +98,7 @@ template<Context context> SQInteger SQPrintHook(void* sqvm, char* fmt, ...)
if (charsWritten > 0)
{
if (buf[charsWritten - 1] == '\n')
- buf[charsWritten - 1] == '\0';
+ buf[charsWritten - 1] = '\0';
spdlog::info("[{} SCRIPT] {}", GetContextName(context), buf);
}
@@ -68,35 +107,78 @@ template<Context context> SQInteger SQPrintHook(void* sqvm, char* fmt, ...)
return 0;
}
-void* CreateNewVMHook(void* a1, Context context)
-{
- spdlog::info("CreateNewVM {}", GetContextName(context));
-
+template<Context context> void* CreateNewVMHook(void* a1, Context realContext)
+{
+ void* sqvm;
+
if (context == CLIENT)
{
- void* sqvm = ClientCreateNewVM(a1, context);
- g_ClientSquirrelManager->sqvm = sqvm;
+ sqvm = ClientCreateNewVM(a1, realContext);
- return sqvm;
+ if (realContext == UI)
+ g_UISquirrelManager->sqvm = sqvm;
+ else
+ g_ClientSquirrelManager->sqvm = sqvm;
}
- else if (context == UI)
+ else if (context == SERVER)
{
- void* sqvm = ClientCreateNewVM(a1, context);
- g_UISquirrelManager->sqvm = sqvm;
+ sqvm = ServerCreateNewVM(a1, context);
+ g_ServerSquirrelManager->sqvm = sqvm;
+ }
+
+ spdlog::info("CreateNewVM {} {}", GetContextName(context), sqvm);
+ return sqvm;
+}
+
+template<Context context> void DestroyVMHook(void* a1, void* sqvm)
+{
+ Context realContext = context; // ui and client use the same function so we use this for prints
- return sqvm;
+ if (context == CLIENT)
+ {
+ if (g_ClientSquirrelManager->sqvm == sqvm)
+ g_ClientSquirrelManager->sqvm = nullptr;
+ else if (g_UISquirrelManager->sqvm == sqvm)
+ {
+ g_UISquirrelManager->sqvm = nullptr;
+ realContext == UI;
+ }
+
+ ClientDestroyVM(a1, sqvm);
}
else if (context == SERVER)
{
- void* sqvm = ServerCreateNewVM(a1, context);
- g_ServerSquirrelManager->sqvm = sqvm;
-
- return sqvm;
+ g_ServerSquirrelManager->sqvm = nullptr;
+ ServerDestroyVM(a1, sqvm);
}
+
+ spdlog::info("DestroyVM {} {}", GetContextName(realContext), sqvm);
}
-// manager
-template<Context context> SquirrelManager<context>::SquirrelManager() : sqvm(nullptr)
+template<Context context> void SQCompileErrorHook(void* sqvm, const char* error, const char* file, int line, int column)
{
-
+ // note: i think vanilla might actually show the script line that errored, might be nice to implement that if it's a thing
+ // look into client.dll+79540 for way better errors too
+
+ Context realContext = context;
+ if (context == CLIENT && sqvm == g_UISquirrelManager->sqvm)
+ realContext = UI;
+
+ spdlog::error("{} SCRIPT COMPILE ERROR {}", GetContextName(realContext), error);
+ spdlog::error("{} line [{}] column [{}]", file, line, column);
+
+ // dont call the original since it kills game
+ // in the future it'd be nice to do an actual error with UICodeCallback_ErrorDialog here, but only if we're compiling level scripts
+ // compilestring and stuff shouldn't tho
+ // though, that also has potential to be REALLY bad if we're compiling ui scripts lol
+}
+
+template<Context context> void ExecuteCodeCommand(const CCommand& args)
+{
+ if (context == CLIENT)
+ g_ClientSquirrelManager->ExecuteCode(args.ArgS());
+ else if (context == UI)
+ g_UISquirrelManager->ExecuteCode(args.ArgS());
+ else if (context == SERVER)
+ g_ServerSquirrelManager->ExecuteCode(args.ArgS());
} \ No newline at end of file
diff --git a/NorthstarDedicatedTest/squirrel.h b/NorthstarDedicatedTest/squirrel.h
index c682fb60..3c8f9fed 100644
--- a/NorthstarDedicatedTest/squirrel.h
+++ b/NorthstarDedicatedTest/squirrel.h
@@ -12,13 +12,94 @@ typedef char SQChar;
typedef SQUnsignedInteger SQBool;
typedef SQInteger SQRESULT;
+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();
+ }
+};
+
+typedef SQRESULT(*sq_compilebufferType)(void* sqvm, CompileBufferState* compileBuffer, const char* file, int a1, int a2);
+extern sq_compilebufferType ClientSq_compilebuffer;
+extern sq_compilebufferType ServerSq_compilebuffer;
+
+typedef void(*sq_pushroottableType)(void* sqvm);
+extern sq_pushroottableType ClientSq_pushroottable;
+extern sq_pushroottableType ServerSq_pushroottable;
+
+typedef SQRESULT(*sq_callType)(void* sqvm, SQInteger s1, SQBool a2, SQBool a3);
+extern sq_callType ClientSq_call;
+extern sq_callType ServerSq_call;
+
+//template<Context context> void ExecuteSQCode(SquirrelManager<context> sqManager, const char* code); // need this because we can't do template class functions in the .cpp file
+
+typedef SQInteger(*SQFunction)(void* sqvm);
+
template<Context context> class SquirrelManager
{
+private:
+ //std::vector<
+
public:
void* sqvm;
public:
- SquirrelManager();
+ SquirrelManager() : sqvm(nullptr)
+ {
+ }
+
+ void ExecuteCode(const char* code)
+ {
+ // ttf2sdk checks ThreadIsInMainThread here, might be good to do that? doesn't seem like an issue rn tho
+
+ if (!sqvm)
+ {
+ spdlog::error("Cannot execute code, {} squirrel vm is not initialised", GetContextName(context));
+ return;
+ }
+
+ void* sqvm2 = *((void**)((char*)sqvm + 8)); // honestly not 100% sure on what this is, but it seems to be what this function is supposed to take
+ // potentially move to a property later if it's used alot
+
+ spdlog::info("Executing {} script code {} ", GetContextName(context), code);
+
+ std::string strCode(code);
+ CompileBufferState bufferState = CompileBufferState(strCode);
+
+ SQRESULT compileResult;
+ if (context == CLIENT || context == UI)
+ compileResult = ClientSq_compilebuffer(sqvm2, &bufferState, "console", -1, context);
+ else if (context == SERVER)
+ compileResult = ServerSq_compilebuffer(sqvm2, &bufferState, "console", -1, context);
+
+ spdlog::info("sq_compilebuffer returned {}", compileResult);
+ if (compileResult >= 0)
+ {
+ if (context == CLIENT || context == UI)
+ {
+ ClientSq_pushroottable(sqvm2);
+ SQRESULT callResult = ClientSq_call(sqvm2, 1, false, false);
+ spdlog::info("sq_call returned {}", callResult);
+ }
+ else if (context == SERVER)
+ {
+ ServerSq_pushroottable(sqvm2);
+ SQRESULT callResult = ServerSq_call(sqvm2, 1, false, false);
+ spdlog::info("sq_call returned {}", callResult);
+ }
+ }
+ }
+ void AddFuncRegistration(std::string returnType, std::string name, std::string argTypes, std::string helpText, SQFunction func)
+ {
+
+ }
};
extern SquirrelManager<CLIENT>* g_ClientSquirrelManager;