-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcommands.cpp
More file actions
281 lines (239 loc) · 8.72 KB
/
commands.cpp
File metadata and controls
281 lines (239 loc) · 8.72 KB
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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
#include <iostream>
#include <vector>
#include <windows.h>
#include <fstream>
#include <limits>
#include <algorithm>
#include <map>
#include <sstream>
//Name of the window the batch file is running on
#define SERVERWND "MCServerCommands"
//Name of the batch file that starts the server
#define STARTBAT "start.bat"
//Location of the live log file
#define LOGFILE "logs/latest.log"
//The location of your C++ compiler
#define CC "E:\\compiling\\cygwin64\\bin\\g++.exe"
//For now only this player can mess with commands
//Eventually I'll make it so all operators can
#define OP "iwwenjoyer"
/******************** unimportant for making commands ********************/
struct ProcessWindowsInfo {
DWORD ProcessID;
std::vector<HWND> Windows;
ProcessWindowsInfo(DWORD const AProcessID) :
ProcessID(AProcessID)
{ }
};
BOOL __stdcall EnumProcessWindowsProc(HWND hwnd, LPARAM lParam) {
ProcessWindowsInfo *Info = reinterpret_cast<ProcessWindowsInfo*>(lParam);
DWORD WindowProcessID;
GetWindowThreadProcessId(hwnd, &WindowProcessID);
if(WindowProcessID == Info->ProcessID)
Info->Windows.push_back(hwnd);
return true;
}
std::ifstream& goto_line(std::ifstream& file, long long num) {
file.seekg(std::ios::beg);
for(long long i = 0; i < num - 1; ++i)
file.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
return file;
}
std::vector<std::string> files_in_dir(std::string dir) {
HANDLE find;
WIN32_FIND_DATAA ffd;
find = FindFirstFileA((dir + "\\*").c_str(), &ffd);
if (find == INVALID_HANDLE_VALUE)
return { };
std::vector<std::string> rv;
do
if(strcmp(ffd.cFileName, ".") && strcmp(ffd.cFileName, "..") && !(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
rv.push_back(ffd.cFileName);
while(FindNextFileA(find, &ffd));
FindClose(find);
return rv;
}
/*************************************************************************/
#include "commands/functions.h"
typedef void(*mccommand)(HWND, message);
std::map<std::string, mccommand> commands { };
std::map<std::string, HMODULE> dlls { };
std::vector<HANDLE> to_close;
HANDLE current_thread;
struct compile_params {
compile_params(HWND hwnd, message msg) :
hwnd { hwnd },
msg { msg }
{ }
HWND hwnd;
message msg;
};
DWORD WINAPI compile_command(LPVOID params) {
HWND hwnd = ((compile_params*)params)->hwnd;
message msg = ((compile_params*)params)->msg;
std::ifstream raw { "commands\\raw\\" + msg[0] + ".txt" };
if(!raw.is_open()) {
delete (compile_params*)params;
return 0;
}
std::stringstream buffer;
buffer << raw.rdbuf() << '\n';
raw.close();
std::ofstream formatted { "commands\\raw\\" + msg[0] + ".cpp" };
if(!formatted.is_open()) {
delete (compile_params*)params;
return 0;
}
formatted << "#include \"commands\\functions.h\"\n";
formatted << "extern \"C\" void __declspec(dllexport) " + msg[0] + "(HWND hwnd, message msg) {\n";
formatted << buffer.str();
formatted << "}";
formatted.close();
SHELLEXECUTEINFOA compile { 0 };
std::string exec = std::string("commands\\raw\\" + msg[0] + ".cpp -shared -o commands\\compiled\\" + msg[0] + ".dll commands\\functions.o");
compile.cbSize = sizeof(SHELLEXECUTEINFOA);
compile.fMask = SEE_MASK_NOCLOSEPROCESS;
compile.lpVerb = "open";
compile.lpFile = CC;
compile.lpParameters = exec.c_str();
compile.lpDirectory = NULL;
compile.nShow = SW_MINIMIZE;
compile.hInstApp = NULL;
ShellExecuteExA(&compile);
WaitForSingleObject(compile.hProcess, INFINITE);
if(GetFileAttributesA(std::string("commands\\raw\\" + msg[0] + ".cpp").c_str()) != INVALID_FILE_ATTRIBUTES)
DeleteFileA(std::string("commands\\raw\\" + msg[0] + ".cpp").c_str());
if(GetFileAttributesA(std::string("commands\\compiled\\" + msg[0] + ".dll").c_str()) == INVALID_FILE_ATTRIBUTES) {
delete (compile_params*)params;
return 0;
}
HINSTANCE dll = LoadLibraryA(std::string("commands\\compiled\\" + msg[0] + ".dll").c_str());
mccommand func = (mccommand)GetProcAddress(dll, msg[0].c_str());
commands.emplace(msg[0], func);
dlls.emplace(msg[0], dll);
if(hwnd)
(*func)(hwnd, msg);
delete (compile_params*)params;
return 0;
}
// Some example commands are provided
// TODO: add a way to query in-game values
void execute(HWND hwnd, message msg) {
// Is it said by a player? Did they say anything?
if(!msg.name.length() || !msg.said.length()) return;
// Is it a command?
if(msg.said.at(0) != '#') return;
// Makes everything lowercase
transform(msg.name.begin(), msg.name.end(), msg.name.begin(), ::tolower);
transform(msg.said.begin(), msg.said.end(), msg.said.begin(), ::tolower);
// Splits up the message into more usable bits
msg.tokenize();
if(msg.name == OP) {
if(msg[0] == "unl" && msg.size() >= 2 && dlls.contains(msg[1])) {
FreeLibrary(dlls[msg[1]]);
dlls.erase(dlls.find(msg[1]));
commands.erase(commands.find(msg[1]));
DeleteFileA(std::string("commands\\compiled\\" + msg[1] + ".dll").c_str());
}
}
if(GetFileAttributesA(std::string("commands\\compiled\\" + msg[0] + ".dll").c_str()) != INVALID_FILE_ATTRIBUTES) {
HINSTANCE dll = LoadLibraryA(std::string("commands\\compiled\\" + msg[0] + ".dll").c_str());
mccommand func = (mccommand)GetProcAddress(dll, msg[0].c_str());
commands.emplace(msg[0], func);
dlls.emplace(msg[0], dll);
}
if(commands.contains(msg[0])) {
(*commands[msg[0]])(hwnd, msg);
return;
}
if(GetFileAttributesA(std::string("commands\\raw\\" + msg[0] + ".txt").c_str()) != INVALID_FILE_ATTRIBUTES) {
HANDLE thread = CreateThread(NULL, 0, &compile_command, new compile_params(hwnd, msg), 0, NULL);
if(thread) to_close.push_back(thread);
return;
}
}
int main(int argc, char** argv) {
// This will be a handle to the command promt window running the server
HWND hwnd = nullptr;
// Checks if the server's window exists and gets a handle to it if it does
hwnd = FindWindowA(NULL, SERVERWND);
// If the server could not be found, start it
if(!hwnd) {
SHELLEXECUTEINFOA sei { 0 };
sei.cbSize = sizeof(SHELLEXECUTEINFOA);
sei.fMask = SEE_MASK_NOCLOSEPROCESS;
sei.lpVerb = "open";
// The batch file that the server will be ran from
sei.lpFile = STARTBAT;
sei.lpDirectory = NULL;
sei.nShow = SW_SHOW;
sei.hInstApp = NULL;
// Starts the batch file that starts the server and saves info to `sei`
ShellExecuteExA(&sei);
// If the server is unable to open the log file, comment this out and uncommend the next line
// That probably means that this program opened the log file before the server was able to and is denying it access
Sleep(5000);
// system("pause");
// Gets all of the window handles associated with the process started by the batch file
ProcessWindowsInfo Info(GetProcessId(sei.hProcess));
EnumWindows((WNDENUMPROC)EnumProcessWindowsProc, reinterpret_cast<LPARAM>(&Info));
// Sets hwnd to the command prompt window the server is being ran from
hwnd = Info.Windows.at(0);
// Changes the window title to the decided server window title
SetWindowTextA(hwnd, SERVERWND);
}
std::vector<std::string> commands = files_in_dir("commands\\raw");
for(std::string &i : commands) {
message msg { };
msg.tokens.push_back(i.substr(0, i.size() - 4));
if(GetFileAttributesA(std::string("commands\\compiled\\" + msg[0] + ".dll").c_str()) == INVALID_FILE_ATTRIBUTES) {
HANDLE thread = CreateThread(NULL, 0, &compile_command, new compile_params(nullptr, msg), 0, NULL);
if(thread) to_close.push_back(thread);
}
}
// Opens the log file
std::ifstream file { LOGFILE, std::ios::in };
// This is the current line in the log file
long long at = 0;
if(!file.is_open())
std::cout << "Couldn't open log file!!\n";
else {
// If the server was started when this process was started the log file will have text in it already
// This will move `at` to the end of that file so we aren't re-reading lines
at = std::count(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>(), '\n') + 1;
file.close();
if(hwnd) std::cout << "Connection to server successful!\n";
}
std::string str;
message msg;
// TODO - HIGH PRIORITY: make this not take an entire thread
while(true) {
// There's probably be a better way to do this but if it isn't closed then opened before reading it won't properly read it
file.open(LOGFILE, std::ios::in);
if(!file.is_open()) {
std::cout << "Couldn't open log file!!\n";
continue;
}
// Go to the last read line
goto_line(file, at);
// Is there anything new?
if(std::getline(file, str)) {
// Parse the new line
msg = message(str);
// Execute if it's a command
execute(hwnd, msg);
// Increments the index of the last read line
at++;
}
// Closes the file
file.close();
if(WaitForSingleObject(current_thread, 0) == WAIT_OBJECT_0) {
CloseHandle(current_thread);
current_thread = NULL;
}
if(!current_thread && to_close.size()) {
current_thread = to_close.front();
to_close.erase(to_close.begin());
}
}
}