Richard Boegli's CnC_Generals_Zero_Hour Fork
WIP
This is documentation of Richard Boegil's Zero Hour Fork
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1
/*
2
** Command & Conquer Generals Zero Hour(tm)
3
** Copyright 2025 Electronic Arts Inc.
4
**
5
** This program is free software: you can redistribute it and/or modify
6
** it under the terms of the GNU General Public License as published by
7
** the Free Software Foundation, either version 3 of the License, or
8
** (at your option) any later version.
9
**
10
** This program is distributed in the hope that it will be useful,
11
** but WITHOUT ANY WARRANTY; without even the implied warranty of
12
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
** GNU General Public License for more details.
14
**
15
** You should have received a copy of the GNU General Public License
16
** along with this program. If not, see <http://www.gnu.org/licenses/>.
17
*/
18
19
#ifdef _WIN32
20
#include <process.h>
21
#endif
22
#include <cstdlib>
23
#include <csignal>
24
#include <iostream>
25
26
#ifdef _WIN32
27
#include <direct.h>
28
#else
29
#include <sys/types.h>
30
#include <sys/stat.h>
31
#endif
32
33
//#define THREADSAFE_HEADER
34
35
#include <wstring.h>
36
#include <wdebug.h>
37
#include <filed.h>
38
#include <stdoutd.h>
39
#include <wstypes.h>
40
#include <xtime.h>
41
#include "
global.h
"
42
#include "
generals.h
"
43
#include "timezone.h"
44
#include <threadfac.h>
45
46
#include <tcp.h>
47
#include "
mydebug.h
"
48
49
#ifdef _UNIX
50
using namespace
std;
51
#else
52
#define sleep(x) Sleep(1000 * (x))
53
#endif
54
55
static
char
*Program_Usage =
"A config filename can be given on the command line (default=matchbot.cfg)\n"
;
56
void
logMonitor
(
void
*);
57
void
paranoidLogMonitor
(
void
*);
58
59
OutputDevice
*
output_device
=
NULL
;
60
OutputDevice
*
paranoid_output_device
=
NULL
;
61
62
63
void
Signal_Quit
(
int
)
64
{
65
INFMSG
(
"Exiting due to signal."
);
66
exit(2);
67
}
68
69
void
Setup_Signals
(
void
)
70
{
71
#ifdef _UNIX
72
struct
sigaction act, oact;
73
act.sa_handler =
Signal_Quit
;
74
sigemptyset(&act.sa_mask);
75
act.sa_flags = 0;
76
sigaction(SIGTERM, &act, &oact);
77
sigaction(SIGINT, &act, &oact);
78
#endif
79
}
80
81
int
VerifyFileDescriptors
(
int
requested)
82
{
83
#ifdef _UNIX
84
struct
rlimit limit;
85
if
(!getrlimit(_SC_OPEN_MAX, &limit))
86
{
87
INFMSG
(
"Hard limit on file descriptors: "
<< limit.rlim_max);
88
if
(limit.rlim_max < (
unsigned
int
)requested)
89
{
90
ERRMSG
(
"Too many file descriptors requested"
);
91
ERRMSG
(
"Hard Limit: "
<< limit.rlim_max <<
", requested: "
<< requested);
92
requested = limit.rlim_max;
93
}
94
95
limit.rlim_cur = limit.rlim_max;
/* make soft limit the max */
96
if
(setrlimit(_SC_OPEN_MAX, &limit) == -1)
97
{
98
ERRMSG
(
"Error setting max file descriptors to "
<< limit.rlim_cur);
99
exit(-1);
100
}
101
INFMSG
(
"Soft limit on file descriptors: "
<< limit.rlim_cur);
102
}
103
else
104
{
105
ERRMSG
(
"Couldn't get limit for _SC_OPEN_MAX"
);
106
exit(-1);
107
}
108
#endif
109
return
requested;
110
}
111
112
113
114
GeneralsMatcher
*
s_generalsMatcher
=
NULL
;
115
GeneralsClientMatcher
*
s_generalsClientMatcher
=
NULL
;
116
117
int
main
(
int
argc,
char
** argv)
118
{
119
Wstring
config_fname =
"matchbot.cfg"
;
120
121
// You can specify the config file on the command line
122
if
(argc == 2)
123
config_fname = argv[1];
124
125
// Read the config file
126
FILE *fp;
127
if
((fp = fopen(config_fname.
get
(),
"r"
)) ==
NULL
)
128
{
129
cerr <<
"\nCan't open the config file '"
<< config_fname.
get
() <<
"'\n\n"
;
130
cerr << Program_Usage << endl;
131
exit( -1);
132
}
133
fclose(fp);
134
135
Global
.ReadFile(config_fname.
get
());
136
137
// Setup debugging & logging output
138
Wstring
output_file;
139
output_file.
set
(
"matchbot.log"
);
140
Global
.config.getString(
"OUTPUTFILE"
, output_file);
141
if
(output_file !=
"STDOUT"
)
142
{
143
int
append = 1;
144
Global
.config.getInt(
"APPENDTOLOG"
, append);
145
if
(append)
146
output_device
=
new
FileD
(output_file.
get
(),
"a"
);
147
else
148
output_device
=
new
FileD
(output_file.
get
(),
"w"
);
149
if
(!
output_device
)
150
{
151
cerr <<
"Could not open "
<< output_file.
get
() <<
" for writing!"
<< endl;
152
exit( -1);
153
}
154
}
155
else
156
{
157
output_device
=
new
StdoutD
;
158
}
159
MsgManager::setAllStreams
(
output_device
);
160
DBGMSG
(
"Matching bot started!"
);
161
INFMSG
(
"Matching bot "
<< argv[0] <<
" started!"
);
162
163
// Setup logging of suspicious activity
164
Wstring
paranoid_output_file;
165
paranoid_output_file.
set
(
"hacks.log"
);
166
Global
.config.getString(
"PARANOIDFILE"
, paranoid_output_file);
167
if
(paranoid_output_file !=
"STDOUT"
)
168
{
169
paranoid_output_device
=
new
FileD
(paranoid_output_file.
get
(),
"a"
);
170
if
(!
paranoid_output_device
)
171
{
172
cerr <<
"Could not open "
<< paranoid_output_file.
get
() <<
" for writing!"
<< endl;
173
exit( -1);
174
}
175
}
176
else
177
{
178
paranoid_output_device
=
new
StdoutD
;
179
}
180
MyMsgManager::setParanoidStream
(
paranoid_output_device
);
181
DBGMSG
(
"Hack log started!"
);
182
PARANOIDMSG
(
"Hack log started!"
);
183
184
Setup_Signals
();
185
186
#ifdef _WINDOWS
187
// ----- Initialize Winsock -----
188
WORD
verReq = MAKEWORD(2, 2);
189
WSADATA wsadata;
190
191
int
err = WSAStartup(verReq, &wsadata);
192
if
(err != 0)
193
{
194
ERRMSG
(
"Winsock Init failed."
);
195
return
1;
196
}
197
198
if
((LOBYTE(wsadata.wVersion) != 2) || (HIBYTE(wsadata.wVersion) !=2))
199
{
200
ERRMSG
(
"Winsock DLL is not 2.2"
);
201
WSACleanup();
202
ERRMSG
(
"Winsock Init failed."
);
203
return
1;
204
}
205
INFMSG
(
"Winsock Init done."
);
206
#endif
207
208
// Check game type & start matcher
209
Wstring
gametype =
"unknown"
;
210
Global
.config.getString(
"GAME"
, gametype);
211
212
// Command-line override for gamemode.
213
// This is for test suites, so they can use
214
// the same config file as the corresponding
215
// matchbot.
216
const
char
*s = argv[0] + strlen(argv[0]);
217
while
(s > argv[0] && *s !=
'/'
)
218
--s;
219
if
(*s ==
'/'
)
220
++s;
221
Wstring
exe = s;
222
exe.
toLower
();
223
DBGMSG
(
"Executable file is ["
<< exe.
get
() <<
"]"
);
224
225
if
(gametype ==
"Generals"
)
226
{
227
DBGMSG
(
"Generals matching behavior"
);
228
s_generalsMatcher
=
new
GeneralsMatcher
;
229
s_generalsMatcher
->connectAndLoop();
230
}
231
else
if
(gametype ==
"GeneralsClient"
)
232
{
233
DBGMSG
(
"Generals TEST client matching behavior"
);
234
s_generalsClientMatcher
=
new
GeneralsClientMatcher
;
235
s_generalsClientMatcher
->connectAndLoop();
236
}
237
else
238
{
239
cerr <<
"\nNo valid GAME entry found!"
<< endl;
240
exit( -1);
241
}
242
243
if
(
s_generalsMatcher
)
244
delete
s_generalsMatcher
;
245
if
(
s_generalsClientMatcher
)
246
delete
s_generalsClientMatcher
;
247
248
return
0;
249
}
250
251
/*----------------------------------------------------------------------+
252
| THREAD: logMonitor |
253
| This thread is spawned once per execution. It will activate after |
254
| midnight and create a new log file. The old one gets put into the |
255
| logfiles directory. |
256
`----------------------------------------------------------------------*/
257
void
logMonitor
(
void
*)
258
{
259
#ifdef _UNIX
260
Xtime
xtime;
261
time_t
curtime;
262
//char timebuf[40];
263
char
filenamebuf[128];
264
int
delay = -1;
265
Global
.config.getInt(
"ROTATEDELAY"
, delay);
266
DBGMSG
(
"ROTATEDELAY: "
<< delay);
267
if
(delay == -1)
268
return
;
269
while
(1)
270
{
271
curtime = time(
NULL
);
272
// get the number of seconds that have passed since midnight
273
// of the current day.
274
curtime -=
TimezoneOffset
();
275
time_t
timeofday = curtime % (delay);
276
if
((timeofday > 0) && (timeofday <= 300))
277
{
278
// We're within 5 minutes of midnight, switch the files.
279
280
DBGMSG
(
"about to switch."
);
281
Wstring
logfilename =
"matchbot.log"
;
282
Global
.config.getString(
"OUTPUTFILE"
, logfilename);
283
Wstring
newfilename =
"tmp.log"
;
284
Global
.config.getString(
"ROTATEFILE"
, newfilename);
285
MsgManager::ReplaceAllStreams
((
FileD
*)
output_device
, logfilename.
get
(), newfilename.
get
());
286
Wstring
logpath =
"logs"
;
287
Global
.config.getString(
"LOGPATH"
, logpath);
288
xtime.
update
();
289
sprintf(filenamebuf,
"%s/%02d%02d%04d_%02d%02d%02d_log"
, logpath.
get
(), xtime.
getMonth
(),
290
xtime.
getMDay
(), xtime.
getYear
(), xtime.
getHour
(), xtime.
getMinute
(), xtime.
getSecond
());
291
rename(newfilename.
get
(), filenamebuf);
292
DBGMSG
(
"Normal: Just been switched. "
<< logfilename.
get
() <<
", "
<< newfilename.
get
());
293
sleep
(60*60*23);
// sleep the next 23 hours
294
}
295
sleep
(300);
296
}
297
#endif
298
}
299
300
void
rotateOutput
(
void
)
301
{
302
Xtime
xtime;
303
char
filenamebuf[128];
304
Wstring
logfilename =
"matchbot.log"
;
305
Wstring
newfilename =
"tmp.log"
;
306
Wstring
logpath =
"logs"
;
307
308
DBGMSG
(
"About to switch."
);
309
310
Global
.config.getString(
"OUTPUTFILE"
, logfilename);
311
Global
.config.getString(
"ROTATEFILE"
, newfilename);
312
Global
.config.getString(
"LOGPATH"
, logpath);
313
314
// This grabs the semaphore, renames the file, and switches the output device
315
MsgManager::ReplaceAllStreams
((
FileD
*)
output_device
, logfilename.
get
(),
316
newfilename.
get
());
317
318
// clean up the tmp filename and move it to the log dir.
319
sprintf(filenamebuf,
"%s/%02d%02d%04d_%02d%02d%02d_log"
, logpath.
get
(),
320
xtime.
getMonth
(), xtime.
getMDay
(), xtime.
getYear
(), xtime.
getHour
(),
321
xtime.
getMinute
(), xtime.
getSecond
());
322
#ifdef _WINDOWS
323
mkdir(logpath.
get
());
324
#else
325
mkdir(logpath.
get
(), 00666);
326
#endif
327
rename(newfilename.
get
(), filenamebuf);
328
329
DBGMSG
(
"Normal: Just been switched. "
<< logfilename.
get
() <<
", "
<<
330
newfilename.
get
());
331
}
332
333
void
paranoidLogMonitor
(
void
*)
334
{
335
#ifdef _UNIX
336
Xtime
xtime;
337
time_t
curtime;
338
//char timebuf[40];
339
char
filenamebuf[128];
340
int
delay = -1;
341
Global
.config.getInt(
"ROTATEDELAY"
, delay);
342
PARANOIDMSG
(
"ROTATEDELAY: "
<< delay);
343
if
(delay == -1)
344
return
;
345
while
(1)
346
{
347
curtime = time(
NULL
);
348
// get the number of seconds that have passed since midnight
349
// of the current day.
350
curtime -=
TimezoneOffset
();
351
time_t
timeofday = curtime % (delay);
352
if
((timeofday > 0) && (timeofday <= 300))
353
{
354
// We're within 5 minutes of midnight, switch the files.
355
356
PARANOIDMSG
(
"about to switch."
);
357
Wstring
logfilename =
"matchbot.log"
;
358
Global
.config.getString(
"PARANOIDFILE"
, logfilename);
359
Wstring
newfilename =
"tmp.log"
;
360
Global
.config.getString(
"ROTATEPARANOIDFILE"
, newfilename);
361
MyMsgManager::ReplaceAllStreams
((
FileD
*)
paranoid_output_device
, logfilename.
get
(), newfilename.
get
());
362
Wstring
logpath =
"logs"
;
363
Global
.config.getString(
"PARANOIDLOGPATH"
, logpath);
364
xtime.
update
();
365
sprintf(filenamebuf,
"%s/%02d%02d%04d_%02d%02d%02d_log"
, logpath.
get
(), xtime.
getMonth
(),
366
xtime.
getMDay
(), xtime.
getYear
(), xtime.
getHour
(), xtime.
getMinute
(), xtime.
getSecond
());
367
rename(newfilename.
get
(), filenamebuf);
368
PARANOIDMSG
(
"Paranoid: Just been switched. "
<< logfilename.
get
() <<
", "
<< newfilename.
get
());
369
sleep
(60*60*23);
// sleep the next 23 hours
370
}
371
sleep
(300);
372
}
373
#endif
374
}
375
376
void
rotateParanoid
(
void
)
377
{
378
Xtime
xtime;
379
char
filenamebuf[128];
380
Wstring
logfilename =
"matchbot.log"
;
381
Wstring
newfilename =
"tmp.log"
;
382
Wstring
logpath =
"logs"
;
383
384
PARANOIDMSG
(
"About to switch."
);
385
386
Global
.config.getString(
"PARANOIDFILE"
, logfilename);
387
Global
.config.getString(
"ROTATEPARANOIDFILE"
, newfilename);
388
Global
.config.getString(
"PARANOIDLOGPATH"
, logpath);
389
390
// This grabs the semaphore, renames the file, and switches the output device
391
MyMsgManager::ReplaceAllStreams
((
FileD
*)
output_device
, logfilename.
get
(),
392
newfilename.
get
());
393
394
// clean up the tmp filename and move it to the log dir.
395
sprintf(filenamebuf,
"%s/%02d%02d%04d_%02d%02d%02d_log"
, logpath.
get
(),
396
xtime.
getMonth
(), xtime.
getMDay
(), xtime.
getYear
(), xtime.
getHour
(),
397
xtime.
getMinute
(), xtime.
getSecond
());
398
#ifdef _WINDOWS
399
mkdir(logpath.
get
());
400
#else
401
mkdir(logpath.
get
(), 00666);
402
#endif
403
rename(newfilename.
get
(), filenamebuf);
404
405
PARANOIDMSG
(
"Paranoid: Just been switched. "
<< logfilename.
get
() <<
", "
<<
406
newfilename.
get
());
407
}
408
409
NULL
#define NULL
Definition
BaseType.h:92
INFMSG
#define INFMSG(X)
Definition
wdebug.h:64
ERRMSG
#define ERRMSG(X)
Definition
wdebug.h:86
DBGMSG
#define DBGMSG(X)
Definition
wdebug.h:128
WORD
unsigned short WORD
Definition
bittype.h:58
global.h
FileD
Definition
filed.h:25
GeneralsClientMatcher
Definition
generals.h:171
GeneralsMatcher
Definition
generals.h:93
MsgManager::setAllStreams
static int setAllStreams(OutputDevice *device)
Definition
wdebug.cpp:45
MsgManager::ReplaceAllStreams
static int ReplaceAllStreams(FileD *output_device, IN char *device_filename, IN char *copy_filename)
Definition
wdebug.cpp:80
MyMsgManager::ReplaceAllStreams
static int ReplaceAllStreams(FileD *output_device, char *device_filename, char *copy_filename)
Definition
mydebug.cpp:78
MyMsgManager::setParanoidStream
static int setParanoidStream(OutputDevice *device)
Definition
mydebug.cpp:55
OutputDevice
Definition
odevice.h:25
StdoutD
Definition
stdoutd.h:25
Wstring
Definition
wstring.h:37
Wstring::toLower
void toLower(void)
Definition
wstring.cpp:511
Wstring::get
char * get(void)
Definition
wstring.cpp:336
Wstring::set
char set(IN char *str)
Definition
wstring.cpp:458
Xtime
Definition
xtime.h:56
Xtime::getYear
int getYear(void) const
Definition
xtime.cpp:774
Xtime::getMDay
int getMDay(void) const
Definition
xtime.cpp:759
Xtime::getMonth
int getMonth(void) const
Definition
xtime.cpp:766
Xtime::getHour
int getHour(void) const
Definition
xtime.cpp:753
Xtime::getMinute
int getMinute(void) const
Definition
xtime.cpp:747
Xtime::update
void update()
Definition
xtime.cpp:284
Xtime::getSecond
int getSecond(void) const
Definition
xtime.cpp:741
main
void main(void)
Definition
test1.cpp:47
generals.h
Global
GlobalClass Global
Definition
global.cpp:22
TimezoneOffset
int TimezoneOffset(void)
Definition
timezone.cpp:66
Signal_Quit
void Signal_Quit(int)
Definition
main.cpp:63
s_generalsClientMatcher
GeneralsClientMatcher * s_generalsClientMatcher
Definition
main.cpp:115
VerifyFileDescriptors
int VerifyFileDescriptors(int requested)
Definition
main.cpp:81
s_generalsMatcher
GeneralsMatcher * s_generalsMatcher
Definition
main.cpp:114
rotateOutput
void rotateOutput(void)
Definition
main.cpp:300
paranoidLogMonitor
void paranoidLogMonitor(void *)
Definition
main.cpp:333
sleep
#define sleep(x)
Definition
main.cpp:52
paranoid_output_device
OutputDevice * paranoid_output_device
Definition
main.cpp:60
output_device
OutputDevice * output_device
Definition
main.cpp:59
Setup_Signals
void Setup_Signals(void)
Definition
main.cpp:69
rotateParanoid
void rotateParanoid(void)
Definition
main.cpp:376
logMonitor
void logMonitor(void *)
Definition
main.cpp:257
mydebug.h
PARANOIDMSG
#define PARANOIDMSG(X)
Definition
mydebug.h:97
return
else return(RetVal)
time_t
long time_t
Definition
wolapi.h:436
Code
Tools
matchbot
main.cpp
Generated by
1.13.2