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