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
50using namespace std;
51#else
52#define sleep(x) Sleep(1000 * (x))
53#endif
54
55static char *Program_Usage = "A config filename can be given on the command line (default=matchbot.cfg)\n";
56void logMonitor(void *);
57void paranoidLogMonitor(void *);
58
61
62
63void Signal_Quit(int)
64{
65 INFMSG("Exiting due to signal.");
66 exit(2);
67}
68
69void 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
81int 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
116
117int 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 {
158 }
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");
171 {
172 cerr << "Could not open " << paranoid_output_file.get() << " for writing!" << endl;
173 exit( -1);
174 }
175 }
176 else
177 {
179 }
181 DBGMSG("Hack log started!");
182 PARANOIDMSG("Hack log started!");
183
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");
229 s_generalsMatcher->connectAndLoop();
230 }
231 else if (gametype == "GeneralsClient")
232 {
233 DBGMSG("Generals TEST client matching behavior");
235 s_generalsClientMatcher->connectAndLoop();
236 }
237 else
238 {
239 cerr << "\nNo valid GAME entry found!" << endl;
240 exit( -1);
241 }
242
244 delete s_generalsMatcher;
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`----------------------------------------------------------------------*/
257void 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
300void 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
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
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
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
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
#define NULL
Definition BaseType.h:92
#define INFMSG(X)
Definition wdebug.h:64
#define ERRMSG(X)
Definition wdebug.h:86
#define DBGMSG(X)
Definition wdebug.h:128
unsigned short WORD
Definition bittype.h:58
Definition filed.h:25
static int setAllStreams(OutputDevice *device)
Definition wdebug.cpp:45
static int ReplaceAllStreams(FileD *output_device, IN char *device_filename, IN char *copy_filename)
Definition wdebug.cpp:80
static int ReplaceAllStreams(FileD *output_device, char *device_filename, char *copy_filename)
Definition mydebug.cpp:78
static int setParanoidStream(OutputDevice *device)
Definition mydebug.cpp:55
void toLower(void)
Definition wstring.cpp:511
char * get(void)
Definition wstring.cpp:336
char set(IN char *str)
Definition wstring.cpp:458
Definition xtime.h:56
int getYear(void) const
Definition xtime.cpp:774
int getMDay(void) const
Definition xtime.cpp:759
int getMonth(void) const
Definition xtime.cpp:766
int getHour(void) const
Definition xtime.cpp:753
int getMinute(void) const
Definition xtime.cpp:747
void update()
Definition xtime.cpp:284
int getSecond(void) const
Definition xtime.cpp:741
void main(void)
Definition test1.cpp:47
GlobalClass Global
Definition global.cpp:22
int TimezoneOffset(void)
Definition timezone.cpp:66
void Signal_Quit(int)
Definition main.cpp:63
GeneralsClientMatcher * s_generalsClientMatcher
Definition main.cpp:115
int VerifyFileDescriptors(int requested)
Definition main.cpp:81
GeneralsMatcher * s_generalsMatcher
Definition main.cpp:114
void rotateOutput(void)
Definition main.cpp:300
void paranoidLogMonitor(void *)
Definition main.cpp:333
#define sleep(x)
Definition main.cpp:52
OutputDevice * paranoid_output_device
Definition main.cpp:60
OutputDevice * output_device
Definition main.cpp:59
void Setup_Signals(void)
Definition main.cpp:69
void rotateParanoid(void)
Definition main.cpp:376
void logMonitor(void *)
Definition main.cpp:257
#define PARANOIDMSG(X)
Definition mydebug.h:97
else return(RetVal)
long time_t
Definition wolapi.h:436