Skip to content
This repository was archived by the owner on Jan 1, 2025. It is now read-only.

Commit 99a8130

Browse files
authored
Python hooks (#4)
* No waiting * Hooks! * Hooking works
1 parent 5eef321 commit 99a8130

16 files changed

+384
-9109
lines changed

Python/hooktest.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import bl2sdk
2+
3+
def test_script_hook(caller, stack, result, function):
4+
print("{} {} {} {}".format(repr(caller), repr(stack), repr(result), repr(function)))
5+
6+
def print_key_press_hook(caller, function, parms, result):
7+
ControllerId = parms.popInt()
8+
Key = parms.popFName()
9+
EventType = parms.popByte()
10+
if EventType == 0:
11+
name = Key.GetName()
12+
print(name)
13+
14+
bl2sdk.RegisterEngineHook("Function WillowGame.WillowGameViewportClient.InputKey", "PrintKeyPress", print_key_press_hook)
15+
16+
bl2sdk.RegisterScriptHook("Function Engine.Console.ShippingConsoleCommand", "CheckCommand", test_script_hook)

bl2-sdk/BL2-SDK.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ namespace BL2SDK
340340
}
341341

342342
// This function is used to ensure that everything gets called in the game thread once the game itself has loaded
343-
bool GameReady(UObject* caller, UFunction* function, void* parms, void* result)
343+
bool GameReady(UObject* caller, FFrame& stack, void* const result, UFunction* function)
344344
{
345345
Logging::LogF("[GameReady] Thread: %i\n", GetCurrentThreadId());
346346

@@ -362,7 +362,7 @@ namespace BL2SDK
362362
*/
363363

364364
GameHooks::UnrealScriptHookManager->RemoveStaticHook(function, "StartupSDK");
365-
GameHooks::EngineHookManager->Register("Function WillowGame.WillowGameViewportClient.PostRender", "GetCanvas", &getCanvasPostRender);
365+
GameHooks::EngineHookManager->Register("Function WillowGame.WillowGameViewportClient.PostRender", "GetCanvas", getCanvasPostRender);
366366
//GameHooks::UnrealScriptHookManager->Register("Function GearboxFramework.LeviathanService.OnSparkInitialized", "CheckSpark", &SparkReady);
367367

368368
return true;
@@ -378,7 +378,7 @@ namespace BL2SDK
378378
LogAllProcessEventCalls(false);
379379
LogAllUnrealScriptCalls(false);
380380

381-
GameHooks::UnrealScriptHookManager->Register("Function Engine.Console.Initialized", "StartupSDK", &GameReady);
381+
GameHooks::UnrealScriptHookManager->Register("Function Engine.Console.Initialized", "StartupSDK", GameReady);
382382
//GameHooks::UnrealScriptHookManager->Register("Function Engine.Interaction.NotifyGameSessionEnded", "ExitGame", &cleanup);
383383
}
384384

bl2-sdk/CHookManager.cpp renamed to bl2-sdk/CEngineHookManager.cpp

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#pragma once
22
#include "stdafx.h"
3-
#include "CHookManager.h"
3+
#include "CEngineHookManager.h"
44

5-
void CHookManager::AddVirtualHook(const std::string& funcName, const tFuncNameHookPair& hookPair)
5+
void CEngineHookManager::AddVirtualHook(const std::string& funcName, const tFuncNameHookPair& hookPair)
66
{
77
tiVirtualHooks iHooks = VirtualHooks.find(funcName);
88
if (iHooks != VirtualHooks.end())
@@ -18,10 +18,10 @@ void CHookManager::AddVirtualHook(const std::string& funcName, const tFuncNameHo
1818
VirtualHooks.emplace(funcName, newMap);
1919
}
2020

21-
Logging::LogF("[CHookManager] (%s) Hook \"%s\" added as virtual hook for \"%s\"\n", this->DebugName.c_str(), hookPair.first.c_str(), funcName.c_str());
21+
Logging::LogF("[CEngineHookManager] (%s) Hook \"%s\" added as virtual hook for \"%s\"\n", this->DebugName.c_str(), hookPair.first.c_str(), funcName.c_str());
2222
}
2323

24-
void CHookManager::AddStaticHook(UFunction* function, const tFuncNameHookPair& hookPair)
24+
void CEngineHookManager::AddStaticHook(UFunction* function, const tFuncNameHookPair& hookPair)
2525
{
2626
tiStaticHooks iHooks = StaticHooks.find(function);
2727
if (iHooks != StaticHooks.end())
@@ -37,27 +37,27 @@ void CHookManager::AddStaticHook(UFunction* function, const tFuncNameHookPair& h
3737
StaticHooks.emplace(function, newMap);
3838
}
3939

40-
Logging::LogF("[CHookManager] (%s) Hook \"%s\" added as static hook for \"%s\"\n", this->DebugName.c_str(), hookPair.first.c_str(), function->GetFullName().c_str());
40+
Logging::LogF("[CEngineHookManager] (%s) Hook \"%s\" added as static hook for \"%s\"\n", this->DebugName.c_str(), hookPair.first.c_str(), function->GetFullName().c_str());
4141
}
4242

43-
bool CHookManager::RemoveFromTable(tHookMap& hookTable, const std::string& funcName, const std::string& hookName)
43+
bool CEngineHookManager::RemoveFromTable(tHookMap& hookTable, const std::string& funcName, const std::string& hookName)
4444
{
4545
// Remove it and ensure that it actually got removed
4646
int removed = hookTable.erase(hookName);
4747

4848
if (removed == 0)
4949
{
50-
Logging::LogF("[CHookManager] (%s) Failed to remove hook \"%s\" for function \"%s\"\n", this->DebugName.c_str(), hookName.c_str(), funcName.c_str());
50+
Logging::LogF("[CEngineHookManager] (%s) Failed to remove hook \"%s\" for function \"%s\"\n", this->DebugName.c_str(), hookName.c_str(), funcName.c_str());
5151
return false;
5252
}
5353
else
5454
{
55-
Logging::LogF("[CHookManager] (%s) Hook \"%s\" removed for function \"%s\" successfully\n", this->DebugName.c_str(), hookName.c_str(), funcName.c_str());
55+
Logging::LogF("[CEngineHookManager] (%s) Hook \"%s\" removed for function \"%s\" successfully\n", this->DebugName.c_str(), hookName.c_str(), funcName.c_str());
5656
return true;
5757
}
5858
}
5959

60-
void CHookManager::Register(const std::string& funcName, const std::string& hookName, void* funcHook)
60+
void CEngineHookManager::Register(const std::string& funcName, const std::string& hookName, std::function<bool(UObject*, UFunction*, void*, void*)> funcHook)
6161
{
6262
char funcNameChar[255];
6363
strcpy(funcNameChar, funcName.c_str());
@@ -79,7 +79,7 @@ void CHookManager::Register(const std::string& funcName, const std::string& hook
7979
}
8080
}
8181

82-
bool CHookManager::Remove(const std::string& funcName, const std::string& hookName)
82+
bool CEngineHookManager::Remove(const std::string& funcName, const std::string& hookName)
8383
{
8484
char funcNameChar[255];
8585
strcpy(funcNameChar, funcName.c_str());
@@ -101,32 +101,32 @@ bool CHookManager::Remove(const std::string& funcName, const std::string& hookNa
101101
}
102102
}
103103

104-
bool CHookManager::RemoveVirtualHook(const std::string& funcName, const std::string& hookName)
104+
bool CEngineHookManager::RemoveVirtualHook(const std::string& funcName, const std::string& hookName)
105105
{
106106
tiVirtualHooks iHooks = VirtualHooks.find(funcName);
107107
if (iHooks == VirtualHooks.end())
108108
{
109-
Logging::LogF("[CHookManager] (%s) ERROR: Failed to remove virtual hook \"%s\" for \"%s\"\n", this->DebugName.c_str(), hookName.c_str(), funcName);
109+
Logging::LogF("[CEngineHookManager] (%s) ERROR: Failed to remove virtual hook \"%s\" for \"%s\"\n", this->DebugName.c_str(), hookName.c_str(), funcName);
110110
return false;
111111
}
112112

113113
return RemoveFromTable(iHooks->second, funcName, hookName);
114114
}
115115

116-
bool CHookManager::RemoveStaticHook(UFunction* function, const std::string& hookName)
116+
bool CEngineHookManager::RemoveStaticHook(UFunction* function, const std::string& hookName)
117117
{
118118
// Since we are getting a UFunction pointer, we don't need to check virtual hooks
119119
tiStaticHooks iHooks = StaticHooks.find(function);
120120
if (iHooks == StaticHooks.end())
121121
{
122-
//Logging::LogF("[CHookManager] (%s) ERROR: Failed to remove static hook \"%s\" for \"%s\"\n", this->DebugName.c_str(), hookName.c_str(), function->GetFullName().c_str());
122+
//Logging::LogF("[CEngineHookManager] (%s) ERROR: Failed to remove static hook \"%s\" for \"%s\"\n", this->DebugName.c_str(), hookName.c_str(), function->GetFullName().c_str());
123123
return false;
124124
}
125125

126126
return RemoveFromTable(iHooks->second, function->GetFullName(), hookName);
127127
}
128128

129-
void CHookManager::ResolveVirtualHooks(UFunction* function)
129+
void CEngineHookManager::ResolveVirtualHooks(UFunction* function)
130130
{
131131
// Resolve any virtual hooks into static hooks
132132
if (VirtualHooks.size() > 0)
@@ -141,7 +141,7 @@ void CHookManager::ResolveVirtualHooks(UFunction* function)
141141
int size = iVHooks->second.size();
142142
StaticHooks.emplace(function, iVHooks->second);
143143
VirtualHooks.erase(iVHooks);
144-
Logging::LogF("[CHookManager] (%s) Function pointer found for \"%s\", added map with %i elements to static hooks map\n", this->DebugName.c_str(), funcName.c_str(), size);
144+
Logging::LogF("[CEngineHookManager] (%s) Function pointer found for \"%s\", added map with %i elements to static hooks map\n", this->DebugName.c_str(), funcName.c_str(), size);
145145
}
146146
}
147147
}

bl2-sdk/CHookManager.h renamed to bl2-sdk/CEngineHookManager.h

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
#pragma once
2-
#ifndef CHOOKMANAGER_H
3-
#define CHOOKMANAGER_H
2+
#ifndef CENGINEHOOKMANAGER_H
3+
#define CENGINEHOOKMANAGER_H
44

55
#include <string>
66
#include <map>
7+
#include <functional>
78

8-
class CHookManager
9+
class CEngineHookManager
910
{
1011
public:
11-
typedef std::map<std::string, void*> tHookMap;
12-
typedef std::pair<std::string, void*> tFuncNameHookPair;
12+
typedef std::map<std::string, std::function<bool(UObject*, UFunction*, void*, void*)>> tHookMap;
13+
typedef std::pair<std::string, std::function<bool(UObject*, UFunction*, void*, void*)>> tFuncNameHookPair;
1314
typedef tHookMap::iterator tiHookMap;
1415
typedef std::map<std::string, tHookMap>::iterator tiVirtualHooks;
1516
typedef std::map<UFunction*, tHookMap>::iterator tiStaticHooks;
@@ -22,10 +23,10 @@ class CHookManager
2223
std::map<UFunction*, tHookMap> StaticHooks;
2324
std::string DebugName;
2425

25-
CHookManager() : DebugName("Unknown") {}
26-
CHookManager(std::string debugName) : DebugName(debugName) {}
26+
CEngineHookManager() : DebugName("Unknown") {}
27+
CEngineHookManager(std::string debugName) : DebugName(debugName) {}
2728

28-
void Register(const std::string& funcName, const std::string& hookName, void* funcHook);
29+
void Register(const std::string& funcName, const std::string& hookName, std::function<bool(UObject*, UFunction*, void*, void*)> funcHook);
2930
bool Remove(const std::string& funcName, const std::string& hookName);
3031
void AddVirtualHook(const std::string& funcName, const tFuncNameHookPair& hookPair);
3132
void AddStaticHook(UFunction* function, const tFuncNameHookPair& hookPair);

bl2-sdk/CPythonInterface.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,40 @@
1212
#include <string>
1313
#include <cstdlib>
1414

15+
void RegisterEngineHook(const std::string& funcName, const std::string& hookName, py::object funcHook) {
16+
GameHooks::EngineHookManager->Register(funcName, hookName, [funcHook](UObject* caller, UFunction* function, void* parms, void* result) {
17+
try {
18+
py::object py_caller = py::cast(caller, py::return_value_policy::reference);
19+
py::object py_function = py::cast(function, py::return_value_policy::reference);
20+
py::object py_parms = py::cast(FStruct(parms), py::return_value_policy::reference);
21+
py::object py_result = py::cast(FStruct(result), py::return_value_policy::reference);
22+
funcHook(py_caller, py_function, py_parms, py_result);
23+
} catch (std::exception e) {
24+
Logging::LogF(e.what());
25+
}
26+
return true;
27+
}
28+
);
29+
}
30+
31+
32+
void RegisterScriptHook(const std::string& funcName, const std::string& hookName, py::object funcHook) {
33+
GameHooks::UnrealScriptHookManager->Register(funcName, hookName, [funcHook](UObject* caller, FFrame& stack, void* const result, UFunction* function) {
34+
try {
35+
py::object py_caller = py::cast(caller, py::return_value_policy::reference);
36+
py::object py_stack = py::cast(stack, py::return_value_policy::reference);
37+
py::object py_result = py::cast(FStruct(result), py::return_value_policy::reference);
38+
py::object py_function = py::cast(function, py::return_value_policy::reference);
39+
funcHook(py_caller, py_stack, py_result, py_function);
40+
} catch (std::exception e) {
41+
Logging::LogF(e.what());
42+
}
43+
return true;
44+
}
45+
);
46+
}
47+
48+
1549
namespace py = pybind11;
1650

1751
PYBIND11_EMBEDDED_MODULE(bl2sdk, m)
@@ -42,8 +76,13 @@ PYBIND11_EMBEDDED_MODULE(bl2sdk, m)
4276
Export_pystes_AGearboxPlayerController(m);
4377
Export_pystes_AWillowPlayerController(m);
4478
Export_pystes_FQWord(m);
79+
Export_pystes_gamedefines(m);
80+
Export_pystes_UStruct(m);
81+
Export_pystes_UFunction(m);
4582
m.def("Log", [](std::string in) { Logging::Log(in.c_str(), in.length()); });
4683
m.def("LoadPackage", &BL2SDK::LoadPackage);
84+
m.def("RegisterEngineHook", &RegisterEngineHook);
85+
m.def("RegisterScriptHook", &RegisterScriptHook);
4786
}
4887

4988
bool PythonGCTick(UObject* caller, UFunction* function, void* parms, void* result)

bl2-sdk/CScriptHookManager.cpp

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
#pragma once
2+
#include "stdafx.h"
3+
#include "CScriptHookManager.h"
4+
5+
void CScriptHookManager::AddVirtualHook(const std::string& funcName, const tFuncNameHookPair& hookPair)
6+
{
7+
tiVirtualHooks iHooks = VirtualHooks.find(funcName);
8+
if (iHooks != VirtualHooks.end())
9+
{
10+
// Otherwise it's fine, add it into the existing table
11+
iHooks->second.insert(hookPair);
12+
}
13+
else
14+
{
15+
// There are no other virtual hooks so we need to create the table for it
16+
tHookMap newMap;
17+
newMap.insert(hookPair);
18+
VirtualHooks.emplace(funcName, newMap);
19+
}
20+
21+
Logging::LogF("[CScriptHookManager] (%s) Hook \"%s\" added as virtual hook for \"%s\"\n", this->DebugName.c_str(), hookPair.first.c_str(), funcName.c_str());
22+
}
23+
24+
void CScriptHookManager::AddStaticHook(UFunction* function, const tFuncNameHookPair& hookPair)
25+
{
26+
tiStaticHooks iHooks = StaticHooks.find(function);
27+
if (iHooks != StaticHooks.end())
28+
{
29+
// Otherwise it's fine, add it into the existing table
30+
iHooks->second.insert(hookPair);
31+
}
32+
else
33+
{
34+
// There are no other hooks so we need to create the table for it
35+
tHookMap newMap;
36+
newMap.insert(hookPair);
37+
StaticHooks.emplace(function, newMap);
38+
}
39+
40+
Logging::LogF("[CScriptHookManager] (%s) Hook \"%s\" added as static hook for \"%s\"\n", this->DebugName.c_str(), hookPair.first.c_str(), function->GetFullName().c_str());
41+
}
42+
43+
bool CScriptHookManager::RemoveFromTable(tHookMap& hookTable, const std::string& funcName, const std::string& hookName)
44+
{
45+
// Remove it and ensure that it actually got removed
46+
int removed = hookTable.erase(hookName);
47+
48+
if (removed == 0)
49+
{
50+
Logging::LogF("[CScriptHookManager] (%s) Failed to remove hook \"%s\" for function \"%s\"\n", this->DebugName.c_str(), hookName.c_str(), funcName.c_str());
51+
return false;
52+
}
53+
else
54+
{
55+
Logging::LogF("[CScriptHookManager] (%s) Hook \"%s\" removed for function \"%s\" successfully\n", this->DebugName.c_str(), hookName.c_str(), funcName.c_str());
56+
return true;
57+
}
58+
}
59+
60+
void CScriptHookManager::Register(const std::string& funcName, const std::string& hookName, std::function<bool(UObject*, FFrame&, void* const, UFunction*)> funcHook)
61+
{
62+
char funcNameChar[255];
63+
strcpy(funcNameChar, funcName.c_str());
64+
65+
// Create pair to insert
66+
tFuncNameHookPair hookPair = std::make_pair(hookName, funcHook);
67+
68+
// Find func
69+
UFunction* function = UObject::FindObject<UFunction>(funcNameChar);
70+
if (function == nullptr)
71+
{
72+
// The function was not found, so we need to create a virtual hook for it
73+
AddVirtualHook(funcName, hookPair);
74+
}
75+
else
76+
{
77+
// The function WAS found, so we can just hook it straight away
78+
AddStaticHook(function, hookPair);
79+
}
80+
}
81+
82+
bool CScriptHookManager::Remove(const std::string& funcName, const std::string& hookName)
83+
{
84+
char funcNameChar[255];
85+
strcpy(funcNameChar, funcName.c_str());
86+
87+
UFunction* function = UObject::FindObject<UFunction>(funcNameChar);
88+
if (function == nullptr)
89+
{
90+
// Function wasn't found, so virtual hook removal time!
91+
return RemoveVirtualHook(funcName, hookName);
92+
}
93+
else
94+
{
95+
// We did find it, so remove the static hook
96+
// TODO: WARNING. MAJOR PROBLEM HERE.
97+
// If a virtual hook has been created because the UFunction didn't exist,
98+
// but then the function is created but NOT CALLED, then the hook system
99+
// will keep the virtual hook, and we won't delete it here.
100+
return RemoveStaticHook(function, hookName);
101+
}
102+
}
103+
104+
bool CScriptHookManager::RemoveVirtualHook(const std::string& funcName, const std::string& hookName)
105+
{
106+
tiVirtualHooks iHooks = VirtualHooks.find(funcName);
107+
if (iHooks == VirtualHooks.end())
108+
{
109+
Logging::LogF("[CScriptHookManager] (%s) ERROR: Failed to remove virtual hook \"%s\" for \"%s\"\n", this->DebugName.c_str(), hookName.c_str(), funcName);
110+
return false;
111+
}
112+
113+
return RemoveFromTable(iHooks->second, funcName, hookName);
114+
}
115+
116+
bool CScriptHookManager::RemoveStaticHook(UFunction* function, const std::string& hookName)
117+
{
118+
// Since we are getting a UFunction pointer, we don't need to check virtual hooks
119+
tiStaticHooks iHooks = StaticHooks.find(function);
120+
if (iHooks == StaticHooks.end())
121+
{
122+
//Logging::LogF("[CScriptHookManager] (%s) ERROR: Failed to remove static hook \"%s\" for \"%s\"\n", this->DebugName.c_str(), hookName.c_str(), function->GetFullName().c_str());
123+
return false;
124+
}
125+
126+
return RemoveFromTable(iHooks->second, function->GetFullName(), hookName);
127+
}
128+
129+
void CScriptHookManager::ResolveVirtualHooks(UFunction* function)
130+
{
131+
// Resolve any virtual hooks into static hooks
132+
if (VirtualHooks.size() > 0)
133+
{
134+
//std::string funcName = GetFuncName(pFunction); TODO: Use this instead of the ugly other thing
135+
std::string funcName = function->GetFullName();
136+
137+
tiVirtualHooks iVHooks = VirtualHooks.find(funcName);
138+
if (iVHooks != VirtualHooks.end())
139+
{
140+
// Insert this map into the static hooks map
141+
int size = iVHooks->second.size();
142+
StaticHooks.emplace(function, iVHooks->second);
143+
VirtualHooks.erase(iVHooks);
144+
Logging::LogF("[CScriptHookManager] (%s) Function pointer found for \"%s\", added map with %i elements to static hooks map\n", this->DebugName.c_str(), funcName.c_str(), size);
145+
}
146+
}
147+
}

0 commit comments

Comments
 (0)