1 /* bzflag
2 * Copyright (c) 1993-2021 Tim Riker
3 *
4 * This package is free software; you can redistribute it and/or
5 * modify it under the terms of the license found in the file
6 * named COPYING that should have accompanied this file.
7 *
8 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
9 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11 */
12
13 /* this should be the only header necessary except for headers specific
14 * to the class implementation (such as version.h)
15 */
16 #include "CmdLineOptions.h"
17
18 // System headers
19 #include <time.h>
20
21 // implementation-specific bzflag headers
22 #include "version.h"
23 #include "Team.h"
24 #include "TextUtils.h"
25 #include "BZDBCache.h"
26 #include "BzMaterial.h"
27 #include "ObstacleMgr.h"
28
29 /* FIXME implementation specific header for global that should eventually go away */
30 #include <vector>
31 #include <set>
32
33 // for -pidfile option
34 #ifdef _WIN32
35 #include <process.h>
36 #else
37 #include <sys/types.h>
38 #include <unistd.h>
39 #endif
40
41 // implementation-specific bzfs-specific headers
42 #include "bzfs.h"
43 #include "RecordReplay.h"
44 #include "BZWError.h"
45 #include "Permissions.h"
46 #include "EntryZones.h"
47
48
49 // import from TextUtils for convenience
50 using TextUtils::compare_nocase;
51
52 const char *usageString =
53 "[-a <vel> <rot>] "
54 "[-adminlagannounce <time/ms>] "
55 "[-admsg <text>] "
56 "[-advertise <group,group...>] "
57 "[-autoTeam] "
58 "[-b] "
59 "[-badwords <filename>] "
60 "[-ban ip{,ip}*] "
61 "[-banfile <filename>] "
62 "[-c] "
63 "[-cache <url prefix>] "
64 "[-cacheout <filename>] "
65 "[-conf <filename>] "
66 "[-cr] "
67 "[-d] "
68 "[-density <num>] "
69 "[-disableBots] "
70 "[+f {good|<id>}] "
71 "[-f {bad|<id>}] "
72 "[-fb] "
73 "[-filterCallsigns] "
74 "[-filterChat] "
75 "[-filterSimple] "
76 "[-g] "
77 "[-gndtex <texture name>] "
78 "[-groupdb <group file>] "
79 "[-h] "
80 "[-handicap] "
81 "[-helpmsg <file> <name>] "
82 "[-i interface] "
83 "[-j] "
84 "[-jitterdrop <num>] "
85 "[-jitterwarn <time/ms>] "
86 "[-lagannounce <time/ms>] "
87 "[-lagdrop <num>] "
88 "[-lagwarn <time/ms>] "
89 "[-loadplugin <pluginname,commandline>] "
90 "[-masterBanURL <URL>] "
91 "[-maxidle <time/s>] "
92 "[-mp {<count>|[<count>][,<count>][,<count>][,<count>][,<count>][,<count>]}] "
93 "[-mps <score>] "
94 "[-ms <shots>] "
95 "[-mts <score>] "
96 "[-noMasterBanlist] "
97 "[-noradar] "
98 "[-noTeamKills] "
99 "[-offa] "
100 "[-p <port>] "
101 "[-packetlossdrop <num>] "
102 "[-packetlosswarn <%>] "
103 "[-passwd <password>] "
104 "[-pidfile <filename>] "
105 "[-poll <variable>=<value>] "
106 #ifdef PRINTSCORE
107 "[-printscore] "
108 #endif
109 "[-publictitle <server-description>] "
110 "[-publicaddr <server-hostname>[:<server-port>]] "
111 "[-publiclist <list-server-url>] "
112 "[-publickey <key>] "
113 "[-q] "
114 "[+r] "
115 "[-rabbit [score|killer|random]] "
116 "[-recbuf <Mbytes>] "
117 "[-recbufonly] "
118 "[-recdir <dirname>] "
119 "[-replay] "
120 "[-reportfile <filename>] "
121 "[-reportpipe <filename>] "
122 "[+s <flag-count>] "
123 "[-s <flag-count>] "
124 "[-sa] "
125 "[-sb] "
126 "[-set <name> <value>] "
127 "[-setforced <name> <value>] "
128 "[-sl <id> <num>] "
129 "[-spamtime <time>] "
130 "[-spamwarn <warnAmt>] "
131 "[-speedtol <tolerance>] "
132 "[-srvmsg <text>] "
133 "[-st <time>] "
134 "[-sw <num>] "
135 "[-synctime] "
136 "[-synclocation] "
137 "[-t] "
138 "[-tftimeout <seconds>] "
139 "[-time {<seconds>|endTime}] "
140 "[-timemanual] "
141 "[-tk] "
142 "[-tkannounce] "
143 "[-tkkr <percent>] "
144 "[-ts [micros]] "
145 #ifdef HAVE_MINIUPNPC_MINIUPNPC_H
146 "[-UPnP] "
147 #endif
148 "[-userdb <user permissions file>] "
149 "[-utc] "
150 "[-vars <filename>] "
151 "[-version] "
152 "[-world <filename>] "
153 "[-worldsize <world size>] "
154 "[-ws <number of wall sides>] ";
155
156 const char *extraUsageString =
157 "\n"
158 "BZFS Option Descriptions\n"
159 "\n"
160 "\t-a: maximum acceleration settings\n"
161 "\t-adminlagannounce: lag announcement threshhold time [ms]\n"
162 "\t-admsg: specify a <msg> which will be broadcast every 15 minutes\n"
163 "\t-advertise: specify which groups to advertise to (list server)\n"
164 "\t-autoTeam: automatically assign players to teams when they join\n"
165 "\t-b: randomly oriented buildings\n"
166 "\t-badwords: bad-word file\n"
167 "\t-ban ip{,ip}*: ban players based on ip address\n"
168 "\t-banfile: specify a file to load and store the banlist in\n"
169 "\t-c: classic capture-the-flag style game,\n"
170 "\t-cache: url to get binary formatted world\n"
171 "\t-cacheout: generate a binary cache file\n"
172 "\t-conf: configuration file\n"
173 "\t-cr: capture-the-flag style game with random world\n"
174 "\t-d: increase debugging level\n"
175 "\t-density: specify building density for random worlds (default=5)\n"
176 "\t-disableBots: disallow clients from using autopilot or robots\n"
177 "\t+f: always have flag <id> available\n"
178 "\t-f: never randomly generate flag <id>\n"
179 "\t-fb: allow flags on box buildings\n"
180 "\t-filterCallsigns: filter callsigns to disallow inappropriate user names\n"
181 "\t-filterChat: filter chat messages\n"
182 "\t-filterSimple: perform simple exact matches with the bad word list\n"
183 "\t-g: serve one game and then exit\n"
184 "\t-gndtex: specify ground texture\n"
185 "\t-groupdb: file to read for group permissions\n"
186 "\t-h: use random building heights\n"
187 "\t-handicap: give advantage based on relative playing ability\n"
188 "\t-helpmsg: show the lines in <file> on command /help <name>\n"
189 "\t-i: listen on <interface>\n"
190 "\t-j: allow jumping\n"
191 "\t-jitterdrop: drop player after this many jitter warnings\n"
192 "\t-jitterwarn: jitter warning threshhold time [ms]\n"
193 "\t-lagannounce: lag announcement threshhold time [ms]\n"
194 "\t-lagdrop: drop player after this many lag warnings\n"
195 "\t-lagwarn: lag warning threshhold time [ms]\n"
196 "\t-loadplugin: load the specified plugin with the specified commandline\n"
197 "\t\tstring\n"
198 "\t-masterBanURL: URL to atempt to get the master ban list from <URL>\n"
199 "\t-maxidle: idle kick threshhold [s]\n"
200 "\t-mp: maximum players total or per team\n"
201 "\t-mps: set player score limit on each game\n"
202 "\t-ms: maximum simultaneous shots per player\n"
203 "\t-mts: set team score limit on each game\n"
204 "\t-noMasterBanlist: has public servers ignore the master ban list\n"
205 "\t-noradar: disallow the use of radar\n"
206 "\t-noTeamKills: Players on the same team are immune to each other's shots. Rogue is excepted.\n"
207 "\t-offa: teamless free-for-all game stye\n"
208 "\t-p: use alternative port (default=5154)\n"
209 "\t-packetlossdrop: drop player after this many packetloss warnings\n"
210 "\t-packetlosswarn: packetloss warning threshold [%]\n"
211 "\t-passwd: specify a <password> for operator commands\n"
212 "\t-pidfile: write the process id into <filename> on startup\n"
213 "\t-poll: configure several aspects of the in-game polling system\n"
214 #ifdef PRINTSCORE
215 "\t-printscore: write score to stdout whenever it changes\n"
216 #endif
217 "\t-publictitle <server-description>\n"
218 "\t-publicaddr <effective-server-hostname>[:<effective-server-port>]\n"
219 "\t-publiclist <list-server-url>\n"
220 "\t-publickey <key>\n"
221 "\t-q: don't listen for or respond to pings\n"
222 "\t+r: all shots ricochet\n"
223 "\t-rabbit [score|killer|random]: rabbit chase style\n"
224 "\t-recbuf <Mbytes>: start with a recording buffer of specified megabytes\n"
225 "\t-recbufonly: disable recording directly to files\n"
226 "\t-recdir <dirname>: specify the directory for recorded file\n"
227 "\t-replay: setup the server to replay a previously saved game\n"
228 "\t-reportfile <filename>: the file to store reports in\n"
229 "\t-reportpipe <filename>: the program to pipe reports through\n"
230 "\t+s: always have <num> super flags (default=16)\n"
231 "\t-s: allow up to <num> super flags (default=16)\n"
232 "\t-sa: insert antidote superflags\n"
233 "\t-sb: allow tanks to respawn on buildings\n"
234 "\t-set <name> <value>: set a BZDB variable's value\n"
235 "\t-setforced <name> <value>: set a BZDB variable's value (whether it\n"
236 "\t\texists or not)\n"
237 "\t-sl: limit flag <id> to <num> shots\n"
238 "\t-spamtime <time>: make <time> be the required time in seconds between\n"
239 "\t\tmessages sent that are alike\n"
240 "\t-spamwarn <warnAmt>: warn a spammer that sends messages before\n"
241 "\t\tspamtime times out <warnAmt> many times\n"
242 "\t-speedtol: multiplier of normal speed for auto kick (default=1.25)\n"
243 "\t\tshould not be less than 1.0\n"
244 "\t-srvmsg: specify a <msg> to print upon client login\n"
245 "\t-st: shake bad flags in <time> seconds\n"
246 "\t-sw: shake bad flags after <num> wins\n"
247 "\t-synctime: synchronize time of day on all clients\n"
248 "\t-synclocation: synchronize latitude and longitude on all clients\n"
249 "\t-t: allow teleporters\n"
250 "\t-tftimeout: set timeout for team flag zapping (default=30)\n"
251 "\t-time: set time limit on each game in format of either seconds or ending time in h[h]:[mm:[ss]] format\n"
252 "\t-timemanual: countdown for timed games is started with /countdown\n"
253 "\t-tk: player does not die when killing a teammate\n"
254 "\t-tkannounce: announces team kills to the admin channel\n"
255 "\t-tkkr: team-kills-to-wins percentage (1-100) for kicking tk-ing players\n"
256 "\t-ts [micros]: timestamp all console output, [micros] to include\n"
257 "\t\tmicroseconds\n"
258 #ifdef HAVE_MINIUPNPC_MINIUPNPC_H
259 "\t-UPnP: enable UPnP\n"
260 #endif
261 "\t-userdb: file to read for user access permissions\n"
262 "\t-utc: timestamp console output in UTC instead of local time, implies -ts\n"
263 "\t-vars: file to read for worlds configuration variables\n"
264 "\t-version: print version and exit\n"
265 "\t-world: world file to load\n"
266 "\t-worldsize: numeric value for the size of the world (default=400)\n"
267 "\t-ws: numeric value for the number off outer walls (default=4)\n"
268 "\n"
269 "Poll Variables: (see -poll)\n"
270 "\n"
271 "\tbanTime: number of minutes player should be banned (default=300)\n"
272 "\tvetoTime: max seconds authorized user has to abort poll (default=20)\n"
273 "\tvotePercentage: percentage of players required to affirm a poll\n"
274 "\t\t(default=50.1%)\n"
275 "\tvoteRepeatTime: minimum seconds required before a player may request\n"
276 "\t\tanother vote (default=300)\n"
277 "\tvotesRequired: minimum number of additional votes required to make a\n"
278 "\t\tvote valid (default=2)\n"
279 "\tvoteTime: maximum amount of time player has to vote, in seconds\n"
280 "\t\t(default=60)\n"
281 "\n";
282
283
284 // temp variables for carrying flag options from parse to finalize
285 static std::vector<std::string> storedFlagDisallows;
286 static std::vector<std::string> storedFlagCounts;
287 static std::map<std::string, int> storedFlagLimits;
288 static std::map<std::string, std::string> bzdbVarQueue;
289
290 /* private */
291
printVersion()292 static void printVersion()
293 {
294 std::cout << "BZFlag server " << getAppVersion() << " (protocol " << getProtocolVersion() <<
295 ") http://BZFlag.org/\n";
296 std::cout << bzfcopyright << std::endl;
297 std::cout.flush();
298 }
299
usage(const char * pname)300 static void usage(const char *pname)
301 {
302 printVersion();
303 std::cerr << "\nUsage: " << pname << ' ' << usageString << std::endl;
304 exit(1);
305 }
306
extraUsage(const char * pname)307 static void extraUsage(const char *pname)
308 {
309 char buffer[64];
310 printVersion();
311 std::cout << "\nUsage: " << pname << ' ' << usageString << std::endl;
312 std::cout << std::endl << extraUsageString << std::endl << "Flag codes:\n";
313 for (FlagTypeMap::iterator it = FlagType::getFlagMap().begin(); it != FlagType::getFlagMap().end(); ++it)
314 {
315 snprintf(buffer, 64, "\t%2.2s %s\n", (*it->second).flagAbbv.c_str(), (*it->second).flagName.c_str());
316 std::cout << buffer;
317 }
318 exit(0);
319 }
320
checkArgc(int count,int & i,int argc,const char * option,const char * type=NULL)321 static void checkArgc(int count, int& i, int argc, const char* option, const char *type = NULL)
322 {
323 if ((i+count) >= argc)
324 {
325 if (count > 1)
326 std::cerr << count << " argument(s) expected for " << option << '\n';
327 else if (type != NULL)
328 std::cerr << type << " argument expected for " << option << '\n';
329 else
330 std::cerr << "argument expected for " << option << '\n';
331 usage("bzfs");
332 }
333
334 i++; // just skip the option argument string
335 }
336
checkFromWorldFile(const char * option,bool fromWorldFile)337 static void checkFromWorldFile (const char *option, bool fromWorldFile)
338 {
339 if (fromWorldFile)
340 {
341 std::cerr << "option \"" << option << "\" cannot be set within a world file" << '\n';
342 usage("bzfs");
343 }
344 }
345
346
347
parsePlayerCount(const char * argv,CmdLineOptions & options)348 static bool parsePlayerCount(const char *argv, CmdLineOptions &options)
349 {
350 /* either a single number or 5 or 6 optional numbers separated by
351 * 4 or 5 (mandatory) commas.
352 */
353 const char *scan = argv;
354 while (*scan && *scan != ',') scan++;
355
356 if (*scan == ',')
357 {
358 // okay, it's the comma separated list. count commas
359 int commaCount = 1;
360 while (*++scan)
361 {
362 if (*scan == ',')
363 commaCount++;
364 }
365 if (commaCount != 5)
366 {
367 std::cerr << "improper player count list\n";
368 return false;
369 }
370
371 // no team size limit by default for real teams, set it to max players
372 int i;
373 for (i = 0; i < CtfTeams ; i++)
374 options.maxTeam[i] = maxRealPlayers;
375 // allow 5 Observers by default
376 options.maxTeam[ObserverTeam] = 5;
377
378 // now get the new counts
379
380 // number of counts given
381 int countCount = 0;
382 scan = argv;
383 // ctf teams plus observers
384 for (i = 0; i < CtfTeams + 1; i++)
385 {
386 char *tail;
387 long count = strtol(scan, &tail, 10);
388 if (tail != scan)
389 {
390 // got a number
391 countCount++;
392 if (count < 0)
393 options.maxTeam[i] = 0;
394 else
395 {
396 if (count > maxRealPlayers)
397 {
398 if (i == ObserverTeam && count > MaxPlayers)
399 options.maxTeam[i] = MaxPlayers;
400 else
401 options.maxTeam[i] = maxRealPlayers;
402 }
403 else
404 options.maxTeam[i] = uint8_t(count);
405 }
406 } // end if tail != scan
407 while (*tail && *tail != ',') tail++;
408 scan = tail + 1;
409 } // end iteration over teams
410
411 // if no/zero players were specified, allow a bunch of rogues
412 bool allZero = true;
413 for (i = 0; i < CtfTeams; i++)
414 {
415 if (options.maxTeam[i] != 0)
416 allZero = false;
417 }
418 if (allZero)
419 options.maxTeam[RogueTeam] = MaxPlayers;
420
421 // if all counts explicitly listed then add 'em up and set maxRealPlayers
422 // (unless max total players was explicitly set)
423 if (countCount >= CtfTeams && maxRealPlayers == MaxPlayers)
424 {
425 maxRealPlayers = 0;
426 for (i = 0; i < CtfTeams ; i++)
427 maxRealPlayers += options.maxTeam[i];
428 }
429
430 }
431 else
432 {
433 /* single number was provided instead of comma-separated */
434 char *tail;
435 long count = strtol(argv, &tail, 10);
436 if (argv == tail)
437 {
438 std::cerr << "improper player count\n";
439 return false;
440 }
441 if (count < 1)
442 maxRealPlayers = 1;
443 else
444 {
445 if (count > MaxPlayers)
446 maxRealPlayers = MaxPlayers;
447 else
448 maxRealPlayers = uint8_t(count);
449 }
450 // limit max team size to max players
451 for (int i = 0; i < CtfTeams ; i++)
452 {
453 if (options.maxTeam[i] > maxRealPlayers)
454 options.maxTeam[i] = maxRealPlayers;
455 }
456 } // end check if comm-separated list
457
458 maxPlayers = maxRealPlayers + options.maxTeam[ObserverTeam];
459 if (maxPlayers > MaxPlayers)
460 maxPlayers = MaxPlayers;
461
462 return true;
463 }
464
parseConfFile(const char * file,int & ac)465 static char **parseConfFile( const char *file, int &ac)
466 {
467 std::vector<std::string> tokens;
468 ac = 0;
469
470 BZWError errorHandler(file);
471
472 std::ifstream confStrm(file);
473 if (confStrm.is_open())
474 {
475 char buffer[1024];
476 confStrm.getline(buffer,1024);
477
478 if (!confStrm.good())
479 {
480 errorHandler.fatalError(std::string("could not find bzflag configuration file"), 0);
481 exit(1);
482 }
483
484 confStrm.seekg(0, std::ifstream::beg);
485 while (confStrm.good())
486 {
487 confStrm.getline(buffer,1024);
488 std::string line = buffer;
489 int startPos = line.find_first_not_of("\t \r\n");
490 while ((startPos >= 0) && (line.at(startPos) != '#'))
491 {
492 int endPos;
493 if (line.at(startPos) == '"')
494 {
495 startPos++;
496 endPos = line.find_first_of('"', startPos);
497 }
498 else
499 endPos = line.find_first_of("\t \r\n", startPos+1);
500 if (endPos < 0)
501 endPos = line.length();
502 tokens.push_back(line.substr(startPos,endPos-startPos));
503 startPos = line.find_first_not_of("\t \r\n", endPos+1);
504 }
505 }
506 }
507 else
508 {
509 errorHandler.fatalError(std::string("could not find bzflag configuration file"), 0);
510 exit(1);
511 }
512
513 char **av = new char*[tokens.size()+1];
514 av[0] = strdup("bzfs");
515 ac = 1;
516 for (std::vector<std::string>::iterator it = tokens.begin(); it != tokens.end(); ++it)
517 av[ac++] = strdup((*it).c_str());
518 return av;
519 }
520
parseWorldOptions(const char * file,int & ac)521 static char **parseWorldOptions (const char *file, int &ac)
522 {
523 std::vector<std::string> tokens;
524 ac = 0;
525
526 std::ifstream confStrm(file);
527 if (confStrm.is_open())
528 {
529 char buffer[1024];
530 confStrm.getline(buffer,1024);
531
532 if (!confStrm.good())
533 {
534 std::cerr << "world file not found\n";
535 usage("bzfs");
536 }
537
538 while (confStrm.good())
539 {
540 std::string line = buffer;
541 int startPos = line.find_first_not_of("\t \r\n");
542 if (strncasecmp ("options", line.c_str() + startPos, 7) == 0)
543 {
544 confStrm.getline(buffer,1024);
545 break;
546 }
547 confStrm.getline(buffer,1024);
548 }
549
550 while (confStrm.good())
551 {
552 std::string line = buffer;
553 int startPos = line.find_first_not_of("\t \r\n");
554 if (strncasecmp ("end", line.c_str() + startPos, 3) == 0)
555 break;
556
557 while ((startPos >= 0) && (line.at(startPos) != '#'))
558 {
559 int endPos;
560 if (line.at(startPos) == '"')
561 {
562 startPos++;
563 endPos = line.find_first_of('"', startPos);
564 }
565 else
566 endPos = line.find_first_of("\t \r\n", startPos+1);
567 if (endPos < 0)
568 endPos = line.length();
569 tokens.push_back(line.substr(startPos,endPos-startPos));
570 startPos = line.find_first_not_of("\t \r\n", endPos+1);
571 }
572 confStrm.getline(buffer,1024);
573 }
574 }
575
576 char **av = new char*[tokens.size()+1];
577 av[0] = strdup("bzfs");
578 ac = 1;
579 for (std::vector<std::string>::iterator it = tokens.begin(); it != tokens.end(); ++it)
580 av[ac++] = strdup((*it).c_str());
581
582 return av;
583 }
584
585
586 static bool accLimitSet = false;
587 static bool allFlagsOut = false;
588
parse(int argc,char ** argv,CmdLineOptions & options,bool fromWorldFile)589 void parse(int argc, char **argv, CmdLineOptions &options, bool fromWorldFile)
590 {
591 // prepare flag counts
592 int i;
593
594 // InertiaGameStyle maintained just for compatibility
595 // Same effect is achieved setting linear/angular Acceleration
596 options.gameOptions |= int(InertiaGameStyle);
597
598 // parse command line
599 int playerCountArg = 0,playerCountArg2 = 0;
600 for (i = 1; i < argc; i++)
601 {
602 if (strcmp(argv[i], "-a") == 0)
603 {
604 // momentum settings
605 accLimitSet = true;
606 checkArgc(2, i, argc, argv[i]);
607 options.linearAcceleration = (float)atof(argv[i]);
608 options.angularAcceleration = (float)atof(argv[++i]);
609 if (options.linearAcceleration < 0.0f)
610 options.linearAcceleration = 0.0f;
611 if (options.angularAcceleration < 0.0f)
612 options.angularAcceleration = 0.0f;
613 }
614 else if (strcmp(argv[i], "-adminlagannounce") == 0)
615 {
616 checkArgc(1, i, argc, argv[i]);
617 options.adminlagannounce = atoi(argv[i])/1000.0f;
618 }
619 else if (strcmp(argv[i], "-admsg") == 0)
620 {
621 checkArgc(1, i, argc, argv[i]);
622 if ((options.advertisemsg != "") || (strlen (argv[i]) == 0))
623 options.advertisemsg += "\\n";
624 options.advertisemsg += argv[i];
625 }
626 else if (strcmp(argv[i], "-advertise") == 0)
627 {
628 checkArgc(1, i, argc, argv[i]);
629 if (checkCommaList (argv[i], 2048))
630 std::cerr << "Invalid group list for -advertise" << std::endl;
631 else
632 options.advertiseGroups = argv[i];
633 }
634 else if (strcmp(argv[i], "-autoTeam") == 0)
635 options.autoTeam = true;
636 else if (strcmp(argv[i], "-b") == 0)
637 {
638 // random rotation to boxes in capture-the-flag game
639 options.randomBoxes = true;
640 }
641 else if (strcmp(argv[i], "-badwords") == 0)
642 {
643 checkFromWorldFile(argv[i], fromWorldFile);
644 checkArgc(1, i, argc, argv[i]);
645 options.filterFilename = argv[i];
646 }
647 else if (strcmp(argv[i], "-ban") == 0)
648 {
649 checkFromWorldFile(argv[i], fromWorldFile);
650 checkArgc(1, i, argc, argv[i]);
651 options.acl.ban(argv[i]);
652 }
653 else if (strcmp(argv[i], "-banfile") == 0)
654 {
655 checkFromWorldFile(argv[i], fromWorldFile);
656 checkArgc(1, i, argc, argv[i]);
657 options.acl.setBanFile(argv[i]);
658 if (!options.acl.load())
659 {
660 std::cerr << "could not load banfile \"" << argv[i] << "\"" << std::endl;
661 usage(argv[0]);
662 }
663 }
664 else if (strcmp(argv[i], "-c") == 0)
665 {
666 // capture the flag style
667 if (options.gameType == RabbitChase)
668 {
669 std::cerr << "Capture the flag incompatible with Rabbit Chase" << std::endl;
670 std::cerr << "Capture the flag assumed" << std::endl;
671 }
672 options.gameType = ClassicCTF;
673 }
674 else if (strcmp(argv[i], "-cache") == 0)
675 {
676 checkFromWorldFile(argv[i], fromWorldFile);
677 checkArgc(1, i, argc, argv[i]);
678 options.cacheURL = argv[i];
679 }
680 else if (strcmp(argv[i], "-cacheout") == 0)
681 {
682 checkFromWorldFile(argv[i], fromWorldFile);
683 checkArgc(1, i, argc, argv[i]);
684 options.cacheOut = argv[i];
685 }
686 else if (strcmp(argv[i], "-conf") == 0)
687 {
688 checkFromWorldFile(argv[i], fromWorldFile);
689 checkArgc(1, i, argc, argv[i]);
690 int ac;
691 char **av;
692 av = parseConfFile(argv[i], ac);
693 // Theoretically we could merge the options specified in the conf file after parsing
694 // the cmd line options. But for now just override them on the spot
695 parse(ac, av, options);
696 for (int j = 0; j < ac; j++)
697 free(av[j]);
698 delete[] av;
699 options.numAllowedFlags = 0;
700 }
701 else if (strcmp(argv[i], "-cr") == 0)
702 {
703 // CTF with random world
704 options.randomCTF = true;
705 // capture the flag style
706 if (options.gameType == RabbitChase)
707 {
708 std::cerr << "Capture the flag incompatible with Rabbit Chase" << std::endl;
709 std::cerr << "Capture the flag assumed" << std::endl;
710 }
711 options.gameType = ClassicCTF;
712 }
713 else if (strcmp(argv[i], "-density") == 0)
714 {
715 if (i+1 != argc && isdigit(*argv[i+1]))
716 {
717 options.citySize = atoi(argv[i+1]);
718 i++;
719 }
720 else
721 checkArgc(1, i, argc, argv[i], "integer");
722 }
723 else if (strcmp(argv[i], "-disableBots") == 0)
724 {
725 // disallow clients from using autopilot or bots
726 BZDB.set(StateDatabase::BZDB_DISABLEBOTS, "true");
727 }
728 else if (strncmp(argv[i], "-d", 2) == 0)
729 {
730 checkFromWorldFile(argv[i], fromWorldFile);
731 // increase debug level - this must be the last
732 // option beginning with -d so that -dd, -ddd, etc. work
733 const char num = argv[i][2];
734 if ((num >= '0') && (num <= '9') && (argv[i][3] == 0))
735 debugLevel = num - '0';
736 else
737 {
738 int count = 0;
739 const char *scan;
740 for (scan = (argv[i] + 1); *scan == 'd'; scan++)
741 count++;
742 if (*scan != '\0')
743 {
744 std::cerr << "ERROR: bad argument [" << argv[i] << "]" << std::endl;
745 usage(argv[0]);
746 }
747 debugLevel += count;
748 // std::cout << "Debug level is now " << debugLevel << "" << std::endl;
749 }
750 }
751 else if (strcmp(argv[i], "-f") == 0)
752 {
753 // disallow given flag
754 checkArgc(1, i, argc, argv[i]);
755 storedFlagDisallows.push_back(argv[i]);
756 }
757 else if (strcmp(argv[i], "+f") == 0)
758 {
759 // add required flag
760 checkArgc(1, i, argc, argv[i]);
761 storedFlagCounts.push_back(argv[i]);
762 }
763 else if (strcmp(argv[i], "-fb") == 0)
764 {
765 // flags on buildings
766 options.flagsOnBuildings = true;
767 }
768 else if (strcmp(argv[i], "-filterCallsigns") == 0)
769 {
770 checkFromWorldFile(argv[i], fromWorldFile);
771 options.filterCallsigns = true;
772 }
773 else if (strcmp(argv[i], "-filterChat") == 0)
774 {
775 checkFromWorldFile(argv[i], fromWorldFile);
776 options.filterChat = true;
777 }
778 else if (strcmp(argv[i], "-filterSimple") == 0)
779 {
780 checkFromWorldFile(argv[i], fromWorldFile);
781 options.filterSimple = true;
782 }
783 else if (strcmp(argv[i], "-g") == 0)
784 options.oneGameOnly = true;
785 else if (strcmp(argv[i], "-gndtex") == 0)
786 {
787 checkArgc(1, i, argc, argv[i]);
788 BzMaterial material;
789 material.setName("GroundMaterial");
790 material.setTexture(argv[i]);
791 MATERIALMGR.addMaterial(&material);
792 }
793 else if (strcmp(argv[i], "-groupdb") == 0)
794 {
795 checkFromWorldFile(argv[i], fromWorldFile);
796 checkArgc(1, i, argc, argv[i]);
797 groupsFile = argv[i];
798 logDebugMessage(1,"using group file \"%s\"\n", argv[i]);
799 }
800 else if (strcmp(argv[i], "-h") == 0)
801 options.randomHeights = true;
802 else if (strcmp(argv[i], "-help") == 0)
803 extraUsage(argv[0]);
804 else if (strcmp(argv[i], "-helpmsg") == 0)
805 {
806 checkFromWorldFile(argv[i], fromWorldFile);
807 checkArgc(2, i, argc, argv[i]);
808 std::ifstream helpfile(argv[i]);
809 if (!helpfile)
810 std::cerr << "warning: helpmsg file \"" << argv[i] << "\" does not exist or can't be read" << std::endl;
811 else if (!options.textChunker.parseFile(argv[i], argv[i+1], 50, MessageLen))
812 {
813 std::cerr << "couldn't read helpmsg file \"" << argv[i] << "\"" << std::endl;
814 usage(argv[0]);
815 }
816 i++;
817 }
818 else if (strcmp(argv[i], "-i") == 0)
819 {
820 checkFromWorldFile(argv[i], fromWorldFile);
821 // use a different interface
822 checkArgc(1, i, argc, argv[i]);
823 options.pingInterface = argv[i];
824 }
825 else if (strcmp(argv[i], "-j") == 0)
826 {
827 // allow jumping
828 options.gameOptions |= int(JumpingGameStyle);
829 }
830 else if (strcmp(argv[i], "-handicap") == 0)
831 {
832 // allow handicap advantage
833 options.gameOptions |= int(HandicapGameStyle);
834 }
835 else if (strcmp(argv[i], "-jitterdrop") == 0)
836 {
837 checkArgc(1, i, argc, argv[i]);
838 options.maxjitterwarn = atoi(argv[i]);
839 }
840 else if (strcmp(argv[i], "-jitterwarn") == 0)
841 {
842 checkArgc(1, i, argc, argv[i]);
843 options.jitterwarnthresh = atoi(argv[i])/1000.0f;
844 }
845 else if (strcmp(argv[i], "-lagannounce") == 0)
846 {
847 checkArgc(1, i, argc, argv[i]);
848 options.lagannounce = atoi(argv[i])/1000.0f;
849 }
850 else if (strcmp(argv[i], "-lagdrop") == 0)
851 {
852 checkArgc(1, i, argc, argv[i]);
853 options.maxlagwarn = atoi(argv[i]);
854 }
855 else if (strcmp(argv[i], "-lagwarn") == 0)
856 {
857 checkArgc(1, i, argc, argv[i]);
858 options.lagwarnthresh = atoi(argv[i])/1000.0f;
859 }
860 else if (strcmp(argv[i], "-loadplugin") == 0)
861 {
862 checkArgc(1, i, argc, argv[i]);
863 std::vector<std::string> a = TextUtils::tokenize(argv[i],std::string(","), 2);
864 CmdLineOptions::pluginDef pDef;
865 if (a.size() >= 1)
866 pDef.plugin = a[0];
867 if (a.size() >= 2)
868 pDef.command = a[1];
869 if (pDef.plugin.size())
870 options.pluginList.push_back(pDef);
871 }
872 else if (strcmp(argv[i], "-maxidle") == 0)
873 {
874 checkArgc(1, i, argc, argv[i]);
875 options.idlekickthresh = (float) atoi(argv[i]);
876 }
877 else if (strcmp(argv[i], "-mp") == 0)
878 {
879 // set maximum number of players
880 checkArgc(1, i, argc, argv[i]);
881 if (playerCountArg == 0)
882 playerCountArg = i;
883 else
884 playerCountArg2 = i;
885 }
886 else if (strcmp(argv[i], "-mps") == 0)
887 {
888 // set maximum player score
889 checkArgc(1, i, argc, argv[i]);
890 options.maxPlayerScore = atoi(argv[i]);
891 if (options.maxPlayerScore < 1)
892 {
893 std::cerr << "disabling player score limit" << std::endl;
894 options.maxPlayerScore = 0;
895 }
896 }
897 else if (strcmp(argv[i], "-ms") == 0)
898 {
899 // set maximum number of shots
900 checkArgc(1, i, argc, argv[i]);
901 int newMaxShots = atoi(argv[i]);
902 if (newMaxShots == 0)
903 {
904 std::cerr << "WARNING: tanks will not be able to shoot" << std::endl;
905 options.maxShots = 0;
906 }
907 else if (newMaxShots < 1)
908 {
909 std::cerr << "using minimum number of shots of 1" << std::endl;
910 options.maxShots = 1;
911 }
912 else if (newMaxShots > MaxShots)
913 {
914 std::cerr << "using maximum number of shots of " << MaxShots << std::endl;
915 options.maxShots = uint16_t(MaxShots);
916 }
917 else options.maxShots = uint16_t(newMaxShots);
918 }
919 else if (strcmp(argv[i], "-mts") == 0)
920 {
921 // set maximum team score
922 checkArgc(1, i, argc, argv[i]);
923 options.maxTeamScore = atoi(argv[i]);
924 if (options.maxTeamScore < 1)
925 {
926 std::cerr << "disabling team score limit" << std::endl;
927 options.maxTeamScore = 0;
928 }
929 }
930 else if (strcmp(argv[i],"-noMasterBanlist") == 0)
931 {
932 checkFromWorldFile(argv[i], fromWorldFile);
933 options.suppressMasterBanList = true;
934 }
935 else if (strcmp(argv[i],"-noradar") == 0)
936 BZDB.set(StateDatabase::BZDB_RADARLIMIT, "-1.0");
937 else if (strcmp(argv[i],"-masterBanURL") == 0)
938 {
939 /* if this is the first master ban url, override the default
940 * list. otherwise just keep adding urls.
941 */
942 checkFromWorldFile(argv[i], fromWorldFile);
943 if (!options.masterBanListOverridden)
944 {
945 options.masterBanListURL.clear();
946 options.masterBanListOverridden = true;
947 }
948 checkArgc(1, i, argc, argv[i]);
949 options.masterBanListURL.push_back(argv[i]);
950 }
951 else if (strcmp(argv[i], "-noTeamKills") == 0)
952 {
953 // disable team killing
954 if (options.gameType == OpenFFA)
955 std::cerr << "noteamkills check WARNING: -noTeamKills is incompatible with -offa, ignoring" << std::endl;
956 else
957 options.gameOptions |= int(NoTeamKillsGameStyle);
958 }
959 else if (strcmp(argv[i], "-p") == 0)
960 {
961 // use a different port
962 checkFromWorldFile(argv[i], fromWorldFile);
963 checkArgc(1, i, argc, argv[i]);
964 options.wksPort = atoi(argv[i]);
965 if (options.wksPort < 1 || options.wksPort > 65535)
966 options.wksPort = ServerPort;
967 else
968 options.useGivenPort = true;
969 }
970 else if (strcmp(argv[i], "-packetlossdrop") == 0)
971 {
972 checkArgc(1, i, argc, argv[i]);
973 options.maxpacketlosswarn = atoi(argv[i]);
974 }
975 else if (strcmp(argv[i], "-packetlosswarn") == 0)
976 {
977 checkArgc(1, i, argc, argv[i]);
978 options.packetlosswarnthresh = atoi(argv[i])/1000.0f;
979 }
980 else if (strcmp(argv[i], "-passwd") == 0 || strcmp(argv[i], "-password") == 0)
981 {
982 checkFromWorldFile(argv[i], fromWorldFile);
983 checkArgc(1, i, argc, argv[i]);
984 // at least put password someplace that ps won't see
985 options.password = argv[i];
986 memset(argv[i], 'X', options.password.size());
987 }
988 else if (strcmp(argv[i], "-pidfile") == 0)
989 {
990 checkFromWorldFile(argv[i], fromWorldFile);
991 unsigned int pid = 0;
992 checkArgc(1, i, argc, argv[i]);
993 FILE *fp = fopen(argv[i], "wt");
994 #ifndef _WIN32
995 pid = getpid();
996 #else
997 pid = _getpid();
998 #endif
999 if (fp)
1000 {
1001 fprintf(fp, "%d", pid);
1002 fclose(fp);
1003 }
1004 }
1005 else if (strcmp(argv[i], "-poll") == 0)
1006 {
1007 // parse the variety of poll system variables
1008 checkArgc(1, i, argc, argv[i]);
1009
1010 std::vector<std::string> args = TextUtils::tokenize(argv[i], std::string("="), 2, true);
1011 if (args.size() != 2)
1012 {
1013 std::cerr << "expected -poll variable=value" << std::endl;
1014 usage(argv[0]);
1015 }
1016
1017 if (compare_nocase(args[0], "bantime") == 0)
1018 options.banTime = (unsigned short int)atoi(args[1].c_str());
1019 else if (compare_nocase(args[0], "vetotime") == 0)
1020 options.vetoTime = (unsigned short int)atoi(args[1].c_str());
1021 else if (compare_nocase(args[0], "votepercentage") == 0)
1022 options.votePercentage = (float)atof(args[1].c_str());
1023 else if (compare_nocase(args[0], "voterepeattime") == 0)
1024 options.voteRepeatTime = (unsigned short int)atoi(args[1].c_str());
1025 else if (compare_nocase(args[0], "votesrequired") == 0)
1026 options.votesRequired = (unsigned short int)atoi(args[1].c_str());
1027 else if (compare_nocase(args[0], "votetime") == 0)
1028 options.voteTime = (unsigned short int)atoi(args[1].c_str());
1029 else
1030 std::cerr << "unknown variable for -poll, skipping";
1031 #ifdef PRINTSCORE
1032 }
1033 else if (strcmp(argv[i], "-printscore") == 0)
1034 {
1035 // dump score whenever it changes
1036 options.printScore = true;
1037 #endif
1038 }
1039 else if (strcmp(argv[i], "-publictitle") == 0)
1040 {
1041 checkArgc(1, i, argc, argv[i]);
1042 options.publicizeServer = true;
1043 options.publicizedTitle = argv[i];
1044 if (options.publicizedTitle.length() > 127)
1045 {
1046 argv[i][127] = '\0';
1047 std::cerr << "description too long... truncated" << std::endl;
1048 }
1049 }
1050 else if (strcmp(argv[i], "-publicaddr") == 0)
1051 {
1052 checkFromWorldFile(argv[i], fromWorldFile);
1053 checkArgc(1, i, argc, argv[i]);
1054 options.publicizedAddress = argv[i];
1055 options.publicizeServer = true;
1056 }
1057 else if (strcmp(argv[i], "-publiclist") == 0)
1058 {
1059 /* if this is the first -publiclist, override the default list
1060 * server. otherwise just keep adding urls.
1061 */
1062 checkFromWorldFile(argv[i], fromWorldFile);
1063 if (!options.listServerOverridden)
1064 {
1065 options.listServerURL.clear();
1066 options.listServerOverridden = true;
1067 }
1068 checkArgc(1, i, argc, argv[i]);
1069 options.listServerURL.push_back(argv[i]);
1070 }
1071 else if (strcmp(argv[i], "-publickey") == 0)
1072 {
1073 checkFromWorldFile(argv[i], fromWorldFile);
1074 checkArgc(1, i, argc, argv[i]);
1075 options.publicizeServer = true;
1076 // at least put publickey someplace that ps won't see
1077 options.publicizedKey = argv[i];
1078 memset(argv[i], 'X', options.publicizedKey.size());
1079 if (options.publicizedKey.length() == 0)
1080 {
1081 options.publicizeServer = false;
1082 std::cerr << "key too short server will not be made public" << std::endl;
1083 }
1084 }
1085 else if (strcmp(argv[i], "-q") == 0)
1086 {
1087 // don't handle pings
1088 checkFromWorldFile(argv[i], fromWorldFile);
1089 handlePings = false;
1090 }
1091 else if (strcmp(argv[i], "+r") == 0)
1092 {
1093 // all shots ricochet style
1094 options.gameOptions |= int(RicochetGameStyle);
1095 }
1096 else if (strcmp(argv[i], "-rabbit") == 0)
1097 {
1098 // rabbit chase style
1099 if (options.gameType == ClassicCTF)
1100 {
1101 std::cerr << "Rabbit Chase incompatible with Capture the flag" << std::endl;
1102 std::cerr << "Rabbit Chase assumed" << std::endl;
1103 }
1104 options.gameType = RabbitChase;
1105 // default selection style
1106 options.rabbitSelection = ScoreRabbitSelection;
1107
1108 // if there are any arguments following, see if they are a
1109 // rabbit selection styles.
1110 if (i+1 != argc)
1111 {
1112 if (strcmp(argv[i+1], "score") == 0)
1113 {
1114 options.rabbitSelection = ScoreRabbitSelection;
1115 i++;
1116 }
1117 else if (strcmp(argv[i+1], "killer") == 0)
1118 {
1119 options.rabbitSelection = KillerRabbitSelection;
1120 i++;
1121 }
1122 else if (strcmp(argv[i+1], "random") == 0)
1123 {
1124 options.rabbitSelection = RandomRabbitSelection;
1125 i++;
1126 }
1127 }
1128 }
1129 else if (strcmp(argv[i], "-recbuf") == 0)
1130 {
1131 checkFromWorldFile(argv[i], fromWorldFile);
1132 checkArgc(1, i, argc, argv[i]);
1133 Record::setSize (ServerPlayer, atoi(argv[i]));
1134 options.startRecording = true;
1135 }
1136 else if (strcmp(argv[i], "-recbufonly") == 0)
1137 {
1138 checkFromWorldFile(argv[i], fromWorldFile);
1139 Record::setAllowFileRecs (false);
1140 }
1141 else if (strcmp(argv[i], "-recdir") == 0)
1142 {
1143 checkFromWorldFile(argv[i], fromWorldFile);
1144 checkArgc(1, i, argc, argv[i]);
1145 Record::setDirectory (argv[i]);
1146 }
1147 else if (strcmp(argv[i], "-replay") == 0)
1148 {
1149 checkFromWorldFile(argv[i], fromWorldFile);
1150 options.replayServer = true;
1151 }
1152 else if (strcmp(argv[i], "-reportfile") == 0)
1153 {
1154 checkFromWorldFile(argv[i], fromWorldFile);
1155 checkArgc(1, i, argc, argv[i]);
1156 options.reportFile = argv[i];
1157 }
1158 else if (strcmp(argv[i], "-reportpipe") == 0)
1159 {
1160 checkFromWorldFile(argv[i], fromWorldFile);
1161 checkArgc(1, i, argc, argv[i]);
1162 options.reportPipe = argv[i];
1163 }
1164 else if (strcmp(argv[i], "-tkannounce") == 0)
1165 options.tkAnnounce = true;
1166 else if (strcmp(argv[i], "+s") == 0 || strcmp(argv[i], "-s") == 0)
1167 {
1168 // with +s all flags are required to exist all the time
1169 allFlagsOut = argv[i][0] == '+' ? true : false;
1170 // set number of random flags
1171 if (i+1 < argc && isdigit(argv[i+1][0]))
1172 {
1173 ++i;
1174 if ((options.numExtraFlags = atoi(argv[i])) == 0)
1175 options.numExtraFlags = 16;
1176 }
1177 else
1178 options.numExtraFlags = 16;
1179 }
1180 else if (strcmp(argv[i], "-sa") == 0)
1181 {
1182 // insert antidote flags
1183 options.gameOptions |= int(AntidoteGameStyle);
1184 }
1185 else if (strcmp(argv[i], "-sb") == 0)
1186 {
1187 // respawns on buildings
1188 options.respawnOnBuildings = true;
1189 }
1190 else if (strcmp(argv[i], "-set") == 0)
1191 {
1192 const char *name, *value;
1193 checkArgc(2, i, argc, argv[i]);
1194 name = argv[i];
1195 i++;
1196 value = argv[i];
1197
1198 // give unknown BZDB variables the benefit of the doubt, allow plug-ins to define them
1199 if (!BZDB.isSet(name))
1200 {
1201 bzdbVarQueue[name] = value;
1202 logDebugMessage(1, "queued variable: %s = %s\n", name, value);
1203
1204 continue;
1205 }
1206
1207 BZDB.set(name, value);
1208 logDebugMessage(1, "set variable: %s = %s\n", name, BZDB.get(name).c_str());
1209 }
1210 else if (strcmp(argv[i], "-setforced") == 0)
1211 {
1212 const char *name, *value;
1213 checkArgc(2, i, argc, argv[i]);
1214 name = argv[i];
1215 i++;
1216 value = argv[i];
1217
1218 BZDB.set(name, value);
1219 logDebugMessage(1, "set variable: %s = %s\n", name, BZDB.get(name).c_str());
1220 }
1221 else if (strcmp(argv[i], "-sl") == 0)
1222 {
1223 // shot limits
1224 checkArgc(2, i, argc, argv[i]);
1225 i++; // move past the flag descriptor for now (we'll store it in a bit)
1226 int x = 1;
1227 if (isdigit(argv[i][0]))
1228 {
1229 x = atoi(argv[i]);
1230 if (x < 1)
1231 {
1232 std::cerr << "can only limit to 1 or more shots, changing to 1" << std::endl;
1233 x = 1;
1234 }
1235 }
1236 else
1237 {
1238 std::cerr << "ERROR: invalid shot limit [" << argv[i] << "]" << std::endl;
1239 usage(argv[0]);
1240 }
1241 storedFlagLimits[argv[i-1]] = x;
1242 }
1243 else if (strcmp(argv[i], "-spamtime") == 0)
1244 {
1245 checkArgc(1, i, argc, argv[i]);
1246 options.msgTimer = atoi(argv[i]);
1247 logDebugMessage(1,"using spam time of %d seconds\n", options.msgTimer);
1248 }
1249 else if (strcmp(argv[i], "-spamwarn") == 0)
1250 {
1251 checkArgc(1, i, argc, argv[i]);
1252 options.spamWarnMax = atoi(argv[i]);
1253 logDebugMessage(1,"using spam warn threshold of %d\n", options.spamWarnMax);
1254 }
1255 else if (strcmp(argv[i], "-speedtol") == 0)
1256 {
1257 checkArgc(1, i, argc, argv[i]);
1258 speedTolerance = (float) atof(argv[i]);
1259 logDebugMessage(1,"using speed autokick tolerance of \"%f\"\n", speedTolerance);
1260 }
1261 else if (strcmp(argv[i], "-srvmsg") == 0)
1262 {
1263 checkArgc(1, i, argc, argv[i]);
1264 if ((options.servermsg != "") || (strlen (argv[i]) == 0))
1265 options.servermsg += "\\n";
1266 options.servermsg += argv[i];
1267 }
1268 else if (strcmp(argv[i], "-st") == 0)
1269 {
1270 // set shake timeout
1271 checkArgc(1, i, argc, argv[i]);
1272 float timeout = (float)atof(argv[i]);
1273 if (timeout < 0.1f)
1274 {
1275 options.shakeTimeout = 1;
1276 std::cerr << "using minimum shake timeout of " << 0.1f * (float)options.shakeTimeout << std::endl;
1277 }
1278 else if (timeout > 300.0f)
1279 {
1280 options.shakeTimeout = 3000;
1281 std::cerr << "using maximum shake timeout of " << 0.1f * (float)options.shakeTimeout << std::endl;
1282 }
1283 else
1284 options.shakeTimeout = uint16_t(timeout * 10.0f + 0.5f);
1285 options.gameOptions |= int(ShakableGameStyle);
1286 }
1287 else if (strcmp(argv[i], "-sw") == 0)
1288 {
1289 // set shake win count
1290 checkArgc(1, i, argc, argv[i]);
1291 int count = atoi(argv[i]);
1292 if (count < 1)
1293 {
1294 options.shakeWins = 1;
1295 std::cerr << "using minimum shake win count of " << options.shakeWins << std::endl;
1296 }
1297 else if (count > 20)
1298 {
1299 options.shakeWins = 20;
1300 std::cerr << "using maximum ttl of " << options.shakeWins << std::endl;
1301 }
1302 else
1303 options.shakeWins = uint16_t(count);
1304 options.gameOptions |= int(ShakableGameStyle);
1305 }
1306 else if (strcmp(argv[i], "-synctime") == 0)
1307 {
1308 // client clocks should be synchronized to server clock
1309 BZDB.set(StateDatabase::BZDB_SYNCTIME, "1.0"); // any positive number
1310 }
1311 else if (strcmp(argv[i], "-synclocation") == 0)
1312 {
1313 // client coordinates should be set to server coordinates
1314 BZDB.set(StateDatabase::BZDB_SYNCLOCATION, "true");
1315 }
1316 else if (strcmp(argv[i], "-t") == 0)
1317 {
1318 // allow teleporters
1319 options.useTeleporters = true;
1320 if (options.worldFile != "")
1321 std::cerr << "-t is meaningless when using a custom world, ignoring" << std::endl;
1322 }
1323 else if (strcmp(argv[i], "-offa") == 0)
1324 {
1325 // capture the flag style
1326 if (options.gameType == RabbitChase || options.gameType == ClassicCTF)
1327 {
1328 std::cerr << "Open (Teamless) Free-for-all incompatible with other modes" << std::endl;
1329 std::cerr << "Open Free-for-all assumed" << std::endl;
1330 }
1331 if ((options.gameOptions & int(NoTeamKillsGameStyle)) != 0)
1332 {
1333 std::cerr << "offa check WARNING: -noTeamKills is incompatible with -offa, ignoring" << std::endl;
1334 options.gameOptions ^= int(NoTeamKillsGameStyle);
1335 }
1336 options.gameType = OpenFFA;
1337 }
1338 else if (strcmp(argv[i], "-tftimeout") == 0)
1339 {
1340 // use team flag timeout
1341 checkArgc(1, i, argc, argv[i]);
1342 options.teamFlagTimeout = atoi(argv[i]);
1343 if (options.teamFlagTimeout < 0)
1344 options.teamFlagTimeout = 0;
1345 logDebugMessage(1,"using team flag timeout of %d seconds\n", options.teamFlagTimeout);
1346 }
1347 else if (strcmp(argv[i], "-time") == 0)
1348 {
1349 checkArgc(1, i, argc, argv[i]);
1350 const std::string timeStr = argv[i];
1351 // If there is no colon in the time, consider it to be the time limit in seconds
1352 if (timeStr.find(':') == std::string::npos)
1353 options.timeLimit = (float)atof(timeStr.c_str());
1354 // Otherwise treat it as the end time
1355 else
1356 {
1357 std::vector<std::string> endTime = TextUtils::tokenize(timeStr, std::string(":"));
1358 {
1359 unsigned int sizer = endTime.size();
1360 if (sizer > 3)
1361 {
1362 std::cerr << "ERROR: too many arguments to -time" << std::endl;
1363 usage(argv[0]);
1364 }
1365 while (sizer != 3)
1366 {
1367 endTime.push_back("00");
1368 ++sizer;
1369 }
1370
1371 // Force the countdown to start right away
1372 gameOver = false;
1373 gameStartTime = TimeKeeper::getCurrent();
1374 countdownActive = true;
1375 }
1376 int hour, min, sec;
1377 TimeKeeper::localTime(nullptr, nullptr, nullptr, &hour, &min, &sec);
1378 unsigned int cmdHour = atoi(endTime[0].c_str()),
1379 cmdMin = atoi(endTime[1].c_str()),
1380 cmdSec = atoi(endTime[2].c_str());
1381 unsigned long secsToday = (hour * 3600) + (min * 60) + sec,
1382 secsTill = (cmdHour * 3600) + (cmdMin * 60) + cmdSec;
1383 if (secsToday > secsTill) //if the requested time has already past
1384 {
1385 options.timeLimit = (float)((86400 - secsToday) + secsTill); //secs left today + till req. time
1386 }
1387 else
1388 options.timeLimit = (float)(secsTill - secsToday);
1389 }
1390 if (options.timeLimit <= 0.0f)
1391 {
1392 // league matches are 30 min
1393 options.timeLimit = 1800.0f;
1394 }
1395 logDebugMessage(1,"using time limit of %d seconds\n", (int)options.timeLimit);
1396 }
1397 else if (strcmp(argv[i], "-timemanual") == 0)
1398 options.timeManualStart = true;
1399 else if (strcmp(argv[i], "-tk") == 0)
1400 {
1401 // team killer does not die
1402 options.teamKillerDies = false;
1403 }
1404 else if (strcmp(argv[i], "-tkkr") == 0)
1405 {
1406 checkArgc(1, i, argc, argv[i]);
1407 options.teamKillerKickRatio = atoi(argv[i]);
1408 if (options.teamKillerKickRatio < 0)
1409 {
1410 options.teamKillerKickRatio = 0;
1411 std::cerr << "disabling team killer kick ratio";
1412 }
1413 }
1414 else if (strcmp(argv[i], "-ts") == 0)
1415 {
1416 checkFromWorldFile(argv[i], fromWorldFile);
1417 // timestamp output
1418 options.timestampLog = true;
1419 // if there is an argument following, see if it is 'micros'
1420 if (i+1 != argc)
1421 {
1422 if (strcasecmp(argv[i+1], "micros") == 0)
1423 {
1424 options.timestampMicros = true;
1425 i++;
1426 }
1427 }
1428 }
1429 #ifdef HAVE_MINIUPNPC_MINIUPNPC_H
1430 else if (strcmp(argv[i], "-UPnP") == 0)
1431 {
1432 checkFromWorldFile(argv[i], fromWorldFile);
1433 // timestamp output
1434 options.UPnP = true;
1435 }
1436 #endif
1437 else if (strcmp(argv[i], "-utc") == 0)
1438 {
1439 checkFromWorldFile(argv[i], fromWorldFile);
1440 // timestamp output
1441 options.timestampLog = true;
1442 options.timestampUTC = true;
1443 }
1444 else if (strcmp(argv[i], "-userdb") == 0)
1445 {
1446 checkFromWorldFile(argv[i], fromWorldFile);
1447 checkArgc(1, i, argc, argv[i]);
1448 userDatabaseFile = argv[i];
1449 logDebugMessage(1,"using userDB file \"%s\"\n", argv[i]);
1450 }
1451 else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "-version") == 0)
1452 {
1453 printVersion();
1454 exit(0);
1455 }
1456 else if (strcmp(argv[i], "-vars") == 0)
1457 {
1458 checkArgc(1, i, argc, argv[i]);
1459 options.bzdbVars = argv[i];
1460 }
1461 else if ((strcmp(argv[i], "-w") == 0) ||
1462 (strcmp(argv[i], "-world") == 0))
1463 {
1464 checkFromWorldFile(argv[i], fromWorldFile);
1465 checkArgc(1, i, argc, argv[i]);
1466 options.worldFile = argv[i];
1467 int ac;
1468 char **av = parseWorldOptions(argv[i], ac);
1469 parse(ac, av, options, true); // true - from a world file
1470
1471 for (int j = 0; j < ac; j++)
1472 free(av[j]);
1473 delete[] av;
1474
1475 options.numAllowedFlags = 0; // FIXME - Huh, does a reset?
1476
1477 if (options.useTeleporters)
1478 std::cerr << "-t is meaningless when using a custom world, ignoring" << std::endl;
1479 }
1480 else if (strcmp(argv[i], "-worldsize") == 0)
1481 {
1482 checkArgc(1, i, argc, argv[i]);
1483 BZDB.set(StateDatabase::BZDB_WORLDSIZE, TextUtils::format("%d",atoi(argv[i])*2));
1484 logDebugMessage(1,"using world size of \"%f\"\n", (float)BZDBCache::worldSize);
1485 }
1486 else if (strcmp(argv[i], "-ws") == 0)
1487 {
1488 checkArgc(1, i, argc, argv[i]);
1489 int sides = atoi(argv[i]);
1490 if (sides > 2)
1491 options.wallSides = sides;
1492 }
1493 else
1494 {
1495 std::cerr << "bad argument \"" << argv[i] << "\"" << std::endl;
1496 usage(argv[0]);
1497 }
1498 }
1499
1500 // get player counts. done after other arguments because we need
1501 // to ignore counts for rogues if rogues aren't allowed.
1502 if ((playerCountArg > 0) && !parsePlayerCount(argv[playerCountArg], options))
1503 usage(argv[0]);
1504 if ((playerCountArg2 > 0)
1505 && !parsePlayerCount(argv[playerCountArg2], options))
1506 usage(argv[0]);
1507
1508 return;
1509 }
1510
1511
getZoneTeamFlagCount(FlagType * flagType,EntryZones & entryZones,const std::set<FlagType * > & forbidden)1512 static int getZoneTeamFlagCount(FlagType* flagType, EntryZones& entryZones,
1513 const std::set<FlagType*>& forbidden)
1514 {
1515 if (forbidden.find(flagType) != forbidden.end())
1516 return 0;
1517 // tally zone team flags
1518 int count = 0;
1519 const ZoneList& zl = entryZones.getZoneList();
1520 for (int z = 0; z < (int)zl.size(); z++)
1521 {
1522 const ZoneFlagMap& zfm = zl[z].getZoneFlagMap();
1523 ZoneFlagMap::const_iterator zfmIt;
1524 for (zfmIt = zfm.begin(); zfmIt != zfm.end(); ++zfmIt)
1525 {
1526 if (zfmIt->first == flagType)
1527 count += zfmIt->second;
1528 }
1529 }
1530 return count;
1531 }
1532
1533
addZoneTeamFlags(int startIndex,FlagType * flagType,EntryZones & entryZones,const std::set<FlagType * > & forbidden)1534 static int addZoneTeamFlags(int startIndex,
1535 FlagType* flagType, EntryZones& entryZones,
1536 const std::set<FlagType*>& forbidden)
1537 {
1538 if (forbidden.find(flagType) != forbidden.end())
1539 return startIndex;
1540 // add zone team flags
1541 const ZoneList& zl = entryZones.getZoneList();
1542 for (int z = 0; z < (int)zl.size(); z++)
1543 {
1544 const ZoneFlagMap& zfm = zl[z].getZoneFlagMap();
1545 ZoneFlagMap::const_iterator zfmIt;
1546 for (zfmIt = zfm.begin(); zfmIt != zfm.end(); ++zfmIt)
1547 {
1548 if (zfmIt->first == flagType)
1549 {
1550 const int count = zfmIt->second;
1551 for (int c = 0; c < count; c++)
1552 {
1553 entryZones.addZoneFlag(z, startIndex);
1554 FlagInfo::get(startIndex++)->setRequiredFlag(flagType);
1555 }
1556 }
1557 }
1558 }
1559 return startIndex;
1560 }
1561
1562
finalizeParsing(int UNUSED (argc),char ** argv,CmdLineOptions & options,EntryZones & entryZones)1563 void finalizeParsing(int UNUSED(argc), char **argv,
1564 CmdLineOptions &options, EntryZones& entryZones)
1565 {
1566 // set queued BZDB variables; allow plugins to define valid BZDB variables
1567 for (auto it = bzdbVarQueue.begin(); it != bzdbVarQueue.end(); it++)
1568 {
1569 if (!BZDB.isSet(it->first))
1570 {
1571 std::cerr << "Unknown BZDB variable: " << it->first << std::endl;
1572 exit(1);
1573 }
1574
1575 BZDB.set(it->first, it->second);
1576 BZDB.setDefault(it->first, it->second);
1577 logDebugMessage(1, "queued variable set: %s = %s\n", it->first.c_str(), BZDB.get(it->first).c_str());
1578 }
1579
1580 if (options.flagsOnBuildings && !(options.gameOptions & JumpingGameStyle))
1581 {
1582 std::cerr << "flags on boxes requires jumping" << std::endl;
1583 usage(argv[0]);
1584 }
1585
1586 if (options.gameType == RabbitChase)
1587 {
1588 for (int j = RedTeam; j <= PurpleTeam; j++)
1589 {
1590 if (options.maxTeam[j] > 0
1591 && options.maxTeam[RogueTeam] != maxRealPlayers)
1592 std::cout << "only rogues are allowed in Rabbit Chase; zeroing out "
1593 << Team::getName((TeamColor) j) << std::endl;
1594 options.maxTeam[j] = 0;
1595 }
1596 options.maxTeam[RabbitTeam] = 1;
1597 options.maxTeam[HunterTeam] = options.maxTeam[RogueTeam];
1598 }
1599 else
1600 {
1601 options.maxTeam[RabbitTeam] = 0;
1602 options.maxTeam[HunterTeam] = 0;
1603 }
1604
1605 // disallowed flags
1606 std::vector<std::string>::iterator vsitr;
1607 for (vsitr = storedFlagDisallows.begin(); vsitr != storedFlagDisallows.end(); ++vsitr)
1608 {
1609 if (strcmp(vsitr->c_str(), "bad") == 0)
1610 {
1611 FlagSet badFlags = Flag::getBadFlags();
1612 for (FlagSet::iterator it = badFlags.begin(); it != badFlags.end(); ++it)
1613 options.flagDisallowed[*it] = true;
1614 }
1615 else if (strcmp(vsitr->c_str(), "good") == 0)
1616 {
1617 FlagSet goodFlags = Flag::getGoodFlags();
1618 for (FlagSet::iterator it = goodFlags.begin(); it != goodFlags.end(); ++it)
1619 options.flagDisallowed[*it] = true;
1620 }
1621 else
1622 {
1623 FlagType* fDesc = Flag::getDescFromAbbreviation(vsitr->c_str());
1624 if (fDesc == Flags::Null)
1625 {
1626 std::cerr << "ERROR: invalid flag [" << (*vsitr) << "]" << std::endl;
1627 usage(argv[0]);
1628 }
1629 options.flagDisallowed[fDesc] = true;
1630 }
1631 }
1632 storedFlagDisallows.clear();
1633
1634 // explicitly added flags
1635 for (vsitr = storedFlagCounts.begin(); vsitr != storedFlagCounts.end(); ++vsitr)
1636 {
1637 size_t rptBgn = vsitr->find_first_of('{');
1638 int rptCnt = 1;
1639 if (rptBgn > 0)
1640 {
1641 rptCnt = atoi(vsitr->substr(rptBgn+1, vsitr->length() - rptBgn).c_str());
1642 if (rptCnt <= 0)
1643 rptCnt = 1;
1644 (*vsitr) = vsitr->substr(0, rptBgn);
1645 }
1646 if (strcmp(vsitr->c_str(), "good") == 0)
1647 {
1648 FlagSet goodFlags = Flag::getGoodFlags();
1649 for (FlagSet::iterator it = goodFlags.begin(); it != goodFlags.end(); ++it)
1650 options.flagCount[*it] += rptCnt;
1651 }
1652 else if (strcmp(vsitr->c_str(), "bad") == 0)
1653 {
1654 FlagSet badFlags = Flag::getBadFlags();
1655 for (FlagSet::iterator it = badFlags.begin(); it != badFlags.end(); ++it)
1656 options.flagCount[*it] += rptCnt;
1657 }
1658 else if (strcmp(vsitr->c_str(), "team") == 0)
1659 {
1660 for (int t = RedTeam; t <= PurpleTeam; t++)
1661 options.numTeamFlags[t] += rptCnt;
1662 }
1663 else
1664 {
1665 FlagType *fDesc = Flag::getDescFromAbbreviation(vsitr->c_str());
1666 if (fDesc == Flags::Null)
1667 {
1668 std::cerr << "ERROR: invalid flag [" << (*vsitr) << "]" << std::endl;
1669 usage(argv[0]);
1670 }
1671 else if (fDesc->flagTeam != NoTeam)
1672 options.numTeamFlags[fDesc->flagTeam] += rptCnt;
1673 else
1674 options.flagCount[fDesc] += rptCnt;
1675 }
1676 }
1677 storedFlagCounts.clear();
1678
1679 if (!accLimitSet)
1680 {
1681 printf("Note: no acceleration limit has been set. Players using \"mouse\n"
1682 "enhancements\" may cause problems on this server due to very high\n"
1683 "acceleration rates which are not handled well by dead reckoning\n"
1684 "algorithms. To eliminate this warning, set the -a switch in your\n"
1685 "configuration. '-a 50 38' is recommended for standard-speed servers.\n"
1686 "Higher speed servers will need higher values for -a in order to not\n"
1687 "affect gameplay. '-a 0 0' may be used to shut this message up without\n"
1688 "affecting any players, including those using \"mouse enhancements\".\n");
1689 }
1690
1691
1692 // do we have any team flags?
1693 bool hasTeam = false;
1694 for (int p = RedTeam; p <= PurpleTeam; p++)
1695 {
1696 if (options.maxTeam[p] > 1)
1697 {
1698 hasTeam = true;
1699 break;
1700 }
1701 }
1702
1703 std::set<FlagType*> forbidden;
1704 forbidden.insert(Flags::Null);
1705
1706 // first disallow flags inconsistent with game style
1707 if (options.gameOptions & JumpingGameStyle)
1708 forbidden.insert(Flags::Jumping);
1709 else
1710 forbidden.insert(Flags::NoJumping);
1711 if (options.gameOptions & RicochetGameStyle)
1712 forbidden.insert(Flags::Ricochet);
1713 if (OBSTACLEMGR.getTeles().size() == 0)
1714 forbidden.insert(Flags::PhantomZone);
1715 if (!hasTeam)
1716 {
1717 forbidden.insert(Flags::Genocide);
1718 forbidden.insert(Flags::Colorblindness);
1719 forbidden.insert(Flags::Masquerade);
1720 }
1721 if ((options.gameType == ClassicCTF) == 0)
1722 {
1723 forbidden.insert(Flags::RedTeam);
1724 forbidden.insert(Flags::GreenTeam);
1725 forbidden.insert(Flags::BlueTeam);
1726 forbidden.insert(Flags::PurpleTeam);
1727 }
1728 if (options.maxTeam[RedTeam] <= 0)
1729 forbidden.insert(Flags::RedTeam);
1730 if (options.maxTeam[GreenTeam] <= 0)
1731 forbidden.insert(Flags::GreenTeam);
1732 if (options.maxTeam[BlueTeam] <= 0)
1733 forbidden.insert(Flags::BlueTeam);
1734 if (options.maxTeam[PurpleTeam] <= 0)
1735 forbidden.insert(Flags::PurpleTeam);
1736
1737 // void the forbidden flags
1738 std::set<FlagType*>::const_iterator sit;
1739 for (sit = forbidden.begin(); sit != forbidden.end(); ++sit)
1740 {
1741 FlagType* ft = *sit;
1742 options.flagCount[ft] = 0;
1743 options.flagDisallowed[ft] = true;
1744 }
1745
1746 // zone team flag counts
1747 const int zoneTeamFlagCounts[5] =
1748 {
1749 0, // rogue
1750 getZoneTeamFlagCount(Flags::RedTeam, entryZones, forbidden),
1751 getZoneTeamFlagCount(Flags::GreenTeam, entryZones, forbidden),
1752 getZoneTeamFlagCount(Flags::BlueTeam, entryZones, forbidden),
1753 getZoneTeamFlagCount(Flags::PurpleTeam, entryZones, forbidden)
1754 };
1755
1756 // make sure there is at least one team flag for each active team
1757 if (options.gameType == ClassicCTF)
1758 {
1759 for (int col = RedTeam; col <= PurpleTeam; col++)
1760 {
1761 if ((options.maxTeam[col] > 0) &&
1762 (options.numTeamFlags[col] <= 0) &&
1763 (zoneTeamFlagCounts[col] <= 0))
1764 options.numTeamFlags[col] = 1;
1765 }
1766 }
1767
1768 // make table of allowed extra flags
1769 if (options.numExtraFlags > 0)
1770 {
1771 // now count how many aren't disallowed
1772 for (FlagTypeMap::iterator it = FlagType::getFlagMap().begin();
1773 it != FlagType::getFlagMap().end(); ++it)
1774 {
1775 if (!options.flagDisallowed[it->second])
1776 options.numAllowedFlags++;
1777 }
1778 // if none allowed then no extra flags either
1779 if (options.numAllowedFlags == 0)
1780 options.numExtraFlags = 0;
1781 else
1782 {
1783 // types of extra flags allowed
1784 std::vector<FlagType*> allowedFlags;
1785 allowedFlags.clear();
1786 for (FlagTypeMap::iterator it = FlagType::getFlagMap().begin();
1787 it != FlagType::getFlagMap().end(); ++it)
1788 {
1789 FlagType *fDesc = it->second;
1790 if ((fDesc == Flags::Null) || (fDesc->flagTeam != ::NoTeam))
1791 continue;
1792 if (!options.flagDisallowed[it->second])
1793 allowedFlags.push_back(it->second);
1794 }
1795 // Loading allowedFlags vector
1796 FlagInfo::setAllowed(allowedFlags);
1797 }
1798 }
1799
1800 const ZoneList& zl = entryZones.getZoneList();
1801
1802 // allocate space for extra flags
1803 numFlags = options.numExtraFlags;
1804 // allocate space for team flags
1805 if (options.gameType & ClassicCTF)
1806 {
1807 for (int col = RedTeam; col <= PurpleTeam; col++)
1808 {
1809 if (options.maxTeam[col] > 0)
1810 numFlags += options.numTeamFlags[col];
1811 }
1812 }
1813 // allocate space for normal flags
1814 for (FlagTypeMap::iterator it = FlagType::getFlagMap().begin();
1815 it != FlagType::getFlagMap().end(); ++it)
1816 numFlags += options.flagCount[it->second];
1817 // allocate space for zone flags (including teams flags)
1818 for (int z = 0; z < (int)zl.size(); z++)
1819 {
1820 const CustomZone& zone = zl[z];
1821 const ZoneFlagMap& zfm = zone.getZoneFlagMap();
1822 ZoneFlagMap::const_iterator zfmIt;
1823 for (zfmIt = zfm.begin(); zfmIt != zfm.end(); ++zfmIt)
1824 {
1825 if (forbidden.find(zfmIt->first) == forbidden.end())
1826 numFlags += zfmIt->second;
1827 }
1828 }
1829
1830 FlagInfo::setSize(numFlags);
1831
1832 // add team flags (ordered)
1833 int f = 0;
1834 if (options.gameType == ClassicCTF)
1835 {
1836 if (options.maxTeam[RedTeam] > 0)
1837 {
1838 f = addZoneTeamFlags(f, Flags::RedTeam, entryZones, forbidden);
1839 for (int n = 0; n < options.numTeamFlags[RedTeam]; n++)
1840 FlagInfo::get(f++)->setRequiredFlag(Flags::RedTeam);
1841 }
1842 if (options.maxTeam[GreenTeam] > 0)
1843 {
1844 f = addZoneTeamFlags(f, Flags::GreenTeam, entryZones, forbidden);
1845 for (int n = 0; n < options.numTeamFlags[GreenTeam]; n++)
1846 FlagInfo::get(f++)->setRequiredFlag(Flags::GreenTeam);
1847 }
1848 if (options.maxTeam[BlueTeam] > 0)
1849 {
1850 f = addZoneTeamFlags(f, Flags::BlueTeam, entryZones, forbidden);
1851 for (int n = 0; n < options.numTeamFlags[BlueTeam]; n++)
1852 FlagInfo::get(f++)->setRequiredFlag(Flags::BlueTeam);
1853 }
1854 if (options.maxTeam[PurpleTeam] > 0)
1855 {
1856 f = addZoneTeamFlags(f, Flags::PurpleTeam, entryZones, forbidden);
1857 for (int n = 0; n < options.numTeamFlags[PurpleTeam]; n++)
1858 FlagInfo::get(f++)->setRequiredFlag(Flags::PurpleTeam);
1859 }
1860 }
1861
1862 // super flags?
1863 if (f < numFlags)
1864 options.gameOptions |= int(SuperFlagGameStyle);
1865
1866 // shot limits
1867 std::map<std::string, int>::iterator msiitr;
1868 for (msiitr = storedFlagLimits.begin(); msiitr != storedFlagLimits.end(); ++msiitr)
1869 {
1870 FlagType *fDesc = Flag::getDescFromAbbreviation(msiitr->first.c_str());
1871 if (fDesc == Flags::Null)
1872 {
1873 std::cerr << "ERROR: invalid flag [" << msiitr->first << "]" << std::endl;
1874 usage(argv[0]);
1875 }
1876 else
1877 {
1878 // this parameter has already been validated
1879 options.flagLimit[fDesc] = msiitr->second;
1880 }
1881 }
1882 storedFlagLimits.clear();
1883
1884 // add normal flags
1885 for (FlagTypeMap::iterator it2 = FlagType::getFlagMap().begin();
1886 it2 != FlagType::getFlagMap().end(); ++it2)
1887 {
1888 FlagType *fDesc = it2->second;
1889
1890 if ((fDesc != Flags::Null) && (fDesc->flagTeam == NoTeam))
1891 {
1892 for (int j = 0; j < options.flagCount[fDesc]; j++)
1893 {
1894 FlagInfo *flag = FlagInfo::get(f++);
1895 if (!flag)
1896 continue;
1897 flag->setRequiredFlag(fDesc);
1898 }
1899 }
1900 }
1901 // add zone flags
1902 for (int z = 0; z < (int)zl.size(); z++)
1903 {
1904 const CustomZone& zone = zl[z];
1905 const ZoneFlagMap& zfm = zone.getZoneFlagMap();
1906 ZoneFlagMap::const_iterator zfmIt;
1907 for (zfmIt = zfm.begin(); zfmIt != zfm.end(); ++zfmIt)
1908 {
1909 FlagType* ft = zfmIt->first;
1910 if ((ft->flagTeam == ::NoTeam) && // no team flags here
1911 (forbidden.find(ft) == forbidden.end()))
1912 {
1913 const int count = zfmIt->second;
1914 for (int c = 0; c < count; c++, f++)
1915 {
1916 FlagInfo *flag = FlagInfo::get(f);
1917 if (!flag)
1918 continue;
1919 entryZones.addZoneFlag(z, f);
1920 flag->setRequiredFlag(ft);
1921 }
1922 }
1923 }
1924 }
1925 // add extra flags
1926 for (; f < numFlags; f++)
1927 {
1928 FlagInfo *flag = FlagInfo::get(f);
1929 if (!flag)
1930 continue;
1931 flag->required = allFlagsOut;
1932 }
1933
1934 // sum the sources of team flags
1935 if (options.gameType & ClassicCTF)
1936 {
1937 for (int col = RedTeam; col <= PurpleTeam; col++)
1938 options.numTeamFlags[col] += zoneTeamFlagCounts[col];
1939 }
1940
1941 // debugging
1942 logDebugMessage(1,"type: %d\n", options.gameType);
1943 if (options.gameType == ClassicCTF)
1944 logDebugMessage(1," capture the flag\n");
1945 if (options.gameType == RabbitChase)
1946 logDebugMessage(1," rabbit chase\n");
1947 if (options.gameType == OpenFFA)
1948 logDebugMessage(1," open free-for-all\n");
1949 if (options.gameType == TeamFFA)
1950 logDebugMessage(1," teamed free-for-all\n");
1951
1952 logDebugMessage(1,"options: %X\n", options.gameOptions);
1953 if (options.gameOptions & int(SuperFlagGameStyle))
1954 logDebugMessage(1," super flags allowed\n");
1955 if (options.gameOptions & int(JumpingGameStyle))
1956 logDebugMessage(1," jumping allowed\n");
1957 if (options.gameOptions & int(RicochetGameStyle))
1958 logDebugMessage(1," all shots ricochet\n");
1959 if (options.gameOptions & int(ShakableGameStyle))
1960 logDebugMessage(1," shakable bad flags: timeout=%f, wins=%i\n",
1961 0.1f * float(options.shakeTimeout), options.shakeWins);
1962 if (options.gameOptions & int(AntidoteGameStyle))
1963 logDebugMessage(1," antidote flags\n");
1964
1965 return;
1966 }
1967
1968
1969 // simple syntax check of comma-seperated list of group names (returns true if error)
checkCommaList(const char * list,int maxlen)1970 bool checkCommaList (const char *list, int maxlen)
1971 {
1972 int x = strlen (list);
1973 unsigned char c;
1974 if (x > maxlen)
1975 return true;
1976 if (*list==',' || list[x-1]==',')
1977 return true;
1978 while ((c=*list++) != '\0')
1979 if (c<' ' || c>'z' || c=='\'' || c=='"')
1980 return true;
1981 return false;
1982 }
1983
1984
1985
1986 // Local Variables: ***
1987 // mode: C++ ***
1988 // tab-width: 4 ***
1989 // c-basic-offset: 4 ***
1990 // indent-tabs-mode: nil ***
1991 // End: ***
1992 // ex: shiftwidth=4 tabstop=4
1993