-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwatchdog.cpp
204 lines (173 loc) · 7.94 KB
/
watchdog.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#include "watchdog.hpp"
#include "global.hpp"
#include "compiler.hpp"
#include "filesystem.hpp"
#include "utils.hpp"
#include "core.hpp"
#include "config.hpp"
#include <tier0/dbg.h>
#include <chrono>
#include <GarrysMod/Lua/LuaInterface.h>
#include <GarrysMod/Lua/LuaShared.h>
#include <GarrysMod/FunctionPointers.hpp>
#include <GarrysMod/Symbol.hpp>
#include <scanning/symbolfinder.hpp>
#include <detouring/hook.hpp>
namespace Symbols {
typedef void (GMCOMMON_CALLING_CONVENTION_STD *HandleFileChange_t)(const std::string& path);
std::vector<Symbol> HandleFileChange = {
#if ARCHITECTURE_IS_X86
#if SYSTEM_IS_WINDOWS
Symbol::FromSignature("\x55\x8b\xec\x83\xec\x60\x56\x8b\x75\x08\x8d\x45\xd0\x56\x50\xe8\x2A\x2A\x2A\x2A\x83\xc4\x08\x83\x7d\xe0\x00\x0f\x84"),
#elif SYSTEM_IS_LINUX
Symbol::FromSignature("\x55\x89\xe5\x57\x56\x53\x8d\x5d\x98\x83\xec\x7c\x8b\x75\x08\x89\x1c\x24\x89\x74\x24\x04\xe8\x2A\x2A\x2A\x2A\x8b\x45\x98\x83\xec\x04\x8b\x50\xf4\x85\xd2\x0f\x84"),
#endif
#elif ARCHITECTURE_IS_X86_64
#if SYSTEM_IS_WINDOWS
Symbol::FromSignature("\x48\x89\x5c\x24\x10\x48\x89\x74\x24\x18\x48\x89\x7c\x24\x20\x55\x48\x8d\x6c\x24\xa9\x48\x81\xec\xb0\x00\x00\x00\x48\x8b\x05\x2A\x2A\x2A\x2A\x48\x33\xc4\x48\x89\x45\x47\x48\x8b\xf9\x48\x8b\xd1\x48\x8d\x4d\xc7\xe8\x2A\x2A\x2A\x2A\x48\x83\x7d\xd7\x00\x0f\x84"),
#elif SYSTEM_IS_LINUX
Symbol::FromSignature("\x55\x48\x89\xfe\x48\x89\xe5\x41\x57\x41\x56\x41\x55\x41\x54\x4c\x8d\x65\x80\x53\x48\x89\xfb\x4c\x89\xe7\x48\x83\xec\x68\xe8\x2A\x2A\x2A\x2A\x48\x8b\x45\x80\x48\x83\x78\xe8\x00\x75")
#elif SYSTEM_IS_MACOSX
Symbol::FromSignature("\x55\x48\x89\xe5\x53\x48\x81\xec\x88\x00\x00\x00\x48\x89\xfb\x48\x8d\x7d\xc8\x48\x89\xde\xe8\x2A\x2A\x2A\x2A\x8a\x4d\xc8\x89\xc8\x24\x01\x74"),
#endif
#endif
};
static SymbolFinder finder;
template<typename T>
static inline std::vector<T> ResolveSymbol(SourceSDK::FactoryLoader& loader, const Symbol& symbol) {
static_assert(std::is_pointer<T>::value, "T must be a pointer");
// vector used to catch if signature eventually find more than one function
// I'm still new into signatures, so they may be wrong
std::vector<T> pointers;
void* ptr = nullptr;
while (ptr = finder.Resolve(loader.GetModule(), symbol.name.c_str(), symbol.length, ptr)) {
pointers.push_back( reinterpret_cast<T>(ptr) );
ptr = reinterpret_cast<void*>(reinterpret_cast<char*>(ptr) + 1);
}
return pointers;
}
template<typename T>
static inline std::vector<T> ResolveSymbols(SourceSDK::FactoryLoader& loader, const std::vector<Symbol>& symbols) {
for (const auto& symbol : symbols) {
auto pointers = ResolveSymbol<T>(loader, symbol);
if (!pointers.empty())
return pointers;
}
return {};
}
}
// This is probably terrible design for menu + server environments
// Probably will be fixed in successor project
void HandleFileChange_detour(const std::string& path) {
MoonLoader::Core::GetAll().front()->watchdog->HandleFileChange(path);
}
using namespace MoonLoader;
void WatchdogListener::handleFileAction(efsw::WatchID watchid, const std::string& dir,
const std::string& filename, efsw::Action action,
std::string oldFilename
) {
// We only care about modified files
if (action == efsw::Actions::Modified) {
std::string path = Utils::Path::Join(dir + filename);
if (auto watchdog = this->watchdog.lock())
watchdog->OnFileModified(path);
}
}
Watchdog::Watchdog(std::shared_ptr<Core> core, std::shared_ptr<Filesystem> fs)
: core(core), fs(fs)
{
m_HandleFileChangeHook = std::make_unique<Detouring::Hook>();
SourceSDK::FactoryLoader server_loader("server");
auto HandleFileChange_pointers = Symbols::ResolveSymbols<Symbols::HandleFileChange_t>(server_loader, Symbols::HandleFileChange);
if (HandleFileChange_pointers.size() == 1) {
auto HandleFileChange_original = HandleFileChange_pointers[0];
if (m_HandleFileChangeHook->Create((void*)HandleFileChange_original, (void*)HandleFileChange_detour) && m_HandleFileChangeHook->Enable()) {
DevMsg("[Moonloader] HandleFileChange: %p\n", HandleFileChange_original);
} else {
core->LUA->ErrorNoHalt("[Moonloader] Failed to hook HandleFileChange function! Autorefresh won't work properly.\n\tPlease, report to " MOONLOADER_URL "/issues\n");
}
} else if (HandleFileChange_pointers.empty()) {
core->LUA->ErrorNoHalt("[Moonloader] HandleFileChange not found! Autorefresh won't work properly.\n\tPlease, report to " MOONLOADER_URL "/issues\n" );
} else {
core->LUA->ErrorNoHalt("[Moonloader] Too many functions were found for HandleFileChange signature! Autorefresh won't work properly.\n\tPlease, report to " MOONLOADER_URL "/issues\n");
}
}
void Watchdog::Start() {
m_Watcher->watch();
m_WatchdogListener->watchdog = weak_from_this();
}
Watchdog::~Watchdog() {
m_HandleFileChangeHook->Disable();
}
void Watchdog::OnFileModified(const std::string& path) {
// We only care about file we are watching
std::string relativePath = fs->FullToRelativePath(path, core->LUA->GetPathID());
Utils::Path::Normalize(relativePath);
std::lock_guard<std::mutex> guard(m_Lock);
if (!IsFileWatched(relativePath))
return;
// Add our modified file to the queue
m_ModifiedFiles.push(std::move(relativePath));
}
void Watchdog::WatchDirectory(const std::string& path) {
if (IsDirectoryWatched(path))
// Our watchdog already registered here
return;
DevMsg("[Moonloader] Watching for directory %s\n", path.c_str());
auto id = m_Watcher->addWatch(path.c_str(), m_WatchdogListener.get(), false);
m_WatchIDs.insert_or_assign(path, id);
}
void MoonLoader::Watchdog::WatchFile(const std::string& path, const char* pathID) {
if (IsFileWatched(path))
// Our watchdog already registered here
return;
std::string fullPath = fs->RelativeToFullPath(path, pathID);
Utils::Path::Normalize(fullPath);
Utils::Path::StripFileName(fullPath);
if (fullPath.empty()) {
DevWarning("[Moonloader] Unable to find full path for %s\n", path.c_str());
return;
}
DevMsg("[Moonloader] Watching for file %s\n", path.c_str());
WatchDirectory(fullPath);
m_WatchedFiles.insert(path.c_str());
}
void Watchdog::Think() {
if (m_ModifiedFiles.empty())
return;
auto currentTimestamp = Utils::Timestamp();
std::lock_guard<std::mutex> guard(m_Lock);
while (!m_ModifiedFiles.empty()) {
auto& path = m_ModifiedFiles.front();
auto timestamp = m_ModifiedFileDelays.find(path);
// Check if modified file is delayed
if (timestamp == m_ModifiedFileDelays.end() || (timestamp->second) < currentTimestamp) {
DevMsg("[Moonloader] %s was updated. Triggering auto-reload...\n", path.c_str());
if (core->compiler->CompileFile(path, true)) {
if (auto file = GetCachedFile(path)) {
RefreshFile(file->name);
} else {
Warning("[Moonloader] Unable to find file %s in cache. Can't autorefresh it :(\n", path.c_str());
}
}
m_ModifiedFileDelays[path] = currentTimestamp + 200; // Add 200ms delay, before we can reload file again
}
m_ModifiedFiles.pop();
}
}
void Watchdog::HandleFileChange(const std::string& path) {
std::string strPath = path.c_str();
Utils::Path::Normalize(strPath);
if (core->compiler->FindFileByFullOutputPath(strPath)) {
// We are handling our own file, supress engine's file change
return;
}
RefreshFile(path);
}
void Watchdog::RefreshFile(const std::string& path) {
if (m_HandleFileChangeHook->IsValid()) {
m_HandleFileChangeHook->GetTrampoline<Symbols::HandleFileChange_t>()(path);
} else {
// TODO: use lua_refresh_file?
}
}