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 // interface header
14 #include "bzfs.h"
15 
16 // implementation-specific system headers
17 #include <algorithm>
18 #include <assert.h>
19 #include <errno.h>
20 #include <vector>
21 #include <string>
22 #include <time.h>
23 #ifdef HAVE_MINIUPNPC_MINIUPNPC_H
24 #include <miniupnpc/miniupnpc.h>
25 #include <miniupnpc/upnpcommands.h>
26 #endif
27 
28 // implementation-specific bzflag headers
29 #include "NetHandler.h"
30 #include "version.h"
31 #include "md5.h"
32 #include "BZDBCache.h"
33 #include "ShotUpdate.h"
34 #include "PhysicsDriver.h"
35 #include "CommandManager.h"
36 #include "ConfigFileManager.h"
37 #include "bzsignal.h"
38 
39 // implementation-specific bzfs-specific headers
40 #include "RejoinList.h"
41 #include "ListServerConnection.h"
42 #include "WorldInfo.h"
43 #include "WorldWeapons.h"
44 #include "BZWReader.h"
45 #include "PackVars.h"
46 #include "SpawnPosition.h"
47 #include "DropGeometry.h"
48 #include "commands.h"
49 #include "MasterBanList.h"
50 #include "Filter.h"
51 #include "WorldEventManager.h"
52 #include "WorldGenerators.h"
53 
54 
55 // common implementation headers
56 #include "Obstacle.h"
57 #include "ObstacleMgr.h"
58 #include "CollisionManager.h"
59 #include "BaseBuilding.h"
60 #include "AnsiCodes.h"
61 #include "GameTime.h"
62 #include "bzfsAPI.h"
63 #include "Teleporter.h"
64 
65 // only include this if we are going to use plugins and export the API
66 #ifdef BZ_PLUGINS
67 #include "bzfsPlugins.h"
68 #endif
69 
70 Shots::Manager ShotManager;
71 
72 unsigned int maxNonPlayerDataChunk = 2048;
73 std::map<int, NetConnectedPeer> netConnectedPeers;
74 
75 VotingArbiter *votingarbiter = NULL;
76 
77 // pass through the SELECT loop
78 static bool dontWait = true;
79 
80 // every ListServerReAddTime seconds add ourself to the list
81 // server again.  this is in case the list server has reset
82 // or dropped us for some reason.
83 static const double ListServerReAddTime = 15 * 60;
84 
85 static const float FlagHalfLife = 10.0f;
86 
87 // do NOT change
88 static const int InvalidPlayer = -1;
89 
90 float speedTolerance = 1.125f;
91 static bool doSpeedChecks = true;
92 
93 // Command Line Options
94 CmdLineOptions *clOptions;
95 
96 // server address to listen on
97 static Address serverAddress;
98 // well known service socket
99 static int wksSocket;
100 bool handlePings = true;
101 static PingPacket pingReply;
102 // highest fd used
103 static int maxFileDescriptor;
104 // team info
105 TeamInfo team[NumTeams];
106 // num flags in flag list
107 int numFlags;
108 bool done = false;
109 // true if hit time/score limit
110 bool gameOver = true;
111 static int exitCode = 0;
112 // "real" players, i.e. do not count observers
113 uint16_t maxRealPlayers = MaxPlayers;
114 // players + observers
115 uint16_t maxPlayers = MaxPlayers;
116 // highest active id
117 uint16_t curMaxPlayers = 0;
118 int debugLevel = 0;
119 
120 static float maxWorldHeight = 0.0f;
121 static bool disableHeightChecks = false;
122 
123 std::string hexDigest;
124 
125 TimeKeeper gameStartTime;
126 TimeKeeper countdownPauseStart = TimeKeeper::getNullTime();
127 bool countdownActive = false;
128 int countdownDelay = -1;
129 int countdownResumeDelay = -1;
130 int readySetGo = -1; // match countdown timer
131 
132 static ListServerLink *listServerLink = NULL;
133 static int listServerLinksCount = 0;
134 
135 // FIXME: should be static, but needed by SpawnPosition
136 WorldInfo *world = NULL;
137 // FIXME: should be static, but needed by RecordReplay
138 char *worldDatabase = NULL;
139 uint32_t worldDatabaseSize = 0;
140 char worldSettings[4 + WorldSettingsSize];
141 float pluginWorldSize = -1;
142 float pluginWorldHeight = -1;
143 bool checkShotMismatch = true;
144 Filter   filter;
145 
146 BasesList bases;
147 
148 // global keeper of world Events
149 WorldEventManager   worldEventManager;
150 
151 // FIXME - define a well-known constant for a null playerid in address.h?
152 // might be handy in other players, too.
153 // Client does not check for rabbit to be 255, but it still works
154 // because 255 should be > curMaxPlayers and thus no matching player will
155 // be found.
156 // FIXME: should be static, but needed by RecordReplay
157 uint8_t rabbitIndex = NoPlayer;
158 
159 static RejoinList rejoinList;
160 
161 static TimeKeeper lastWorldParmChange;
162 static bool       playerHadWorld = false;
163 
164 bool          publiclyDisconnected = false;
165 
166 
167 void sendFilteredMessage(int playerIndex, PlayerId dstPlayer, const char *message, MessageType type = ChatMessage);
168 static void dropAssignedFlag(int playerIndex);
169 static std::string evaluateString(const std::string&);
170 
171 // logging to the API
172 class APILoggingCallback : public LoggingCallback
173 {
174 public:
log(int level,const char * message)175     void log ( int level, const char* message )
176     {
177         bz_LoggingEventData_V1 data;
178         data.level = level;
179         data.message = message;
180 
181         worldEventManager.callEvents(bz_eLoggingEvent,&data);
182     }
183 };
184 
185 APILoggingCallback apiLoggingCallback;
186 
187 class BZFSNetLogCB : NetworkDataLogCallback
188 {
189 public:
Init()190     void Init()
191     {
192         addNetworkLogCallback(this);
193     }
194 
~BZFSNetLogCB()195     virtual ~BZFSNetLogCB()
196     {
197         removeNetworkLogCallback(this);
198     }
199 
networkDataLog(bool send,bool udp,const unsigned char * data,unsigned int size,void * param)200     virtual void networkDataLog ( bool send, bool udp, const unsigned char *data, unsigned int size, void *param )
201     {
202         // let any listeners know we got net data
203         NetHandler *h = (NetHandler*)param;
204 
205         bz_NetTransferEventData_V1 eventData;
206         if (send)
207             eventData.eventType = bz_eNetDataSendEvent;
208         else
209             eventData.eventType = bz_eNetDataReceiveEvent;
210         eventData.send = send;
211         eventData.udp = udp;
212         eventData.iSize = size;
213         if (h)
214             eventData.playerID = h->getPlayerID();
215 
216         // make a copy of the data, just in case any plug-ins decide to MESS with it.
217         eventData.data = (unsigned char*)malloc(size);
218         memcpy(eventData.data,data,size);
219         worldEventManager.callEvents(eventData.eventType,&eventData);
220         free(eventData.data);
221     }
222 };
223 
224 BZFSNetLogCB netLogCB;
225 
getCurMaxPlayers()226 int getCurMaxPlayers()
227 {
228     return curMaxPlayers;
229 }
230 
realPlayer(const PlayerId & id)231 static bool realPlayer(const PlayerId& id)
232 {
233     GameKeeper::Player *playerData = GameKeeper::Player::getPlayerByIndex(id);
234     return playerData && playerData->player.isPlaying() && !playerData->isParting;
235 }
236 
realPlayerWithNet(const PlayerId & id)237 static bool realPlayerWithNet(const PlayerId& id)
238 {
239     GameKeeper::Player *playerData = GameKeeper::Player::getPlayerByIndex(id);
240     return playerData && playerData->netHandler && playerData->player.isPlaying() && !playerData->isParting;
241 }
242 
243 
pwrite(GameKeeper::Player & playerData,const void * b,int l)244 static int pwrite(GameKeeper::Player &playerData, const void *b, int l)
245 {
246     if (!playerData.netHandler)
247         return l;
248 
249     int result = playerData.netHandler->pwrite(b, l);
250     if (result == -1)
251         removePlayer(playerData.getIndex(), "ECONNRESET/EPIPE", false);
252     return result;
253 }
254 
255 static char sMsgBuf[MaxPacketLen];
getDirectMessageBuffer()256 char *getDirectMessageBuffer()
257 {
258     return &sMsgBuf[2*sizeof(uint16_t)];
259 }
260 
261 // FIXME? 4 bytes before msg must be valid memory, will get filled in with len+code
262 // usually, the caller gets a buffer via getDirectMessageBuffer(), but for example
263 // for MsgShotBegin the receiving buffer gets used directly
directMessage(GameKeeper::Player & playerData,uint16_t code,int len,void * msg)264 static int directMessage(GameKeeper::Player &playerData,
265                          uint16_t code, int len, void *msg)
266 {
267     if (playerData.isParting)
268         return -1;
269     // send message to one player
270     void *bufStart = (char *)msg - 2*sizeof(uint16_t);
271 
272     void *buf = bufStart;
273     buf = nboPackUShort(buf, uint16_t(len));
274     buf = nboPackUShort(buf, code);
275     return pwrite(playerData, bufStart, len + 4);
276 }
277 
directMessage(int playerIndex,uint16_t code,int len,void * msg)278 void directMessage(int playerIndex, uint16_t code, int len, void *msg)
279 {
280     GameKeeper::Player *playerData
281         = GameKeeper::Player::getPlayerByIndex(playerIndex);
282     if (!playerData)
283         return;
284 
285     directMessage(*playerData, code, len, msg);
286 }
287 
broadcastMessage(uint16_t code,int len,void * msg)288 void broadcastMessage(uint16_t code, int len, void *msg)
289 {
290     // send message to everyone
291     for (int i = 0; i < curMaxPlayers; i++)
292     {
293         if (realPlayerWithNet(i))
294             directMessage(i, code, len, msg);
295     }
296 
297     // record the packet
298     if (Record::enabled())
299         Record::addPacket(code, len, msg);
300 
301     return;
302 }
303 
304 
305 //
306 // global variable callback
307 //
onGlobalChanged(const std::string & name,void *)308 static void onGlobalChanged(const std::string& name, void*)
309 {
310     // This Callback is removed in replay mode. As
311     // well, the /set and /reset commands are blocked.
312 
313     std::string value = BZDB.get(name);
314     void *bufStart = getDirectMessageBuffer();
315     void *buf = nboPackUShort(bufStart, 1);
316     buf = nboPackUByte(buf, (uint8_t)name.length());
317     buf = nboPackString(buf, name.c_str(), name.length());
318     buf = nboPackUByte(buf, (uint8_t)value.length());
319     buf = nboPackString(buf, value.c_str(), value.length());
320     broadcastMessage(MsgSetVar, (char*)buf - (char*)bufStart, bufStart);
321 }
322 
323 
sendUDPupdate(int playerIndex)324 static void sendUDPupdate(int playerIndex)
325 {
326     // confirm inbound UDP with a TCP message
327     directMessage(playerIndex, MsgUDPLinkEstablished, 0, getDirectMessageBuffer());
328     // request/test outbound UDP with a UDP back to where we got client's packet
329     directMessage(playerIndex, MsgUDPLinkRequest, 0, getDirectMessageBuffer());
330 }
331 
lookupPlayer(const PlayerId & id)332 static int lookupPlayer(const PlayerId& id)
333 {
334     if (id == ServerPlayer)
335         return id;
336 
337     if (!realPlayer(id))
338         return InvalidPlayer;
339 
340     return id;
341 }
342 
setNoDelay(int fd)343 static void setNoDelay(int fd)
344 {
345     // turn off TCP delay (collection).  we want packets sent immediately.
346 #if defined(_WIN32)
347     BOOL on = TRUE;
348 #else
349     int on = 1;
350 #endif
351     struct protoent *p = getprotobyname("tcp");
352     if (p && setsockopt(fd, p->p_proto, TCP_NODELAY, (SSOType)&on, sizeof(on)) < 0)
353         nerror("enabling TCP_NODELAY");
354 }
355 
sendFlagUpdate(FlagInfo & flag)356 void sendFlagUpdate(FlagInfo &flag)
357 {
358     void *buf, *bufStart = getDirectMessageBuffer();
359     buf = nboPackUShort(bufStart,1);
360     bool hide
361         = (flag.flag.type->flagTeam == ::NoTeam)
362           && (flag.player == -1);
363     buf = flag.pack(buf, hide);
364     broadcastMessage(MsgFlagUpdate, (char*)buf - (char*)bufStart, bufStart);
365 }
366 
nextGameTime()367 static float nextGameTime()
368 {
369     float nextTime = +MAXFLOAT;
370     const TimeKeeper nowTime = TimeKeeper::getCurrent();
371     for (int i = 0; i < curMaxPlayers; i++)
372     {
373         GameKeeper::Player *gkPlayer = GameKeeper::Player::getPlayerByIndex(i);
374         if ((gkPlayer != NULL) && gkPlayer->player.isHuman())
375         {
376             const TimeKeeper& pTime = gkPlayer->getNextGameTime();
377             const float pNextTime = (float)(pTime - nowTime);
378             if (pNextTime < nextTime)
379                 nextTime = pNextTime;
380         }
381     }
382     return nextTime;
383 }
384 
makeGameTime(void * bufStart,float lag)385 static int makeGameTime(void* bufStart, float lag)
386 {
387     void *buf = bufStart;
388     buf = GameTime::pack(buf, lag);
389     return ((char*)buf - (char*)bufStart);
390 }
391 
sendGameTime(GameKeeper::Player * gkPlayer)392 static void sendGameTime(GameKeeper::Player* gkPlayer)
393 {
394     if (Replay::enabled())
395         return;
396     if (gkPlayer != NULL)
397     {
398         void* buf = getDirectMessageBuffer();
399         const float lag = gkPlayer->lagInfo.getLagAvg();
400         const int length = makeGameTime(buf, lag);
401         directMessage(*gkPlayer, MsgGameTime, length, buf);
402         gkPlayer->updateNextGameTime();
403     }
404     return;
405 }
406 
sendPendingGameTime()407 static void sendPendingGameTime()
408 {
409     const TimeKeeper nowTime = TimeKeeper::getCurrent();
410     for (int i = 0; i < curMaxPlayers; i++)
411     {
412         GameKeeper::Player *gkPlayer = GameKeeper::Player::getPlayerByIndex(i);
413         if ((gkPlayer != NULL)
414                 && gkPlayer->player.isHuman()
415                 && (gkPlayer->getNextGameTime() - nowTime) < 0.0f)
416             sendGameTime(gkPlayer);
417     }
418     return;
419 }
420 
421 
422 // Update the player "playerIndex" with all the flags status
sendFlagUpdate(int playerIndex)423 static void sendFlagUpdate(int playerIndex)
424 {
425     GameKeeper::Player *playerData
426         = GameKeeper::Player::getPlayerByIndex(playerIndex);
427     if (!playerData)
428         return;
429     int result;
430 
431     void *buf, *bufStart = getDirectMessageBuffer();
432 
433     buf = nboPackUShort(bufStart,0); //placeholder
434     int cnt = 0;
435     int length = sizeof(uint16_t);
436     for (int flagIndex = 0; flagIndex < numFlags; flagIndex++)
437     {
438         FlagInfo *flag = FlagInfo::get(flagIndex);
439         if (!flag)
440             continue;
441         if (flag->exist())
442         {
443             if ((length + sizeof(uint16_t) + FlagPLen) > MaxPacketLen - 2*sizeof(uint16_t))
444             {
445                 nboPackUShort(bufStart, cnt);
446                 result = directMessage(*playerData, MsgFlagUpdate, (char*)buf - (char*)bufStart, bufStart);
447                 if (result == -1)
448                     return;
449                 cnt = 0;
450                 length = sizeof(uint16_t);
451                 buf = nboPackUShort(bufStart,0); //placeholder
452             }
453 
454             bool hide = (flag->flag.type->flagTeam == ::NoTeam) &&
455                         (flag->player == -1);
456             buf = flag->pack(buf, hide);
457             length += sizeof(uint16_t)+FlagPLen;
458             cnt++;
459         }
460     }
461 
462     if (cnt > 0)
463     {
464         nboPackUShort(bufStart, cnt);
465         directMessage(*playerData, MsgFlagUpdate, (char*)buf - (char*)bufStart, bufStart);
466     }
467 }
468 
469 
sendTeamUpdate(int playerIndex,int teamIndex1,int teamIndex2)470 void sendTeamUpdate(int playerIndex, int teamIndex1, int teamIndex2)
471 {
472     // If teamIndex1 is -1, send all teams
473     // If teamIndex2 is -1, just send teamIndex1 team
474     // else send both teamIndex1 and teamIndex2 teams
475 
476     void *buf, *bufStart = getDirectMessageBuffer();
477     if (teamIndex1 == -1)
478     {
479         buf = nboPackUByte(bufStart, CtfTeams);
480         for (int t = 0; t < CtfTeams; t++)
481         {
482             buf = nboPackUShort(buf, t);
483             buf = team[t].team.pack(buf);
484         }
485     }
486     else if (teamIndex2 == -1)
487     {
488         buf = nboPackUByte(bufStart, 1);
489         buf = nboPackUShort(buf, teamIndex1);
490         buf = team[teamIndex1].team.pack(buf);
491     }
492     else
493     {
494         buf = nboPackUByte(bufStart, 2);
495         buf = nboPackUShort(buf, teamIndex1);
496         buf = team[teamIndex1].team.pack(buf);
497         buf = nboPackUShort(buf, teamIndex2);
498         buf = team[teamIndex2].team.pack(buf);
499     }
500 
501     if (playerIndex == -1)
502         broadcastMessage(MsgTeamUpdate, (char*)buf - (char*)bufStart, bufStart);
503     else
504         directMessage(playerIndex, MsgTeamUpdate, (char*)buf - (char*)bufStart, bufStart);
505 }
506 
sendClosestFlagMessage(int playerIndex,FlagType * type,float pos[3])507 void sendClosestFlagMessage(int playerIndex,FlagType *type, float pos[3] )
508 {
509     if (!type)
510         return;
511     void *buf, *bufStart = getDirectMessageBuffer();
512     buf = nboPackVector(bufStart, pos);
513     buf = nboPackStdString(buf, std::string(type->flagName));
514     directMessage(playerIndex, MsgNearFlag,(char*)buf - (char*)bufStart, bufStart);
515 }
516 
sendPlayerUpdate(GameKeeper::Player * playerData,int index)517 static void sendPlayerUpdate(GameKeeper::Player *playerData, int index)
518 {
519     if (!playerData->player.isPlaying())
520         return;
521 
522     void *bufStart = getDirectMessageBuffer();
523     void *buf      = playerData->packPlayerUpdate(bufStart);
524 
525     if (playerData->getIndex() == index)
526     {
527         // send all players info about player[playerIndex]
528         broadcastMessage(MsgAddPlayer, (char*)buf - (char*)bufStart, bufStart);
529     }
530     else
531         directMessage(index, MsgAddPlayer, (char*)buf - (char*)bufStart, bufStart);
532 }
533 
sendAutopilotStatus(GameKeeper::Player * playerData,int index)534 void sendAutopilotStatus(GameKeeper::Player *playerData, int index)
535 {
536     void *buf, *bufStart = getDirectMessageBuffer();
537     buf = nboPackUByte(bufStart, playerData->getIndex());
538     buf = nboPackUByte(buf, playerData->player.isAutoPilot());
539 
540     if (playerData->getIndex() == index)
541     {
542         // send all players autopilot status for player[playerIndex]
543         broadcastMessage(MsgAutoPilot, (char*)buf - (char*)bufStart, bufStart);
544     }
545     else
546         directMessage(index, MsgAutoPilot, (char*)buf - (char*)bufStart, bufStart);
547 }
548 
GetPlayerIPAddress(int i)549 std::string GetPlayerIPAddress( int i)
550 {
551     std::string tmp = "localhost";
552     GameKeeper::Player *playerData = GameKeeper::Player::getPlayerByIndex(i);
553     if (!playerData || !playerData->netHandler)
554         return tmp;
555 
556     unsigned int address = (unsigned int)playerData->netHandler->getIPAddress().s_addr;
557     unsigned char* a = (unsigned char*)&address;
558 
559     tmp = TextUtils::format("%d.%d.%d.%d",(int)a[0],(int)a[1],(int)a[2],(int)a[3]);
560     return tmp;
561 }
562 
sendPlayerInfo()563 void sendPlayerInfo()
564 {
565     void *buf, *bufStart = getDirectMessageBuffer();
566     int i, numPlayers = 0;
567     for (i = 0; i < int(NumTeams); i++)
568         numPlayers += team[i].team.size;
569     buf = nboPackUByte(bufStart, numPlayers);
570     for (i = 0; i < curMaxPlayers; ++i)
571     {
572         GameKeeper::Player *playerData = GameKeeper::Player::getPlayerByIndex(i);
573         if (!playerData)
574             continue;
575 
576         if (playerData->player.isPlaying())
577         {
578             // see if any events want to update the playerInfo before it is sent out
579             bz_GetPlayerInfoEventData_V1 playerInfoData;
580             playerInfoData.playerID = i;
581             playerInfoData.callsign = playerData->player.getCallSign();
582             playerInfoData.team = convertTeam(playerData->player.getTeam());
583             playerInfoData.verified = playerData->accessInfo.isVerified();
584             playerInfoData.registered = playerData->accessInfo.isRegistered();
585             playerInfoData.admin = playerData->accessInfo.showAsAdmin();
586             playerInfoData.ipAddress =GetPlayerIPAddress(i);
587 
588             worldEventManager.callEvents(bz_eGetPlayerInfoEvent,&playerInfoData);
589 
590             buf = PackPlayerInfo(buf,i,GetPlayerProperties(playerInfoData.registered,playerInfoData.verified,playerInfoData.admin));
591         }
592     }
593     broadcastMessage(MsgPlayerInfo, (char*)buf - (char*)bufStart, bufStart);
594 }
595 
596 // Send score updates to players
sendPlayerScores(GameKeeper::Player ** players,int nPlayers)597 void sendPlayerScores(GameKeeper::Player ** players, int nPlayers)
598 {
599     void *buf, *bufStart;
600     bufStart = getDirectMessageBuffer();
601 
602     buf = nboPackUByte(bufStart, nPlayers);
603 
604     for (int i = 0; i < nPlayers; i++)
605     {
606         GameKeeper::Player *player = players[i];
607 
608         buf = nboPackUByte(buf, player->getIndex());
609         buf = player->score.pack(buf);
610     }
611     broadcastMessage(MsgScore, (char*)buf-(char*)bufStart, bufStart);
612 }
613 
sendIPUpdate(int targetPlayer,int playerIndex)614 void sendIPUpdate(int targetPlayer, int playerIndex)
615 {
616     // targetPlayer = -1: send to all players with the PLAYERLIST permission
617     // playerIndex = -1: send info about all players
618 
619     GameKeeper::Player *playerData = GameKeeper::Player::getPlayerByIndex(playerIndex);
620     if (playerIndex >= 0)
621     {
622         if (!playerData || !playerData->player.isPlaying())
623             return;
624     }
625 
626     // send to who?
627     std::vector<int> receivers
628         = GameKeeper::Player::allowed(PlayerAccessInfo::playerList, targetPlayer);
629 
630     // pack and send the message(s)
631     void *buf, *bufStart = getDirectMessageBuffer();
632     if (playerIndex >= 0)
633     {
634         buf = nboPackUByte(bufStart, 1);
635         buf = playerData->packAdminInfo(buf);
636         for (unsigned int i = 0; i < receivers.size(); ++i)
637         {
638             directMessage(receivers[i], MsgAdminInfo,
639                           (char*)buf - (char*)bufStart, bufStart);
640         }
641         if (Record::enabled())
642         {
643             Record::addPacket(MsgAdminInfo,
644                               (char*)buf - (char*)bufStart, bufStart, HiddenPacket);
645         }
646     }
647     else
648     {
649         int ipsPerPackage = (MaxPacketLen - 3) / (PlayerIdPLen + 7);
650         int i, c = 0;
651         buf = nboPackUByte(bufStart, 0); // will be overwritten later
652         for (i = 0; i < curMaxPlayers; ++i)
653         {
654             playerData = GameKeeper::Player::getPlayerByIndex(i);
655             if (!playerData)
656                 continue;
657             if (playerData->player.isPlaying())
658             {
659                 buf = playerData->packAdminInfo(buf);
660                 ++c;
661             }
662             if (c == ipsPerPackage || ((i + 1 == curMaxPlayers) && c))
663             {
664                 int size = (char*)buf - (char*)bufStart;
665                 buf = nboPackUByte(bufStart, c);
666                 c = 0;
667                 for (unsigned int j = 0; j < receivers.size(); ++j)
668                     directMessage(receivers[j], MsgAdminInfo, size, bufStart);
669             }
670         }
671     }
672 }
673 
resetTeamScores(void)674 void resetTeamScores ( void )
675 {
676     // reset team scores
677     for (int i = RedTeam; i <= PurpleTeam; i++)
678     {
679         bz_TeamScoreChangeEventData_V1 eventData = bz_TeamScoreChangeEventData_V1(convertTeam(i), bz_eWins,
680                 team[i].team.getWins(), 0);
681         worldEventManager.callEvents(&eventData);
682         eventData = bz_TeamScoreChangeEventData_V1(convertTeam(i), bz_eWins, team[i].team.getLosses(), 0);
683         worldEventManager.callEvents(&eventData);
684         team[i].team.setLosses(0);
685         team[i].team.setWins(0);
686     }
687     sendTeamUpdate();
688 }
689 
resetPlayerScores(void)690 void resetPlayerScores ( void )
691 {
692     // Players to notify of new scores
693     GameKeeper::Player **playersToUpdate = new GameKeeper::Player*[curMaxPlayers];
694     int nPlayersToUpdate = 0;
695 
696     for (int i = 0; i < curMaxPlayers; i++)
697     {
698         GameKeeper::Player *player;
699 
700         player = GameKeeper::Player::getPlayerByIndex(i);
701 
702         if (player)
703         {
704             player->score.reset();
705             playersToUpdate[nPlayersToUpdate++] = player;
706         }
707     }
708 
709     // Tell the players the new scores
710     sendPlayerScores(playersToUpdate, nPlayersToUpdate);
711 
712     delete[] playersToUpdate;
713 }
714 
pauseCountdown(int pausedBy)715 void pauseCountdown ( int pausedBy )
716 {
717     if (clOptions->countdownPaused)
718         return;
719 
720     const char* callsign = bz_getPlayerCallsign(pausedBy);
721 
722     clOptions->countdownPaused = true;
723     countdownResumeDelay = -1; // reset back to "unset"
724 
725     if (callsign != NULL)
726         sendMessage(ServerPlayer, AllPlayers, TextUtils::format("Countdown paused by %s", callsign).c_str());
727     else
728         sendMessage(ServerPlayer, AllPlayers, "Countdown paused");
729 
730     // fire off a game pause event
731     bz_GamePauseResumeEventData_V2 pauseEventData;
732     pauseEventData.eventType = bz_eGamePauseEvent;
733     pauseEventData.playerID = pausedBy;
734     pauseEventData.actionBy = callsign;
735     worldEventManager.callEvents(bz_eGamePauseEvent, &pauseEventData);
736 }
737 
resumeCountdown(int resumedBy)738 void resumeCountdown ( int resumedBy )
739 {
740     if (!clOptions->countdownPaused)
741         return;
742 
743     const char* callsign = bz_getPlayerCallsign(resumedBy);
744 
745     clOptions->countdownPaused = false;
746     countdownResumeDelay = (int) BZDB.eval(StateDatabase::BZDB_COUNTDOWNRESDELAY);
747 
748     if (countdownResumeDelay <= 0)
749     {
750         // resume instantly
751         countdownResumeDelay = -1; // reset back to "unset"
752 
753         if (callsign != NULL)
754             sendMessage(ServerPlayer, AllPlayers, TextUtils::format("Countdown resumed by %s", callsign).c_str());
755         else
756             sendMessage(ServerPlayer, AllPlayers, "Countdown resumed");
757 
758         // fire off a game resume event
759         bz_GamePauseResumeEventData_V2 resumeEventData;
760         resumeEventData.eventType = bz_eGameResumeEvent;
761         resumeEventData.playerID = resumedBy;
762         resumeEventData.actionBy = callsign;
763         worldEventManager.callEvents(bz_eGameResumeEvent, &resumeEventData);
764     }
765     else
766     {
767         // resume after number of seconds in countdownResumeDelay
768         if (callsign != NULL)
769             sendMessage(ServerPlayer, AllPlayers, TextUtils::format("Countdown is being resumed by %s", callsign).c_str());
770         else
771             sendMessage(ServerPlayer, AllPlayers, "Countdown is being resumed");
772     }
773 }
774 
startCountdown(int delay,float limit,int playerID)775 void startCountdown ( int delay, float limit, int playerID )
776 {
777     const char* callsign = bz_getPlayerCallsign(playerID);
778 
779     if (callsign != NULL)
780         sendMessage(ServerPlayer, AllPlayers, TextUtils::format("Team scores reset, countdown started by %s.",
781                     callsign).c_str());
782     else
783         sendMessage(ServerPlayer, AllPlayers, "Team scores reset, countdown started.");
784 
785     clOptions->timeLimit = limit;
786     countdownDelay = delay;
787 
788     // let everyone know what's going on
789     long int timeArray[4];
790     std::string matchBegins;
791     if (countdownDelay == 0)
792         matchBegins = "Match begins now!";
793     else
794     {
795         TimeKeeper::convertTime(countdownDelay, timeArray);
796         std::string countdowntime = TimeKeeper::printTime(timeArray);
797         matchBegins = TextUtils::format("Match begins in about %s", countdowntime.c_str());
798     }
799     sendMessage(ServerPlayer, AllPlayers, matchBegins.c_str());
800 
801     TimeKeeper::convertTime(clOptions->timeLimit, timeArray);
802     std::string timelimit = TimeKeeper::printTime(timeArray);
803     matchBegins = TextUtils::format("Match duration is %s", timelimit.c_str());
804     sendMessage(ServerPlayer, AllPlayers, matchBegins.c_str());
805 
806     // make sure the game always start unpaused
807     clOptions->countdownPaused = false;
808     clOptions->countdownStarter = playerID;
809     countdownPauseStart = TimeKeeper::getNullTime();
810 }
811 
cancelCountdown(int playerID)812 void cancelCountdown ( int playerID )
813 {
814     const char* callsign = bz_getPlayerCallsign(playerID);
815 
816     if (callsign != NULL)
817         sendMessage(ServerPlayer, AllPlayers, TextUtils::format("Countdown cancelled by %s.", callsign).c_str());
818     else
819         sendMessage(ServerPlayer, AllPlayers, "Countdown cancelled");
820 
821     countdownDelay = -1;
822     readySetGo = -1;
823 }
824 
getTeamCounts()825 PingPacket getTeamCounts()
826 {
827     pingReply.rogueCount = 0;
828     pingReply.redCount = 0;
829     pingReply.greenCount = 0;
830     pingReply.blueCount = 0;
831     pingReply.purpleCount = 0;
832     pingReply.observerCount = 0;
833 
834     if (gameOver && clOptions->timeLimit > 0.0f && !clOptions->timeManualStart)
835     {
836         // pretend there are no players if the game is over, but only
837         // for servers with automatic countdown because we want the server
838         // to become empty, so a new countdown can start.
839         // Servers with -timemanual (match servers) or plugins whch handle gameover
840         // usually want people to join even when last game has just ended.
841         // (FIXME: the countdown/gameover handling really needs a new concept,
842         //   originally it was not possible to even join a server when gameover
843         //   was reached, but this was changed for manual countdown (match) servers)
844     }
845     else
846     {
847         // update player counts in ping reply.
848 
849         for (int i = 0; i < curMaxPlayers; i++)
850         {
851             GameKeeper::Player *p = GameKeeper::Player::getPlayerByIndex(i);
852             if (p == NULL)
853                 continue;
854 
855             if (p->player.isHuman())
856             {
857                 switch (p->player.getTeam())
858                 {
859                 case RabbitTeam:
860                 case RogueTeam:
861                 case HunterTeam:
862                     pingReply.rogueCount++;
863                     break;
864 
865                 case RedTeam:
866                     pingReply.redCount++;
867                     break;
868 
869                 case GreenTeam:
870                     pingReply.greenCount++;
871                     break;
872 
873                 case BlueTeam:
874                     pingReply.blueCount++;
875                     break;
876 
877                 case PurpleTeam:
878                     pingReply.purpleCount++;
879                     break;
880 
881                 case ObserverTeam:
882                     pingReply.observerCount++;
883                     break;
884 
885                 default:
886                     break;
887                 }
888             }
889         }
890     }
891     return pingReply;
892 }
893 
894 
895 //============================================================================//
896 
897 static std::string publicOwner = "";
898 
899 
getPublicOwner()900 const std::string& getPublicOwner()
901 {
902     return publicOwner;
903 }
904 
905 
setPublicOwner(const std::string & owner)906 void setPublicOwner(const std::string& owner)
907 {
908     publicOwner = owner;
909 }
910 
911 
912 //============================================================================//
913 
publicize()914 void publicize()
915 {
916     /* // hangup any previous list server sockets
917     if (listServerLinksCount)
918       listServerLink.closeLink(); */
919 
920     listServerLinksCount = 0;
921 
922     if (listServerLink)
923         delete listServerLink;
924 
925     if (clOptions->publicizeServer)
926     {
927         // list server initialization
928         for (std::vector<std::string>::const_iterator i = clOptions->listServerURL.begin(); i < clOptions->listServerURL.end();
929                 ++i)
930         {
931             listServerLink = new ListServerLink(i->c_str(),
932                                                 clOptions->publicizedAddress, clOptions->publicizedTitle, clOptions->advertiseGroups);
933             listServerLinksCount++;
934         }
935     }
936     else
937     {
938         // don't use a list server; we need a ListServerLink object anyway
939         // pass no arguments to the constructor, so the object will exist but do nothing if called
940         listServerLink = new ListServerLink();
941         listServerLinksCount = 0;
942     }
943 }
944 
945 
serverStart()946 static bool serverStart()
947 {
948 #ifdef SO_REUSEADDR
949 #if defined(_WIN32)
950     const BOOL optOn = TRUE;
951     BOOL opt;
952 #else
953     const int optOn = 1;
954     int opt;
955 #endif
956 #endif
957     maxFileDescriptor = 0;
958 
959     // init addr:port structure
960     struct sockaddr_in addr;
961     memset(&addr, 0, sizeof(addr));
962     addr.sin_family = AF_INET;
963     addr.sin_addr = serverAddress;
964 
965     pingReply.serverId.port = addr.sin_port = htons(clOptions->wksPort);
966 
967     // open well known service port
968     wksSocket = socket(AF_INET, SOCK_STREAM, 0);
969     if (wksSocket == -1)
970     {
971         nerror("couldn't make connect socket");
972         return false;
973     }
974 #ifdef SO_REUSEADDR
975     /* set reuse address */
976     opt = optOn;
977     if (setsockopt(wksSocket, SOL_SOCKET, SO_REUSEADDR, (SSOType)&opt, sizeof(opt)) < 0)
978     {
979         nerror("serverStart: setsockopt SO_REUSEADDR");
980         close(wksSocket);
981         return false;
982     }
983 #endif
984     if (bind(wksSocket, (const struct sockaddr*)&addr, sizeof(addr)) == -1)
985     {
986         nerror("couldn't bind connect socket");
987         close(wksSocket);
988         return false;
989     }
990 
991     if (listen(wksSocket, 5) == -1)
992     {
993         nerror("couldn't make connect socket queue");
994         close(wksSocket);
995         return false;
996     }
997 
998     addr.sin_port = htons(clOptions->wksPort);
999     if (!NetHandler::initHandlers(addr))
1000     {
1001         close(wksSocket);
1002         return false;
1003     }
1004 
1005     listServerLinksCount = 0;
1006     publicize();
1007     return true;
1008 }
1009 
1010 
serverStop()1011 static void serverStop()
1012 {
1013     // shut down server
1014     // first ignore further attempts to kill me
1015     bzSignal(SIGINT, SIG_IGN);
1016     bzSignal(SIGTERM, SIG_IGN);
1017 
1018     // reject attempts to talk to server
1019     shutdown(wksSocket, 2);
1020     close(wksSocket);
1021 
1022     // tell players to quit
1023     for (int i = 0; i < curMaxPlayers; i++)
1024         directMessage(i, MsgSuperKill, 0, getDirectMessageBuffer());
1025 
1026     // close connections
1027     NetHandler::destroyHandlers();
1028 }
1029 
1030 
relayPlayerPacket(int index,uint16_t len,const void * rawbuf,uint16_t code)1031 static void relayPlayerPacket(int index, uint16_t len, const void *rawbuf, uint16_t code)
1032 {
1033     if (Record::enabled())
1034         Record::addPacket(code, len, (const char*)rawbuf + 4);
1035 
1036     // relay packet to all players except origin
1037     for (int i = 0; i < curMaxPlayers; i++)
1038     {
1039         GameKeeper::Player *playerData = GameKeeper::Player::getPlayerByIndex(i);
1040         if (!playerData)
1041             continue;
1042         PlayerInfo& pi = playerData->player;
1043 
1044         if (i != index && pi.isPlaying())
1045             pwrite(*playerData, rawbuf, len + 4);
1046     }
1047 }
1048 
makeWalls(void)1049 void makeWalls ( void )
1050 {
1051     float worldSize = BZDBCache::worldSize;
1052     if (pluginWorldSize > 0)
1053         worldSize = pluginWorldSize;
1054 
1055     float wallHeight = BZDB.eval(StateDatabase::BZDB_WALLHEIGHT);
1056     if (pluginWorldHeight > 0)
1057         wallHeight = pluginWorldHeight;
1058 
1059     double halfSize = worldSize * 0.5;
1060     double angleDelta = 360.0 / clOptions->wallSides;
1061     double startAngle = -angleDelta*0.5;
1062     double radius = sqrt(halfSize*halfSize + halfSize*halfSize);
1063 
1064     float degToRad = (float)(M_PI/180.0);
1065 
1066     double segmentLen = (sin(angleDelta*0.5*(degToRad)) * radius)*2;
1067 
1068     if (0)
1069     {
1070         for ( int w = 0; w < clOptions->wallSides; w++ )
1071         {
1072             float midpointRad = sqrtf((float)radius*(float)radius-((float)segmentLen*0.5f)*((float)segmentLen*0.5f));
1073             float midpointAngle = (float)startAngle + ((float)angleDelta*0.5f) + ((float)angleDelta*w);
1074 
1075             float x = sinf(midpointAngle*degToRad)*midpointRad;
1076             float y = cosf(midpointAngle*degToRad)*midpointRad;
1077 
1078             world->addWall(x, y, 0.0f, (270.0f-midpointAngle)*degToRad, (float)segmentLen, wallHeight);
1079 
1080         }
1081     }
1082     else
1083     {
1084         world->addWall(0.0f, 0.5f * worldSize, 0.0f, (float)(1.5 * M_PI), 0.5f * worldSize, wallHeight);
1085         world->addWall(0.5f * worldSize, 0.0f, 0.0f, (float)M_PI, 0.5f * worldSize, wallHeight);
1086         world->addWall(0.0f, -0.5f * worldSize, 0.0f, (float)(0.5 * M_PI), 0.5f * worldSize, wallHeight);
1087         world->addWall(-0.5f * worldSize, 0.0f, 0.0f, 0.0f, 0.5f * worldSize, wallHeight);
1088     }
1089 }
1090 
1091 
defineWorld(void)1092 bool defineWorld ( void )
1093 {
1094     // clean up old database
1095     if (world)
1096         delete world;
1097     if (worldDatabase)
1098         delete[] worldDatabase;
1099 
1100     bz_GetWorldEventData_V1   worldData;
1101     worldData.ctf     = (clOptions->gameType == ClassicCTF);
1102     worldData.rabbit  = (clOptions->gameType == RabbitChase);
1103     worldData.openFFA = (clOptions->gameType == OpenFFA);
1104     worldData.worldFile = clOptions->worldFile;
1105 
1106     worldData.eventTime = TimeKeeper::getCurrent().getSeconds();
1107     worldEventManager.callEvents(bz_eGetWorldEvent, &worldData);
1108 
1109     if (!worldData.generated && worldData.worldFile.size())
1110     {
1111         clOptions->worldFile = worldData.worldFile.c_str();
1112         if (worldData.ctf)
1113             clOptions->gameType = ClassicCTF;
1114         else if (worldData.rabbit)
1115             clOptions->gameType = RabbitChase;
1116         else if (worldData.openFFA)
1117             clOptions->gameType = OpenFFA;
1118         else
1119             clOptions->gameType = TeamFFA;
1120 
1121         // todo.. load this maps options and vars and stuff.
1122     }
1123 
1124     // make world and add buildings
1125     if (worldData.worldFile.size())
1126     {
1127         BZWReader* reader = new BZWReader(std::string(worldData.worldFile.c_str()));
1128         world = reader->defineWorldFromFile();
1129         delete reader;
1130 
1131         if (clOptions->gameType == ClassicCTF)
1132         {
1133             for (int i = RedTeam; i <= PurpleTeam; i++)
1134             {
1135                 if ((clOptions->maxTeam[i] > 0) && bases.find(i) == bases.end())
1136                 {
1137                     std::cerr << "base was not defined for "
1138                               << Team::getName((TeamColor)i)
1139                               << std::endl;
1140                     return false;
1141                 }
1142             }
1143         }
1144     }
1145     else
1146     {
1147         // check and see if anyone wants to define the world from an event
1148 
1149         world = new WorldInfo;
1150         if (!worldData.generated)   // if the plugin didn't make a world, make one
1151         {
1152             delete world;
1153             if (clOptions->gameType == ClassicCTF)
1154                 world = defineTeamWorld();
1155             else
1156                 world = defineRandomWorld();
1157         }
1158         else
1159         {
1160             makeWalls();
1161 
1162             OBSTACLEMGR.makeWorld();
1163             world->finishWorld();
1164         }
1165     }
1166 
1167     if (world == NULL)
1168         return false;
1169 
1170     maxWorldHeight = world->getMaxWorldHeight();
1171 
1172     // package up world
1173     world->packDatabase();
1174 
1175     // now get world packaged for network transmission
1176     worldDatabaseSize = 4 + WorldCodeHeaderSize +
1177                         world->getDatabaseSize() + 4 + WorldCodeEndSize;
1178 
1179     worldDatabase = new char[worldDatabaseSize];
1180     // this should NOT happen but it does sometimes
1181     if (!worldDatabase)
1182         return false;
1183     memset(worldDatabase, 0, worldDatabaseSize);
1184 
1185     void *buf = worldDatabase;
1186     buf = nboPackUShort(buf, WorldCodeHeaderSize);
1187     buf = nboPackUShort(buf, WorldCodeHeader);
1188     buf = nboPackUShort(buf, mapVersion);
1189     buf = nboPackUInt(buf, world->getUncompressedSize());
1190     buf = nboPackUInt(buf, world->getDatabaseSize());
1191     buf = nboPackString(buf, world->getDatabase(), world->getDatabaseSize());
1192     buf = nboPackUShort(buf, WorldCodeEndSize);
1193     buf = nboPackUShort(buf, WorldCodeEnd);
1194 
1195     TimeKeeper startTime = TimeKeeper::getCurrent();
1196     MD5 md5;
1197     md5.update((unsigned char *)worldDatabase, worldDatabaseSize);
1198     md5.finalize();
1199     hexDigest = (clOptions->worldFile == "") ? 't' : 'p';
1200     hexDigest += md5.hexdigest();
1201     TimeKeeper endTime = TimeKeeper::getCurrent();
1202     logDebugMessage(3,"MD5 generation: %.3f seconds\n", endTime - startTime);
1203     logDebugMessage(3,"MD5 = %s\n", hexDigest.c_str()+1);
1204 
1205     // water levels probably require flags on buildings
1206     const float waterLevel = world->getWaterLevel();
1207     if (!clOptions->flagsOnBuildings && (waterLevel > 0.0f))
1208     {
1209         clOptions->flagsOnBuildings = true;
1210         clOptions->respawnOnBuildings = true;
1211         logDebugMessage(1,"WARNING: enabling flag and tank spawns on buildings due to waterLevel\n");
1212     }
1213 
1214     // reset other stuff
1215     int i;
1216     for (i = 0; i < NumTeams; i++)
1217     {
1218         team[i].team.size = 0;
1219         team[i].team.setWins(0);
1220         team[i].team.setLosses(0);
1221     }
1222     FlagInfo::setNoFlagInAir();
1223     for (i = 0; i < numFlags; i++)
1224     {
1225         FlagInfo *flag = FlagInfo::get(i);
1226         if (!flag)
1227             continue;
1228         resetFlag(*flag);
1229     }
1230     bz_EventData eventData = bz_EventData(bz_eWorldFinalized);
1231     worldEventManager.callEvents(&eventData);
1232     return true;
1233 }
1234 
saveWorldCache(const char * fileName)1235 bool saveWorldCache( const char* fileName )
1236 {
1237     FILE* file;
1238     if (fileName)
1239         file = fopen (fileName, "wb");
1240     else
1241         file = fopen (clOptions->cacheOut.c_str(), "wb");
1242     if (file == NULL)
1243         return false;
1244     size_t written =
1245         fwrite (worldDatabase, sizeof(char), worldDatabaseSize, file);
1246     fclose (file);
1247     if (written != worldDatabaseSize)
1248         return false;
1249     return true;
1250 }
1251 
getMaxWorldHeight(void)1252 float getMaxWorldHeight( void )
1253 {
1254     float heightFudge = 1.10f; /* 10% */
1255     float wingsGravity = BZDB.eval(StateDatabase::BZDB_WINGSGRAVITY);
1256     float normalGravity = BZDB.eval(StateDatabase::BZDB_GRAVITY);
1257 
1258     if ((wingsGravity < 0.0f) && (normalGravity < 0.0f))
1259     {
1260         float wingsMaxHeight = BZDB.eval(StateDatabase::BZDB_WINGSJUMPVELOCITY);
1261         wingsMaxHeight *= wingsMaxHeight;
1262         wingsMaxHeight *= (1 + BZDB.eval(StateDatabase::BZDB_WINGSJUMPCOUNT));
1263         wingsMaxHeight /= (-wingsGravity * 0.5f);
1264 
1265         float normalMaxHeight = BZDB.eval(StateDatabase::BZDB_JUMPVELOCITY);
1266         normalMaxHeight *= normalMaxHeight;
1267         normalMaxHeight /= (-normalGravity * 0.5f);
1268 
1269         float maxHeight = (wingsMaxHeight > normalMaxHeight) ? wingsMaxHeight : normalMaxHeight;
1270 
1271         // final adjustments
1272         maxHeight *= heightFudge;
1273         maxHeight += maxWorldHeight;
1274 
1275         return maxHeight;
1276     }
1277 
1278     return -1;
1279 }
1280 
whoseBase(float x,float y,float z)1281 TeamColor whoseBase(float x, float y, float z)
1282 {
1283     if (clOptions->gameType!= ClassicCTF)
1284         return NoTeam;
1285 
1286     float highest = -1;
1287     int highestteam = -1;
1288 
1289     for (BasesList::iterator it = bases.begin(); it != bases.end(); ++it)
1290     {
1291         float baseZ = it->second.findBaseZ(x,y,z);
1292         if (baseZ > highest)
1293         {
1294             highest = baseZ;
1295             highestteam = it->second.getTeam();
1296         }
1297     }
1298 
1299     if (highestteam == -1)
1300         return NoTeam;
1301     else
1302         return TeamColor(highestteam);
1303 }
1304 
1305 
1306 #ifdef PRINTSCORE
dumpScore()1307 static void dumpScore()
1308 {
1309     if (!clOptions->printScore)
1310         return;
1311     if (clOptions->timeLimit > 0.0f)
1312         std::cout << "#time " << clOptions->timeLimit - clOptions->timeElapsed << std::endl;
1313     std::cout << "#teams";
1314     for (int i = int(RedTeam); i < NumTeams; i++)
1315         std::cout << ' ' << team[i].team.getWins() << '-' << team[i].team.getLosses() << ' ' << Team::getName(TeamColor(i));
1316     GameKeeper::Player::dumpScore();
1317     std::cout << "#end\n";
1318 }
1319 #endif
1320 
1321 static void handleTcp(NetHandler &netPlayer, int i, const RxStatus e);
1322 
acceptClient()1323 static void acceptClient()
1324 {
1325     // client (not a player yet) is requesting service.
1326     // accept incoming connection on our well known port
1327     struct sockaddr_in clientAddr;
1328     AddrLen addr_len = sizeof(clientAddr);
1329     int fd = accept(wksSocket, (struct sockaddr*)&clientAddr, &addr_len);
1330     if (fd == -1)
1331     {
1332         nerror("accepting on wks");
1333         return;
1334     }
1335 
1336     // don't buffer info, send it immediately
1337     setNoDelay(fd);
1338     BzfNetwork::setNonBlocking(fd);
1339 
1340     int keepalive = 1, n;
1341     n = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
1342                    (SSOType)&keepalive, sizeof(int));
1343     if (n < 0)
1344         nerror("couldn't set keepalive");
1345 
1346     // they aren't a player yet till they send us the connection string
1347     NetConnectedPeer peer;
1348     peer.netHandler = new NetHandler(clientAddr, fd);
1349     peer.apiHandler = NULL;
1350     peer.player = -1;
1351     peer.socket = fd;
1352     peer.deleteMe = false;
1353     peer.sent = false;
1354     peer.minSendTime = 0;
1355     peer.lastSend = TimeKeeper::getCurrent();
1356     peer.startTime = TimeKeeper::getCurrent();
1357     peer.deleteWhenDoneSending = false;
1358     peer.inactivityTimeout = 30;
1359 
1360     netConnectedPeers[fd] = peer;
1361 }
1362 
getNewPlayerID()1363 PlayerId getNewPlayerID()
1364 {
1365     PlayerId playerIndex;
1366 
1367     // find open slot in players list
1368     PlayerId minPlayerId = 0, maxPlayerId = (PlayerId)MaxPlayers;
1369     if (Replay::enabled())
1370     {
1371         minPlayerId = MaxPlayers;
1372         maxPlayerId = MaxPlayers + ReplayObservers;
1373     }
1374     playerIndex = GameKeeper::Player::getFreeIndex(minPlayerId, maxPlayerId);
1375 
1376     if (playerIndex >= maxPlayerId)
1377         return 0xff;
1378 
1379     if (playerIndex >= curMaxPlayers)
1380         curMaxPlayers = playerIndex + 1;
1381 
1382     return playerIndex;
1383 }
1384 
MakePlayer(NetHandler * handler)1385 static bool MakePlayer ( NetHandler *handler )
1386 {
1387     // send server version and playerid
1388     char buffer[9];
1389     memcpy(buffer, getServerVersion(), 8);
1390     // send 0xff if list is full
1391     buffer[8] = (char)0xff;
1392 
1393     PlayerId playerIndex = getNewPlayerID();
1394 
1395     if (playerIndex != 0xff)
1396     {
1397         logDebugMessage(1,"Player [%d] accept() from %s:%d on %i\n", playerIndex,
1398                         inet_ntoa(handler->getUADDR().sin_addr), ntohs(handler->getUADDR().sin_port), handler->getFD());
1399 
1400         if (playerIndex >= curMaxPlayers)
1401             curMaxPlayers = playerIndex+1;
1402     }
1403     else     // full? reject by closing socket
1404     {
1405         logDebugMessage(1,"all slots occupied, rejecting accept() from %s:%d on %i\n",
1406                         inet_ntoa(handler->getUADDR().sin_addr), ntohs(handler->getUADDR().sin_port), handler->getFD());
1407 
1408         // send back 0xff before closing
1409         send(handler->getFD(), (const char*)buffer, sizeof(buffer), 0);
1410 
1411         close(handler->getFD());
1412         return false;
1413     }
1414 
1415     buffer[8] = (uint8_t)playerIndex;
1416     send(handler->getFD(), (const char*)buffer, sizeof(buffer), 0);
1417 
1418     // FIXME add new client server welcome packet here when client code is ready
1419     new GameKeeper::Player(playerIndex, handler, handleTcp);
1420 
1421     // send the GameTime
1422     GameKeeper::Player* gkPlayer =
1423         GameKeeper::Player::getPlayerByIndex(playerIndex);
1424     if (gkPlayer != NULL
1425             && gkPlayer->player.isHuman() )
1426         sendGameTime(gkPlayer);
1427 
1428     checkGameOn();
1429     return true;
1430 }
1431 
checkGameOn()1432 void checkGameOn()
1433 {
1434     // if game was over and this is the first player then game is on
1435     if (gameOver)
1436     {
1437         int count = GameKeeper::Player::count();
1438         if (count == 0)
1439         {
1440             gameOver = false;
1441             gameStartTime = TimeKeeper::getCurrent();
1442             if (clOptions->timeLimit > 0.0f && !clOptions->timeManualStart)
1443             {
1444                 clOptions->timeElapsed = 0.0f;
1445                 countdownActive = true;
1446             }
1447         }
1448     }
1449 }
1450 
1451 
respondToPing(Address addr)1452 static void respondToPing(Address addr)
1453 {
1454     // reply with current game info
1455     pingReply.sourceAddr = addr;
1456     // Total up rogue + rabbit + hunter teams
1457     pingReply.rogueCount = (uint8_t)team[0].team.size + (uint8_t)team[6].team.size + (uint8_t)team[7].team.size;
1458     pingReply.redCount = (uint8_t)team[1].team.size;
1459     pingReply.greenCount = (uint8_t)team[2].team.size;
1460     pingReply.blueCount = (uint8_t)team[3].team.size;
1461     pingReply.purpleCount = (uint8_t)team[4].team.size;
1462     pingReply.observerCount = (uint8_t)team[5].team.size;
1463 }
1464 
1465 
sendPlayerMessage(GameKeeper::Player * playerData,PlayerId dstPlayer,const char * message)1466 void sendPlayerMessage(GameKeeper::Player *playerData, PlayerId dstPlayer,
1467                        const char *message)
1468 {
1469     const PlayerId srcPlayer = playerData->getIndex();
1470     MessageType type = ChatMessage;
1471 
1472     // Check for spoofed /me messages
1473     if ((strlen(message) >= 3) && (message[0] == '*') && (message[strlen(message)-2] == '\t')
1474             && (message[strlen(message)-1] == '*'))
1475         return;
1476 
1477     // reformat any '/me' action messages
1478     // this is here instead of in commands.cxx to allow player-player/player-channel targetted messages
1479     if (strncasecmp(message, "/me", 3) == 0)
1480     {
1481 
1482         // don't bother with empty messages
1483         if (message[3] == '\0' || (isspace(message[3]) && message[4] == '\0'))
1484         {
1485             char reply[MessageLen] = {0};
1486             snprintf(reply, MessageLen, "%s, the /me command requires an argument", playerData->player.getCallSign());
1487             sendMessage(ServerPlayer, srcPlayer, reply);
1488             return;
1489         }
1490 
1491         // don't intercept other messages beginning with /me...
1492         if (!isspace(message[3]))
1493         {
1494             parseServerCommand(message, srcPlayer, dstPlayer);
1495             return;
1496         }
1497 
1498         // check for permissions
1499         if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::actionMessage))
1500         {
1501             char reply[MessageLen] = {0};
1502             snprintf(reply, MessageLen, "%s, you are not presently authorized to perform /me actions",
1503                      playerData->player.getCallSign());
1504             sendMessage(ServerPlayer, srcPlayer, reply);
1505             return;
1506         }
1507 
1508         // Trim off the command to leave the player's message
1509         message = message + 4;
1510 
1511         // Set the message type to an action messsage
1512         type = ActionMessage;
1513     }
1514 
1515     // check for a server command
1516     else if ((message[0] == '/') && (isalpha(message[1]) || message[1] == '?'))
1517     {
1518         // record server commands
1519         if (Record::enabled())
1520         {
1521             void *buf, *bufStart = getDirectMessageBuffer();
1522             buf = nboPackUByte(bufStart, srcPlayer);
1523             buf = nboPackUByte(buf, dstPlayer);
1524             buf = nboPackUByte(buf, type);
1525             buf = nboPackString(buf, message, strlen(message) + 1);
1526             Record::addPacket(MsgMessage, (char*)buf - (char*)bufStart, bufStart,
1527                               HiddenPacket);
1528         }
1529         parseServerCommand(message, srcPlayer, dstPlayer);
1530         return; // bail out
1531     }
1532 
1533     // check if the player has permission to use the admin channel
1534     if ((dstPlayer == AdminPlayers) &&
1535             !playerData->accessInfo.hasPerm(PlayerAccessInfo::adminMessageSend))
1536     {
1537         sendMessage(ServerPlayer, srcPlayer,
1538                     "You do not have permission to speak on the admin channel.");
1539         return; // bail out
1540     }
1541 
1542     // check for bogus targets
1543     if ((dstPlayer < LastRealPlayer) && !realPlayer(dstPlayer))
1544     {
1545         sendMessage(ServerPlayer, srcPlayer,
1546                     "The player you tried to talk to does not exist!");
1547         return; // bail out
1548     }
1549 
1550     sendChatMessage(srcPlayer, dstPlayer, message, type);
1551     return;
1552 }
1553 
sendChatMessage(PlayerId srcPlayer,PlayerId dstPlayer,const char * message,MessageType type)1554 void sendChatMessage(PlayerId srcPlayer, PlayerId dstPlayer, const char *message, MessageType type)
1555 {
1556     bz_ChatEventData_V2 chatData;
1557     chatData.from = BZ_SERVER;
1558     if (srcPlayer != ServerPlayer)
1559         chatData.from = srcPlayer;
1560 
1561     chatData.to = BZ_NULLUSER;
1562 
1563     if (dstPlayer == AllPlayers)
1564         chatData.to = BZ_ALLUSERS;
1565     else if ( dstPlayer == AdminPlayers )
1566         chatData.team = eAdministrators;
1567     else if ( dstPlayer > LastRealPlayer )
1568         chatData.team = convertTeam((TeamColor)(FirstTeam - dstPlayer));
1569     else
1570         chatData.to = dstPlayer;
1571 
1572     if (type == ActionMessage)
1573         chatData.messageType = eActionMessage;
1574     else
1575         chatData.messageType = eChatMessage;
1576 
1577     chatData.message = message;
1578 
1579     // send any events that want to watch the chat
1580     if (chatData.message.size())
1581         worldEventManager.callEvents(bz_eRawChatMessageEvent,&chatData);
1582 
1583     if (chatData.message.size())
1584         sendFilteredMessage(srcPlayer, dstPlayer, chatData.message.c_str(), type);
1585 }
1586 
sendFilteredMessage(int sendingPlayer,PlayerId recipientPlayer,const char * message,MessageType type)1587 void sendFilteredMessage(int sendingPlayer, PlayerId recipientPlayer, const char *message, MessageType type)
1588 {
1589     const char* msg = message;
1590 
1591     if (clOptions->filterChat)
1592     {
1593         char filtered[MessageLen];
1594         strncpy(filtered, message, MessageLen - 1);
1595         filtered[MessageLen - 1] = '\0';
1596         if (clOptions->filterSimple)
1597             clOptions->filter.filter(filtered, true);
1598         else
1599             clOptions->filter.filter(filtered, false);
1600         if (strcmp(message,filtered) != 0)  // the filter did do something so barf a message
1601         {
1602             bz_MessageFilteredEventData_V1    eventData;
1603 
1604             eventData.playerID = sendingPlayer;
1605             eventData.rawMessage = message;
1606             eventData.filteredMessage = filtered;
1607 
1608             worldEventManager.callEvents(bz_eMessageFilteredEvent,&eventData);
1609 
1610             size_t size = MessageLen-1;
1611             if (eventData.filteredMessage.size() < size)
1612                 size = eventData.filteredMessage.size();
1613             strncpy(filtered, eventData.filteredMessage.c_str(), size);
1614             filtered[size] = '\0';
1615         }
1616 
1617         msg = filtered;
1618 
1619     }
1620 
1621     // if the message is empty, stop.
1622     if (strlen(msg) == 0)
1623         return;
1624 
1625     // check that the player has the talk permission
1626     GameKeeper::Player *senderData = GameKeeper::Player::getPlayerByIndex(sendingPlayer);
1627 
1628     if (!senderData && sendingPlayer != ServerPlayer)
1629         return;
1630 
1631     if (senderData)
1632     {
1633         if (!senderData->accessInfo.hasPerm(PlayerAccessInfo::talk))
1634         {
1635             // If the user does not have the TALK permission, he can't send any messages
1636             // He's only allowed to talk with admins, if he has the adminMessageSend permission
1637             if (senderData->accessInfo.hasPerm(PlayerAccessInfo::adminMessageSend))
1638             {
1639                 if (recipientPlayer == AdminPlayers)
1640                 {
1641                     sendMessage(sendingPlayer, recipientPlayer, msg, type);
1642                     return;
1643                 }
1644 
1645                 // Let the user send a private message to admins
1646                 GameKeeper::Player *recipientData = GameKeeper::Player::getPlayerByIndex(recipientPlayer);
1647                 if (recipientData && recipientData->accessInfo.hasPerm(PlayerAccessInfo::adminMessageReceive) &&
1648                         !recipientData->accessInfo.hasPerm(PlayerAccessInfo::hideAdmin))
1649                 {
1650                     sendMessage(sendingPlayer, recipientPlayer, msg, type);
1651                     return;
1652                 }
1653             }
1654 
1655             sendMessage(ServerPlayer, sendingPlayer, "We're sorry, you are not allowed to talk!");
1656             return; // bail out
1657         }
1658 
1659         if (recipientPlayer < LastRealPlayer && !senderData->accessInfo.hasPerm(PlayerAccessInfo::privateMessage))
1660         {
1661             sendMessage(ServerPlayer, sendingPlayer, "You are not allowed to send private messages to other players!");
1662             return;
1663         }
1664 
1665     }
1666 
1667     sendMessage(sendingPlayer, recipientPlayer, msg, type);
1668 }
1669 
sendMessage(int playerIndex,PlayerId dstPlayer,const char * message,MessageType type)1670 void sendMessage(int playerIndex, PlayerId dstPlayer, const char *message, MessageType type)
1671 {
1672     long int msglen = strlen(message) + 1; // include null terminator
1673     const char *msg = message;
1674 
1675     if (message[0] == '/' && message[1] == '/')
1676     {
1677         ++msg;
1678         --msglen;
1679     }
1680 
1681     // Should cut the message
1682     if (msglen > MessageLen)
1683     {
1684         logDebugMessage(1,"WARNING: Network message being sent is too long! "
1685                         "(message is %li, cutoff at %d)\n", msglen, MessageLen);
1686         msglen = MessageLen;
1687     }
1688 
1689     void *buf, *bufStart = getDirectMessageBuffer();
1690     buf = nboPackUByte(bufStart, playerIndex);
1691     buf = nboPackUByte(buf, dstPlayer);
1692     buf = nboPackUByte(buf, type);
1693     buf = nboPackString(buf, msg, msglen);
1694 
1695     ((char*)bufStart)[MessageLen - 1 + 3] = '\0'; // always terminate
1696 
1697     int len = 3 + msglen;
1698     bool broadcast = false;
1699 
1700     if (dstPlayer <= LastRealPlayer)
1701     {
1702         directMessage(dstPlayer, MsgMessage, len, bufStart);
1703         if (playerIndex <= LastRealPlayer && dstPlayer != playerIndex)
1704             directMessage(playerIndex, MsgMessage, len, bufStart);
1705     }
1706     // FIXME this teamcolor <-> player id conversion is in several files now
1707     else if (LastRealPlayer < dstPlayer && dstPlayer <= FirstTeam)
1708     {
1709         TeamColor _team = TeamColor(FirstTeam - dstPlayer);
1710         // send message to all team members only
1711         GameKeeper::Player *playerData;
1712         for (int i = 0; i < curMaxPlayers; i++)
1713             if ((playerData = GameKeeper::Player::getPlayerByIndex(i))
1714                     && playerData->player.isPlaying()
1715                     && playerData->player.isTeam(_team))
1716                 directMessage(i, MsgMessage, len, bufStart);
1717     }
1718     else if (dstPlayer == AdminPlayers)
1719     {
1720         // admin messages
1721 
1722         // Notify any plugins
1723         if (playerIndex == ServerPlayer)
1724         {
1725             bz_ServerMsgEventData_V1 serverMsgData;
1726             serverMsgData.to = BZ_NULLUSER;
1727             serverMsgData.team = eAdministrators;
1728             serverMsgData.message = message;
1729             worldEventManager.callEvents(bz_eServerMsgEvent, &serverMsgData);
1730         }
1731 
1732         std::vector<int> admins
1733             = GameKeeper::Player::allowed(PlayerAccessInfo::adminMessageReceive);
1734         for (unsigned int i = 0; i < admins.size(); ++i)
1735             directMessage(admins[i], MsgMessage, len, bufStart);
1736 
1737     }
1738     else
1739     {
1740         // message to all players
1741 
1742         // Notify any plugins
1743         if (playerIndex == ServerPlayer)
1744         {
1745             bz_ServerMsgEventData_V1 serverMsgData;
1746             serverMsgData.to = BZ_ALLUSERS;
1747             serverMsgData.message = message;
1748             worldEventManager.callEvents(bz_eServerMsgEvent, &serverMsgData);
1749         }
1750 
1751         broadcastMessage(MsgMessage, len, bufStart);
1752         broadcast = true;
1753     }
1754 
1755     if (Record::enabled() && !broadcast)   // don't record twice
1756         Record::addPacket(MsgMessage, len, bufStart, HiddenPacket);
1757 }
1758 
rejectPlayer(int playerIndex,uint16_t code,const char * reason)1759 static void rejectPlayer(int playerIndex, uint16_t code, const char *reason)
1760 {
1761     void *buf, *bufStart = getDirectMessageBuffer();
1762     buf = nboPackUShort(bufStart, code);
1763     buf = nboPackString(buf, reason, strlen(reason) + 1);
1764     directMessage(playerIndex, MsgReject, sizeof (uint16_t) + MessageLen, bufStart);
1765     // Fixing security hole, because a client can ignore the reject message
1766     // then he can avoid a ban, hostban...
1767     const std::string msg = "/rejected: " + stripAnsiCodes(reason);
1768     removePlayer(playerIndex, msg.c_str());
1769 }
1770 
1771 // FIXME this is a workaround for a bug, still needed?
1772 // Team Size is wrong at some time
fixTeamCount()1773 static void fixTeamCount()
1774 {
1775     int playerIndex, teamNum;
1776     for (teamNum = RogueTeam; teamNum < NumTeams; teamNum++)
1777         team[teamNum].team.size = 0;
1778     for (playerIndex = 0; playerIndex < curMaxPlayers; playerIndex++)
1779     {
1780         GameKeeper::Player *p = GameKeeper::Player::getPlayerByIndex(playerIndex);
1781         if (p && p->player.isPlaying())
1782         {
1783             teamNum = p->player.getTeam();
1784             if (teamNum == RabbitTeam)
1785                 teamNum = HunterTeam;
1786             team[teamNum].team.size++;
1787         }
1788     }
1789 }
1790 
1791 
1792 // helper struct and predicates used in autoTeamSelect()
1793 struct TeamSize
1794 {
1795     TeamColor color;
1796     int       current;
1797     int       max;
operator <TeamSize1798     bool operator<(const TeamSize &x) const
1799     {
1800         return x.current < current;
1801     }
1802 };
1803 
teamFull(const TeamSize & x)1804 bool teamFull(const TeamSize &x)
1805 {
1806     return x.current == x.max;
1807 }
teamEmpty(const TeamSize & x)1808 bool teamEmpty(const TeamSize &x)
1809 {
1810     return x.current == 0;
1811 }
1812 
1813 struct teamHasSize
1814 {
1815     int sz;
teamHasSizeteamHasSize1816     teamHasSize(int sz_) : sz(sz_) {}
operator ()teamHasSize1817     bool operator() (const TeamSize &x) const
1818     {
1819         return x.current == sz;
1820     }
1821 };
1822 
1823 struct teamHasntSize
1824 {
1825     int sz;
teamHasntSizeteamHasntSize1826     teamHasntSize(int sz_) : sz(sz_) {}
operator ()teamHasntSize1827     bool operator() (const TeamSize &x) const
1828     {
1829         return x.current != sz;
1830     }
1831 };
1832 
1833 
teamSelect(TeamColor t,std::vector<TeamSize> teams)1834 static TeamColor teamSelect(TeamColor t, std::vector<TeamSize> teams)
1835 {
1836     if (teams.empty())
1837         return RogueTeam;
1838 
1839     // see if the player's choice was a weak team
1840     for (unsigned int i = 0; i < teams.size(); i++)
1841         if (teams[i].color == t)
1842             return t;
1843 
1844     // eliminate all teams except those with the lowest total player scores
1845     if (teams.size() > 1)
1846     {
1847         std::vector<int> totalPlayerScores;
1848         for (unsigned int i = 0; i < teams.size(); ++i)
1849             totalPlayerScores.push_back(0);
1850 
1851         for (int i = 0; i < GameKeeper::Player::count(); ++i)
1852         {
1853             GameKeeper::Player *thisPlayer = GameKeeper::Player::getPlayerByIndex(i);
1854             if (thisPlayer == 0)
1855                 continue;
1856 
1857             unsigned int thisTeamIndex = 0;
1858             while (thisTeamIndex < teams.size())
1859             {
1860                 if (teams[thisTeamIndex].color == thisPlayer->player.getTeam())
1861                     break;
1862 
1863                 ++thisTeamIndex;
1864             }
1865             if (thisTeamIndex < teams.size())
1866                 totalPlayerScores[thisTeamIndex] += thisPlayer->score.getWins() -
1867                                                     thisPlayer->score.getLosses();
1868         }
1869 
1870         int minScore = totalPlayerScores[0];
1871         for (unsigned int i = 1; i < teams.size(); ++i)
1872             if (totalPlayerScores[i] < minScore)
1873                 minScore = totalPlayerScores[i];
1874         unsigned int i = 0;
1875         while (i < teams.size())
1876         {
1877             if (totalPlayerScores[i] > minScore)
1878             {
1879                 teams.erase(teams.begin() + i);
1880                 totalPlayerScores.erase(totalPlayerScores.begin() + i);
1881             }
1882             else
1883                 ++i;
1884         }
1885     }
1886 
1887     return teams[rand() % teams.size()].color;
1888 }
1889 
autoTeamSelect(TeamColor t)1890 static TeamColor autoTeamSelect(TeamColor t)
1891 {
1892     // Asking for Observer gives observer
1893     if (t == ObserverTeam)
1894         return ObserverTeam;
1895 
1896     // When replaying, joining tank can only be observer
1897     if (Replay::enabled())
1898         return ObserverTeam;
1899 
1900     // count current number of players
1901     int numplayers = 0, i = 0;
1902     for (i = 0; i < int(NumTeams); i++)
1903         if (i != int(ObserverTeam))
1904             numplayers += team[i].team.size;
1905 
1906     // if no player are available, join as Observer
1907     if (numplayers == maxRealPlayers)
1908         return ObserverTeam;
1909 
1910     // if we're running rabbit chase, all non-observers start as hunters
1911     if (clOptions->gameType == RabbitChase)
1912         return HunterTeam;
1913 
1914     // If tank ask for rogues, and rogues are allowed, give it
1915     if ((t == RogueTeam)
1916             && team[RogueTeam].team.size < clOptions->maxTeam[RogueTeam])
1917         return RogueTeam;
1918 
1919     // If no auto-team, server or client, go back with client choice
1920     if (!clOptions->autoTeam && t != AutomaticTeam)
1921         return t;
1922 
1923     // Fill a vector with teams status, not putting in not enabled teams
1924     std::vector<TeamSize> teams;
1925 
1926     for (i = (int)RedTeam; i < (int)ObserverTeam; i++)
1927     {
1928         TeamSize currTeam = {(TeamColor)i,
1929                              team[i].team.size,
1930                              clOptions->maxTeam[i]
1931                             };
1932         if (currTeam.max > 0)
1933             teams.push_back(currTeam);
1934     }
1935 
1936     // Give rogue if that is the only team
1937     if (teams.empty())
1938         return RogueTeam;
1939 
1940     // Sort it by current team number
1941     std::sort(teams.begin(), teams.end());
1942 
1943     // all teams are empty, select just one of them
1944     if (teams[0].current == 0)
1945         return teamSelect(t, teams);
1946 
1947     int maxTeamSize = teams[0].current;
1948 
1949     teams.erase(std::remove_if(teams.begin(), teams.end(), teamFull), teams.end());
1950     // no non-full teams? then there must be a free rogue spot
1951     if (teams.empty())
1952         return RogueTeam;
1953 
1954     bool unBalanced = (teams.back().current < maxTeamSize);
1955 
1956     if (unBalanced)
1957     {
1958         // if you come with a 1-1-x-x try to add a player to these 1 to have team
1959         if ((maxTeamSize == 1) && (teams.size() >= 2) && (teams[1].current == 1))
1960         {
1961             // remove empty teams
1962             teams.erase(std::remove_if(teams.begin(), teams.end(), teamEmpty), teams.end());
1963         }
1964         else
1965         {
1966             // remove biggest teams
1967             teams.erase(std::remove_if(teams.begin(), teams.end(), teamHasSize(maxTeamSize)), teams.end());
1968             // Search for the lowest existing team and remove uppers. If there
1969             // are non empty teams don't start a new team but try to balance the lower
1970             if (teams[0].current > 0)
1971             {
1972                 teams.erase(std::remove_if(teams.begin(), teams.end(), teamEmpty), teams.end());
1973                 const int lowerTeam = teams.back().current;
1974                 teams.erase(std::remove_if(teams.begin(), teams.end(), teamHasntSize(lowerTeam)), teams.end());
1975             }
1976         }
1977     }
1978     return teamSelect(t, teams);
1979 }
1980 
evaluateString(const std::string & raw)1981 static std::string evaluateString(const std::string &raw)
1982 {
1983     std::string eval;
1984     const int rawLen = (int)raw.size();
1985     for (int i = 0; i < rawLen; i++)
1986     {
1987         char current = raw[i];
1988         if (current != '\\')
1989             eval += current;
1990         else
1991         {
1992             char next = raw[i+1];
1993             switch (next)
1994             {
1995             case '\\' :
1996             {
1997                 eval += '\\';
1998                 i++;
1999                 break;
2000             }
2001             case 'n' :
2002             {
2003                 eval += "\\n";
2004                 i++;
2005                 break;
2006             }
2007             case '{' :
2008             {
2009                 unsigned int start = (i + 2);
2010                 std::string::size_type end = raw.find_first_of('}', start);
2011                 if (end == std::string::npos)
2012                 {
2013                     i = rawLen; // unterminated, ignore the rest of the string
2014                 }
2015                 else
2016                 {
2017                     const std::string var = raw.substr(start, end - start);
2018                     i += (end - start) + 2;
2019                     if (BZDB.isSet(var))
2020                         eval += BZDB.get(var);
2021                     else
2022                         eval += "*BADBZDB*";
2023                 }
2024                 break;
2025             }
2026             case '(' :
2027             {
2028                 unsigned int start = (i + 2);
2029                 std::string::size_type end = raw.find_first_of(')', start);
2030                 if (end == std::string::npos)
2031                 {
2032                     i = rawLen; // unterminated, ignore the rest of the string
2033                 }
2034                 else
2035                 {
2036                     const std::string var = raw.substr(start, end - start);
2037                     i += (end - start) + 2;
2038                     if (var == "uptime")
2039                     {
2040                         char buffer[16];
2041                         const float uptime = (float)(TimeKeeper::getCurrent() - TimeKeeper::getStartTime());
2042                         snprintf(buffer, 16, "%i", (int)uptime);
2043                         eval += buffer;
2044                     }
2045                     else
2046                         eval += "*BADVAR*";
2047                 }
2048                 break;
2049             }
2050             default:
2051             {
2052                 break;
2053             }
2054             }
2055         }
2056     }
2057     return eval;
2058 }
2059 
handicapAllowed(void)2060 static bool handicapAllowed( void )
2061 {
2062     return (clOptions->gameOptions & short(HandicapGameStyle)) != 0;
2063 }
2064 
2065 // calculate handicap value for playerIndex and store in score object
recalcHandicap(int playerIndex)2066 static void recalcHandicap(int playerIndex)
2067 {
2068     if (!handicapAllowed())
2069         return;
2070 
2071     int relscore = 0;
2072 
2073     GameKeeper::Player *me  = GameKeeper::Player::getPlayerByIndex(playerIndex);
2074     for (int i = 0; i < curMaxPlayers; i++)
2075     {
2076         if (i == playerIndex)
2077             continue;
2078         GameKeeper::Player *p = GameKeeper::Player::getPlayerByIndex(i);
2079         if (p != NULL && realPlayer(i) && !p->player.isObserver())
2080             relscore += me->player.howManyTimesKilledBy(i) - p->player.howManyTimesKilledBy(playerIndex);
2081     }
2082 
2083     bz_ComputeHandicap_V1 data;
2084     data.playerID = playerIndex;
2085     data.desiredHandicap = std::max(0, relscore);
2086 
2087     worldEventManager.callEvents(bz_eComputeHandicapEvent, &data);
2088 
2089     me->score.setHandicap(data.desiredHandicap);
2090 }
2091 
2092 // calculate handicap values for all players
recalcAllHandicaps()2093 void recalcAllHandicaps()
2094 {
2095     if (!handicapAllowed())
2096         return;
2097 
2098     bz_EventData beginData = bz_EventData(bz_eBeginHandicapRefreshEvent);
2099     worldEventManager.callEvents(&beginData);
2100 
2101     for (int i = 0; i < curMaxPlayers; i++)
2102     {
2103         GameKeeper::Player *p = GameKeeper::Player::getPlayerByIndex(i);
2104         if (p && realPlayer(i) && !p->player.isObserver())
2105             recalcHandicap(i);
2106     }
2107 
2108     bz_EventData endData = bz_EventData(bz_eEndHandicapRefreshEvent);
2109     worldEventManager.callEvents(&endData);
2110 }
2111 
2112 // send handicap values for all players to all players
broadcastHandicaps(int toPlayer)2113 void broadcastHandicaps(int toPlayer)
2114 {
2115     if (!handicapAllowed())
2116         return;
2117 
2118     int numHandicaps = 0;
2119 
2120     void *bufStart = getDirectMessageBuffer();
2121     void *buf = nboPackUByte(bufStart, numHandicaps);
2122     for (int i = 0; i < curMaxPlayers; i++)
2123     {
2124         if (i == toPlayer)
2125             continue;
2126         GameKeeper::Player *p = GameKeeper::Player::getPlayerByIndex(i);
2127         if (p && realPlayer(i) && !p->player.isObserver())
2128         {
2129             numHandicaps++;
2130             buf = nboPackUByte(buf, i);
2131             buf = nboPackShort(buf, p->score.getHandicap());
2132         }
2133     }
2134     nboPackUByte(bufStart, numHandicaps);
2135     if (toPlayer >= 0)
2136         directMessage(toPlayer, MsgHandicap, (char*)buf-(char*)bufStart, bufStart);
2137     else
2138         broadcastMessage(MsgHandicap, (char*)buf-(char*)bufStart, bufStart);
2139 }
2140 
2141 
2142 static bool spawnSoon = false;
2143 
2144 
AddPlayer(int playerIndex,GameKeeper::Player * playerData)2145 void AddPlayer(int playerIndex, GameKeeper::Player *playerData)
2146 {
2147     playerData->addWasDelayed = false;
2148     uint16_t rejectCode;
2149     char rejectMsg[MessageLen];
2150     // check for a name clash
2151     bool resultEnter = playerData->loadEnterData(rejectCode, rejectMsg);
2152 
2153     // Name clash ... if the new player is not verified, reject it
2154     // We cannot have 2 players with same nick
2155     if (!resultEnter && playerData->_LSAState != GameKeeper::Player::verified)
2156     {
2157         rejectPlayer(playerIndex, rejectCode, rejectMsg);
2158         return;
2159     }
2160 
2161     if (!resultEnter)
2162     {
2163         // Find the user already logged on and kick it. The new player
2164         // has been globally authenticated.
2165         for (int i = 0; i < curMaxPlayers; i++)
2166         {
2167             // don't kick _us_, kick the other guy
2168             if (playerIndex == i)
2169                 continue;
2170             GameKeeper::Player *otherPlayer
2171                 = GameKeeper::Player::getPlayerByIndex(i);
2172             if (!otherPlayer)
2173                 continue;
2174 
2175             // check and see if the other player was reged
2176             if (strcasecmp(otherPlayer->player.getCallSign(), playerData->player.getCallSign()) == 0)
2177             {
2178                 sendMessage(ServerPlayer, i,
2179                             "Another client has demonstrated ownership of your "
2180                             "callsign with the correct password.  You have been "
2181                             "ghosted.");
2182                 removePlayer(i, "Ghost");
2183                 break;
2184             }
2185         }
2186     }
2187 
2188     if (clOptions->filterCallsigns)
2189     {
2190         int filterIndex = 0;
2191         Filter::Action filterAction = filter.check(*playerData, filterIndex);
2192         if (filterAction == Filter::DROP)
2193         {
2194             rejectPlayer(playerIndex, RejectBadCallsign, "Player has been banned");
2195             return;
2196         }
2197     }
2198 
2199     const bool playerIsAntiBanned =
2200         playerData->accessInfo.hasPerm(PlayerAccessInfo::antiban);
2201 
2202     // check against the ip ban list
2203     in_addr playerIP = playerData->netHandler->getIPAddress();
2204     BanInfo info(playerIP);
2205     if (!playerIsAntiBanned && !clOptions->acl.validate(playerIP,&info))
2206     {
2207         std::string rejectionMessage;
2208 
2209         rejectionMessage = BanRefusalString;
2210         rejectionMessage += " "; // add space between the ban reason and the protocol string, "REFUSED"
2211 
2212         if (info.reason.size ())
2213             rejectionMessage += info.reason;
2214         else
2215             rejectionMessage += "General Ban";
2216 
2217         rejectionMessage += ColorStrings[WhiteColor];
2218         rejectionMessage += " (";
2219 
2220         double duration = info.banEnd - TimeKeeper::getCurrent();
2221         if (duration < 365.0f * 24 * 3600)
2222         {
2223             long int timeArray[4];
2224             TimeKeeper::convertTime(duration, timeArray);
2225             std::string bantime = TimeKeeper::printTime(timeArray);
2226             rejectionMessage += bantime;
2227             rejectionMessage += " remaining";
2228         }
2229         else
2230             rejectionMessage += "indefinite";
2231 
2232         rejectionMessage += ")";
2233 
2234         rejectionMessage += ColorStrings[GreenColor];
2235         if (info.fromMaster)
2236             rejectionMessage += " [you are on the master ban list]";
2237 
2238         rejectPlayer (playerIndex, RejectIPBanned, rejectionMessage.c_str ());
2239         return;
2240     }
2241 
2242     // check against the id ban list
2243     const std::string& bzid = playerData->getBzIdentifier();
2244     IdBanInfo idInfo("");
2245     if (!playerIsAntiBanned && !clOptions->acl.idValidate(bzid.c_str(), &idInfo))
2246     {
2247         std::string rejectionMessage;
2248 
2249         rejectionMessage = BanRefusalString;
2250         if (idInfo.reason.size())
2251             rejectionMessage += idInfo.reason;
2252         else
2253             rejectionMessage += "General Ban";
2254 
2255         rejectionMessage += ColorStrings[WhiteColor];
2256         if (idInfo.bannedBy.size())
2257         {
2258             rejectionMessage += " by ";
2259             rejectionMessage += ColorStrings[BlueColor];
2260             rejectionMessage += idInfo.bannedBy;
2261         }
2262 
2263         rejectionMessage += ColorStrings[GreenColor];
2264         if (idInfo.fromMaster)
2265             rejectionMessage += " [from the master server]";
2266         rejectPlayer(playerIndex, RejectIDBanned, rejectionMessage.c_str());
2267         return;
2268     }
2269 
2270     // check against id and hostname ban lists (on the next cycle)
2271     playerData->setNeedThisHostbanChecked(true);
2272 
2273     // see if any watchers don't want this guy
2274     bz_AllowPlayerEventData_V1 allowData;
2275     allowData.callsign = playerData->player.getCallSign();
2276     allowData.ipAddress = playerData->netHandler->getTargetIP();
2277     allowData.playerID = playerIndex;
2278 
2279     worldEventManager.callEvents(bz_eAllowPlayer,&allowData);
2280     if (!allowData.allow)
2281     {
2282         rejectPlayer(playerIndex, RejectBadRequest, allowData.reason.c_str());
2283         return;
2284     }
2285 
2286     // pick a team
2287     TeamColor t = autoTeamSelect(playerData->player.getTeam());
2288 
2289     bz_GetAutoTeamEventData_V1 autoTeamData;
2290     autoTeamData.playerID = playerIndex;
2291     autoTeamData.team = convertTeam(t);
2292     autoTeamData.callsign = playerData->player.getCallSign();
2293 
2294     worldEventManager.callEvents(bz_eGetAutoTeamEvent,&autoTeamData);
2295 
2296     t = (TeamColor)convertTeam((bz_eTeamType)autoTeamData.team);  // team may be modified
2297     playerData->player.setTeam(t);
2298     playerData->player.endShotCredit = 0;     // reset shotEndCredit
2299     playerData->player.endShotShieldCredit = 0;   // endShotCredit for holding the shield flag (0 or 1)
2300 
2301     // count current number of players and players+observers
2302     int numplayers = 0;
2303     for (int i = 0; i < int(ObserverTeam); i++)
2304         numplayers += team[i].team.size;
2305     const int numplayersobs = numplayers + team[ObserverTeam].team.size;
2306 
2307     // reject player if asks for bogus team or rogue and rogues aren't allowed
2308     // or if the team is full or if the server is full
2309     if (!playerData->player.isHuman() && !playerData->player.isBot())
2310     {
2311         rejectPlayer(playerIndex, RejectBadType,
2312                      "Communication error joining game [Rejected].");
2313         return;
2314     }
2315     else if (t == NoTeam)
2316     {
2317         rejectPlayer(playerIndex, RejectBadTeam,
2318                      "Communication error joining game [Rejected].");
2319         return;
2320     }
2321     else if (t == ObserverTeam && playerData->player.isBot())
2322     {
2323         rejectPlayer(playerIndex, RejectServerFull,
2324                      "This game is full.  Try again later.");
2325         return;
2326     }
2327     else if (numplayersobs == maxPlayers)
2328     {
2329         // server is full
2330         rejectPlayer(playerIndex, RejectServerFull,
2331                      "This game is full.  Try again later.");
2332         return;
2333     }
2334     else if (team[int(t)].team.size >= clOptions->maxTeam[int(t)])
2335     {
2336         rejectPlayer(playerIndex, RejectTeamFull,
2337                      "This team is full.  Try another team.");
2338         return ;
2339     }
2340     // accept player
2341     void *buf, *bufStart = getDirectMessageBuffer();
2342     buf = nboPackUByte(bufStart, playerIndex);
2343     int result = directMessage(*playerData, MsgAccept,
2344                                (char*)buf-(char*)bufStart, bufStart);
2345     if (result == -1)
2346         return;
2347 
2348     //send SetVars
2349     {
2350         // scoping is mandatory
2351         PackVars pv(bufStart, playerIndex);
2352         BZDB.iterate(PackVars::packIt, &pv);
2353     }
2354 
2355     // abort if we hung up on the client
2356     if (!GameKeeper::Player::getPlayerByIndex(playerIndex))
2357         return;
2358 
2359     // player is signing on (has already connected via addClient).
2360     playerData->signingOn(clOptions->gameType == ClassicCTF);
2361 
2362     // update team state and if first player on team, reset it's score
2363     int teamIndex = int(playerData->player.getTeam());
2364     team[teamIndex].team.size++;
2365     if (team[teamIndex].team.size == 1
2366             && Team::isColorTeam((TeamColor)teamIndex))
2367     {
2368         team[teamIndex].team.setWins(0);
2369         team[teamIndex].team.setLosses(0);
2370     }
2371 
2372     // send new player updates on each player, all existing flags, and all teams.
2373     // don't send robots any game info.  watch out for connection being closed
2374     // because of an error.
2375     if (!playerData->player.isBot())
2376     {
2377         sendTeamUpdate(playerIndex);
2378         sendFlagUpdate(playerIndex);
2379         GameKeeper::Player *otherData;
2380         for (int i = 0; i < curMaxPlayers
2381                 && GameKeeper::Player::getPlayerByIndex(playerIndex); i++)
2382             if (i != playerIndex)
2383             {
2384                 otherData = GameKeeper::Player::getPlayerByIndex(i);
2385                 if (otherData)
2386                 {
2387                     sendPlayerUpdate(otherData, playerIndex);
2388                     if (otherData->player.isAutoPilot())
2389                         sendAutopilotStatus(otherData, playerIndex);
2390                 }
2391             }
2392 
2393         broadcastHandicaps(playerIndex);
2394     }
2395 
2396     // if new player connection was closed (because of an error) then stop here
2397     if (!GameKeeper::Player::getPlayerByIndex(playerIndex))
2398         return;
2399 
2400     // see if the API wants to set the motto
2401     bz_GetPlayerMottoData_V2 mottoEvent(playerData->player.getMotto());
2402     mottoEvent.record = bz_getPlayerByIndex(playerIndex);
2403     worldEventManager.callEvents(&mottoEvent);
2404     playerData->player.setMotto(mottoEvent.motto.c_str());
2405 
2406     // broadcast motto only if player has SHOWMOTTO permission
2407     if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::showMotto) && strlen(playerData->player.getMotto()) != 0)
2408     {
2409         sendMessage(ServerPlayer, playerIndex, "\"showMotto\" permission is required to show your motto");
2410         playerData->player.setMotto("");
2411     }
2412 
2413     // send MsgAddPlayer to everybody -- this concludes MsgEnter response
2414     // to joining player
2415     sendPlayerUpdate(playerData, playerIndex);
2416 
2417     // send update of info for team just joined
2418     sendTeamUpdate(-1, teamIndex);
2419 
2420     // send IP update to everyone with PLAYERLIST permission
2421     sendIPUpdate(-1, playerIndex);
2422     sendIPUpdate(playerIndex, -1);
2423 
2424     // send rabbit information
2425     if (clOptions->gameType == RabbitChase)
2426     {
2427         bufStart = getDirectMessageBuffer();
2428         buf = nboPackUByte(bufStart, rabbitIndex);
2429         directMessage(playerIndex, MsgNewRabbit, (char*)buf-(char*)bufStart, bufStart);
2430     }
2431 
2432     // again check if player was disconnected
2433     if (!GameKeeper::Player::getPlayerByIndex(playerIndex))
2434         return;
2435 
2436     // send time update to new player if we're counting down
2437     if (countdownActive && clOptions->timeLimit > 0.0f
2438             && !playerData->player.isBot())
2439     {
2440         float timeLeft;
2441 
2442         if (countdownPauseStart)
2443         {
2444             // the game is paused
2445             timeLeft = -1.0f;
2446         }
2447         else
2448         {
2449             timeLeft = clOptions->timeLimit - (float)(TimeKeeper::getCurrent() - gameStartTime);
2450             if (timeLeft < 0.0f)
2451             {
2452                 // oops
2453                 timeLeft = 0.0f;
2454             }
2455         }
2456 
2457         bufStart = getDirectMessageBuffer();
2458         buf = nboPackInt(bufStart, (int32_t)timeLeft);
2459         result = directMessage(*playerData, MsgTimeUpdate,
2460                                (char*)buf-(char*)bufStart, bufStart);
2461         if (result == -1)
2462             return;
2463     }
2464 
2465     // if first player on team add team's flag
2466     if (team[teamIndex].team.size == 1
2467             && Team::isColorTeam((TeamColor)teamIndex))
2468     {
2469         if (clOptions->gameType == ClassicCTF)
2470         {
2471             int flagid = FlagInfo::lookupFirstTeamFlag(teamIndex);
2472             if (flagid >= 0 && !FlagInfo::get(flagid)->exist())
2473             {
2474                 // reset those flags
2475                 for (int n = 0; n < clOptions->numTeamFlags[teamIndex]; n++)
2476                     resetFlag(*FlagInfo::get(n + flagid));
2477             }
2478         }
2479     }
2480 
2481     fixTeamCount();
2482 
2483     // tell the list server the new number of players
2484     listServerLink->queueMessage(ListServerLink::ADD);
2485 
2486 #ifdef PRINTSCORE
2487     dumpScore();
2488 #endif
2489     char message[MessageLen] = {0};
2490 
2491 #ifdef SERVERLOGINMSG
2492     snprintf(message, MessageLen, "BZFlag server %s, http://BZFlag.org/", getAppVersion());
2493     sendMessage(ServerPlayer, playerIndex, message);
2494 
2495     if (clOptions->servermsg != "")
2496     {
2497         const std::string srvmsg = evaluateString(clOptions->servermsg);
2498 
2499         // split the servermsg into several lines if it contains '\n'
2500         const char* i = srvmsg.c_str();
2501         const char* j;
2502         while ((j = strstr(i, "\\n")) != NULL)
2503         {
2504             unsigned int l = j - i < MessageLen - 1 ? j - i : MessageLen - 1;
2505             strncpy(message, i, l);
2506             message[l] = '\0';
2507             sendMessage(ServerPlayer, playerIndex, message);
2508             i = j + 2;
2509         }
2510         strncpy(message, i, MessageLen - 1);
2511         message[strlen(i) < MessageLen - 1 ? strlen(i) : MessageLen - 1] = '\0';
2512         sendMessage(ServerPlayer, playerIndex, message);
2513     }
2514 
2515     // look for a startup message -- from a file
2516     static const std::vector<std::string>* lines = clOptions->textChunker.getTextChunk("srvmsg");
2517     if (lines != NULL)
2518     {
2519         for (int i = 0; i < (int)lines->size(); i ++)
2520         {
2521             const std::string srvmsg = evaluateString((*lines)[i]);
2522             sendMessage(ServerPlayer, playerIndex, srvmsg.c_str());
2523         }
2524     }
2525 #endif
2526 
2527     if (playerData->player.isObserver())
2528         sendMessage(ServerPlayer, playerIndex, "You are in observer mode.");
2529 
2530     // no quick rejoining, make 'em wait
2531     // you can switch to observer immediately, or switch from observer
2532     // to regular player immediately, but only if last time time you
2533     // were a regular player isn't in the rejoin list. As well, this all
2534     // only applies if the game isn't currently empty.
2535     if ((t != ObserverTeam) && (GameKeeper::Player::count() >= 0))
2536     {
2537         float waitTime = rejoinList.waitTime (playerIndex);
2538         if (waitTime > 0.0f)
2539         {
2540             char buffer[MessageLen];
2541             logDebugMessage(2,"Player %s [%d] rejoin wait of %.1f seconds\n",playerData->player.getCallSign(), playerIndex,
2542                             waitTime);
2543             snprintf (buffer, MessageLen, "You are unable to begin playing for %.1f seconds.", waitTime);
2544             sendMessage(ServerPlayer, playerIndex, buffer);
2545         }
2546     }
2547 
2548     if (GameKeeper::Player::getPlayerByIndex(playerIndex)
2549             && playerData->accessInfo.isRegistered()
2550             && playerData->_LSAState != GameKeeper::Player::verified)
2551     {
2552         // If the name is registered but not authenticated, tell them to identify
2553         sendMessage(ServerPlayer, playerIndex,
2554                     "This callsign is registered.  "
2555                     "You must use global authentication.");
2556     }
2557 
2558     dropAssignedFlag(playerIndex);
2559 
2560     sendPlayerInfo();
2561 
2562     // call any on join events
2563     bz_PlayerJoinPartEventData_V1 joinEventData;
2564     joinEventData.eventType = bz_ePlayerJoinEvent;
2565     joinEventData.playerID = playerIndex;
2566     joinEventData.record = bz_getPlayerByIndex(playerIndex);
2567 
2568     if ((playerData->player.getTeam() != NoTeam) && strlen(playerData->player.getCallSign()))
2569         worldEventManager.callEvents(bz_ePlayerJoinEvent,&joinEventData);
2570 
2571     if (spawnSoon)
2572         playerAlive(playerIndex);
2573 
2574     playerData->player.setCompletelyAdded();
2575 }
2576 
2577 
resetFlag(FlagInfo & flag)2578 void resetFlag(FlagInfo &flag)
2579 {
2580     // NOTE -- must not be called until world is defined
2581     assert(world != NULL);
2582 
2583     // first drop the flag if someone has it
2584     if (flag.flag.status == FlagOnTank)
2585     {
2586         int player = flag.player;
2587 
2588         sendDrop(flag);
2589 
2590         // trigger the API event
2591         bz_FlagDroppedEventData_V1 data;
2592         data.playerID = player;
2593         data.flagID = flag.getIndex();
2594         data.flagType = flag.flag.type->flagAbbv.c_str();
2595         memcpy(data.pos, flag.flag.position, sizeof(float)*3);
2596 
2597         worldEventManager.callEvents(bz_eFlagDroppedEvent,&data);
2598     }
2599 
2600     float baseSize = BZDB.eval(StateDatabase::BZDB_BASESIZE);
2601 
2602     // reposition flag (middle of the map might be a bad idea)
2603     float flagPos[3] = {0.0f, 0.0f, 0.0f};
2604 
2605     TeamColor teamIndex = flag.teamIndex();
2606     if ((teamIndex >= ::RedTeam) &&  (teamIndex <= ::PurpleTeam)
2607             && (bases.find(teamIndex) != bases.end()))
2608     {
2609         if (!world->getFlagSpawnPoint(&flag, flagPos))
2610         {
2611             // return the flag to the center of the top of one of the team
2612             // bases.. we assume it'll fit.
2613             TeamBases &teamBases = bases[teamIndex];
2614             const TeamBase &base = teamBases.getRandomBase(flag.getIndex());
2615             flagPos[0] = base.position[0];
2616             flagPos[1] = base.position[1];
2617             flagPos[2] = base.position[2] + base.size[2];
2618         }
2619     }
2620     else
2621     {
2622         // random position (not in a building)
2623         const float waterLevel = world->getWaterLevel();
2624         float minZ = 0.0f;
2625         if (waterLevel > minZ)
2626             minZ = waterLevel;
2627         float maxZ = MAXFLOAT;
2628         if (!clOptions->flagsOnBuildings)
2629             maxZ = 0.0f;
2630         float worldSize = BZDBCache::worldSize;
2631         int i;
2632         for (i = 0; i < 10000; i++)
2633         {
2634             if (!world->getFlagSpawnPoint(&flag, flagPos))
2635             {
2636                 flagPos[0] = (worldSize - baseSize) * ((float)bzfrand() - 0.5f);
2637                 flagPos[1] = (worldSize - baseSize) * ((float)bzfrand() - 0.5f);
2638                 flagPos[2] = world->getMaxWorldHeight() * (float)bzfrand();
2639             }
2640             if (DropGeometry::dropFlag(flagPos, minZ, maxZ))
2641                 break;
2642         }
2643         if (i == 10000)
2644             std::cerr << "Unable to position flags on this world.\n";
2645     }
2646 
2647     bool teamIsEmpty = true;
2648     if (teamIndex != ::NoTeam)
2649         teamIsEmpty = (team[teamIndex].team.size == 0);
2650 
2651 //   bz_FlagResetEventData_V1 eventData;
2652 //   memcpy(eventData.pos,flagPos,sizeof(float)*3);
2653 //   eventData.teamIsEmpty = teamIsEmpty;
2654 //   eventData.flagID = flag.getIndex();
2655 //   eventData.flagType = flag.flag.type->label().c_str();
2656 
2657     // reset a flag's info
2658     flag.resetFlag(flagPos, teamIsEmpty);
2659 
2660     sendFlagUpdate(flag);
2661 }
2662 
2663 
sendDrop(FlagInfo & flag)2664 void sendDrop(FlagInfo &flag)
2665 {
2666     // see if someone had grabbed flag.  tell 'em to drop it.
2667     const int playerIndex = flag.player;
2668 
2669     GameKeeper::Player *playerData
2670         = GameKeeper::Player::getPlayerByIndex(playerIndex);
2671     if (!playerData)
2672         return;
2673 
2674     flag.player      = -1;
2675     playerData->player.resetFlag();
2676 
2677     void *bufStart = getDirectMessageBuffer();
2678     void *buf  = nboPackUByte(bufStart, playerIndex);
2679     buf = flag.pack(buf);
2680     broadcastMessage(MsgDropFlag, (char*)buf-(char*)bufStart, bufStart);
2681 }
2682 
zapFlag(FlagInfo & flag)2683 void zapFlag(FlagInfo &flag)
2684 {
2685     // called when a flag must just disappear -- doesn't fly
2686     // into air, just *poof* vanishes.
2687 
2688     int player = flag.player;
2689 
2690     sendDrop(flag);
2691 
2692     // trigger the API event
2693     bz_FlagDroppedEventData_V1 data;
2694     data.playerID = player;
2695     data.flagID = flag.getIndex();
2696     data.flagType = flag.flag.type->flagAbbv.c_str();
2697     memcpy(data.pos, flag.flag.position, sizeof(float)*3);
2698 
2699     worldEventManager.callEvents(bz_eFlagDroppedEvent,&data);
2700 
2701     // if flag was flying then it flies no more
2702     flag.landing(TimeKeeper::getSunExplodeTime());
2703 
2704     flag.flag.status = FlagNoExist;
2705 
2706     // reset flag status
2707     resetFlag(flag);
2708 }
2709 
2710 // try to get over a bug where extraneous flag are attached to a tank
2711 // not really found why, but this should fix
2712 // Should be called when we sure that tank does not hold any
dropAssignedFlag(int playerIndex)2713 static void dropAssignedFlag(int playerIndex)
2714 {
2715     for (int flagIndex = 0; flagIndex < numFlags; flagIndex++)
2716     {
2717         FlagInfo *flag = FlagInfo::get(flagIndex);
2718         if (!flag)
2719             continue;
2720         if (flag->flag.status == FlagOnTank && flag->flag.owner == playerIndex)
2721             resetFlag(*flag);
2722     }
2723 } // dropAssignedFlag
2724 
anointNewRabbit(int killerId=NoPlayer)2725 static void anointNewRabbit(int killerId = NoPlayer)
2726 {
2727     GameKeeper::Player *killerData
2728         = GameKeeper::Player::getPlayerByIndex(killerId);
2729     GameKeeper::Player *oldRabbitData
2730         = GameKeeper::Player::getPlayerByIndex(rabbitIndex);
2731     int oldRabbit = rabbitIndex;
2732     rabbitIndex = NoPlayer;
2733 
2734     if (clOptions->rabbitSelection == KillerRabbitSelection)
2735         // check to see if the rabbit was just killed by someone; if so, make them the rabbit if they're still around.
2736         if (killerId != oldRabbit && killerData && killerData->player.isPlaying()
2737                 && killerData->player.canBeRabbit())
2738             rabbitIndex = killerId;
2739 
2740     if (rabbitIndex == NoPlayer)
2741         rabbitIndex = GameKeeper::Player::anointRabbit(oldRabbit);
2742 
2743     if (rabbitIndex != oldRabbit)
2744     {
2745         logDebugMessage(3,"rabbitIndex is set to %d\n", rabbitIndex);
2746         if (oldRabbitData)
2747             oldRabbitData->player.wasARabbit();
2748         if (rabbitIndex != NoPlayer)
2749         {
2750             GameKeeper::Player *rabbitData
2751                 = GameKeeper::Player::getPlayerByIndex(rabbitIndex);
2752             rabbitData->player.setTeam(RabbitTeam);
2753             void *buf, *bufStart = getDirectMessageBuffer();
2754             buf = nboPackUByte(bufStart, rabbitIndex);
2755             broadcastMessage(MsgNewRabbit, (char*)buf-(char*)bufStart, bufStart);
2756         }
2757     }
2758     else
2759     {
2760         logDebugMessage(3,"no other than old rabbit to choose from, rabbitIndex is %d\n",
2761                         rabbitIndex);
2762     }
2763 }
2764 
2765 
pausePlayer(int playerIndex,bool paused=true)2766 static void pausePlayer(int playerIndex, bool paused = true)
2767 {
2768     GameKeeper::Player *playerData
2769         = GameKeeper::Player::getPlayerByIndex(playerIndex);
2770     if (!playerData)
2771         return;
2772 
2773     if (!playerData->player.isAlive())
2774     {
2775         logDebugMessage(2,"Player %s [%d] %spause while not alive\n",
2776                         playerData->player.getCallSign(), playerIndex, paused ? "" : "un");
2777         return;
2778     }
2779     if (playerData->player.isPaused() == paused)
2780     {
2781         logDebugMessage(2,"Player %s [%d] duplicate %spause\n",
2782                         playerData->player.getCallSign(), playerIndex, paused ? "" : "un");
2783         return;
2784     }
2785     // TODO: enforce 5-second delay from one pause to the next
2786 
2787     playerData->player.setPaused(paused);
2788     if (clOptions->gameType == RabbitChase)
2789     {
2790         if (paused && (rabbitIndex == playerIndex))
2791             anointNewRabbit();
2792         else if (!paused && (rabbitIndex == NoPlayer))
2793             anointNewRabbit();
2794     }
2795 
2796     void *buf, *bufStart = getDirectMessageBuffer();
2797     buf = nboPackUByte(bufStart, playerIndex);
2798     buf = nboPackUByte(buf, paused);
2799     broadcastMessage(MsgPause, (char*)buf-(char*)bufStart, bufStart);
2800 
2801     bz_PlayerPausedEventData_V1   pauseEventData;
2802     pauseEventData.playerID = playerIndex;
2803     pauseEventData.pause = paused;
2804 
2805     worldEventManager.callEvents(bz_ePlayerPausedEvent,&pauseEventData);
2806 }
2807 
autopilotPlayer(int playerIndex,bool autopilot)2808 static void autopilotPlayer(int playerIndex, bool autopilot)
2809 {
2810     GameKeeper::Player *playerData
2811         = GameKeeper::Player::getPlayerByIndex(playerIndex);
2812     if (!playerData)
2813         return;
2814 
2815     // Allow disabling but not enabling autopilot if bots are disabled
2816     if (autopilot && BZDB.isTrue(StateDatabase::BZDB_DISABLEBOTS))
2817     {
2818         sendMessage(ServerPlayer, playerIndex, "I'm sorry, we do not allow autopilot on this server.");
2819         removePlayer(playerIndex, "AutopilotPlayer");
2820         return;
2821     }
2822 
2823     // Ensure that observers can't toggle autopilot
2824     if (playerData->player.getTeam() == ObserverTeam)
2825         return;
2826 
2827     playerData->player.setAutoPilot(autopilot);
2828 
2829     // Send the status update to everyone
2830     sendAutopilotStatus(playerData, playerIndex);
2831 }
2832 
zapFlagByPlayer(int playerIndex)2833 void zapFlagByPlayer(int playerIndex)
2834 {
2835     GameKeeper::Player *playerData
2836         = GameKeeper::Player::getPlayerByIndex(playerIndex);
2837     if (!playerData)
2838         return;
2839 
2840     int flagid = playerData->player.getFlag();
2841     if (flagid < 0)
2842         return;
2843 
2844     FlagInfo &flag = *FlagInfo::get(flagid);
2845     flag.player = playerIndex;
2846     // do not simply zap team flag
2847     Flag &carriedflag = flag.flag;
2848     if (carriedflag.type->flagTeam != ::NoTeam)
2849         dropPlayerFlag(*playerData, playerData->lastState.pos);
2850     else
2851         zapFlag(flag);
2852 }
2853 
flushKilledByCounts(int removeID)2854 void flushKilledByCounts( int removeID )
2855 {
2856     for (int i = 0; i < curMaxPlayers; i++)
2857     {
2858         GameKeeper::Player *player = GameKeeper::Player::getPlayerByIndex(i);
2859         if (player)
2860             player->player.flushKiller(removeID);
2861     }
2862 }
2863 
removePlayer(int playerIndex,const char * reason,bool notify)2864 void removePlayer(int playerIndex, const char *reason, bool notify)
2865 {
2866     // player is signing off or sent a bad packet.  since the
2867     // bad packet can come before MsgEnter, we must be careful
2868     // not to undo operations that haven't been done.
2869     // first shutdown connection
2870 
2871     // remove the player from any kill counts
2872     flushKilledByCounts(playerIndex);
2873 
2874     // clear any shots they had flying around
2875     ShotManager.RemovePlayer(playerIndex);
2876 
2877     GameKeeper::Player *playerData
2878         = GameKeeper::Player::getPlayerByIndex(playerIndex);
2879     if (!playerData)
2880         return;
2881 
2882     playerData->isParting = true;
2883 
2884     // call any on part events
2885     bz_PlayerJoinPartEventData_V1 partEventData;
2886     partEventData.eventType = bz_ePlayerPartEvent;
2887     partEventData.playerID = playerIndex;
2888     partEventData.record = bz_getPlayerByIndex(playerIndex);
2889     if (reason)
2890         partEventData.reason = reason;
2891 
2892     if ((playerData->player.getTeam() != NoTeam) && strlen(playerData->player.getCallSign()))
2893         worldEventManager.callEvents(bz_ePlayerPartEvent,&partEventData);
2894 
2895     if (notify)
2896     {
2897         // send a super kill to be polite
2898         // send message to one player
2899         // do not use directMessage as he can remove player
2900         void *buf  = sMsgBuf;
2901         buf = nboPackUShort(buf, 0);
2902         buf = nboPackUShort(buf, MsgSuperKill);
2903         playerData->netHandler->pwrite(sMsgBuf, 4);
2904     }
2905 
2906 
2907     // if there is an active poll, cancel any vote this player may have made
2908     static VotingArbiter *arbiter = (VotingArbiter *)BZDB.getPointer("poll");
2909     if ((arbiter != NULL) && (arbiter->knowsPoll()))
2910         arbiter->retractVote(std::string(playerData->player.getCallSign()));
2911 
2912     // status message
2913     std::string timeStamp = TimeKeeper::timestamp();
2914     logDebugMessage(1,"Player %s [%d] removed at %s: %s\n",
2915                     playerData->player.getCallSign(),
2916                     playerIndex, timeStamp.c_str(), reason);
2917     bool wasPlaying = playerData->player.isPlaying();
2918     playerData->netHandler->closing();
2919 
2920     zapFlagByPlayer(playerIndex);
2921 
2922     // player is outta here.  if player never joined a team then
2923     // don't count as a player.
2924 
2925     if (wasPlaying)
2926     {
2927         // make them wait from the time they left, but only if they
2928         // have spawned at least once and are not already waiting
2929         if (!playerData->player.hasNeverSpawned() &&
2930                 (rejoinList.waitTime (playerIndex) <= 0.0f) &&
2931                 !playerData->accessInfo.hasPerm(PlayerAccessInfo::rejoin))
2932             rejoinList.add (playerIndex);
2933 
2934         // tell everyone player has left
2935         void *buf, *bufStart = getDirectMessageBuffer();
2936         buf = nboPackUByte(bufStart, playerIndex);
2937         broadcastMessage(MsgRemovePlayer, (char*)buf-(char*)bufStart, bufStart);
2938 
2939         for (int i = 0; i < curMaxPlayers; i++)
2940         {
2941             GameKeeper::Player *p = GameKeeper::Player::getPlayerByIndex(i);
2942             if ((p == NULL) || !p->playerHandler || playerIndex == p->getIndex())
2943                 continue;
2944             p->playerHandler->playerRemoved(playerIndex);
2945         }
2946 
2947         // decrease team size
2948         int teamNum = int(playerData->player.getTeam());
2949         --team[teamNum].team.size;
2950 
2951         // if last active player on team then remove team's flag if no one
2952         // is carrying it
2953         if (Team::isColorTeam((TeamColor)teamNum)
2954                 && team[teamNum].team.size == 0 &&
2955                 (clOptions->gameType == ClassicCTF))
2956         {
2957             int flagid = FlagInfo::lookupFirstTeamFlag(teamNum);
2958             if (flagid >= 0)
2959             {
2960                 GameKeeper::Player *otherData;
2961                 for (int n = 0; n < clOptions->numTeamFlags[teamNum]; n++)
2962                 {
2963                     FlagInfo &flag = *FlagInfo::get(flagid+n);
2964                     otherData
2965                         = GameKeeper::Player::getPlayerByIndex(flag.player);
2966                     if (!otherData || otherData->player.isTeam((TeamColor)teamNum))
2967                         zapFlag(flag);
2968                 }
2969             }
2970         }
2971 
2972         // send team update
2973         sendTeamUpdate(-1, teamNum);
2974     }
2975 
2976     playerData->close();
2977 
2978     if (wasPlaying)
2979     {
2980         // 'fixing' the count after deleting player
2981         fixTeamCount();
2982 
2983         // tell the list server the new number of players
2984         listServerLink->queueMessage(ListServerLink::ADD);
2985     }
2986 
2987     if (clOptions->gameType == RabbitChase)
2988         if (playerIndex == rabbitIndex)
2989             anointNewRabbit();
2990 
2991     // recompute curMaxPlayers
2992     if (playerIndex + 1 == curMaxPlayers)
2993         while (true)
2994         {
2995             curMaxPlayers--;
2996             if (curMaxPlayers <= 0
2997                     || GameKeeper::Player::getPlayerByIndex(curMaxPlayers - 1))
2998                 break;
2999         }
3000 
3001     if (wasPlaying)
3002     {
3003         // if everybody left then reset world
3004         if (GameKeeper::Player::count() == 0)
3005         {
3006 
3007             if (clOptions->oneGameOnly)
3008             {
3009                 done = true;
3010                 exitCode = 0;
3011             }
3012             else
3013             {
3014                 // republicize ourself.  this dereferences the URL chain
3015                 // again so we'll notice any pointer change when any game
3016                 // is over (i.e. all players have quit).
3017                 publicize();
3018             }
3019         }
3020         else
3021         {
3022             recalcAllHandicaps();
3023             broadcastHandicaps();
3024         }
3025     }
3026 }
3027 
3028 // are the two teams foes with the current game style?
areFoes(TeamColor team1,TeamColor team2)3029 bool areFoes(TeamColor team1, TeamColor team2)
3030 {
3031     if (!allowTeams())
3032         return true;
3033     return team1!=team2 || (team1==RogueTeam);
3034 }
3035 
3036 
sendWorld(int playerIndex,uint32_t ptr)3037 static void sendWorld(int playerIndex, uint32_t ptr)
3038 {
3039     playerHadWorld = true;
3040     // send another small chunk of the world database
3041     assert((world != NULL) && (worldDatabase != NULL));
3042     void *buf, *bufStart = getDirectMessageBuffer();
3043     uint32_t size = MaxPacketLen - 2*sizeof(uint16_t) - sizeof(uint32_t);
3044     uint32_t left = worldDatabaseSize - ptr;
3045     if (ptr >= worldDatabaseSize)
3046     {
3047         size = 0;
3048         left = 0;
3049     }
3050     else if (ptr + size >= worldDatabaseSize)
3051     {
3052         size = worldDatabaseSize - ptr;
3053         left = 0;
3054     }
3055     buf = nboPackUInt(bufStart, uint32_t(left));
3056     buf = nboPackString(buf, (char*)worldDatabase + ptr, size);
3057     directMessage(playerIndex, MsgGetWorld, (char*)buf - (char*)bufStart, bufStart);
3058 }
3059 
3060 
makeGameSettings()3061 static void makeGameSettings()
3062 {
3063     void* buf = worldSettings;
3064 
3065     // the header
3066     buf = nboPackUShort (buf, WorldSettingsSize); // length
3067     buf = nboPackUShort (buf, MsgGameSettings);   // code
3068 
3069     // the settings
3070     buf = nboPackFloat  (buf, BZDBCache::worldSize);
3071     buf = nboPackUShort (buf, clOptions->gameType);
3072     buf = nboPackUShort (buf, clOptions->gameOptions);
3073     // An hack to fix a bug on the client
3074     buf = nboPackUShort (buf, PlayerSlot);
3075     buf = nboPackUShort (buf, clOptions->maxShots);
3076     buf = nboPackUShort (buf, numFlags);
3077     buf = nboPackFloat  (buf, clOptions->linearAcceleration);
3078     buf = nboPackFloat  (buf, clOptions->angularAcceleration);
3079     buf = nboPackUShort (buf, clOptions->shakeTimeout);
3080     buf = nboPackUShort (buf, clOptions->shakeWins);
3081     buf = nboPackUInt   (buf, 0); // FIXME - used to be sync time
3082 
3083     return;
3084 }
3085 
3086 
sendGameSettings(int playerIndex)3087 static void sendGameSettings(int playerIndex)
3088 {
3089     GameKeeper::Player *playerData;
3090     playerData = GameKeeper::Player::getPlayerByIndex(playerIndex);
3091     if (playerData == NULL)
3092         return;
3093 
3094     pwrite (*playerData, worldSettings, 4 + WorldSettingsSize);
3095 
3096     return;
3097 }
3098 
3099 
sendQueryGame(int playerIndex)3100 static void sendQueryGame(int playerIndex)
3101 {
3102     // much like a ping packet but leave out useless stuff (like
3103     // the server address, which must already be known, and the
3104     // server version, which was already sent).
3105     void *buf, *bufStart = getDirectMessageBuffer();
3106     buf = nboPackUShort(bufStart, pingReply.gameType);
3107     buf = nboPackUShort(buf, pingReply.gameOptions);
3108     buf = nboPackUShort(buf, pingReply.maxPlayers);
3109     buf = nboPackUShort(buf, pingReply.maxShots);
3110     buf = nboPackUShort(buf, team[0].team.size);
3111     buf = nboPackUShort(buf, team[1].team.size);
3112     buf = nboPackUShort(buf, team[2].team.size);
3113     buf = nboPackUShort(buf, team[3].team.size);
3114     buf = nboPackUShort(buf, team[4].team.size);
3115     buf = nboPackUShort(buf, team[5].team.size);
3116     buf = nboPackUShort(buf, pingReply.rogueMax);
3117     buf = nboPackUShort(buf, pingReply.redMax);
3118     buf = nboPackUShort(buf, pingReply.greenMax);
3119     buf = nboPackUShort(buf, pingReply.blueMax);
3120     buf = nboPackUShort(buf, pingReply.purpleMax);
3121     buf = nboPackUShort(buf, pingReply.observerMax);
3122     buf = nboPackUShort(buf, pingReply.shakeWins);
3123     // 1/10ths of second
3124     buf = nboPackUShort(buf, pingReply.shakeTimeout);
3125     buf = nboPackUShort(buf, pingReply.maxPlayerScore);
3126     buf = nboPackUShort(buf, pingReply.maxTeamScore);
3127     buf = nboPackUShort(buf, pingReply.maxTime);
3128     buf = nboPackUShort(buf, (uint16_t)clOptions->timeElapsed);
3129 
3130     // send it
3131     directMessage(playerIndex, MsgQueryGame, (char*)buf-(char*)bufStart, bufStart);
3132 }
3133 
sendQueryPlayers(int playerIndex)3134 static void sendQueryPlayers(int playerIndex)
3135 {
3136     GameKeeper::Player *playerData
3137         = GameKeeper::Player::getPlayerByIndex(playerIndex);
3138     if (!playerData)
3139         return;
3140 
3141     // count the number of active players
3142     int numPlayers = GameKeeper::Player::count();
3143 
3144     // first send number of teams and players being sent
3145     void *buf, *bufStart = getDirectMessageBuffer();
3146     buf = nboPackUShort(bufStart, NumTeams);
3147     buf = nboPackUShort(buf, numPlayers);
3148     int result = directMessage(*playerData, MsgQueryPlayers,
3149                                (char*)buf-(char*)bufStart, bufStart);
3150     if (result == -1)
3151         return;
3152 
3153     // now send the teams and players
3154     sendTeamUpdate(playerIndex);
3155     GameKeeper::Player *otherData;
3156     for (int i = 0; i < curMaxPlayers
3157             && GameKeeper::Player::getPlayerByIndex(playerIndex); i++)
3158     {
3159         if (i == playerIndex)
3160             continue;
3161         otherData = GameKeeper::Player::getPlayerByIndex(i);
3162         if (otherData)
3163             sendPlayerUpdate(otherData, playerIndex);
3164     }
3165 }
3166 
playerAlive(int playerIndex)3167 void playerAlive(int playerIndex)
3168 {
3169     GameKeeper::Player *playerData
3170         = GameKeeper::Player::getPlayerByIndex(playerIndex);
3171     if (!playerData)
3172         return;
3173 
3174     if (!playerData->player.isPlaying())
3175     {
3176         spawnSoon = true;
3177         return;
3178     }
3179     spawnSoon = false;
3180     // ignore multiple MsgAlive; also observer should not send MsgAlive;
3181     // diagnostic?
3182     if (playerData->player.isAlive() || playerData->player.isObserver())
3183         return;
3184 
3185     // make sure the user identifies themselves if required.
3186     if (!playerData->accessInfo.isAllowedToEnter())
3187     {
3188         sendMessage(ServerPlayer, playerIndex, "This callsign is registered.  You must use global identification");
3189         sendMessage(ServerPlayer, playerIndex, "before playing or use a different callsign.");
3190         removePlayer(playerIndex, "unidentified");
3191         return;
3192     }
3193 
3194     bz_AllowSpawnData_V2  spawnAllowData;
3195     spawnAllowData.playerID = playerIndex;
3196     spawnAllowData.team = convertTeam(playerData->player.getTeam());
3197 
3198     if (!playerData->accessInfo.hasPerm(PlayerAccessInfo::spawn))
3199     {
3200         sendMessage(ServerPlayer, playerIndex, "You do not have permission to spawn on this server.");
3201         sendMessage(ServerPlayer, playerIndex, "This server may require identification before you can join.");
3202         sendMessage(ServerPlayer, playerIndex, "register on https://forums.bzflag.org/ and use that callsign/password.");
3203         spawnAllowData.allow = false;
3204     }
3205 
3206     // a plug-in has set the spawnability of a player, let's not kick them for that
3207     if (!playerData->player.isAllowedToSpawn())
3208     {
3209         spawnAllowData.allow = false;
3210         spawnAllowData.kickPlayer = false;
3211     }
3212 
3213     // check for any spawn allow events
3214     worldEventManager.callEvents(bz_eAllowSpawn, &spawnAllowData);
3215 
3216     if (!spawnAllowData.allow)
3217     {
3218         // check if the player has been notified that they may not spawn
3219         if (!playerData->player.notifiedOfSpawnable())
3220         {
3221             sendMessage(ServerPlayer, playerIndex, spawnAllowData.message.c_str());
3222             playerData->player.setNotifiedOfSpawnable(true);
3223         }
3224 
3225         // client won't send another enter so kick em =(
3226         if (spawnAllowData.kickPlayer)
3227             removePlayer(playerIndex, spawnAllowData.kickReason.c_str());
3228 
3229         return;
3230     }
3231 
3232     // player is coming alive.
3233     dropAssignedFlag(playerIndex);
3234 
3235     // get the spawn position
3236     SpawnPosition spawnPosition
3237     (playerIndex,
3238      (!clOptions->respawnOnBuildings) || (playerData->player.isBot()),
3239      clOptions->gameType == ClassicCTF);
3240 
3241     // see if there is anyone to handle the spawn event, and if they want to change it.
3242     bz_GetPlayerSpawnPosEventData_V1  spawnData;
3243     spawnData.playerID = playerIndex;
3244     spawnData.team   = convertTeam(playerData->player.getTeam());
3245     spawnData.pos[0]   = spawnPosition.getX();
3246     spawnData.pos[1]   = spawnPosition.getY();
3247     spawnData.pos[2]   = spawnPosition.getZ();
3248     spawnData.rot      = spawnPosition.getAzimuth();
3249 
3250     worldEventManager.callEvents(bz_eGetPlayerSpawnPosEvent,&spawnData);
3251 
3252     // update last position immediately
3253     playerData->player.setRestartOnBase(false);
3254     playerData->setPlayerState(spawnData.pos, spawnData.rot);
3255 
3256     // send MsgAlive
3257     void *buf, *bufStart = getDirectMessageBuffer();
3258     buf = nboPackUByte(bufStart, playerIndex);
3259     buf = nboPackVector(buf, playerData->lastState.pos);
3260     buf = nboPackFloat(buf, playerData->lastState.azimuth);
3261     broadcastMessage(MsgAlive, (char*)buf - (char*)bufStart, bufStart);
3262 
3263     // call any events for a playerspawn
3264     bz_PlayerSpawnEventData_V1    spawnEvent;
3265     spawnEvent.playerID = playerIndex;
3266     spawnEvent.team = convertTeam(playerData->player.getTeam());
3267 
3268     playerStateToAPIState(spawnEvent.state, playerData->lastState);
3269 
3270     worldEventManager.callEvents(bz_ePlayerSpawnEvent,&spawnEvent);
3271 
3272     if (clOptions->gameType == RabbitChase)
3273     {
3274         playerData->player.wasNotARabbit();
3275         if (rabbitIndex == NoPlayer)
3276             anointNewRabbit();
3277     }
3278 }
3279 
3280 // TODO: In 2.6, we should just handle the kills fully from the server
cleanupGameOver()3281 void cleanupGameOver()
3282 {
3283     double spawnDelay = (double)BZDB.eval(StateDatabase::BZDB_EXPLODETIME);
3284     for (int i = 0; i < curMaxPlayers; i++)
3285     {
3286         GameKeeper::Player *p = GameKeeper::Player::getPlayerByIndex(i);
3287         if (p != NULL && p->player.getTeam() != ObserverTeam)
3288         {
3289             if (p->player.isAlive())
3290             {
3291                 p->player.setDead();
3292                 p->player.setSpawnDelay(spawnDelay);
3293             }
3294             zapFlagByPlayer(i);
3295             p->player.setRestartOnBase(true);
3296         }
3297     }
3298 }
3299 
checkTeamScore(int playerIndex,int teamIndex)3300 void checkTeamScore(int playerIndex, int teamIndex)
3301 {
3302     if (clOptions->maxTeamScore == 0 || !Team::isColorTeam(TeamColor(teamIndex))) return;
3303     if (team[teamIndex].team.getWins() - team[teamIndex].team.getLosses() >= clOptions->maxTeamScore)
3304     {
3305         void *buf, *bufStart = getDirectMessageBuffer();
3306         buf = nboPackUByte(bufStart, playerIndex);
3307         buf = nboPackUShort(buf, uint16_t(teamIndex));
3308         broadcastMessage(MsgScoreOver, (char*)buf-(char*)bufStart, bufStart);
3309 
3310         cleanupGameOver();
3311 
3312         gameOver = true;
3313         if (clOptions->oneGameOnly)
3314         {
3315             done = true;
3316             exitCode = 0;
3317         }
3318     }
3319 }
3320 
allowTeams(void)3321 bool allowTeams ( void )
3322 {
3323     return clOptions->gameType != OpenFFA;
3324 }
3325 
3326 // FIXME - needs extra checks for killerIndex=ServerPlayer (world weapons)
3327 // (was broken before); it turns out that killerIndex=-1 for world weapon?
3328 // No need to check on victimIndex.
3329 //   It is taken as the index of the udp table when called by incoming message
3330 //   It is taken by killerIndex when autocalled, but only if != -1
3331 // killer could be InvalidPlayer or a number within [0 curMaxPlayer)
playerKilled(int victimIndex,int killerIndex,int reason,int16_t shotIndex,const FlagType * flagType,int phydrv,bool respawnOnBase)3332 void playerKilled(int victimIndex, int killerIndex, int reason,
3333                   int16_t shotIndex, const FlagType* flagType, int phydrv, bool respawnOnBase )
3334 {
3335     GameKeeper::Player *killerData = NULL;
3336     GameKeeper::Player *victimData
3337         = GameKeeper::Player::getPlayerByIndex(victimIndex);
3338 
3339     if (!victimData || !victimData->player.isPlaying())
3340         return;
3341 
3342     if (killerIndex != InvalidPlayer && killerIndex != ServerPlayer)
3343         killerData = GameKeeper::Player::getPlayerByIndex(killerIndex);
3344 
3345     // log the kill with the player
3346     if (killerData || killerIndex == ServerPlayer)
3347         victimData->player.killedBy(killerIndex);
3348 
3349     // aliases for convenience
3350     // Warning: killer should not be used when killerIndex == InvalidPlayer or ServerPlayer
3351     PlayerInfo *killer = realPlayer(killerIndex) ? &killerData->player : 0,
3352                 *victim = &victimData->player;
3353 
3354     // victim was already dead. keep score.
3355     if (!victim->isAlive()) return;
3356 
3357     victim->setRestartOnBase(respawnOnBase);
3358     victim->setSpawnDelay((double)BZDB.eval(StateDatabase::BZDB_EXPLODETIME));
3359     victim->setDead();
3360 
3361     // call any events for a playerdeath
3362     bz_PlayerDieEventData_V2  dieEvent;
3363     dieEvent.playerID = victimIndex;
3364     dieEvent.team = convertTeam(victim->getTeam());
3365     dieEvent.killerID = killerIndex;
3366     dieEvent.shotID = shotIndex;
3367 
3368     if (killer)
3369         dieEvent.killerTeam = convertTeam(killer->getTeam());
3370 
3371     dieEvent.driverID = phydrv;
3372     dieEvent.flagKilledWith = flagType->flagAbbv;
3373 
3374     playerStateToAPIState(dieEvent.state, victimData->lastState);
3375 
3376     if (victim->haveFlag())   // this may not return true in the current protocol since tanks drop flags before death, when that's fixed the else wont' be needed
3377     {
3378         int flagid = victim->getFlag();
3379         if (flagid >= 0)
3380             dieEvent.flagHeldWhenKilled = flagid;
3381     }
3382     else if (victimData->lastHeldFlagID >= 0)
3383     {
3384         auto f = FlagInfo::get(victimData->lastHeldFlagID);
3385 
3386         if (f->flag.status != FlagOnGround) // if the last held flag was just a moment ago, they probably died with this.
3387             dieEvent.flagHeldWhenKilled = victimData->lastHeldFlagID;
3388     }
3389 
3390     worldEventManager.callEvents(bz_ePlayerDieEvent,&dieEvent);
3391 
3392     // If a plugin changed the killer, we need to update the data.
3393     if (dieEvent.killerID != killerIndex)
3394     {
3395         killerIndex = dieEvent.killerID;
3396         if (killerIndex != InvalidPlayer && killerIndex != ServerPlayer)
3397             killerData = GameKeeper::Player::getPlayerByIndex(killerIndex);
3398         killer = realPlayer(killerIndex) ? &killerData->player : 0;
3399     }
3400 
3401     // Call a new event when all the plugins have finished reassigning any kills
3402     worldEventManager.callEvents(bz_ePlayerDeathFinalizedEvent, &dieEvent);
3403 
3404     // killing rabbit or killing anything when I am a dead ex-rabbit is allowed
3405     bool teamkill = false;
3406     if (killer)
3407     {
3408         const bool rabbitinvolved = killer->isARabbitKill(*victim);
3409         const bool foe = areFoes(victim->getTeam(), killer->getTeam());
3410         teamkill = !foe && !rabbitinvolved;
3411     }
3412 
3413     // send MsgKilled
3414     void *buf, *bufStart = getDirectMessageBuffer();
3415     buf = nboPackUByte(bufStart, victimIndex);
3416     buf = nboPackUByte(buf, killerIndex);
3417     buf = nboPackShort(buf, reason);
3418     buf = nboPackShort(buf, shotIndex);
3419     buf = flagType->pack(buf);
3420     if (reason == PhysicsDriverDeath)
3421         buf = nboPackInt(buf, phydrv);
3422     broadcastMessage(MsgKilled, (char*)buf-(char*)bufStart, bufStart);
3423 
3424     // update tk-score
3425     if ((victimIndex != killerIndex) && teamkill)
3426     {
3427         killerData->score.tK();
3428         char message[MessageLen];
3429         if (clOptions->tkAnnounce)
3430         {
3431             snprintf(message, MessageLen, "Team kill: %s killed %s",
3432                      killerData->player.getCallSign(), victimData->player.getCallSign());
3433             sendMessage(ServerPlayer, AdminPlayers, message);
3434         }
3435         if (killerData->score.isTK())
3436         {
3437             strcpy(message, "You have been automatically kicked for team killing" );
3438             sendMessage(ServerPlayer, killerIndex, message);
3439             snprintf(message, MessageLen, "Player %s removed: team killing", killerData->player.getCallSign());
3440             sendMessage(ServerPlayer, AdminPlayers, message);
3441             removePlayer(killerIndex, "teamkilling");
3442         }
3443     }
3444 
3445     // zap flag player was carrying.  clients should send a drop flag
3446     // message before sending a killed message, so this shouldn't happen.
3447     zapFlagByPlayer(victimIndex);
3448 
3449     victimData = GameKeeper::Player::getPlayerByIndex(victimIndex);
3450     // victimData will be NULL if the player has been kicked for TK'ing
3451     // so don't bother doing any score stuff for him
3452     if (victimData != NULL)
3453     {
3454         // change the player score
3455         bufStart = getDirectMessageBuffer();
3456         victimData->score.killedBy();
3457         if (killer)
3458         {
3459             if (victimIndex != killerIndex)
3460             {
3461                 if (teamkill)
3462                 {
3463                     if (clOptions->teamKillerDies)
3464                         playerKilled(killerIndex, killerIndex, reason, -1, Flags::Null, -1);
3465                     else
3466                         killerData->score.killedBy();
3467                 }
3468                 else
3469                     killerData->score.kill();
3470             }
3471 
3472             // send killer & victim
3473             GameKeeper::Player *kAndV[] = {killerData, victimData};
3474             sendPlayerScores(kAndV, 2);
3475         }
3476         else
3477         {
3478             // send victim
3479             sendPlayerScores(&victimData, 1);
3480         }
3481 
3482         if (handicapAllowed())
3483         {
3484             bufStart = getDirectMessageBuffer();
3485             if (killer)
3486             {
3487                 recalcHandicap(killerIndex);
3488                 buf = nboPackUByte(bufStart, 2);
3489                 buf = nboPackUByte(buf, killerIndex);
3490                 buf = nboPackShort(buf, killerData->score.getHandicap());
3491             }
3492             else
3493                 buf = nboPackUByte(bufStart, 1);
3494             recalcHandicap(victimIndex);
3495             buf = nboPackUByte(buf, victimIndex);
3496             buf = nboPackShort(buf, victimData->score.getHandicap());
3497             broadcastMessage(MsgHandicap, (char*)buf-(char*)bufStart, bufStart);
3498         }
3499 
3500         // see if the player reached the score limit
3501         if (clOptions->maxPlayerScore != 0
3502                 && killerIndex != InvalidPlayer
3503                 && killerIndex != ServerPlayer
3504                 && killerData->score.reached())
3505         {
3506             bufStart = getDirectMessageBuffer();
3507             buf = nboPackUByte(bufStart, killerIndex);
3508             buf = nboPackUShort(buf, uint16_t(NoTeam));
3509             broadcastMessage(MsgScoreOver, (char*)buf-(char*)bufStart, bufStart);
3510 
3511             cleanupGameOver();
3512 
3513             gameOver = true;
3514             if (clOptions->oneGameOnly)
3515             {
3516                 done = true;
3517                 exitCode = 0;
3518             }
3519         }
3520     }
3521 
3522     if (clOptions->gameType == RabbitChase)
3523     {
3524         if (victimIndex == rabbitIndex)
3525             anointNewRabbit(killerIndex);
3526     }
3527     else if (Score::KeepTeamScores)
3528     {
3529         // change the team scores -- rogues don't have team scores.  don't
3530         // change team scores for individual player's kills in capture the
3531         // flag mode.
3532         // Team score is even not used on RabbitChase
3533         int winningTeam = (int)NoTeam;
3534         if ( clOptions->gameType == OpenFFA || clOptions->gameType == TeamFFA )
3535         {
3536             int killerTeam = -1;
3537             if (killer && victim->getTeam() == killer->getTeam())
3538             {
3539                 if (!killer->isTeam(RogueTeam))
3540                 {
3541                     int delta = 0;
3542                     if (killerIndex == victimIndex)
3543                         delta += 1;
3544                     else
3545                         delta += 2;
3546 
3547                     int old = team[int(victim->getTeam())].team.getLosses();
3548                     team[int(victim->getTeam())].team.setLosses(old+delta);
3549                     bz_TeamScoreChangeEventData_V1 eventData = bz_TeamScoreChangeEventData_V1(convertTeam(victim->getTeam()), bz_eLosses,
3550                             old, old+delta);
3551                     worldEventManager.callEvents(&eventData);
3552                 }
3553             }
3554             else
3555             {
3556                 if (killer && !killer->isTeam(RogueTeam))
3557                 {
3558                     winningTeam = int(killer->getTeam());
3559 
3560                     int old = team[winningTeam].team.getWins();
3561                     team[winningTeam].team.setWins(old+1);
3562                     bz_TeamScoreChangeEventData_V1 eventData = bz_TeamScoreChangeEventData_V1(convertTeam(killer->getTeam()), bz_eWins, old,
3563                             old+1);
3564                     worldEventManager.callEvents(&eventData);
3565                 }
3566                 if (!victim->isTeam(RogueTeam))
3567                 {
3568                     int old = team[int(victim->getTeam())].team.getLosses();
3569                     team[int(victim->getTeam())].team.setLosses(old+1);
3570                     bz_TeamScoreChangeEventData_V1 eventData = bz_TeamScoreChangeEventData_V1(convertTeam(victim->getTeam()), bz_eLosses,
3571                             old, old+1);
3572                     worldEventManager.callEvents(&eventData);
3573                 }
3574                 if (killer)
3575                     killerTeam = killer->getTeam();
3576             }
3577             sendTeamUpdate(-1,int(victim->getTeam()), killerTeam);
3578         }
3579 #ifdef PRINTSCORE
3580         dumpScore();
3581 #endif
3582         if (winningTeam != (int)NoTeam)
3583             checkTeamScore(killerIndex, winningTeam);
3584     }
3585 }
3586 
doSpawns()3587 void doSpawns()
3588 {
3589     TimeKeeper curTime = TimeKeeper::getCurrent();
3590     for (int i = 0; i < curMaxPlayers; i++)
3591     {
3592         GameKeeper::Player *p = GameKeeper::Player::getPlayerByIndex(i);
3593         if (p == NULL)
3594             continue;
3595         if (p->player.waitingToSpawn() && (p->player.getNextSpawnTime() <= curTime))
3596         {
3597             // Let them live!
3598             playerAlive(i);
3599         }
3600     }
3601 }
3602 
3603 // Currently only used for the server-side ID flag
searchFlag(GameKeeper::Player & playerData)3604 static void searchFlag(GameKeeper::Player &playerData)
3605 {
3606     if (!playerData.player.isAlive())
3607         return;
3608 
3609     // Only continue if the player has the ID flag
3610     int flagId = playerData.player.getFlag();
3611     if (flagId < 0)
3612         return;
3613 
3614     FlagInfo &playerFlag = *FlagInfo::get(flagId);
3615     if (playerFlag.flag.type != Flags::Identify)
3616         return;
3617 
3618     float radius = BZDB.eval(StateDatabase::BZDB_IDENTIFYRANGE);
3619 
3620     const PlayerId playerIndex = playerData.getIndex();
3621 
3622     const float *tpos    = playerData.lastState.pos;
3623     float radius2 = radius * radius;
3624 
3625     int closestFlag = -1;
3626     for (int i = 0; i < numFlags; i++)
3627     {
3628         FlagInfo &flag = *FlagInfo::get(i);
3629         if (!flag.exist())
3630             continue;
3631         if (flag.flag.status != FlagOnGround)
3632             continue;
3633 
3634         const float *fpos = flag.flag.position;
3635         float dist = (tpos[0] - fpos[0]) * (tpos[0] - fpos[0])
3636                      + (tpos[1] - fpos[1]) * (tpos[1] - fpos[1])
3637                      + (tpos[2] - fpos[2]) * (tpos[2] - fpos[2]);
3638 
3639         if (dist < radius2)
3640         {
3641             radius2     = dist;
3642             closestFlag = i;
3643         }
3644     }
3645 
3646     if (closestFlag < 0)
3647     {
3648         playerData.setLastIdFlag(-1);
3649         return;
3650     }
3651 
3652     FlagInfo &flag = *FlagInfo::get(closestFlag);
3653     if (closestFlag != playerData.getLastIdFlag())
3654     {
3655         sendClosestFlagMessage(playerIndex,flag.flag.type,flag.flag.position);
3656         playerData.setLastIdFlag(closestFlag);
3657     }
3658 }
3659 
3660 
grabFlag(int playerIndex,FlagInfo & flag,bool checkPos)3661 void grabFlag(int playerIndex, FlagInfo &flag, bool checkPos)
3662 {
3663     GameKeeper::Player *playerData
3664         = GameKeeper::Player::getPlayerByIndex(playerIndex);
3665 
3666     // player wants to take possession of flag
3667     if (!playerData ||
3668             playerData->player.isObserver() ||
3669             !playerData->player.isAlive() ||
3670             playerData->player.haveFlag() ||
3671             (checkPos && flag.flag.status != FlagOnGround))
3672         return;
3673 
3674     const float* fpos = flag.flag.position;
3675     if (checkPos)
3676     {
3677         //last Pos might be lagged by TankSpeed so include in calculation
3678         const float tankRadius = BZDBCache::tankRadius;
3679         const float tankSpeed = BZDBCache::tankSpeed;
3680         const float radius2 = (tankSpeed + tankRadius + BZDBCache::flagRadius) * (tankSpeed + tankRadius +
3681                               BZDBCache::flagRadius);
3682         const float* tpos = playerData->lastState.pos;
3683         const float delta = (tpos[0] - fpos[0]) * (tpos[0] - fpos[0]) +
3684                             (tpos[1] - fpos[1]) * (tpos[1] - fpos[1]);
3685 
3686         if ((fabs(tpos[2] - fpos[2]) < 0.1f) && (delta > radius2))
3687         {
3688             logDebugMessage(2,"Player %s [%d] %f %f %f tried to grab distant flag %f %f %f: distance=%f\n",
3689                             playerData->player.getCallSign(), playerIndex,
3690                             tpos[0], tpos[1], tpos[2], fpos[0], fpos[1], fpos[2], sqrt(delta));
3691             // @TODO make a better test for this to reduce false positives
3692             //removePlayer(playerIndex, "attempted illegal flag grab");
3693             return;
3694         }
3695     }
3696 
3697     bz_AllowFlagGrabData_V1 allow;
3698     allow.playerID = playerIndex;
3699     allow.flagID = flag.getIndex();
3700     allow.flagType = flag.flag.type->flagAbbv.c_str();
3701 
3702     worldEventManager.callEvents(bz_eAllowFlagGrab,&allow);
3703 
3704     if (!allow.allow)
3705         return;
3706 
3707     // okay, player can have it
3708     flag.grab(playerIndex);
3709     playerData->player.setFlag(flag.getIndex());
3710 
3711     playerData->lastHeldFlagID = flag.getIndex();
3712 
3713     // send MsgGrabFlag
3714     void *buf, *bufStart = getDirectMessageBuffer();
3715     buf = nboPackUByte(bufStart, playerIndex);
3716     buf = flag.pack(buf);
3717 
3718     broadcastMessage(MsgGrabFlag, (char*)buf-(char*)bufStart, bufStart);
3719 
3720     playerData->flagHistory.add(flag.flag.type);
3721 
3722     bz_FlagGrabbedEventData_V1 data;
3723     data.flagID = flag.getIndex();
3724     data.flagType = flag.flag.type->flagAbbv.c_str();
3725     memcpy(data.pos,fpos,sizeof(float)*3);
3726     data.playerID = playerIndex;
3727 
3728     worldEventManager.callEvents(bz_eFlagGrabbedEvent,&data);
3729 }
3730 
3731 
dropFlag(FlagInfo & drpFlag,const float dropPos[3])3732 void dropFlag(FlagInfo& drpFlag, const float dropPos[3])
3733 {
3734     assert(world != NULL);
3735 
3736     const float size = BZDBCache::worldSize * 0.5f;
3737     float pos[3];
3738     pos[0] = ((dropPos[0] < -size) || (dropPos[0] > size)) ? 0.0f : dropPos[0];
3739     pos[1] = ((dropPos[1] < -size) || (dropPos[1] > size)) ? 0.0f : dropPos[1];
3740     pos[2] = (dropPos[2] > maxWorldHeight) ? maxWorldHeight : dropPos[2];
3741 
3742     // player wants to drop flag.  we trust that the client won't tell
3743     // us to drop a sticky flag until the requirements are satisfied.
3744     const int flagIndex = drpFlag.getIndex();
3745     if (drpFlag.flag.status != FlagOnTank)
3746         return;
3747     int flagTeam = drpFlag.flag.type->flagTeam;
3748     bool isTeamFlag = (flagTeam != ::NoTeam);
3749 
3750     // limited flags that have been fired should be disposed of
3751     bool limited = clOptions->flagLimit[drpFlag.flag.type] != -1;
3752     if (limited && drpFlag.numShots > 0) drpFlag.grabs = 0;
3753 
3754 
3755     const float waterLevel = world->getWaterLevel();
3756     float minZ = 0.0f;
3757     if (waterLevel > minZ)
3758         minZ = waterLevel;
3759     const float maxZ = MAXFLOAT;
3760 
3761     float landing[3] = {pos[0], pos[1], pos[2]};
3762     bool safelyDropped =
3763         DropGeometry::dropTeamFlag(landing, minZ, maxZ, flagTeam);
3764 
3765     bool vanish;
3766 
3767     if (isTeamFlag)
3768         vanish = false;
3769     else if (--drpFlag.grabs <= 0)
3770     {
3771         vanish = true;
3772         drpFlag.grabs = 0;
3773     }
3774     else if (!clOptions->flagsOnBuildings && (landing[2] > 0.0f))
3775         vanish = true;
3776     else
3777         vanish = !safelyDropped;
3778 
3779     // With Team Flag, we should absolutely go for finding a landing
3780     // position, while, for other flags, we could stay with default, or
3781     // just let them vanish
3782     if (isTeamFlag && !safelyDropped)
3783     {
3784         // figure out landing spot -- if flag in a Bad Place
3785         // when dropped, move to safety position or make it going
3786         if (!world->getFlagDropPoint(&drpFlag, pos, landing))
3787         {
3788             // try the center
3789             landing[0] = landing[1] = landing[2] = 0.0f;
3790             safelyDropped =
3791                 DropGeometry::dropTeamFlag(landing, minZ, maxZ, flagTeam);
3792             if (!safelyDropped)
3793             {
3794                 // ok, we give up, send it home
3795                 TeamBases &teamBases = bases[flagTeam];
3796                 const TeamBase &base = teamBases.getRandomBase(flagIndex);
3797                 landing[0] = base.position[0];
3798                 landing[1] = base.position[1];
3799                 landing[2] = base.position[2] + base.size[2];
3800             }
3801         }
3802     }
3803 
3804     if (isTeamFlag)
3805     {
3806         // flag timeout gets started when the LAST team flag held is dropped,
3807         // AND the team is already empty
3808         if (team[drpFlag.flag.type->flagTeam].team.size == 0)
3809         {
3810             // see if the flag being dropped was the last team flag being held
3811             bool startTimeout = true;
3812             for (int i = 0; i < numFlags; i++)
3813             {
3814                 FlagInfo* fi = FlagInfo::get(i);
3815                 if (fi->teamIndex() == drpFlag.teamIndex() && fi->getIndex() != drpFlag.getIndex())
3816                 {
3817                     // this is another flag belonging to the same team
3818                     if (realPlayer(fi->player))
3819                     {
3820                         startTimeout = false; // can't start timeout if another team flag is being carried
3821                         break;
3822                     }
3823                 }
3824             }
3825 
3826             if (startTimeout)
3827             {
3828                 team[flagIndex + 1].flagTimeout = TimeKeeper::getCurrent();
3829                 team[flagIndex + 1].flagTimeout += (float)clOptions->teamFlagTimeout;
3830             }
3831         }
3832     }
3833 
3834     int player = drpFlag.player;
3835 
3836     GameKeeper::Player *playerData = GameKeeper::Player::getPlayerByIndex(player);
3837     playerData->lastHeldFlagID = drpFlag.getIndex();
3838 
3839     drpFlag.dropFlag(pos, landing, vanish);
3840 
3841     // player no longer has flag -- send MsgDropFlag
3842     sendDrop(drpFlag);
3843 
3844     // notify of new flag state
3845     sendFlagUpdate(drpFlag);
3846 
3847     // trigger the api event
3848     bz_FlagDroppedEventData_V1 data;
3849     data.playerID = player;
3850     data.flagID = flagIndex;
3851     data.flagType = drpFlag.flag.type->flagAbbv.c_str();
3852     memcpy(data.pos, pos, sizeof(float)*3);
3853 
3854     worldEventManager.callEvents(bz_eFlagDroppedEvent,&data);
3855 
3856 }
3857 
3858 
dropPlayerFlag(GameKeeper::Player & playerData,const float dropPos[3])3859 void dropPlayerFlag(GameKeeper::Player &playerData, const float dropPos[3])
3860 {
3861     const int flagIndex = playerData.player.getFlag();
3862     if (flagIndex < 0)
3863         return;
3864 
3865     FlagInfo &flag = *FlagInfo::get(flagIndex);
3866     if (flag.flag.type == Flags::Shield)
3867     {
3868         playerData.player.endShotCredit -= playerData.player.endShotShieldCredit;
3869         playerData.player.endShotShieldCredit = 0;
3870     }
3871 
3872     dropFlag(flag, dropPos);
3873 
3874     return;
3875 }
3876 
captureFlag(int playerIndex,TeamColor teamCaptured,TeamColor teamCapped,bool checkCheat)3877 bool captureFlag(int playerIndex, TeamColor teamCaptured, TeamColor teamCapped, bool checkCheat)
3878 {
3879     GameKeeper::Player *playerData
3880         = GameKeeper::Player::getPlayerByIndex(playerIndex);
3881     if (!playerData)
3882         return false;
3883 
3884     // Sanity check
3885     if (teamCaptured < RedTeam || teamCaptured > PurpleTeam)
3886         return false;
3887 
3888     if (teamCapped != NoTeam && (teamCapped < RedTeam || teamCapped > PurpleTeam))
3889         return false;
3890 
3891     // player captured a flag.  can either be enemy flag in player's own
3892     // team base, or player's own flag in enemy base.
3893     int flagIndex = playerData->player.getFlag();
3894     if (flagIndex < 0)
3895         return false;
3896     FlagInfo &flag = *FlagInfo::get(flagIndex);
3897 
3898     TeamColor teamIndex;
3899 
3900     if (teamCapped != NoTeam)
3901         teamIndex = teamCapped;
3902     else
3903         teamIndex = flag.teamIndex();
3904 
3905     if (teamIndex == ::NoTeam)
3906         return false;
3907     /*
3908      * The flag object always shows that it is the player's own team.
3909      * TODO: understand this situation better and change or document it.
3910      *if (teamIndex != teamCaptured)
3911      *    logDebugMessage(1,"Player %s [%d] claimed to capture %s flag while carrying %s flag\n",
3912      *  playerData->player.getCallSign(), playerIndex,
3913      *  Team::getName(teamCaptured), Team::getName(teamIndex));
3914      */
3915 
3916     if (checkCheat)   //cheat checking
3917     {
3918         TeamColor base = whoseBase(playerData->lastState.pos[0],
3919                                    playerData->lastState.pos[1],
3920                                    playerData->lastState.pos[2]);
3921         if ((teamIndex == playerData->player.getTeam() &&
3922                 base == playerData->player.getTeam()))
3923         {
3924             logDebugMessage(1,"Player %s [%d] might have sent MsgCaptureFlag for taking their own "
3925                             "flag onto their own base\n",
3926                             playerData->player.getCallSign(), playerIndex);
3927             //return; //sanity check
3928         }
3929         if ((teamIndex != playerData->player.getTeam() &&
3930                 base != playerData->player.getTeam()))
3931         {
3932             logDebugMessage(1,"Player %s [%d] (%s) might have tried to capture %s flag without "
3933                             "reaching their own base. (Player position: %f %f %f)\n",
3934                             playerData->player.getCallSign(), playerIndex,
3935                             Team::getName(playerData->player.getTeam()),
3936                             Team::getName(teamIndex),
3937                             playerData->lastState.pos[0], playerData->lastState.pos[1],
3938                             playerData->lastState.pos[2]);
3939             //char message[MessageLen];
3940             //strcpy(message, "Autokick: Tried to capture opponent flag without landing on your base");
3941             //sendMessage(ServerPlayer, playerIndex, message);
3942             //removePlayer(playerIndex, "capturecheat"); //FIXME: kicks honest players at times
3943             //return;
3944         }
3945     }
3946 
3947     bz_AllowCTFCaptureEventData_V1 allowCap;
3948 
3949     allowCap.teamCapped = convertTeam(teamIndex);
3950     allowCap.teamCapping = convertTeam(teamCaptured);
3951     allowCap.playerCapping = playerIndex;
3952     playerData->getPlayerState(allowCap.pos, allowCap.rot);
3953 
3954     allowCap.allow = true;
3955 
3956     worldEventManager.callEvents(bz_eAllowCTFCaptureEvent,&allowCap);
3957 
3958     if (!allowCap.allow)
3959         return false;
3960 
3961     // player no longer has flag and put flag back at it's base
3962     playerData->player.resetFlag();
3963     resetFlag(flag);
3964 
3965     // send MsgCaptureFlag
3966     void *buf, *bufStart = getDirectMessageBuffer();
3967     buf = nboPackUByte(bufStart, playerIndex);
3968     buf = nboPackUShort(buf, uint16_t(flagIndex));
3969     buf = nboPackUShort(buf, uint16_t(teamCaptured));
3970     broadcastMessage(MsgCaptureFlag, (char*)buf-(char*)bufStart, bufStart);
3971 
3972     // find any events for capturing the flags on the capped team or events for ANY team
3973     bz_CTFCaptureEventData_V1 eventData;
3974     eventData.teamCapped = convertTeam(teamIndex);
3975     eventData.teamCapping = convertTeam(teamCaptured);
3976     eventData.playerCapping = playerIndex;
3977     playerData->getPlayerState(eventData.pos, eventData.rot);
3978 
3979     worldEventManager.callEvents(bz_eCaptureEvent,&eventData);
3980 
3981     // everyone on losing team is dead
3982     double spawnDelay = (double)BZDB.eval(StateDatabase::BZDB_EXPLODETIME);
3983     for (int i = 0; i < curMaxPlayers; i++)
3984     {
3985         GameKeeper::Player *p = GameKeeper::Player::getPlayerByIndex(i);
3986         if ((p == NULL) || (teamIndex != (int)p->player.getTeam()))
3987             continue;
3988         if (p->player.isAlive())
3989         {
3990             p->player.setDead();
3991             p->player.setSpawnDelay(spawnDelay);
3992         }
3993         p->player.setRestartOnBase(true);
3994         zapFlagByPlayer(i);
3995     }
3996 
3997     if (Score::KeepTeamScores)
3998     {
3999         // update score (rogues can't capture flags)
4000         int winningTeam = (int)NoTeam;
4001         if (teamIndex != int(playerData->player.getTeam()))
4002         {
4003             // player captured enemy flag
4004             winningTeam = int(playerData->player.getTeam());
4005 
4006             int old = team[winningTeam].team.getWins();
4007             team[winningTeam].team.setWins(old+1);
4008             bz_TeamScoreChangeEventData_V1 eventData2 = bz_TeamScoreChangeEventData_V1(convertTeam(winningTeam), bz_eWins, old,
4009                     old+1);
4010             worldEventManager.callEvents(&eventData2);
4011         }
4012         int old = team[teamIndex].team.getLosses();
4013         team[teamIndex].team.setLosses(old+1);
4014         bz_TeamScoreChangeEventData_V1 eventData3 = bz_TeamScoreChangeEventData_V1(convertTeam(teamIndex), bz_eLosses, old,
4015                 old+1);
4016         worldEventManager.callEvents(&eventData3);
4017 
4018         sendTeamUpdate(-1, winningTeam, teamIndex);
4019 #ifdef PRINTSCORE
4020         dumpScore();
4021 #endif
4022         if (winningTeam != (int)NoTeam)
4023             checkTeamScore(playerIndex, winningTeam);
4024     }
4025 
4026     return true;
4027 }
4028 
shotUpdate(int playerIndex,void * buf,int len)4029 static void shotUpdate(int playerIndex, void *buf, int len)
4030 {
4031     GameKeeper::Player *playerData
4032         = GameKeeper::Player::getPlayerByIndex(playerIndex);
4033     if (!playerData)
4034         return;
4035 
4036     const PlayerInfo &shooter = playerData->player;
4037     if (!shooter.isAlive() || shooter.isObserver())
4038         return;
4039 
4040     ShotUpdate shot;
4041     PlayerId targetId;
4042     nboUnpackUByte(shot.unpack(buf), targetId);
4043 
4044     // verify playerId
4045     if (shot.player != playerIndex)
4046     {
4047         logDebugMessage(2,"Player %s [%d] shot playerid mismatch\n", shooter.getCallSign(),
4048                         playerIndex);
4049         return;
4050     }
4051 
4052     if (!playerData->updateShot(shot.id & 0xff, shot.id >> 8))
4053         return;
4054 
4055     uint32_t shotGUID = ShotManager.FindShotGUID(playerIndex,shot.id & 0xff);
4056     ShotManager.SetShotTarget(shotGUID,targetId);
4057 
4058     // TODO, Remove this and let the GM update logic send the updates,
4059     broadcastMessage(MsgGMUpdate, len, buf);
4060 }
4061 
shotFired(int playerIndex,void * buf,int len)4062 static void shotFired(int playerIndex, void *buf, int len)
4063 {
4064     GameKeeper::Player *playerData
4065         = GameKeeper::Player::getPlayerByIndex(playerIndex);
4066     if (!playerData)
4067         return;
4068 
4069     bool repack = false;
4070     const PlayerInfo &shooter = playerData->player;
4071     if (!shooter.isAlive() || shooter.isObserver())
4072         return;
4073     FiringInfo firingInfo;
4074     firingInfo.unpack(buf);
4075     const ShotUpdate &shot = firingInfo.shot;
4076 
4077     // verify playerId
4078     if (shot.player != playerIndex)
4079     {
4080         logDebugMessage(2,"Player %s [%d] shot playerid mismatch\n", shooter.getCallSign(),
4081                         playerIndex);
4082         return;
4083     }
4084 
4085     // make sure the shooter flag is a valid index to prevent segfaulting later
4086     if (!shooter.haveFlag())
4087     {
4088         firingInfo.flagType = Flags::Null;
4089         repack = true;
4090     }
4091 
4092     float shotSpeed = BZDB.eval(StateDatabase::BZDB_SHOTSPEED);
4093     FlagInfo &fInfo = *FlagInfo::get(shooter.getFlag());
4094     // verify player flag
4095     if ((firingInfo.flagType != Flags::Null)
4096             && (firingInfo.flagType != fInfo.flag.type))
4097     {
4098         std::string fireFlag = "unknown";
4099         std::string holdFlag = "unknown";
4100         if (firingInfo.flagType)
4101             fireFlag = firingInfo.flagType->flagAbbv;
4102         if (fInfo.flag.type)
4103         {
4104             if (fInfo.flag.type == Flags::Null)
4105                 holdFlag = "none";
4106             else
4107                 holdFlag = fInfo.flag.type->flagAbbv;
4108         }
4109 
4110         // probably a cheater using wrong shots.. exception for thief since they steal someone elses
4111         if (firingInfo.flagType != Flags::Thief && checkShotMismatch)
4112         {
4113             // bye bye supposed cheater
4114             logDebugMessage(1,"Kicking Player %s [%d] Player using wrong shots\n", shooter.getCallSign(), playerIndex);
4115             sendMessage(ServerPlayer, playerIndex, "Autokick: Your shots do not to match the expected shot type.");
4116             removePlayer(playerIndex, "Player shot mismatch");
4117         }
4118 
4119         logDebugMessage(2,"Player %s [%d] shot flag mismatch %s %s\n", shooter.getCallSign(),
4120                         playerIndex, fireFlag.c_str(), holdFlag.c_str());
4121         return;
4122     }
4123 
4124     if (shooter.haveFlag())
4125         firingInfo.flagType = fInfo.flag.type;
4126     else
4127         firingInfo.flagType = Flags::Null;
4128 
4129     if (!playerData->addShot(shot.id & 0xff, shot.id >> 8, firingInfo))
4130         return;
4131 
4132     const float maxTankSpeed  = BZDBCache::tankSpeed;
4133     const float tankSpeedMult = BZDB.eval(StateDatabase::BZDB_VELOCITYAD);
4134     float tankSpeed       = maxTankSpeed;
4135     float lifetime        = BZDB.eval(StateDatabase::BZDB_RELOADTIME);
4136     if (handicapAllowed())
4137     {
4138         tankSpeed *= BZDB.eval(StateDatabase::BZDB_HANDICAPVELAD);
4139         shotSpeed *= BZDB.eval(StateDatabase::BZDB_HANDICAPSHOTAD);
4140     }
4141     if (firingInfo.flagType == Flags::ShockWave)
4142     {
4143         shotSpeed = 0.0f;
4144         tankSpeed = 0.0f;
4145     }
4146     else if (firingInfo.flagType == Flags::Velocity)
4147         tankSpeed *= tankSpeedMult;
4148     else if (firingInfo.flagType == Flags::Thief)
4149         tankSpeed *= BZDB.eval(StateDatabase::BZDB_THIEFVELAD);
4150     else if ((firingInfo.flagType == Flags::Burrow)
4151              && (firingInfo.shot.pos[2] < BZDB.eval(StateDatabase::BZDB_MUZZLEHEIGHT)))
4152         tankSpeed *= BZDB.eval(StateDatabase::BZDB_BURROWSPEEDAD);
4153     else if (firingInfo.flagType == Flags::Agility)
4154         tankSpeed *= BZDB.eval(StateDatabase::BZDB_AGILITYADVEL);
4155     else
4156     {
4157         //If shot is different height than player, can't be sure they didn't drop V in air
4158         if (playerData->lastState.pos[2]
4159                 != (shot.pos[2]-BZDB.eval(StateDatabase::BZDB_MUZZLEHEIGHT)))
4160             tankSpeed *= tankSpeedMult;
4161     }
4162 
4163     // FIXME, we should look at the actual TankSpeed ;-)
4164     shotSpeed += tankSpeed;
4165 
4166     // verify lifetime
4167     if (fabs(firingInfo.lifetime - lifetime) > Epsilon)
4168     {
4169         logDebugMessage(2,"Player %s [%d] shot lifetime mismatch %f %f\n",
4170                         shooter.getCallSign(),
4171                         playerIndex, firingInfo.lifetime, lifetime);
4172         return;
4173     }
4174 
4175     if (doSpeedChecks)
4176     {
4177         // verify velocity
4178         if (hypotf(shot.vel[0], hypotf(shot.vel[1], shot.vel[2])) > shotSpeed * 1.01f)
4179         {
4180             logDebugMessage(2,"Player %s [%d] shot over speed %f %f\n", shooter.getCallSign(),
4181                             playerIndex, hypotf(shot.vel[0], hypotf(shot.vel[1], shot.vel[2])),
4182                             shotSpeed);
4183             return;
4184         }
4185 
4186         // verify position
4187         float muzzleFront = BZDB.eval(StateDatabase::BZDB_MUZZLEFRONT);
4188         float muzzleHeight = BZDB.eval(StateDatabase::BZDB_MUZZLEHEIGHT);
4189         if (firingInfo.flagType == Flags::Obesity)
4190             muzzleFront *= BZDB.eval(StateDatabase::BZDB_OBESEFACTOR);
4191         const PlayerState &last = playerData->lastState;
4192         float dx = last.pos[0] - shot.pos[0];
4193         float dy = last.pos[1] - shot.pos[1];
4194         float dz = last.pos[2] + muzzleHeight - shot.pos[2];
4195 
4196         // ignore z error for falling tanks
4197         if (last.status & PlayerState::Falling)
4198             dz = 0.0f;
4199         float delta = dx*dx + dy*dy + dz*dz;
4200         if (delta > (maxTankSpeed * tankSpeedMult + 2.0f * muzzleFront) *
4201                 (maxTankSpeed * tankSpeedMult + 2.0f * muzzleFront))
4202         {
4203             logDebugMessage(2,"Player %s [%d] shot origination %f %f %f too far from tank %f %f %f: distance=%f\n",
4204                             shooter.getCallSign(), playerIndex,
4205                             shot.pos[0], shot.pos[1], shot.pos[2],
4206                             last.pos[0], last.pos[1], last.pos[2], sqrt(delta));
4207             return;
4208         }
4209     }
4210 
4211     // ask the API if it wants to modify this shot
4212     bz_ShotFiredEventData_V1 shotEvent;
4213 
4214     shotEvent.pos[0] = shot.pos[0];
4215     shotEvent.pos[1] = shot.pos[1];
4216     shotEvent.pos[2] = shot.pos[2];
4217 
4218     shotEvent.vel[0] = shot.vel[0];
4219     shotEvent.vel[1] = shot.vel[1];
4220     shotEvent.vel[2] = shot.vel[2];
4221 
4222     shotEvent.playerID = shooter.getPlayerIndex();
4223 
4224     shotEvent.shotID = firingInfo.shot.id;
4225 
4226     shotEvent.type = firingInfo.flagType->flagAbbv;
4227 
4228     worldEventManager.callEvents(bz_eShotFiredEvent,&shotEvent);
4229 
4230     if (shotEvent.changed)
4231     {
4232         if (shotEvent.type == "DELETE")
4233             return;
4234         firingInfo.flagType = Flag::getDescFromAbbreviation(shotEvent.type.c_str());
4235         repack = true;
4236     }
4237 
4238     // repack if changed
4239     if (repack)
4240     {
4241         void *bufStart = getDirectMessageBuffer();
4242         firingInfo.pack(bufStart);
4243         buf = bufStart;
4244     }
4245 
4246 
4247     // if shooter has a flag
4248 
4249     char message[MessageLen];
4250     if (shooter.haveFlag())
4251     {
4252 
4253         fInfo.numShots++; // increase the # shots fired
4254 
4255         int limit = clOptions->flagLimit[fInfo.flag.type];
4256         if (limit != -1)   // if there is a limit for players flag
4257         {
4258             int shotsLeft = limit -  fInfo.numShots;
4259             if (shotsLeft > 0)   //still have some shots left
4260             {
4261                 // give message each shot below 5, each 5th shot & at start
4262                 if (shotsLeft % 5 == 0 || shotsLeft <= 3 || shotsLeft == limit-1)
4263                 {
4264                     if (shotsLeft > 1)
4265                         snprintf(message, MessageLen, "%d shots left",shotsLeft);
4266                     else
4267                         strcpy(message,"1 shot left");
4268                     sendMessage(ServerPlayer, playerIndex, message);
4269                 }
4270             }
4271             else     // no shots left
4272             {
4273                 if (shotsLeft == 0 || (limit == 0 && shotsLeft < 0))
4274                 {
4275                     // drop flag at last known position of player
4276                     // also handle case where limit was set to 0
4277                     float lastPos [3];
4278                     for (int i = 0; i < 3; i ++)
4279                         lastPos[i] = playerData->lastState.pos[i];
4280                     fInfo.grabs = 0; // recycle this flag now
4281                     dropFlag(fInfo, lastPos);
4282                 }
4283                 else     // more shots fired than allowed
4284                 {
4285                     // do nothing for now -- could return and not allow shot
4286                 }
4287             } // end no shots left
4288         } // end is limit
4289     } // end of player has flag
4290 
4291     if (firingInfo.flagType == Flags::GuidedMissile)
4292         playerData->player.endShotCredit--;
4293 
4294     ShotManager.AddShot(firingInfo,playerData->getIndex());
4295 
4296     broadcastMessage(MsgShotBegin, len, buf);
4297 
4298 }
4299 
shotEnded(const PlayerId & id,int16_t shotIndex,uint16_t reason)4300 static void shotEnded(const PlayerId& id, int16_t shotIndex, uint16_t reason)
4301 {
4302     GameKeeper::Player *playerData = GameKeeper::Player::getPlayerByIndex(id);
4303 
4304     if (!playerData && id != ServerPlayer)
4305         return;
4306 
4307     if (id != ServerPlayer)
4308         playerData->removeShot(shotIndex & 0xff, shotIndex >> 8);
4309 
4310     ShotManager.RemoveShot(ShotManager.FindShotGUID(id,shotIndex & 0xff));
4311 
4312     // shot has ended prematurely -- send MsgShotEnd
4313     void *buf, *bufStart = getDirectMessageBuffer();
4314     buf = nboPackUByte(bufStart, id);
4315     buf = nboPackShort(buf, shotIndex);
4316     buf = nboPackUShort(buf, reason);
4317     broadcastMessage(MsgShotEnd, (char*)buf-(char*)bufStart, bufStart);
4318 
4319     bz_ShotEndedEventData_V1 shotEvent;
4320     shotEvent.playerID = (int)id;
4321     shotEvent.shotID = shotIndex;
4322     shotEvent.explode = reason == 0;
4323     worldEventManager.callEvents(bz_eShotEndedEvent,&shotEvent);
4324 }
4325 
sendTeleport(int playerIndex,uint16_t from,uint16_t to)4326 static void sendTeleport(int playerIndex, uint16_t from, uint16_t to)
4327 {
4328     void *buf, *bufStart = getDirectMessageBuffer();
4329     buf = nboPackUByte(bufStart, playerIndex);
4330     buf = nboPackUShort(buf, from);
4331     buf = nboPackUShort(buf, to);
4332     broadcastMessage(MsgTeleport, (char*)buf-(char*)bufStart, bufStart);
4333 }
4334 
4335 
4336 /* Players who are paused or have never spawned and observers should not be
4337  * sending updates. Don't bother to kick observers who try and fail to cheat.
4338  */
invalidPlayerAction(PlayerInfo & p,int t,const char * action)4339 static bool invalidPlayerAction(PlayerInfo &p, int t, const char *action)
4340 {
4341     const char *state = NULL;
4342     if (p.isObserver())
4343         state = "as an observer";
4344     else if (p.isPaused())
4345     {
4346         if (strcmp(action, "die") != 0) // allow self destruct while paused
4347             state = "while paused";
4348     }
4349     else if (p.hasNeverSpawned())
4350         state = "before spawning";
4351     if (state)
4352     {
4353         logDebugMessage(1,"Player %s [%d] tried to %s %s\n", p.getCallSign(), t, action, state);
4354         char buffer[MessageLen];
4355         snprintf(buffer, MessageLen, "Autokick: Looks like you tried to %s %s.", action, state);
4356         sendMessage(ServerPlayer, t, buffer);
4357         snprintf(buffer, MessageLen, "Invalid attempt to %s %s", action, state);
4358         removePlayer(t, buffer);
4359         return true;
4360     }
4361     return false;
4362 }
4363 
4364 
lagKick(int playerIndex)4365 static void lagKick(int playerIndex)
4366 {
4367     char message[MessageLen];
4368     snprintf(message, MessageLen,
4369              "You have been kicked due to excessive lag (you have been warned %d times).",
4370              clOptions->maxlagwarn);
4371     GameKeeper::Player *playerData = GameKeeper::Player::getPlayerByIndex(playerIndex);
4372     if (playerData != NULL)
4373     {
4374         sendMessage(ServerPlayer, playerIndex, message);
4375         snprintf(message, MessageLen,"Lagkick: %s", playerData->player.getCallSign());
4376         sendMessage(ServerPlayer, AdminPlayers, message);
4377         removePlayer(playerIndex, "lag");
4378     }
4379 }
4380 
jitterKick(int playerIndex)4381 static void jitterKick(int playerIndex)
4382 {
4383     char message[MessageLen];
4384     snprintf(message, MessageLen,
4385              "You have been kicked due to excessive jitter"
4386              " (you have been warned %d times).",
4387              clOptions->maxjitterwarn);
4388     GameKeeper::Player *playerData
4389         = GameKeeper::Player::getPlayerByIndex(playerIndex);
4390     if (playerData != NULL)
4391     {
4392         sendMessage(ServerPlayer, playerIndex, message);
4393         snprintf(message, MessageLen,"Jitterkick: %s",
4394                  playerData->player.getCallSign());
4395         sendMessage(ServerPlayer, AdminPlayers, message);
4396         removePlayer(playerIndex, "jitter", true);
4397     }
4398 }
4399 
packetLossKick(int playerIndex)4400 void packetLossKick(int playerIndex)
4401 {
4402     char message[MessageLen];
4403     snprintf(message, MessageLen,
4404              "You have been kicked due to excessive packetloss (you have been warned %d times).",
4405              clOptions->maxpacketlosswarn);
4406     GameKeeper::Player *playerData
4407         = GameKeeper::Player::getPlayerByIndex(playerIndex);
4408     if (playerData != NULL)
4409     {
4410         sendMessage(ServerPlayer, playerIndex, message);
4411         snprintf(message, MessageLen,"Packetlosskick: %s",
4412                  playerData->player.getCallSign());
4413         sendMessage(ServerPlayer, AdminPlayers, message);
4414         removePlayer(playerIndex, "packetloss", true);
4415     }
4416 }
4417 
adjustTolerances()4418 static void adjustTolerances()
4419 {
4420     // check for handicap adjustment
4421     if (handicapAllowed())
4422     {
4423         const float speedAdj = BZDB.eval(StateDatabase::BZDB_HANDICAPVELAD);
4424         speedTolerance *= speedAdj * speedAdj;
4425     }
4426 
4427     // check for physics driver disabling
4428     disableHeightChecks = BZDB.isTrue("_disableHeightChecks");
4429     bool disableSpeedChecks = BZDB.isTrue("_disableSpeedChecks");
4430     int i = 0;
4431     const PhysicsDriver* phydrv = PHYDRVMGR.getDriver(i);
4432     while (phydrv)
4433     {
4434         const float* v = phydrv->getLinearVel();
4435         const float av = phydrv->getAngularVel();
4436         if (!phydrv->getIsDeath())
4437         {
4438             if (!phydrv->getIsSlide() &&
4439                     ((v[0] != 0.0f) || (v[1] != 0.0f) || (av != 0.0f)))
4440                 disableSpeedChecks = true;
4441             if (v[2] > 0.0f)
4442                 disableHeightChecks = true;
4443         }
4444         i++;
4445         phydrv = PHYDRVMGR.getDriver(i);
4446     }
4447 
4448 
4449     if (disableSpeedChecks)
4450     {
4451         doSpeedChecks = false;
4452         speedTolerance = MAXFLOAT;
4453         logDebugMessage(1,"Warning: disabling speed checking due to physics drivers\n");
4454     }
4455     if (disableHeightChecks)
4456         logDebugMessage(1,"Warning: disabling height checking due to physics drivers\n");
4457 
4458     return;
4459 }
4460 
4461 
isSpamOrGarbage(char * message,GameKeeper::Player * playerData,int t)4462 bool isSpamOrGarbage(char* message, GameKeeper::Player* playerData, int t)
4463 {
4464     // Shortcut to the player info
4465     PlayerInfo &player = playerData->player;
4466 
4467     // Grab the length of the raw message
4468     const int totalChars = strlen(message);
4469 
4470     // Count visible and bad characters
4471     int badChars = 0;
4472     int visibleChars = 0;
4473     for (int i=0; i < totalChars; i++)
4474     {
4475         // Is it a visible character?
4476         if (TextUtils::isVisible(message[i]))
4477             visibleChars++;
4478         // Not visible? Then is it something other than a space?
4479         else if (message[i] != 32)
4480             badChars++;
4481     }
4482 
4483     // Kick the player if any bad characters are found
4484     if (badChars > 0)
4485     {
4486         sendMessage(ServerPlayer, t, "You were kicked because of a garbage message.");
4487         logDebugMessage(2,"Kicking player %s [%d] for sending a garbage message: %d disallowed chars\n",
4488                         player.getCallSign(), t, badChars);
4489         removePlayer(t, "garbage");
4490 
4491         // Ignore garbage message
4492         return true;
4493     }
4494 
4495     // Ignore message if there are no visible characters
4496     if (visibleChars == 0)
4497         return true;
4498 
4499     // Get last message and last message time
4500     const std::string &oldMsg = player.getLastMsg();
4501     float dt = (float)(TimeKeeper::getCurrent() - player.getLastMsgTime());
4502 
4503     // Ignore whitespace
4504     std::string newMsg = TextUtils::no_whitespace(message);
4505 
4506     // if it's first message, or enough time since last message - can't
4507     // be spam yet
4508     if (oldMsg.length() > 0 && dt < clOptions->msgTimer)
4509     {
4510         // might be spam, start doing comparisons
4511         // does it match the last message? (disregarding whitespace and case)
4512         if (TextUtils::compare_nocase(newMsg, oldMsg) == 0)
4513         {
4514             player.incSpamWarns();
4515             sendMessage(ServerPlayer, t, "***Server Warning: Please do not spam.");
4516 
4517             // has this player already had his share of warnings?
4518             if (player.getSpamWarns() > clOptions->spamWarnMax
4519                     || clOptions->spamWarnMax == 0)
4520             {
4521                 sendMessage(ServerPlayer, t, "You were kicked because of spamming.");
4522                 logDebugMessage(2,"Kicking player %s [%d] for spamming too much: "
4523                                 "2 messages sent within %fs after %d warnings\n",
4524                                 player.getCallSign(), t, dt, player.getSpamWarns());
4525                 removePlayer(t, "spam");
4526                 return true;
4527             }
4528         }
4529     }
4530 
4531     // record this message for next time
4532     player.setLastMsg(newMsg);
4533     return false;
4534 }
4535 
squareAndAdd(float v1,float v2)4536 float squareAndAdd ( float v1, float v2 )
4537 {
4538     return v1*v1+v2*v2;
4539 }
4540 
isTeleporterMotion(GameKeeper::Player & playerData,PlayerState & state,float maxSquaredMovement,float realDist2)4541 static bool isTeleporterMotion ( GameKeeper::Player &playerData, PlayerState &state, float maxSquaredMovement,
4542                                  float realDist2 )
4543 {
4544     const ObstacleList &teleporterList = OBSTACLEMGR.getTeles();
4545     unsigned int i;
4546     unsigned int maxTele = teleporterList.size();
4547     float finalDist2   = realDist2;
4548     float initialDist2   = realDist2;
4549 
4550     // find the porter they WERE close to, and the porter they ARE close too
4551     // and save off the distances from each.
4552     for (i = 0; i < maxTele; i++)
4553     {
4554         Obstacle *currentTele = teleporterList[i];
4555         const float *telePosition = currentTele->getPosition();
4556         float deltaInitial2 = squareAndAdd(state.pos[0] - telePosition[0],state.pos[1] - telePosition[1]);
4557         float deltaFinal2 = squareAndAdd(playerData.lastState.pos[0] - telePosition[0],
4558                                          playerData.lastState.pos[1] - telePosition[1]);
4559 
4560         if (deltaInitial2 < initialDist2)
4561             initialDist2 = deltaInitial2;
4562 
4563         if (deltaFinal2 < finalDist2)
4564             finalDist2 = deltaFinal2;
4565     }
4566 
4567     // if the distance from where you were, to your probable entry porter
4568     // plus the distance from where you are to your probable exit porter
4569     // is greater then the max move, your a cheater.
4570     if (sqrt(initialDist2) + sqrt(finalDist2) <= sqrt(maxSquaredMovement))
4571         return false;
4572 
4573     return true;
4574 }
4575 
4576 // check the distance to see if they went WAY too far
isCheatingMovement(GameKeeper::Player & playerData,PlayerState & state,float maxMovement,int t)4577 static bool isCheatingMovement(GameKeeper::Player &playerData, PlayerState &state, float maxMovement, int t)
4578 {
4579     // easy out, if we arn't doing distance checks.
4580     if (!BZDB.isTrue("_enableDistanceCheck"))
4581         return false;
4582 
4583     float movementDelta[2];
4584     movementDelta[0] = state.pos[0] - playerData.lastState.pos[0];
4585     movementDelta[1] = state.pos[1] - playerData.lastState.pos[1];
4586 
4587     float realDist2 = squareAndAdd(movementDelta[0], movementDelta[1]);
4588     logDebugMessage(4,"isCheatingMovement: dist %f, maxDist %f\n",sqrt(realDist2),maxMovement);
4589 
4590     // if the movement is less then the max, they are cool
4591     if (realDist2 <= (maxMovement*maxMovement))
4592         return false;
4593 
4594     bool kickem = !isTeleporterMotion(playerData, state, maxMovement * maxMovement, realDist2);
4595 
4596     if (kickem)
4597         logDebugMessage(1,"Kicking Player %s [%d] too large movement (tank: %f, allowed: %f)\n",
4598                         playerData.player.getCallSign(),t,sqrt(realDist2),maxMovement);
4599     return kickem;
4600 }
4601 
handleCommand(int t,void * rawbuf,bool udp)4602 static void handleCommand(int t, void *rawbuf, bool udp)
4603 {
4604     if (!rawbuf)
4605     {
4606         std::cerr << "WARNING: handleCommand got a null rawbuf?!" << std::endl;
4607         return;
4608     }
4609 
4610     GameKeeper::Player *playerData = GameKeeper::Player::getPlayerByIndex(t);
4611     if (!playerData)
4612         return;
4613     NetHandler *handler = playerData->netHandler;
4614 
4615     uint16_t len, code;
4616     const void *buf = rawbuf;
4617     buf = nboUnpackUShort(buf, len);
4618     buf = nboUnpackUShort(buf, code);
4619     char buffer[MessageLen];
4620 
4621     if (udp)
4622     {
4623         switch (code)
4624         {
4625         case MsgShotBegin:
4626         case MsgShotEnd:
4627         case MsgPlayerUpdate:
4628         case MsgPlayerUpdateSmall:
4629         case MsgGMUpdate:
4630         case MsgUDPLinkRequest:
4631         case MsgUDPLinkEstablished:
4632             break;
4633         default:
4634             logDebugMessage(1,"Player [%d] sent packet type (%x) via udp, "
4635                             "possible attack from %s\n",
4636                             t, code, handler->getTargetIP());
4637             return;
4638         }
4639     }
4640 
4641     if (!playerData->player.isCompletelyAdded())
4642     {
4643         switch (code)
4644         {
4645         case MsgEnter:
4646         case MsgQueryGame:
4647         case MsgQueryPlayers:
4648         case MsgWantWHash:
4649         case MsgNegotiateFlags:
4650         case MsgGetWorld:
4651         case MsgUDPLinkRequest:
4652         case MsgUDPLinkEstablished:
4653         case MsgWantSettings:
4654         case MsgExit:
4655         case MsgAlive:
4656         case MsgAutoPilot:
4657             break;
4658 
4659         case MsgMessage:
4660         case MsgPlayerUpdateSmall:
4661             // FIXME: this is a workaround for a protocol problem
4662             logDebugMessage(4,"Ignoring premature message 0x%4hx from host %s\n",code,handler->getTargetIP());
4663             return;
4664 
4665         default:
4666             logDebugMessage(1,"Host %s tried to send invalid message before Enter; 0x%4hx\n",handler->getTargetIP(),code);
4667             rejectPlayer(t, RejectBadRequest, "invalid request");
4668             return;
4669         }
4670     }
4671 
4672     if (!playerData->hadEnter)
4673     {
4674         switch (code)
4675         {
4676         case MsgExit:
4677         case MsgAlive:
4678         case MsgAutoPilot:
4679         case MsgMessage:
4680         case MsgPlayerUpdateSmall:
4681             logDebugMessage(1, "Host %s tried to send invalid message before Enter; 0x%4hx\n", handler->getTargetIP(), code);
4682             rejectPlayer(t, RejectBadRequest, "invalid request");
4683             return;
4684         }
4685     }
4686 
4687     bz_MsgDebugEventData_V1 debugEventData;
4688     debugEventData.code[0] = ((char*)&code)[0];
4689     debugEventData.code[1] = ((char*)&code)[1];
4690     debugEventData.len = (size_t)len;
4691     debugEventData.msg = (unsigned char*)rawbuf + 2 * sizeof(uint16_t);
4692     debugEventData.playerID = playerData->getIndex();
4693 
4694     worldEventManager.callEvents(&debugEventData);
4695 
4696     switch (code)
4697     {
4698     // player joining
4699     case MsgEnter:
4700     {
4701         // a previous MsgEnter will have set the name a few lines down from here
4702         if (!playerData->accessInfo.getName().empty() && playerData->hasEntered)
4703         {
4704             logDebugMessage(1,"Player %s [%d] sent another MsgEnter\n",
4705                             playerData->player.getCallSign(), t);
4706             rejectPlayer(t, RejectBadRequest, "invalid request");
4707             break;
4708         }
4709         uint16_t rejectCode;
4710         char     rejectMsg[MessageLen];
4711         if (!playerData->player.unpackEnter(buf, rejectCode, rejectMsg))
4712         {
4713             rejectPlayer(t, rejectCode, rejectMsg);
4714             break;
4715         }
4716         playerData->hasEntered = true;
4717         playerData->accessInfo.setName(playerData->player.getCallSign());
4718         std::string timeStamp = TimeKeeper::timestamp();
4719         logDebugMessage(1,"Player %s [%d] has joined from %s at %s with token \"%s\"\n",
4720                         playerData->player.getCallSign(),
4721                         t, handler->getTargetIP(), timeStamp.c_str(),
4722                         playerData->player.getToken());
4723 
4724         if (!clOptions->publicizeServer)
4725             playerData->_LSAState = GameKeeper::Player::notRequired;
4726         else if (strlen(playerData->player.getCallSign()))
4727             playerData->_LSAState = GameKeeper::Player::required;
4728         playerData->hadEnter = true;
4729         dontWait = true;
4730         break;
4731     }
4732 
4733     // player closing connection
4734     case MsgExit:
4735         // data: <none>
4736         removePlayer(t, "left", false);
4737         break;
4738 
4739     case MsgNegotiateFlags:
4740     {
4741         FlagTypeMap::iterator it;
4742         FlagSet::iterator m_it;
4743         FlagOptionMap hasFlag;
4744         FlagSet missingFlags;
4745         unsigned short numClientFlags = len/2;
4746 
4747         /* Unpack incoming message containing the list of flags our client supports */
4748         for (int i = 0; i < numClientFlags; i++)
4749         {
4750             FlagType *fDesc;
4751             buf = FlagType::unpack(buf, fDesc);
4752             if (fDesc != Flags::Null)
4753                 hasFlag[fDesc] = true;
4754         }
4755 
4756         /* Compare them to the flags this game might need, generating a list of missing flags */
4757         for (it = FlagType::getFlagMap().begin();
4758                 it != FlagType::getFlagMap().end(); ++it)
4759         {
4760             if (!hasFlag[it->second])
4761             {
4762                 if (clOptions->flagCount[it->second] > 0)
4763                     missingFlags.insert(it->second);
4764                 if ((clOptions->numExtraFlags > 0) && !clOptions->flagDisallowed[it->second])
4765                     missingFlags.insert(it->second);
4766             }
4767         }
4768 
4769         /* Pack a message with the list of missing flags */
4770         char *bufStart = getDirectMessageBuffer();
4771         void *tmpbuf = bufStart;
4772         for (m_it = missingFlags.begin(); m_it != missingFlags.end(); ++m_it)
4773         {
4774             if ((*m_it) != Flags::Null)
4775             {
4776                 if ((*m_it)->custom)
4777                 {
4778                     // custom flag, tell the client about it
4779                     static char cfbuffer[MaxPacketLen];
4780                     char *cfbufStart = &cfbuffer[0] + 2 * sizeof(uint16_t);
4781                     char *cfbuf = cfbufStart;
4782                     cfbuf = (char*)(*m_it)->packCustom(cfbuf);
4783                     directMessage(t, MsgFlagType, cfbuf-cfbufStart, cfbufStart);
4784                 }
4785                 else
4786                 {
4787                     // they should already know about this one, dump it back to them
4788                     tmpbuf = (*m_it)->pack(tmpbuf);
4789                 }
4790             }
4791         }
4792         directMessage(t, MsgNegotiateFlags, (char*)tmpbuf-bufStart, bufStart);
4793         break;
4794     }
4795 
4796 
4797 
4798     // player wants more of world database
4799     case MsgGetWorld:
4800     {
4801         // data: count (bytes read so far)
4802         uint32_t ptr;
4803         buf = nboUnpackUInt(buf, ptr);
4804         sendWorld(t, ptr);
4805         break;
4806     }
4807 
4808     case MsgWantSettings:
4809     {
4810         sendGameSettings(t);
4811         break;
4812     }
4813 
4814     case MsgWantWHash:
4815     {
4816         void *obuf, *obufStart = getDirectMessageBuffer();
4817         if (clOptions->cacheURL.size() > 0)
4818         {
4819             obuf = nboPackString(obufStart, clOptions->cacheURL.c_str(),
4820                                  clOptions->cacheURL.size() + 1);
4821             directMessage(t, MsgCacheURL, (char*)obuf-(char*)obufStart, obufStart);
4822         }
4823         obuf = nboPackString(obufStart, hexDigest.c_str(), hexDigest.size() + 1);
4824         directMessage(t, MsgWantWHash, (char*)obuf-(char*)obufStart, obufStart);
4825         break;
4826     }
4827 
4828     case MsgQueryGame:
4829         sendQueryGame(t);
4830         break;
4831 
4832     case MsgQueryPlayers:
4833         sendQueryPlayers(t);
4834         break;
4835 
4836     // player is coming alive
4837     case MsgAlive:
4838     {
4839         // player is on the waiting list
4840         float waitTime = rejoinList.waitTime(t);
4841         if (waitTime > 0.0f)
4842         {
4843             snprintf (buffer, MessageLen, "You are unable to begin playing for %.1f seconds.", waitTime);
4844             sendMessage(ServerPlayer, t, buffer);
4845 
4846             // Make them wait for trying to rejoin quickly
4847             playerData->player.setSpawnDelay((double)waitTime);
4848             playerData->player.queueSpawn();
4849 
4850             break;
4851         }
4852 
4853         // player moved before countdown started
4854         if (clOptions->timeLimit>0.0f && !countdownActive)
4855             playerData->player.setPlayedEarly();
4856         playerData->player.queueSpawn();
4857         break;
4858     }
4859 
4860     // player declaring self destroyed
4861     case MsgKilled:
4862     {
4863         if (invalidPlayerAction(playerData->player, t, "die"))
4864             break;
4865         // data: id of killer, shot id of killer
4866         PlayerId killer;
4867         FlagType* flagType;
4868         int16_t shot, reason;
4869         int phydrv = -1;
4870         buf = nboUnpackUByte(buf, killer);
4871         buf = nboUnpackShort(buf, reason);
4872         buf = nboUnpackShort(buf, shot);
4873         buf = FlagType::unpack(buf, flagType);
4874         if (reason == PhysicsDriverDeath)
4875         {
4876             int32_t inPhyDrv;
4877             buf = nboUnpackInt(buf, inPhyDrv);
4878             phydrv = int(inPhyDrv);
4879         }
4880 
4881         // Sanity check on shot: Here we have the killer
4882         if (killer != ServerPlayer)
4883         {
4884             int si = (shot == -1 ? -1 : shot & 0x00FF);
4885             if ((si < -1) || (si >= clOptions->maxShots))
4886                 break;
4887         }
4888         playerData->player.endShotCredit--;
4889         playerKilled(t, lookupPlayer(killer), reason, shot, flagType, phydrv);
4890 
4891         break;
4892     }
4893 
4894     // player requesting to grab flag
4895     case MsgGrabFlag:
4896     {
4897         // data: flag index
4898         uint16_t flag;
4899 
4900         if (invalidPlayerAction(playerData->player, t, "grab a flag"))
4901             break;
4902 
4903         buf = nboUnpackUShort(buf, flag);
4904         // Sanity check
4905         if (flag < numFlags)
4906             grabFlag(t, *FlagInfo::get(flag), true);
4907         break;
4908     }
4909 
4910     // player requesting to drop flag
4911     case MsgDropFlag:
4912     {
4913         // data: position of drop
4914         float pos[3];
4915         buf = nboUnpackVector(buf, pos);
4916         dropPlayerFlag(*playerData, pos);
4917         break;
4918     }
4919 
4920     // player has captured a flag
4921     case MsgCaptureFlag:
4922     {
4923         // data: team whose territory flag was brought to
4924         uint16_t _team;
4925 
4926         if (invalidPlayerAction(playerData->player, t, "capture a flag"))
4927             break;
4928 
4929         buf = nboUnpackUShort(buf, _team);
4930         captureFlag(t, TeamColor(_team));
4931         break;
4932     }
4933 
4934     // shot fired
4935     case MsgShotBegin:
4936         if (invalidPlayerAction(playerData->player, t, "shoot"))
4937             break;
4938 
4939         // Sanity check
4940         if (len == FiringInfoPLen)
4941             shotFired(t, const_cast<void *>(buf), int(len));
4942         break;
4943 
4944     // shot ended prematurely
4945     case MsgShotEnd:
4946     {
4947         if (invalidPlayerAction(playerData->player, t, "end a shot"))
4948             break;
4949 
4950         // endShot anti-cheat
4951         playerData->player.endShotCredit++;
4952 
4953         int pFlag = playerData->player.getFlag();
4954         if (pFlag >= 0)
4955         {
4956             FlagInfo &flag = *FlagInfo::get(pFlag);
4957             if (flag.flag.type == Flags::Shield)
4958                 playerData->player.endShotShieldCredit = 1;
4959         }
4960 
4961         const int endShotLimit =  (int) BZDB.eval(StateDatabase::BZDB_ENDSHOTDETECTION);
4962         if ((BZDB.isTrue(StateDatabase::BZDB_ENDSHOTDETECTION) && endShotLimit > 0) &&
4963                 (playerData->player.endShotCredit > endShotLimit))    // default endShotLimit 2
4964         {
4965             char testmessage[MessageLen];
4966             snprintf(testmessage, MessageLen, "Kicking Player %s EndShot credit: %d \n",
4967                      playerData->player.getCallSign(), playerData->player.endShotCredit );
4968             logDebugMessage(1,"endShot Detection: %s\n", testmessage);
4969             sendMessage(ServerPlayer, AdminPlayers, testmessage);
4970             sendMessage(ServerPlayer, t, "Autokick: wrong end shots detected.");
4971             removePlayer(t, "EndShot");
4972         }
4973         // endShotDetection finished
4974 
4975         // data: shooter id, shot number, reason
4976         PlayerId sourcePlayer;
4977         int16_t shot;
4978         uint16_t reason;
4979         buf = nboUnpackUByte(buf, sourcePlayer);
4980         buf = nboUnpackShort(buf, shot);
4981         buf = nboUnpackUShort(buf, reason);
4982         shotEnded(sourcePlayer, shot, reason);
4983 
4984         break;
4985     }
4986 
4987     // player teleported
4988     case MsgTeleport:
4989     {
4990         uint16_t from, to;
4991 
4992         if (invalidPlayerAction(playerData->player, t, "teleport"))
4993             break;
4994 
4995         buf = nboUnpackUShort(buf, from);
4996         buf = nboUnpackUShort(buf, to);
4997 
4998         // Validate the teleport source and destination
4999         const ObstacleList &teleporterList = OBSTACLEMGR.getTeles();
5000         unsigned int maxTele = teleporterList.size();
5001 
5002         if (from < maxTele * 2 && to < maxTele * 2)
5003             sendTeleport(t, from, to);
5004         else
5005         {
5006             logDebugMessage(2,"Player %s [%d] tried to send invalid teleport (from %u to %u)\n",
5007                             playerData->player.getCallSign(), t, from, to);
5008         }
5009         break;
5010     }
5011 
5012     // player sending a message
5013     case MsgMessage:
5014     {
5015         // data: target player/team, message string
5016         PlayerId dstPlayer;
5017         char message[MessageLen];
5018         buf = nboUnpackUByte(buf, dstPlayer);
5019         buf = nboUnpackString(buf, message, sizeof(message));
5020         message[MessageLen - 1] = '\0';
5021         playerData->player.hasSent();
5022         if (dstPlayer == AllPlayers)
5023         {
5024             logDebugMessage(1,"Player %s [%d] -> All: %s\n", playerData->player.getCallSign(),
5025                             t, message);
5026         }
5027         else if (dstPlayer == AdminPlayers)
5028         {
5029             logDebugMessage(1,"Player %s [%d] -> Admin: %s\n",
5030                             playerData->player.getCallSign(), t, message);
5031         }
5032         else if (dstPlayer > LastRealPlayer)
5033         {
5034             logDebugMessage(1,"Player %s [%d] -> Team: %s\n",
5035                             playerData->player.getCallSign(), t, message);
5036         }
5037         else
5038         {
5039             GameKeeper::Player *p = GameKeeper::Player::getPlayerByIndex(dstPlayer);
5040             if (p != NULL)
5041             {
5042                 logDebugMessage(1,"Player %s [%d] -> Player %s [%d]: %s\n",
5043                                 playerData->player.getCallSign(), t, p->player.getCallSign(), dstPlayer, message);
5044             }
5045             else
5046             {
5047                 logDebugMessage(1,"Player %s [%d] -> Player Unknown [%d]: %s\n",
5048                                 playerData->player.getCallSign(), t, dstPlayer, message);
5049             }
5050         }
5051         // check for spamming or garbage
5052         if (isSpamOrGarbage(message, playerData, t))
5053             break;
5054 
5055         bz_ChatEventData_V2 chatData;
5056         chatData.from = t;
5057         chatData.to = BZ_NULLUSER;
5058 
5059         if (dstPlayer == AllPlayers)
5060             chatData.to = BZ_ALLUSERS;
5061         else if ( dstPlayer == AdminPlayers )
5062             chatData.team = eAdministrators;
5063         else if ( dstPlayer > LastRealPlayer )
5064             chatData.team =convertTeam((TeamColor)(FirstTeam - dstPlayer));
5065         else
5066             chatData.to = dstPlayer;
5067 
5068 
5069         chatData.messageType = eChatMessage;
5070         chatData.message = message;
5071 
5072         // send the actual Message after all the callbacks have done their magic to it.
5073         if (chatData.message.size())
5074             sendPlayerMessage(playerData, dstPlayer, chatData.message.c_str());
5075         break;
5076     }
5077 
5078     // player has transferred flag to another tank
5079     case MsgTransferFlag:
5080     {
5081         PlayerId from, to;
5082 
5083         buf = nboUnpackUByte(buf, from);
5084         if (from != t)
5085         {
5086             logDebugMessage(1,"Kicking Player %s [%d] Player trying to transfer flag\n",
5087                             playerData->player.getCallSign(), t);
5088             removePlayer(t, "flag transfer");
5089             break;
5090         }
5091         buf = nboUnpackUByte(buf, to);
5092 
5093         GameKeeper::Player *fromData = playerData;
5094 
5095         int flagIndex = fromData->player.getFlag();
5096 
5097         FlagInfo* flagInfo = FlagInfo::get(flagIndex);
5098 
5099         if (to == ServerPlayer)
5100         {
5101             if (flagIndex >= 0)
5102                 zapFlag (*flagInfo);
5103             return;
5104         }
5105 
5106         // Sanity check
5107         if (to >= curMaxPlayers)
5108             return;
5109 
5110         if (flagIndex == -1)
5111             return;
5112 
5113         GameKeeper::Player *toData
5114             = GameKeeper::Player::getPlayerByIndex(to);
5115         if (!toData)
5116             return;
5117 
5118         // verify that the player stealing HAS thief
5119         int toPlayersFlag = toData->player.getFlag();
5120         bool cheater = false;
5121         if (!toData->player.haveFlag())
5122             cheater = true;
5123 
5124         FlagInfo *toFlag = FlagInfo::get(toPlayersFlag);
5125         // without flag or with a non thief flag? Then is probably cheating
5126         if (!toFlag || toFlag->flag.type != Flags::Thief)
5127             cheater = true;
5128 
5129         // TODO, check thief radius here
5130 
5131         if (cheater)
5132         {
5133             logDebugMessage(1,"No Thief check %s [%d] Player trying to transfer to target without thief\n",
5134                             playerData->player.getCallSign(), t);
5135             // There is a lot of reason why the player could not have thief,
5136             // network delay is one of this.
5137             // Don't kick then, just discard the message
5138             return;
5139         }
5140 
5141         bz_FlagTransferredEventData_V1 ftEventData;
5142 
5143         ftEventData.fromPlayerID = fromData->player.getPlayerIndex();
5144         ftEventData.toPlayerID = toData->player.getPlayerIndex();
5145         if (flagInfo)
5146             ftEventData.flagType = flagInfo->flag.type->flagAbbv.c_str();
5147         else
5148             ftEventData.flagType = "";
5149         ftEventData.action = ftEventData.ContinueSteal;
5150 
5151         worldEventManager.callEvents(bz_eFlagTransferredEvent,&ftEventData);
5152 
5153         if (ftEventData.action != ftEventData.CancelSteal)
5154         {
5155             int oFlagIndex = toData->player.getFlag();
5156             if (oFlagIndex >= 0)
5157                 zapFlag (*FlagInfo::get(oFlagIndex));
5158         }
5159 
5160         if (ftEventData.action == ftEventData.ContinueSteal)
5161         {
5162             void *obufStart = getDirectMessageBuffer();
5163             void *obuf = nboPackUByte(obufStart, from);
5164             obuf = nboPackUByte(obuf, to);
5165             FlagInfo &flag = *FlagInfo::get(flagIndex);
5166             flag.flag.owner = to;
5167             flag.player = to;
5168             toData->player.resetFlag();
5169             toData->player.setFlag(flagIndex);
5170             fromData->player.resetFlag();
5171             obuf = flag.pack(obuf);
5172             broadcastMessage(MsgTransferFlag, (char*)obuf - (char*)obufStart,
5173                              obufStart);
5174         }
5175         break;
5176     }
5177 
5178     case MsgUDPLinkEstablished:
5179         break;
5180 
5181     case MsgNewRabbit:
5182     {
5183         if (clOptions->gameType == RabbitChase)
5184         {
5185             if (t == rabbitIndex)
5186                 anointNewRabbit();
5187         }
5188         else
5189         {
5190             logDebugMessage(1,"Kicking Player %s [%d] Illegal rabbit\n",
5191                             playerData->player.getCallSign(), t);
5192             sendMessage(ServerPlayer, t, "Autokick: not a rabbit chase game.");
5193             removePlayer(t, "Illegal rabbit");
5194         }
5195         break;
5196     }
5197 
5198     case MsgPause:
5199     {
5200         uint8_t pause;
5201         nboUnpackUByte(buf, pause);
5202         pausePlayer(t, pause != 0);
5203         break;
5204     }
5205 
5206     case MsgAutoPilot:
5207     {
5208         uint8_t autopilot;
5209         nboUnpackUByte(buf, autopilot);
5210 
5211         bz_AutoPilotData_V1 autoPilotData;
5212         autoPilotData.playerID = t;
5213         autoPilotData.enabled = (autopilot != 0);
5214 
5215         autopilotPlayer(t, autoPilotData.enabled);
5216 
5217         worldEventManager.callEvents(bz_eAutoPilotEvent,&autoPilotData);
5218         break;
5219     }
5220 
5221     case MsgLagPing:
5222     {
5223         bool warn, kick, jittwarn, jittkick, plosswarn, plosskick, alagannouncewarn, lagannouncewarn;
5224         playerData->lagInfo.updatePingLag(buf, warn, kick, jittwarn, jittkick, plosswarn, plosskick, alagannouncewarn,
5225                                           lagannouncewarn);
5226         if (warn)
5227         {
5228             char message[MessageLen];
5229             snprintf(message, MessageLen, "*** Server Warning: your lag is too high (%d ms) ***",
5230                      playerData->lagInfo.getLag());
5231             sendMessage(ServerPlayer, t, message);
5232             if (kick)
5233                 lagKick(t);
5234         }
5235         if (alagannouncewarn)
5236         {
5237             char message[MessageLen];
5238             snprintf(message, MessageLen, "*** Server Warning: player %s has too high lag (%d ms) ***",
5239                      playerData->player.getCallSign(), playerData->lagInfo.getLag());
5240             sendMessage(ServerPlayer, AdminPlayers, message);
5241         }
5242         if (lagannouncewarn)
5243         {
5244             char message[MessageLen];
5245             snprintf(message, MessageLen, "*** Server Warning:player %s has too high lag (%d ms) ***",
5246                      playerData->player.getCallSign(), playerData->lagInfo.getLag());
5247             sendMessage(ServerPlayer, AllPlayers, message);
5248         }
5249         if (jittwarn)
5250         {
5251             char message[MessageLen];
5252             snprintf(message, MessageLen,
5253                      "*** Server Warning: your jitter is too high (%d ms) ***",
5254                      playerData->lagInfo.getJitter());
5255             sendMessage(ServerPlayer, t, message);
5256             if (jittkick)
5257                 jitterKick(t);
5258         }
5259         if (plosswarn)
5260         {
5261             char message[MessageLen];
5262             snprintf(message, MessageLen,
5263                      "*** Server Warning: your packetloss is too high (%d%%) ***",
5264                      playerData->lagInfo.getLoss());
5265             sendMessage(ServerPlayer, t, message);
5266             if (plosskick)
5267                 packetLossKick(t);
5268         }
5269 
5270         break;
5271     }
5272 
5273     // player is sending his position/speed (bulk data)
5274     case MsgPlayerUpdate:
5275     case MsgPlayerUpdateSmall:
5276     {
5277         float timestamp;
5278         PlayerId id;
5279         PlayerState state;
5280 
5281         buf = nboUnpackFloat(buf, timestamp);
5282         buf = nboUnpackUByte(buf, id);
5283         buf = state.unpack(buf, code);
5284 
5285         // Verify that player update is actually for this player
5286         // TODO: Remove the player ID from this message so we don't have to check for this.
5287         if (id != t)
5288         {
5289             logDebugMessage(1, "Kicking Player %s [%d] sent player update as player index %d\n",
5290                             playerData->player.getCallSign(), t, id);
5291             sendMessage(ServerPlayer, t, "Autokick: Player sent spoofed player update.");
5292             removePlayer(t, "spoofed update");
5293             break;
5294         }
5295 
5296         bz_PlayerUpdateEventData_V1 puEventData;
5297         playerStateToAPIState(puEventData.lastState,state);
5298 
5299         // observer updates are not relayed
5300         if (playerData->player.isObserver())
5301         {
5302             // skip all of the checks
5303             playerData->setPlayerState(state, timestamp);
5304             break;
5305         }
5306         // tell the API that they moved.
5307 
5308         playerStateToAPIState(puEventData.state,state);
5309         puEventData.stateTime = TimeKeeper::getCurrent().getSeconds();
5310         puEventData.playerID = playerData->getIndex();
5311         worldEventManager.callEvents(bz_ePlayerUpdateEvent,&puEventData);
5312 
5313         // silently drop old packet
5314         if (state.order <= playerData->lastState.order)
5315             break;
5316 
5317         // Don't kick players up to 10 seconds after a world parm has changed,
5318         TimeKeeper now = TimeKeeper::getCurrent();
5319 
5320         if (now - lastWorldParmChange > 10.0f)
5321         {
5322 
5323             float maxHeight = getMaxWorldHeight();
5324 
5325             if (!disableHeightChecks && state.pos[2] > maxHeight)
5326             {
5327                 logDebugMessage(1,"Kicking Player %s [%d] jumped too high [max: %f height: %f]\n",
5328                                 playerData->player.getCallSign(), t, maxHeight, state.pos[2]);
5329                 sendMessage(ServerPlayer, t, "Autokick: Player location was too high.");
5330                 removePlayer(t, "too high");
5331                 break;
5332             }
5333 
5334             bool InBounds = true;
5335             // exploding or dead players can do unpredictable things
5336             if (state.status != PlayerState::Exploding && state.status != PlayerState::DeadStatus)
5337             {
5338                 // make sure the player is still in the map
5339                 // test all the map bounds + some fudge factor, just in case
5340                 static const float positionFudge = 10.0f; /* linear distance */
5341                 float worldSize = BZDBCache::worldSize;
5342                 if ( (state.pos[1] >= worldSize*0.5f + positionFudge) || (state.pos[1] <= -worldSize*0.5f - positionFudge))
5343                 {
5344                     logDebugMessage(2,"Player %s [%d] y position %.2f is out of bounds (%.2f + %.2f)\n",
5345                                     playerData->player.getCallSign(), t, state.pos[1], worldSize * 0.5f, positionFudge);
5346                     InBounds = false;
5347                 }
5348                 else if ( (state.pos[0] >= worldSize*0.5f + positionFudge) || (state.pos[0] <= -worldSize*0.5f - positionFudge))
5349                 {
5350                     logDebugMessage(2,"Player %s [%d] x position %.2f is out of bounds (%.2f + %.2f)\n",
5351                                     playerData->player.getCallSign(), t, state.pos[0], worldSize * 0.5f, positionFudge);
5352                     InBounds = false;
5353                 }
5354             }
5355 
5356             static const float burrowFudge = 1.0f; /* linear distance */
5357             if (state.pos[2]<BZDB.eval(StateDatabase::BZDB_BURROWDEPTH) - burrowFudge)
5358             {
5359                 logDebugMessage(2,"Player %s [%d] z depth %.2f is less than burrow depth (%.2f + %.2f)\n",
5360                                 playerData->player.getCallSign(), t, state.pos[2], BZDB.eval(StateDatabase::BZDB_BURROWDEPTH), burrowFudge);
5361                 InBounds = false;
5362             }
5363 
5364             // kick em cus they are most likely cheating or using a buggy client
5365             if (!InBounds)
5366             {
5367                 logDebugMessage(1,"Kicking Player %s [%d] Out of map bounds at position (%.2f,%.2f,%.2f)\n",
5368                                 playerData->player.getCallSign(), t,
5369                                 state.pos[0], state.pos[1], state.pos[2]);
5370                 sendMessage(ServerPlayer, t, "Autokick: Player location was outside the playing area.");
5371                 removePlayer(t, "Out of map bounds");
5372             }
5373 
5374             // Speed problems occur around flag drops, so don't check for
5375             // a short period of time after player drops a flag. Currently
5376             // 2 second, adjust as needed.
5377             if (playerData->player.isFlagTransitSafe())
5378             {
5379 
5380                 // we'll be checking against the player's flag type
5381                 int pFlag = playerData->player.getFlag();
5382 
5383                 // check for highspeed cheat; if inertia is enabled, skip test for now
5384                 if (clOptions->linearAcceleration == 0.0f)
5385                 {
5386                     // Doesn't account for going fast backwards, or jumping/falling
5387                     float curPlanarSpeedSqr = state.velocity[0]*state.velocity[0] +
5388                                               state.velocity[1]*state.velocity[1];
5389 
5390                     float maxPlanarSpeed = BZDBCache::tankSpeed;
5391 
5392                     bool logOnly = false;
5393                     if (BZDB.isTrue("_speedChecksLogOnly"))
5394                         logOnly = true;
5395 
5396                     // if tank is not driving cannot be sure it didn't toss
5397                     // (V) in flight
5398 
5399                     // if tank is not alive cannot be sure it didn't just toss
5400                     // (V)
5401                     if (pFlag >= 0)
5402                     {
5403                         FlagInfo &flag = *FlagInfo::get(pFlag);
5404                         if (flag.flag.type == Flags::Velocity)
5405                             maxPlanarSpeed *= BZDB.eval(StateDatabase::BZDB_VELOCITYAD);
5406                         else if (flag.flag.type == Flags::Thief)
5407                             maxPlanarSpeed *= BZDB.eval(StateDatabase::BZDB_THIEFVELAD);
5408                         else if (flag.flag.type == Flags::Agility)
5409                             maxPlanarSpeed *= BZDB.eval(StateDatabase::BZDB_AGILITYADVEL);
5410                         else if ((flag.flag.type == Flags::Burrow) &&
5411                                  (playerData->lastState.pos[2] == state.pos[2]) &&
5412                                  (playerData->lastState.velocity[2] == state.velocity[2]) &&
5413                                  (state.pos[2] <= BZDB.eval(StateDatabase::BZDB_BURROWDEPTH)))
5414                             // if we have burrow and are not actively burrowing
5415                             // You may have burrow and still be above ground. Must
5416                             // check z in ground!!
5417                             maxPlanarSpeed *= BZDB.eval(StateDatabase::BZDB_BURROWSPEEDAD);
5418                     }
5419                     float maxPlanarSpeedSqr = maxPlanarSpeed * maxPlanarSpeed;
5420 
5421                     // If player is moving vertically, or not alive the speed checks
5422                     // seem to be problematic. If this happens, just log it for now,
5423                     // but don't actually kick
5424                     // don't kick if the player is paused, because problems if have V
5425                     float smallTol = 0.001f;
5426                     if ((fabs(playerData->lastState.pos[2]-state.pos[2]) > smallTol)
5427                             ||  (fabs(playerData->lastState.velocity[2]-state.velocity[2])> smallTol)
5428                             ||  ((state.status & PlayerState::Alive) == 0)
5429                             ||  (playerData->player.isPaused()))
5430                         logOnly = true;
5431 
5432                     // allow a 10% tolerance level for speed if -speedtol is not sane
5433                     if (doSpeedChecks)
5434                     {
5435                         float realtol = 1.1f;
5436                         if (speedTolerance > 1.0f)
5437                             realtol = speedTolerance;
5438 
5439                         if (realtol < 100.0f )
5440                         {
5441                             maxPlanarSpeedSqr *= realtol;
5442                             maxPlanarSpeed *= realtol;
5443 
5444                             if (curPlanarSpeedSqr > maxPlanarSpeedSqr)
5445                             {
5446                                 if (logOnly)
5447                                 {
5448                                     logDebugMessage(1,"Logging Player %s [%d] tank too fast (tank: %f, allowed: %f){Dead or v[z] != 0}\n",
5449                                                     playerData->player.getCallSign(), t,
5450                                                     sqrt(curPlanarSpeedSqr), sqrt(maxPlanarSpeedSqr));
5451                                 }
5452                                 else
5453                                 {
5454                                     logDebugMessage(1,  "Kicking Player %s [%d] tank too fast (tank: %f, allowed: %f)\n",
5455                                                     playerData->player.getCallSign(), t, sqrt(curPlanarSpeedSqr), sqrt(maxPlanarSpeedSqr));
5456                                     sendMessage(ServerPlayer, t, "Autokick: Player tank is moving too fast.");
5457                                     removePlayer(t, "too fast");
5458                                 }
5459                                 break;
5460                             }
5461 
5462 
5463                             // check the distance to see if they went WAY too far
5464                             // max time since last update
5465                             float timeDelta = (float)now.getSeconds() - playerData->serverTimeStamp;
5466                             if (timeDelta < 0.005f)
5467                                 timeDelta = 0.005f;
5468 
5469                             // the maximum distance they could have moved (assume 0 lag)
5470                             float maxDist = maxPlanarSpeed * timeDelta;
5471                             logDebugMessage(4,"Speed log, max %f, time %f, dist %f\n",maxPlanarSpeed,timeDelta,maxDist);
5472 
5473                             if (isCheatingMovement(*playerData, state, maxDist, t) && !logOnly)
5474                             {
5475                                 sendMessage(ServerPlayer, t, "Autokick: Player tank is moving too fast.");
5476                                 removePlayer(t, "too fast");
5477                             }
5478                         }
5479                     }
5480                 }
5481             }
5482         }
5483 
5484         playerData->setPlayerState(state, timestamp);
5485 
5486         // Player might already be dead and did not know it yet (e.g. teamkill)
5487         // do not propogate
5488         if (!playerData->player.isAlive() &&
5489                 (state.status & short(PlayerState::Alive)))
5490             break;
5491 
5492         // observer shouldn't send bulk messages anymore, they used to
5493         // when it was a server-only hack; but the check does not hurt,
5494         // either
5495         if (playerData->player.isObserver())
5496             break;
5497 
5498         searchFlag(*playerData);
5499 
5500         relayPlayerPacket(t, len, rawbuf, code);
5501         break;
5502     }
5503 
5504     case MsgGMUpdate:
5505         shotUpdate(t, const_cast<void *>(buf), int(len));
5506         break;
5507 
5508     // FIXME handled inside uread, but not discarded
5509     case MsgUDPLinkRequest:
5510         break;
5511 
5512     // unknown msg type
5513     default:
5514         logDebugMessage(1,"Player [%d] sent unknown packet type (%x), possible attack from %s\n",
5515                         t, code, handler->getTargetIP());
5516         removePlayer(t, "Autokick: Sent unknown packet type");
5517     }
5518 }
5519 
handleTcp(NetHandler & netPlayer,int i,const RxStatus e)5520 static void handleTcp(NetHandler &netPlayer, int i, const RxStatus e)
5521 {
5522     if (e != ReadAll)
5523     {
5524         if (e == ReadReset)
5525             removePlayer(i, "ECONNRESET/EPIPE", false);
5526         else if (e == ReadError)
5527         {
5528             // dump other errors and remove the player
5529             nerror("error on read");
5530             removePlayer(i, "Read error", false);
5531         }
5532         else if (e == ReadDiscon)
5533         {
5534             // disconnected
5535             removePlayer(i, "Disconnected", false);
5536         }
5537         else if (e == ReadHuge)
5538             removePlayer(i, "large packet recvd", false);
5539         return;
5540     }
5541 
5542     uint16_t len, code;
5543     const void *buf = netPlayer.getTcpBuffer();
5544     buf = nboUnpackUShort(buf, len);
5545     buf = nboUnpackUShort(buf, code);
5546 
5547     // trying to get the real player from the message: bots share tcp
5548     // connection with the player
5549     PlayerId t = i;
5550     switch (code)
5551     {
5552     case MsgShotBegin:
5553     {
5554         nboUnpackUByte(buf, t);
5555         break;
5556     }
5557     case MsgPlayerUpdate:
5558     case MsgPlayerUpdateSmall:
5559     {
5560         float timestamp;
5561         buf = nboUnpackFloat(buf, timestamp);
5562         buf = nboUnpackUByte(buf, t);
5563         break;
5564     }
5565     default:
5566         break;
5567     }
5568     // Make sure is a bot
5569     GameKeeper::Player *playerData = NULL;
5570     if (t != i)
5571     {
5572         playerData = GameKeeper::Player::getPlayerByIndex(t);
5573         if (!playerData || !playerData->player.isBot())
5574         {
5575             t = i;
5576             playerData = GameKeeper::Player::getPlayerByIndex(t);
5577         }
5578         // Should check also if bot and player are related
5579     }
5580     else
5581         playerData = GameKeeper::Player::getPlayerByIndex(t);
5582 
5583     // simple ruleset, if player sends a MsgShotBegin over TCP he/she
5584     // must not be using the UDP link
5585     if (true && playerData != NULL && !playerData->player.isBot())
5586     {
5587         if (code == MsgShotBegin)
5588         {
5589             char message[MessageLen];
5590             snprintf(message, MessageLen, "Your end is not using UDP.");
5591             sendMessage(ServerPlayer, i, message);
5592 
5593             snprintf(message, MessageLen, "Turn on UDP on your firewall or router.");
5594             sendMessage(ServerPlayer, i, message);
5595 
5596             removePlayer(i, "no UDP");
5597             return;
5598         }
5599     }
5600 
5601     // handle the command
5602     handleCommand(t, netPlayer.getTcpBuffer(), false);
5603 }
5604 
terminateServer(int UNUSED (sig))5605 static void terminateServer(int UNUSED(sig))
5606 {
5607     bzSignal(SIGINT, SIG_PF(terminateServer));
5608     bzSignal(SIGTERM, SIG_PF(terminateServer));
5609     exitCode = 0;
5610     done = true;
5611 }
5612 
5613 
cmdSet(const std::string &,const CommandManager::ArgList & args,bool * worked)5614 static std::string cmdSet(const std::string&, const CommandManager::ArgList& args, bool *worked)
5615 {
5616     if (worked) *worked = true;
5617     switch (args.size())
5618     {
5619     case 2:
5620         if (BZDB.isSet(args[0]))
5621         {
5622             StateDatabase::Permission permission = BZDB.getPermission(args[0]);
5623             if ((permission == StateDatabase::ReadWrite) || (permission == StateDatabase::Locked))
5624             {
5625                 BZDB.set(args[0], args[1], StateDatabase::Server);
5626                 lastWorldParmChange = TimeKeeper::getCurrent();
5627                 return args[0] + " set";
5628             }
5629             if (worked) *worked = false;
5630             return "variable " + args[0] + " is not writeable";
5631         }
5632         else
5633         {
5634             if (worked) *worked = false;
5635             return "variable " + args[0] + " does not exist";
5636         }
5637     case 1:
5638         if (BZDB.isSet(args[0]))
5639             return args[0] + " is " + BZDB.get(args[0]);
5640         else
5641         {
5642             if (worked) *worked = false;
5643             return "variable " + args[0] + " does not exist";
5644         }
5645     default:
5646         if (worked) *worked = false;
5647         return "usage: set <name> [<value>]";
5648     }
5649 }
5650 
resetAllCallback(const std::string & name,void *)5651 static void resetAllCallback(const std::string &name, void*)
5652 {
5653     StateDatabase::Permission permission = BZDB.getPermission(name);
5654     if ((permission == StateDatabase::ReadWrite) || (permission == StateDatabase::Locked))
5655         BZDB.set(name, BZDB.getDefault(name), StateDatabase::Server);
5656 }
5657 
cmdReset(const std::string &,const CommandManager::ArgList & args,bool * cmdError)5658 static std::string cmdReset(const std::string&, const CommandManager::ArgList& args, bool* cmdError)
5659 {
5660     if (cmdError) *cmdError = true;
5661     if (args.size() == 1)
5662     {
5663         if (args[0] == "*")
5664         {
5665             BZDB.iterate(resetAllCallback,NULL);
5666             if (cmdError) *cmdError = false;
5667             return "all variables reset";
5668         }
5669         else if (BZDB.isSet(args[0]))
5670         {
5671             StateDatabase::Permission permission = BZDB.getPermission(args[0]);
5672             if ((permission == StateDatabase::ReadWrite) || (permission == StateDatabase::Locked))
5673             {
5674                 BZDB.set(args[0], BZDB.getDefault(args[0]), StateDatabase::Server);
5675                 lastWorldParmChange = TimeKeeper::getCurrent();
5676                 if (cmdError) *cmdError = false;
5677                 return args[0] + " reset";
5678             }
5679             return "variable " + args[0] + " is not writeable";
5680         }
5681         else
5682             return "variable " + args[0] + " does not exist";
5683     }
5684     else
5685         return "usage: reset <name>";
5686 }
5687 
5688 static bool requestAuthentication;
5689 
doStuffOnPlayer(GameKeeper::Player & playerData)5690 static void doStuffOnPlayer(GameKeeper::Player &playerData)
5691 {
5692     int p = playerData.getIndex();
5693 
5694     // kick idle players
5695     if (clOptions->idlekickthresh > 0)
5696     {
5697         if ((!playerData.accessInfo.hasPerm(PlayerAccessInfo::antikick)) &&
5698                 (playerData.player.isTooMuchIdling(clOptions->idlekickthresh)))
5699         {
5700             char message[MessageLen]
5701                 = "You were kicked because you were idle too long";
5702             sendMessage(ServerPlayer, p,  message);
5703             removePlayer(p, "idling");
5704             logDebugMessage(1,"Kicked player %s [%d] for idling (thresh = %f)\n",
5705                             playerData.player.getCallSign(), p, clOptions->idlekickthresh);
5706             return;
5707         }
5708     }
5709 
5710     // Check authorization
5711     if (playerData._LSAState == GameKeeper::Player::required)
5712     {
5713         requestAuthentication = true;
5714         playerData._LSAState = GameKeeper::Player::requesting;
5715     }
5716     else if (playerData.netHandler && playerData.netHandler->reverseDNSDone())
5717     {
5718         if ((playerData._LSAState == GameKeeper::Player::verified)  ||
5719                 (playerData._LSAState == GameKeeper::Player::timedOut)  ||
5720                 (playerData._LSAState == GameKeeper::Player::failed)    ||
5721                 (playerData._LSAState == GameKeeper::Player::notRequired))
5722         {
5723             bz_ServerAddPlayerData_V1 eventData;
5724             eventData.player = bz_getPlayerByIndex(playerData.getIndex());
5725             worldEventManager.callEvents(&eventData);
5726             if (eventData.allow)
5727                 AddPlayer(p, &playerData);
5728             else
5729             {
5730                 playerData.addWasDelayed = true;
5731                 playerData.addDelayStartTime = TimeKeeper::getCurrent().getSeconds();
5732             }
5733             playerData._LSAState = GameKeeper::Player::done;
5734         }
5735     }
5736 
5737     if (playerData.addWasDelayed) // check to see that the API doesn't leave them hanging
5738     {
5739         double delta = TimeKeeper::getCurrent().getSeconds() - playerData.addDelayStartTime;
5740         if (delta > BZDB.eval("_maxPlayerAddDelay"))
5741             AddPlayer(p, &playerData);
5742     }
5743 
5744     if (playerData.netHandler)
5745     {
5746         // Check for hung player connections that never entered the game completely
5747         if (!playerData.player.isCompletelyAdded()
5748                 && TimeKeeper::getCurrent() - playerData.netHandler->getTimeAccepted() > 300.0f)
5749             rejectPlayer(p, RejectBadRequest, "Failed to connect within reasonable timeframe");
5750 
5751         // Check host bans
5752         const char *hostname = playerData.netHandler->getHostname();
5753         if (hostname && playerData.needsHostbanChecked())
5754         {
5755             if (!playerData.accessInfo.hasPerm(PlayerAccessInfo::antiban))
5756             {
5757                 HostBanInfo hostInfo("*");
5758                 if (!clOptions->acl.hostValidate(hostname, &hostInfo))
5759                 {
5760                     std::string reason = "Host banned for: ";
5761 
5762                     if (hostInfo.reason.size())
5763                         reason += hostInfo.reason;
5764                     else
5765                         reason += "General Ban";
5766 
5767                     reason += ColorStrings[WhiteColor];
5768                     reason += " (";
5769 
5770                     double duration = hostInfo.banEnd - TimeKeeper::getCurrent();
5771                     if (duration < 365.0f * 24 * 3600)
5772                     {
5773                         long int timeArray[4];
5774                         TimeKeeper::convertTime(duration, timeArray);
5775                         std::string bantime = TimeKeeper::printTime(timeArray);
5776                         reason += bantime;
5777                         reason += " remaining";
5778                     }
5779                     else
5780                         reason += "indefinite";
5781 
5782                     reason += ")";
5783                     reason += ColorStrings[GreenColor];
5784 
5785                     if (hostInfo.fromMaster)
5786                         reason += " from the master server";
5787 
5788                     rejectPlayer(p, RejectHostBanned, reason.c_str());
5789                     return;
5790                 }
5791             }
5792             playerData.setNeedThisHostbanChecked(false);
5793         }
5794     }
5795 
5796     // update notResponding
5797     if (playerData.player.hasStartedToNotRespond())
5798     {
5799         // if player is the rabbit, anoint a new one
5800         if (p == rabbitIndex)
5801         {
5802             anointNewRabbit();
5803             // Should recheck if player is still available
5804             if (!GameKeeper::Player::getPlayerByIndex(p))
5805                 return;
5806         }
5807         // if player is holding a flag, drop it
5808         for (int j = 0; j < numFlags; j++)
5809         {
5810             if (FlagInfo::get(j)->player == p)
5811             {
5812                 dropPlayerFlag(playerData, playerData.lastState.pos);
5813                 // Should recheck if player is still available
5814                 if (!GameKeeper::Player::getPlayerByIndex(p))
5815                     return;
5816             }
5817         }
5818     }
5819 
5820     // send lag pings
5821     bool warn;
5822     bool kick;
5823     int nextPingSeqno = playerData.lagInfo.getNextPingSeqno(warn, kick);
5824     if (nextPingSeqno > 0)
5825     {
5826         void *buf, *bufStart = getDirectMessageBuffer();
5827         buf = nboPackUShort(bufStart, nextPingSeqno);
5828         int result = directMessage(playerData, MsgLagPing,
5829                                    (char*)buf - (char*)bufStart, bufStart);
5830         if (result == -1)
5831             return;
5832         if (warn)
5833         {
5834             char message[MessageLen];
5835             snprintf(message, MessageLen, "*** Server Warning: your lag is too high (failed to return ping) ***");
5836             sendMessage(ServerPlayer, p, message);
5837             // Should recheck if player is still available
5838             if (!GameKeeper::Player::getPlayerByIndex(p))
5839                 return;
5840             if (kick)
5841             {
5842                 lagKick(p);
5843                 return;
5844             }
5845         }
5846     }
5847 
5848     // kick any clients that need to be
5849     if (playerData.netHandler)
5850     {
5851         std::string reasonToKick = playerData.netHandler->reasonToKick();
5852         if (reasonToKick != "")
5853         {
5854             removePlayer(p, reasonToKick.c_str(), false);
5855             return;
5856         }
5857     }
5858 }
5859 
loadBadwordsList()5860 void loadBadwordsList ()
5861 {
5862     if (clOptions->filterFilename.length() != 0)
5863     {
5864         if (clOptions->filterChat || clOptions->filterCallsigns)
5865         {
5866             if (debugLevel >= 1)
5867             {
5868                 unsigned int count;
5869                 logDebugMessage(1,"Loading %s\n", clOptions->filterFilename.c_str());
5870                 count = clOptions->filter.loadFromFile(clOptions->filterFilename, true);
5871                 logDebugMessage(1,"Loaded %u words\n", count);
5872             }
5873             else
5874                 clOptions->filter.loadFromFile(clOptions->filterFilename, false);
5875         }
5876         else
5877             logDebugMessage(1,"Bad word filter specified without -filterChat or -filterCallsigns\n");
5878     }
5879 }
5880 
rescanForBans(bool isOperator,const char * callsign,int playerID)5881 void rescanForBans ( bool isOperator, const char* callsign, int playerID )
5882 {
5883     // Validate all of the current players
5884 
5885     std::string banner = "SERVER";
5886     if (callsign && strlen(callsign))
5887         banner = callsign;
5888 
5889     std::string reason;
5890     char kickmessage[MessageLen];
5891 
5892     // Check host bans
5893     GameKeeper::Player::setAllNeedHostbanChecked(true);
5894 
5895     // Check IP bans
5896     for (int i = 0; i < curMaxPlayers; i++)
5897     {
5898         GameKeeper::Player *otherPlayer = GameKeeper::Player::getPlayerByIndex(i);
5899         if (otherPlayer && !clOptions->acl.validate(otherPlayer->netHandler->getIPAddress()))
5900         {
5901             // operators can override antiperms
5902             if (!isOperator)
5903             {
5904                 // make sure this player isn't protected
5905                 GameKeeper::Player *p = GameKeeper::Player::getPlayerByIndex(i);
5906                 if ((p != NULL) && (p->accessInfo.hasPerm(PlayerAccessInfo::antiban)))
5907                 {
5908                     if (playerID != -1)
5909                     {
5910                         snprintf(kickmessage, MessageLen, "%s is protected from being banned (skipped).", p->player.getCallSign());
5911                         sendMessage(ServerPlayer, playerID, kickmessage);
5912                     }
5913                     continue;
5914                 }
5915             }
5916 
5917             snprintf(kickmessage, MessageLen, "You were banned from this server by %s", banner.c_str());
5918             sendMessage(ServerPlayer, i, kickmessage);
5919             if (reason.length() > 0)
5920             {
5921                 snprintf(kickmessage, MessageLen, "Reason given: %s", reason.c_str());
5922                 sendMessage(ServerPlayer, i, kickmessage);
5923             }
5924             removePlayer(i, "/ban");
5925         }
5926     }
5927 }
5928 
initGroups()5929 void initGroups()
5930 {
5931     // make sure that the 'admin' & 'default' groups exist
5932 
5933     PlayerAccessInfo info;
5934     info.explicitAllows[PlayerAccessInfo::actionMessage] = true;
5935     info.explicitAllows[PlayerAccessInfo::adminMessageSend] = true;
5936     info.explicitAllows[PlayerAccessInfo::clientQuery] = true;
5937     info.explicitAllows[PlayerAccessInfo::date] = true;
5938     info.explicitAllows[PlayerAccessInfo::flagHistory] = true;
5939     info.explicitAllows[PlayerAccessInfo::idleStats] = true;
5940     info.explicitAllows[PlayerAccessInfo::lagStats] = true;
5941     info.explicitAllows[PlayerAccessInfo::privateMessage] = true;
5942     info.explicitAllows[PlayerAccessInfo::showMotto] = true;
5943     info.explicitAllows[PlayerAccessInfo::spawn] = true;
5944     info.explicitAllows[PlayerAccessInfo::talk] = true;
5945     info.groupState[PlayerAccessInfo::isGroup] = true;
5946     info.groupState[PlayerAccessInfo::isDefault] = true;
5947     groupAccess["EVERYONE"] = info;
5948 
5949     // VERIFIED
5950     info.explicitAllows.reset();
5951     info.groupState.reset();
5952     info.explicitAllows[PlayerAccessInfo::listPlugins] = true;
5953     info.explicitAllows[PlayerAccessInfo::poll] = true;
5954     info.explicitAllows[PlayerAccessInfo::report] = true;
5955     info.explicitAllows[PlayerAccessInfo::vote] = true;
5956     info.explicitAllows[PlayerAccessInfo::pollBan] = true;
5957     info.explicitAllows[PlayerAccessInfo::pollKick] = true;
5958     info.explicitAllows[PlayerAccessInfo::pollFlagReset] = true;
5959     info.groupState[PlayerAccessInfo::isGroup] = true;
5960     info.groupState[PlayerAccessInfo::isDefault] = true;
5961     groupAccess["VERIFIED"] = info;
5962 
5963     //  LOCAL.ADMIN
5964     info.explicitAllows.reset();
5965     info.groupState.reset();
5966     for (int i = 0; i < PlayerAccessInfo::lastPerm; i++)
5967         info.explicitAllows[i] = true;
5968     info.groupState[PlayerAccessInfo::isGroup] = true;
5969     info.groupState[PlayerAccessInfo::isDefault] = true;
5970     info.explicitAllows[PlayerAccessInfo::hideAdmin] = false;
5971     groupAccess["LOCAL.ADMIN"] = info;
5972 
5973     // load databases
5974     if (groupsFile.size())
5975         PlayerAccessInfo::readGroupsFile(groupsFile);
5976 }
5977 
sendBufferedNetDataForPeer(NetConnectedPeer & peer)5978 void sendBufferedNetDataForPeer (NetConnectedPeer &peer )
5979 {
5980     if (peer.netHandler->hasTcpOutbound())
5981     {
5982         peer.netHandler->bufferedSend(NULL, 0);
5983         return;
5984     }
5985 
5986     if (peer.sendChunks.empty())
5987     {
5988         if (peer.deleteWhenDoneSending && peer.sent && !peer.netHandler->hasTcpOutbound())
5989             peer.deleteMe = true;
5990         return;
5991     }
5992 
5993     TimeKeeper now = TimeKeeper::getCurrent();
5994 
5995     if (peer.lastSend.getSeconds() + peer.minSendTime > now.getSeconds())
5996         return;
5997 
5998     peer.sent = true;
5999     peer.lastActivity = now;
6000 
6001     const std::string& chunk = peer.sendChunks.front();
6002     peer.netHandler->bufferedSend(chunk.data(), chunk.size());
6003 
6004     peer.sendChunks.pop_front();
6005 }
6006 
getIPFromHandler(NetHandler * netHandler)6007 std::string getIPFromHandler (NetHandler* netHandler)
6008 {
6009     unsigned int address = (unsigned int)netHandler->getIPAddress().s_addr;
6010     unsigned char* a = (unsigned char*)&address;
6011 
6012     static std::string strRet;
6013 
6014     strRet = TextUtils::format("%d.%d.%d.%d",(int)a[0],(int)a[1],(int)a[2],(int)a[3]);
6015 
6016     return strRet;
6017 }
6018 
processConnectedPeer(NetConnectedPeer & peer,int sockFD,fd_set & read_set,fd_set & UNUSED (write_set))6019 static void processConnectedPeer(NetConnectedPeer& peer, int sockFD, fd_set& read_set, fd_set& UNUSED(write_set))
6020 {
6021     const double connectionTimeout = 2.5; // timeout in seconds
6022 
6023     if (peer.deleteMe)
6024         return; // skip it, it's dead to us, we'll close and purge it later
6025 
6026     NetHandler* netHandler = peer.netHandler;
6027 
6028     // see if the sucker is banned
6029 
6030     const size_t headerLen = strlen(BZ_CONNECT_HEADER);
6031     size_t readLen = headerLen;
6032 
6033     if (peer.apiHandler == NULL && peer.player == -1)
6034     {
6035         // they arn't anything yet, see if they have any data
6036 
6037         if (peer.bufferedInput.size() >= headerLen )
6038             readLen = 1024;
6039 
6040         bool retry = false;
6041 
6042         RxStatus e = netHandler->receive(readLen,&retry);
6043         if (retry) // try one more time, just in case it was blocked
6044         {
6045             int retries = 1;
6046             if (BZDB.isSet("_maxConnectionRetries"))
6047                 retries = (int) BZDB.eval("_maxConnectionRetries");
6048 
6049             retry = false;
6050             for ( int t = 0; t < retries; t++)
6051             {
6052                 e = netHandler->receive(readLen,&retry);
6053                 if (!retry)
6054                     break;
6055             }
6056         }
6057 
6058         if ((e == ReadAll) || (e == ReadPart))
6059         {
6060             peer.lastActivity = TimeKeeper::getCurrent();
6061 
6062             unsigned int readSize = netHandler->getTcpReadSize();
6063 
6064             if (readSize > 0)
6065             {
6066                 // the dude has sent SOME data
6067                 peer.sent = true;
6068 
6069                 void *buf = netHandler->getTcpBuffer();
6070 
6071                 const char* header = BZ_CONNECT_HEADER;
6072 
6073                 char* tmp = (char*)malloc(readSize+1);
6074                 strncpy(tmp,(char*)buf,readSize);
6075                 tmp[readSize] = '\0';
6076 
6077                 peer.bufferedInput += tmp;
6078                 free(tmp);
6079 
6080                 if (peer.bufferedInput.size() >= headerLen && strncmp(peer.bufferedInput.c_str(),header, headerLen) == 0)
6081                 {
6082                     if (peer.bufferedInput.size() > headerLen+2)
6083                     {
6084                         peer.deleteMe = true;
6085                         return;
6086                     }
6087 
6088                     bz_AllowConnectionData_V1 data(getIPFromHandler(netHandler).c_str());
6089                     worldEventManager.callEvents(&data);
6090                     if (!data.allow)
6091                     {
6092                         logDebugMessage(2,"Game peer %s not allowed\n", netHandler->getTargetIP());
6093                         peer.deleteMe = true;
6094                         return;
6095                     }
6096 
6097                     netHandler->flushData();
6098                     // it's a player
6099                     if (!MakePlayer(netHandler))
6100                         peer.deleteMe = true;
6101                     else
6102                         peer.player = netHandler->getPlayerID();
6103 
6104                     return;
6105                 }
6106                 else
6107                 {
6108                     // it isn't a player yet, see if anyone else wants it.
6109                     // build up a buffer of all the data that is pending
6110                     while (e == ReadAll)
6111                     {
6112                         netHandler->flushData();
6113                         e = netHandler->receive(256);
6114 
6115                         readSize = netHandler->getTcpReadSize();
6116                         buf = netHandler->getTcpBuffer();
6117 
6118                         peer.bufferedInput.append(static_cast<char const*>(buf), readSize);
6119                     }
6120                     netHandler->flushData();
6121 
6122                     bz_AllowConnectionData_V1 data(getIPFromHandler(netHandler).c_str());
6123                     worldEventManager.callEvents(&data);
6124                     if (!data.allow)
6125                     {
6126                         logDebugMessage(2,"Peer %s not allowed\n", netHandler->getTargetIP());
6127                         peer.deleteMe = true;
6128                         return;
6129                     }
6130 
6131                     in_addr IP = netHandler->getIPAddress();
6132                     BanInfo info(IP);
6133                     if (!clOptions->acl.validate(IP, &info))
6134                     {
6135                         logDebugMessage(2,"Peer %s banned\n", netHandler->getTargetIP());
6136                         std::string banMsg = "banned for " + info.reason + " by " + info.bannedBy;
6137                         peer.sendChunks.push_back(banMsg);
6138                         peer.deleteMe = true;
6139                     }
6140 
6141                     // call an event to let people know we got a new connect
6142                     bz_NewNonPlayerConnectionEventData_V1 eventData;
6143 
6144                     eventData.data = strdup(peer.bufferedInput.c_str());
6145                     eventData.size = peer.bufferedInput.size();
6146                     eventData.connectionID = sockFD;
6147 
6148                     worldEventManager.callEvents(bz_eNewNonPlayerConnection, &eventData);
6149                     free(eventData.data);
6150 
6151                     // if someone wanted him they'd have set his handler and he'll never get here again
6152                 }
6153             }
6154         }
6155     }
6156 
6157     if (peer.apiHandler == NULL && peer.player == -1)
6158     {
6159         if (TimeKeeper::getCurrent().getSeconds() > peer.startTime.getSeconds() + connectionTimeout)
6160         {
6161             logDebugMessage(2,"Peer %s connection timeout with data \"%s\"\n",peer.netHandler->getTargetIP(),
6162                             TextUtils::escape_nonprintable(peer.bufferedInput,'"').c_str()); // FIXME: sanitize data
6163             std::string discoBuffer = getServerVersion();
6164             discoBuffer += "\r\n\r\n";
6165             peer.sendChunks.push_back(discoBuffer);
6166             peer.deleteMe = true; // nobody loves him
6167         }
6168     }
6169 
6170     // we like them see if they have gotten new data
6171     if (peer.apiHandler && peer.player < 0 && netHandler->isFdSet(&read_set))
6172     {
6173         in_addr IP = netHandler->getIPAddress();
6174         BanInfo info(IP);
6175         if (!clOptions->acl.validate(IP, &info))
6176         {
6177             logDebugMessage(2,"API peer %s banned\n", netHandler->getTargetIP());
6178             std::string banMsg = "banned for " + info.reason + " by " + info.bannedBy;
6179             peer.sendChunks.push_back(banMsg);
6180             peer.deleteMe = true;
6181         }
6182 
6183         if (!peer.deleteMe)
6184         {
6185             bool retry = false;
6186 
6187             RxStatus e = netHandler->receive(1024,&retry);
6188             if (retry) // try one more time, just in case it was blocked
6189             {
6190                 int retries = 1;
6191                 if (BZDB.isSet("_maxConnectionRetries"))
6192                     retries = (int) BZDB.eval("_maxConnectionRetries");
6193 
6194                 retry = false;
6195                 for ( int t = 0; t < retries; t++)
6196                 {
6197                     e = netHandler->receive(1024,&retry);
6198                     if (!retry)
6199                         break;
6200                 }
6201             }
6202 
6203             if (e == ReadPart || e == ReadAll )
6204             {
6205                 peer.lastActivity = TimeKeeper::getCurrent();
6206                 peer.apiHandler->pending(peer.socket,netHandler->getTcpBuffer(),netHandler->getTcpReadSize());
6207                 netHandler->flushData();
6208             }
6209             else
6210             {
6211                 // they done disconnected
6212                 peer.apiHandler->disconnect(peer.socket);
6213                 peer.deleteMe = true;
6214             }
6215         }
6216     }
6217 
6218     if (peer.player < 0) // only send data if he's not a player, there may be disco data
6219         sendBufferedNetDataForPeer(peer);
6220 }
6221 
6222 //
6223 // global variable callbacks
6224 //
6225 
bzdbGlobalCallback(const std::string & name,void * UNUSED (data))6226 static void bzdbGlobalCallback(const std::string& name, void* UNUSED(data))
6227 {
6228     const std::string value = BZDB.get(name);
6229     bz_BZDBChangeData_V1 eventData(name, value);
6230     worldEventManager.callEvents(&eventData);
6231 }
6232 
6233 class UPnP
6234 {
6235 public:
6236     UPnP();
6237     void start();
6238     void stop();
6239 private:
6240     void setIGD();
6241     void clearIGD();
6242     void setPorts();
6243     void setLocalInterface();
6244     void setRemoteInterface();
6245     void addPortForwarding();
6246     void deletePortForwarding();
6247 #ifdef HAVE_MINIUPNPC_MINIUPNPC_H
6248     struct UPNPUrls urls;
6249     struct IGDdatas data;
6250 #endif
6251     char    lanaddr[128];
6252     bool    IGD_Found;
6253     std::string remotePort;
6254     char    localPort[16];
6255 };
6256 
6257 UPnP bzUPnP;
6258 
UPnP()6259 UPnP::UPnP(): IGD_Found(false)
6260 {
6261     lanaddr[0]   = 0;
6262     localPort[0] = 0;
6263 }
6264 
setIGD()6265 void UPnP::setIGD()
6266 {
6267 #ifdef HAVE_MINIUPNPC_MINIUPNPC_H
6268     // Discover uPnP devices waiting for 200ms
6269 #if (MINIUPNPC_API_VERSION >= 14)
6270     struct UPNPDev *devlist = upnpDiscover(200, NULL, NULL, 0, 0, 2, NULL);
6271 #else
6272     struct UPNPDev *devlist = upnpDiscover(200, NULL, NULL, 0, 0, NULL);
6273 #endif
6274     if (!devlist)
6275     {
6276         std::cerr << "No UPnP device found"
6277                   << std::endl;
6278         return;
6279     }
6280     // Select a good IGD (Internet Gateway Device)
6281     int i = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
6282     freeUPNPDevlist(devlist);
6283     if (!i)
6284         std::cerr << "No recognized device" << std::endl;
6285     else if (i != 1)
6286     {
6287         switch (i)
6288         {
6289         case 2:
6290             std::cerr << "Found a non connected IGD"
6291                       << urls.controlURL
6292                       << std::endl;
6293             break;
6294         default:
6295             std::cerr << "Not recognized as IGD"
6296                       << urls.controlURL
6297                       << std::endl;
6298             break;
6299         }
6300         FreeUPNPUrls(&urls);
6301     }
6302     else
6303         IGD_Found = true;
6304 #endif
6305 }
6306 
clearIGD()6307 void UPnP::clearIGD()
6308 {
6309     if (!IGD_Found)
6310         return;
6311 
6312 #ifdef HAVE_MINIUPNPC_MINIUPNPC_H
6313     FreeUPNPUrls(&urls);
6314 #endif
6315     IGD_Found = true;
6316 }
6317 
setLocalInterface()6318 void UPnP::setLocalInterface()
6319 {
6320     // Use UPnP to set the local interface
6321     // Override with -i argument
6322     if (clOptions->pingInterface == "")
6323         clOptions->pingInterface = lanaddr;
6324 }
6325 
setRemoteInterface()6326 void UPnP::setRemoteInterface()
6327 {
6328 #ifdef HAVE_MINIUPNPC_MINIUPNPC_H
6329     char externalIPAddress[128];
6330     int result = UPNP_GetExternalIPAddress(
6331                      urls.controlURL,
6332                      data.first.servicetype,
6333                      externalIPAddress);
6334     if (result != UPNPCOMMAND_SUCCESS)
6335     {
6336         std::cerr << "GetExternalIPAddress returned"
6337                   << result
6338                   << std::endl;
6339         FreeUPNPUrls(&urls);
6340         IGD_Found = false;
6341         return;
6342     }
6343 
6344     // Use UPnP to set the public IP interface
6345     // override with - publicaddr argument
6346     if ((clOptions->publicizedAddress.length() == 0)
6347             || (clOptions->publicizedAddress[0] == ':'))
6348     {
6349         if (clOptions->publicizedAddress.length() == 0)
6350             clOptions->publicizedAddress = externalIPAddress
6351                                            + TextUtils::format(":%d", clOptions->wksPort);
6352         else
6353             clOptions->publicizedAddress = externalIPAddress
6354                                            + clOptions->publicizedAddress;
6355     }
6356 #endif
6357 }
6358 
setPorts()6359 void UPnP::setPorts()
6360 {
6361     size_t colonPos = clOptions->publicizedAddress.find(':');
6362     if (colonPos == std::string::npos)
6363         remotePort = "5154";
6364     else
6365         remotePort = std::string(clOptions->publicizedAddress, colonPos + 1);
6366     snprintf(localPort, sizeof(localPort), "%d", clOptions->wksPort);
6367 }
6368 
addPortForwarding()6369 void UPnP::addPortForwarding()
6370 {
6371 #ifdef HAVE_MINIUPNPC_MINIUPNPC_H
6372     int result;
6373     result = UPNP_AddPortMapping(
6374                  urls.controlURL,
6375                  data.first.servicetype,
6376                  remotePort.c_str(),
6377                  localPort,
6378                  lanaddr,
6379                  "bzfs",
6380                  "TCP",
6381                  0,
6382                  "0");
6383     if (result == UPNPCOMMAND_SUCCESS)
6384         result = UPNP_AddPortMapping(
6385                      urls.controlURL,
6386                      data.first.servicetype,
6387                      remotePort.c_str(),
6388                      localPort,
6389                      lanaddr,
6390                      "bzfs",
6391                      "UDP",
6392                      0,
6393                      "0");
6394     if (result != UPNPCOMMAND_SUCCESS)
6395     {
6396         switch (result)
6397         {
6398         case 402:
6399             std::cerr << "UPNP_AddPortMapping returned "
6400                       << "Invalid Args"
6401                       << std::endl;
6402             break;
6403         case 501:
6404             std::cerr << "UPNP_AddPortMapping returned "
6405                       << "Action Failed"
6406                       << std::endl;
6407             break;
6408         case 715:
6409             std::cerr << "UPNP_AddPortMapping returned "
6410                       << "WildCardNotPermittedInSrcIP"
6411                       << std::endl;
6412             break;
6413         case 716:
6414             std::cerr << "UPNP_AddPortMapping returned "
6415                       << "WildCardNotPermittedInExtPort"
6416                       << std::endl;
6417             break;
6418         case 718:
6419             std::cerr << "UPNP_AddPortMapping returned "
6420                       << "ConflictInMappingEntry"
6421                       << std::endl;
6422             break;
6423         case 724:
6424             std::cerr << "UPNP_AddPortMapping returned "
6425                       << "SamePortValuesRequired"
6426                       << std::endl;
6427             break;
6428         case 725:
6429             std::cerr << "UPNP_AddPortMapping returned "
6430                       << "OnlyPermanentLeasesSupported"
6431                       << std::endl;
6432             break;
6433         case 726:
6434             std::cerr << "UPNP_AddPortMapping returned "
6435                       << "RemoteHostOnlySupportsWildcard"
6436                       << std::endl;
6437             break;
6438         case 727:
6439             std::cerr << "UPNP_AddPortMapping returned "
6440                       << "ExternalPortOnlySupportsWildcard"
6441                       << std::endl;
6442             break;
6443         default:
6444             std::cerr << "UPNP_AddPortMapping returned "
6445                       << result
6446                       << std::endl;
6447             break;
6448         }
6449         FreeUPNPUrls(&urls);
6450         IGD_Found = false;
6451     }
6452 #endif
6453 }
6454 
deletePortForwarding()6455 void UPnP::deletePortForwarding()
6456 {
6457 #ifdef HAVE_MINIUPNPC_MINIUPNPC_H
6458     int result;
6459     result = UPNP_DeletePortMapping(
6460                  urls.controlURL,
6461                  data.first.servicetype,
6462                  remotePort.c_str(),
6463                  "TCP",
6464                  0);
6465     if (result == UPNPCOMMAND_SUCCESS)
6466         result = UPNP_DeletePortMapping(
6467                      urls.controlURL,
6468                      data.first.servicetype,
6469                      remotePort.c_str(),
6470                      "UDP",
6471                      0);
6472     if (result != UPNPCOMMAND_SUCCESS)
6473     {
6474         switch (result)
6475         {
6476         case 402:
6477             std::cerr << "UPNP_AddPortMapping returned "
6478                       << "Invalid Args"
6479                       << std::endl;
6480             break;
6481         case 714:
6482             std::cerr << "UPNP_AddPortMapping returned "
6483                       << "NoSuchEntryInArray"
6484                       << std::endl;
6485             break;
6486         default:
6487             std::cerr << "UPNP_AddPortMapping returned "
6488                       << result
6489                       << std::endl;
6490             break;
6491         }
6492         FreeUPNPUrls(&urls);
6493         IGD_Found = false;
6494     }
6495 #endif
6496 }
6497 
start()6498 void UPnP::start()
6499 {
6500     setIGD();
6501     if (!IGD_Found)
6502         return;
6503 
6504     setLocalInterface();
6505     setRemoteInterface();
6506     setPorts();
6507     if (!IGD_Found)
6508         return;
6509     addPortForwarding();
6510 }
6511 
stop()6512 void UPnP::stop()
6513 {
6514     if (!IGD_Found)
6515         return;
6516     deletePortForwarding();
6517     if (!IGD_Found)
6518         return;
6519     clearIGD();
6520 }
6521 
6522 /** main parses command line options and then enters an event and activity
6523  * dependant main loop.  once inside the main loop, the server is up and
6524  * running and should be ready to process connections and activity.
6525  */
main(int argc,char ** argv)6526 int main(int argc, char **argv)
6527 {
6528     int nfound;
6529     votingarbiter = (VotingArbiter *)NULL;
6530 
6531     loggingCallback = &apiLoggingCallback;
6532 
6533     netLogCB.Init();
6534 #ifndef _WIN32
6535     setvbuf(stdout, (char *)NULL, _IOLBF, 0);
6536     setvbuf(stderr, (char *)NULL, _IOLBF, 0);
6537 #endif
6538 
6539     Record::init();
6540 
6541     // initialize
6542 #if defined(_WIN32)
6543     {
6544         static const int major = 2, minor = 2;
6545         WSADATA wsaData;
6546         if (WSAStartup(MAKEWORD(major, minor), &wsaData))
6547         {
6548             logDebugMessage(2,"Failed to initialize Winsock.  Terminating.\n");
6549             return 1;
6550         }
6551         if (LOBYTE(wsaData.wVersion) != major || HIBYTE(wsaData.wVersion) != minor)
6552         {
6553             logDebugMessage(2,"Version mismatch in Winsock;"
6554                             "  got %d.%d, expected %d.%d.  Terminating.\n",
6555                             (int)LOBYTE(wsaData.wVersion),
6556                             (int)HIBYTE(wsaData.wVersion),
6557                             major,
6558                             minor);
6559             WSACleanup();
6560             return 1;
6561         }
6562     }
6563 #else
6564     // don't die on broken pipe
6565     bzSignal(SIGPIPE, SIG_IGN);
6566 
6567 #endif /* defined(_WIN32) */
6568 
6569     bzfsrand((unsigned int)time(0));
6570 
6571     Flags::init();
6572 
6573     ShotManager.Init();
6574 
6575     clOptions = new CmdLineOptions();
6576 
6577     // set default DB entries
6578     for (unsigned int gi = 0; gi < numGlobalDBItems; ++gi)
6579     {
6580         assert(globalDBItems[gi].name != NULL);
6581         if (globalDBItems[gi].value != NULL)
6582         {
6583             BZDB.set(globalDBItems[gi].name, globalDBItems[gi].value);
6584             BZDB.setDefault(globalDBItems[gi].name, globalDBItems[gi].value);
6585         }
6586         BZDB.setPersistent(globalDBItems[gi].name, globalDBItems[gi].persistent);
6587         BZDB.setPermission(globalDBItems[gi].name, globalDBItems[gi].permission);
6588         BZDB.addCallback(std::string(globalDBItems[gi].name), onGlobalChanged, (void*) NULL);
6589     }
6590 
6591     // add the global callback for worldEventManager
6592     BZDB.addGlobalCallback(bzdbGlobalCallback, NULL);
6593 
6594     CMDMGR.add("set", cmdSet, "set <name> [<value>]");
6595     CMDMGR.add("reset", cmdReset, "reset <name>");
6596 
6597     BZDBCache::init();
6598 
6599     // any set in parse this is a default value
6600     BZDB.setSaveDefault(true);
6601 
6602     // parse arguments  (finalized later)
6603     parse(argc, argv, *clOptions);
6604     setDebugTimestamp (clOptions->timestampLog, clOptions->timestampMicros, clOptions->timestampUTC);
6605 
6606     // no more defaults
6607     BZDB.setSaveDefault(false);
6608 
6609     if (clOptions->bzdbVars.length() > 0)
6610     {
6611         logDebugMessage(1,"Loading variables from %s\n", clOptions->bzdbVars.c_str());
6612         bool success = CFGMGR.read(clOptions->bzdbVars);
6613         if (success)
6614             logDebugMessage(1,"Successfully loaded variable(s)\n");
6615         else
6616             logDebugMessage(1,"WARNING: unable to load the variable file\n");
6617     }
6618 
6619     if (clOptions->publicizeServer && clOptions->publicizedKey.empty())
6620     {
6621         logDebugMessage(0,
6622                         "\nWARNING: Publicly listed bzfs servers must register"
6623                         " using the '-publickey <key>'option.\n\n");
6624     }
6625 
6626 #ifdef BZ_PLUGINS
6627     // see if we are going to load any plugins
6628     initPlugins();
6629     // check for python by default
6630     //    loadPlugin(std::string("python"),std::string(""));
6631     for (unsigned int plugin = 0; plugin < clOptions->pluginList.size(); plugin++)
6632     {
6633         if (!loadPlugin(clOptions->pluginList[plugin].plugin,
6634                         clOptions->pluginList[plugin].command))
6635         {
6636             std::string text = "WARNING: unable to load the plugin; ";
6637             text += clOptions->pluginList[plugin].plugin + "\n";
6638             logDebugMessage(0,text.c_str());
6639         }
6640     }
6641 #endif
6642 
6643     // start listening and prepare world database
6644     if (!defineWorld())
6645     {
6646 #ifdef BZ_PLUGINS
6647         unloadPlugins();
6648 #endif
6649 #if defined(_WIN32)
6650         WSACleanup();
6651 #endif /* defined(_WIN32) */
6652         std::cerr << "ERROR: A world was not specified" << std::endl;
6653         return 1;
6654     }
6655     else if (clOptions->cacheOut != "")
6656     {
6657         if (!saveWorldCache())
6658         {
6659             std::cerr << "ERROR: could not save world cache file: "
6660                       << clOptions->cacheOut << std::endl;
6661         }
6662         done = true;
6663     }
6664 
6665     // make flags, check sanity, etc...
6666     // (do this after the world has been loaded)
6667     finalizeParsing(argc, argv, *clOptions, world->getEntryZones());
6668 
6669     FlagInfo::setNoFlagInAir();
6670     for (int i = 0; i < numFlags; i++)
6671     {
6672         FlagInfo *flag = FlagInfo::get(i);
6673         if (!flag)
6674             continue;
6675         resetFlag(*flag);
6676     }
6677 
6678     // loading extra flag number
6679     FlagInfo::setExtra(clOptions->numExtraFlags);
6680 
6681     // loading lag announcement thresholds
6682     LagInfo::setAdminLagAnnounceThreshold(clOptions->adminlagannounce);
6683     LagInfo::setLagAnnounceThreshold(clOptions->lagannounce);
6684 
6685     // loading lag thresholds
6686     LagInfo::setThreshold(clOptions->lagwarnthresh,(float)clOptions->maxlagwarn);
6687 
6688     // loading jitter thresholds
6689     LagInfo::setJitterThreshold(clOptions->jitterwarnthresh,
6690                                 (float)clOptions->maxjitterwarn);
6691 
6692     // loading packetloss thresholds
6693     LagInfo::setPacketLossThreshold(clOptions->packetlosswarnthresh, (float)clOptions->maxpacketlosswarn);
6694 
6695     // loading player callsign/motto filters
6696     PlayerInfo::setFilterParameters(clOptions->filterCallsigns,
6697                                     clOptions->filter,
6698                                     clOptions->filterSimple);
6699 
6700     GameKeeper::Player::setMaxShots(clOptions->maxShots);
6701 
6702     // enable replay server mode
6703     if (clOptions->replayServer)
6704     {
6705 
6706         Replay::init();
6707 
6708         // we don't send flags to a client that isn't expecting them
6709         numFlags = 0;
6710         clOptions->numExtraFlags = 0;
6711 
6712         // disable the BZDB callbacks
6713         for (unsigned int gi = 0; gi < numGlobalDBItems; ++gi)
6714         {
6715             assert(globalDBItems[gi].name != NULL);
6716             BZDB.removeCallback(std::string(globalDBItems[gi].name),
6717                                 onGlobalChanged, (void*) NULL);
6718         }
6719 
6720         // maxPlayers is sent in the world data to the client.
6721         // the client then uses this to setup it's players
6722         // data structure, so we need to send it the largest
6723         // PlayerId it might see.
6724         maxPlayers = MaxPlayers + ReplayObservers;
6725 
6726         if (clOptions->maxTeam[ObserverTeam] == 0)
6727         {
6728             std::cerr << "replay needs at least 1 observer, set to 1" << std::endl;
6729             clOptions->maxTeam[ObserverTeam] = 1;
6730         }
6731         else if (clOptions->maxTeam[ObserverTeam] > ReplayObservers)
6732         {
6733             std::cerr << "observer count limited to " << ReplayObservers <<
6734                       " for replay" << std::endl;
6735             clOptions->maxTeam[ObserverTeam] = ReplayObservers;
6736         }
6737     }
6738 
6739     /* load the bad word filter if it was set */
6740     loadBadwordsList();
6741 
6742     /* initialize the poll arbiter for voting if necessary */
6743     if (clOptions->voteTime > 0)
6744     {
6745         votingarbiter =
6746             new VotingArbiter(clOptions->voteTime, clOptions->vetoTime,
6747                               clOptions->votesRequired, clOptions->votePercentage,
6748                               clOptions->voteRepeatTime);
6749         logDebugMessage(1,"There is a voting arbiter with the following settings:\n");
6750         logDebugMessage(1,"\tvote time is %d seconds\n", clOptions->voteTime);
6751         logDebugMessage(1,"\tveto time is %d seconds\n", clOptions->vetoTime);
6752         logDebugMessage(1,"\tvotes required are %d\n", clOptions->votesRequired);
6753         logDebugMessage(1,"\tvote percentage necessary is %f\n", clOptions->votePercentage);
6754         logDebugMessage(1,"\tvote repeat time is %d seconds\n", clOptions->voteRepeatTime);
6755         logDebugMessage(1,"\tavailable voters is initially set to %d\n", maxPlayers);
6756 
6757         // override the default voter count to the max number of players possible
6758         votingarbiter->setAvailableVoters(maxPlayers);
6759         BZDB.setPointer("poll", (void *)votingarbiter);
6760         BZDB.setPermission("poll", StateDatabase::ReadOnly);
6761     }
6762 
6763     // look up service name and use that port if no port given on
6764     // command line.  if no service then use default port.
6765     if (!clOptions->useGivenPort)
6766     {
6767         struct servent *service = getservbyname("bzfs", "tcp");
6768         if (service)
6769             clOptions->wksPort = ntohs(service->s_port);
6770     }
6771 
6772     if (clOptions->UPnP)
6773         bzUPnP.start();
6774 
6775     if (clOptions->pingInterface != "")
6776         serverAddress = Address::getHostAddress(clOptions->pingInterface);
6777 
6778     // my address to publish.  allow arguments to override (useful for
6779     // firewalls).  use my official hostname if it appears to be
6780     // canonicalized, otherwise use my IP in dot notation.
6781     // set publicized address if not set by arguments
6782     // Only set the public address through this automated generation if the
6783     // hostname is a FQDN or the IP address is not 0.0.0.0 or a private IP.
6784     if (clOptions->publicizedAddress.empty())
6785     {
6786         std::string generatedPublicAddress = Address::getHostName();
6787         if (generatedPublicAddress.find('.') == std::string::npos)
6788         {
6789             if (!serverAddress.isAny() && !serverAddress.isPrivate())
6790                 generatedPublicAddress = serverAddress.getDotNotation();
6791             else
6792                 generatedPublicAddress = "";
6793         }
6794         if (!generatedPublicAddress.empty())
6795             clOptions->publicizedAddress += generatedPublicAddress + TextUtils::format(":%d", clOptions->wksPort);
6796     }
6797 
6798     /* print debug information about how the server is running */
6799     if (clOptions->publicizeServer)
6800     {
6801         logDebugMessage(1,"Running a public server with the following settings:\n");
6802         logDebugMessage(1,"\tpublic address is %s\n", clOptions->publicizedAddress.c_str());
6803     }
6804     else
6805         logDebugMessage(1,"Running a private server with the following settings:\n");
6806 
6807     // get the master ban list
6808     if (clOptions->publicizeServer && !clOptions->suppressMasterBanList)
6809     {
6810         MasterBanList banList;
6811         std::vector<std::string>::const_iterator it;
6812         for (it = clOptions->masterBanListURL.begin();
6813                 it != clOptions->masterBanListURL.end(); ++it)
6814         {
6815             clOptions->acl.merge(banList.get(it->c_str()));
6816             logDebugMessage(1,"Loaded master ban list from %s\n", it->c_str());
6817         }
6818     }
6819 
6820     Score::setTeamKillRatio(clOptions->teamKillerKickRatio);
6821     Score::setWinLimit(clOptions->maxPlayerScore);
6822     if (clOptions->rabbitSelection == RandomRabbitSelection)
6823         Score::setRandomRanking();
6824     // print networking info
6825     logDebugMessage(1,"\tlistening on %s:%i\n",
6826                     serverAddress.getDotNotation().c_str(), clOptions->wksPort);
6827     logDebugMessage(1,"\twith title of \"%s\"\n", clOptions->publicizedTitle.c_str());
6828 
6829     // prep ping reply
6830     pingReply.serverId.serverHost = serverAddress;
6831     pingReply.serverId.port = htons(clOptions->wksPort);
6832     pingReply.serverId.number = 0;
6833     pingReply.gameType = clOptions->gameType;
6834     pingReply.gameOptions = clOptions->gameOptions;
6835     pingReply.maxPlayers = (uint8_t)maxRealPlayers;
6836     pingReply.maxShots = clOptions->maxShots;
6837     pingReply.rogueMax = (uint8_t)clOptions->maxTeam[0];
6838     pingReply.redMax = (uint8_t)clOptions->maxTeam[1];
6839     pingReply.greenMax = (uint8_t)clOptions->maxTeam[2];
6840     pingReply.blueMax = (uint8_t)clOptions->maxTeam[3];
6841     pingReply.purpleMax = (uint8_t)clOptions->maxTeam[4];
6842     pingReply.observerMax = (uint8_t)clOptions->maxTeam[5];
6843     pingReply.shakeWins = clOptions->shakeWins;
6844     pingReply.shakeTimeout = clOptions->shakeTimeout;
6845     pingReply.maxTime = (uint16_t)clOptions->timeLimit;
6846     pingReply.maxPlayerScore = clOptions->maxPlayerScore;
6847     pingReply.maxTeamScore = clOptions->maxTeamScore;
6848 
6849     // adjust speed and height checking as required
6850     adjustTolerances();
6851 
6852     // setup the game settings
6853     makeGameSettings();
6854 
6855     // no original world weapons in replay mode
6856     if (Replay::enabled())
6857         world->getWorldWeapons().clear();
6858 
6859     TimeKeeper nextSuperFlagInsertion = TimeKeeper::getCurrent();
6860     const float flagExp = -logf(0.5f) / FlagHalfLife;
6861 
6862     // load up the access permissions & stuff
6863     initGroups();
6864     if (userDatabaseFile.size())
6865         PlayerAccessInfo::readPermsFile(userDatabaseFile);
6866 
6867     if (clOptions->startRecording)
6868         Record::start(ServerPlayer);
6869 
6870     // trap some signals
6871     if (bzSignal(SIGINT, SIG_IGN) != SIG_IGN)
6872         bzSignal(SIGINT, SIG_PF(terminateServer));
6873     bzSignal(SIGTERM, SIG_PF(terminateServer));
6874 
6875     // start the server
6876     if (!serverStart())
6877     {
6878         bzUPnP.stop();
6879 #ifdef BZ_PLUGINS
6880         unloadPlugins();
6881 #endif
6882 #if defined(_WIN32)
6883         WSACleanup();
6884 #endif /* defined(_WIN32) */
6885         std::cerr << "ERROR: Unable to start the server, perhaps one is already running?" << std::endl;
6886         return 2;
6887     }
6888 
6889 
6890     /* MAIN SERVER RUN LOOP
6891      *
6892      * the main loop runs at approximately 2 iterations per 5 seconds
6893      * when there are no players on the field.  this can increase to
6894      * about 100 iterations per 5 seconds with a single player, though
6895      * average is about 20-40 iterations per five seconds.  Adding
6896      * world weapons will increase the number of iterations
6897      * substantially (about x10)
6898      **/
6899 
6900     int i;
6901     while (!done)
6902     {
6903 
6904         // see if the octree needs to be reloaded
6905         world->checkCollisionManager();
6906 
6907         maxFileDescriptor = 0;
6908         // prepare select set
6909         fd_set read_set, write_set;
6910         FD_ZERO(&read_set);
6911         FD_ZERO(&write_set);
6912         NetHandler::setFd(&read_set, &write_set, maxFileDescriptor);
6913         // always listen for connections
6914         FD_SET((unsigned int)wksSocket, &read_set);
6915         if (wksSocket > maxFileDescriptor)
6916             maxFileDescriptor = wksSocket;
6917 
6918         // Check for cURL needed activity
6919         int cURLmaxFile = cURLManager::fdset(read_set, write_set);
6920         if (cURLmaxFile > maxFileDescriptor)
6921             maxFileDescriptor = cURLmaxFile;
6922 
6923         // find timeout when next flag would hit ground
6924         TimeKeeper tm = TimeKeeper::getCurrent();
6925         // lets start by waiting 3 sec
6926         float waitTime = 3.0f;
6927 
6928         if ((countdownDelay >= 0) || (countdownResumeDelay >= 0))
6929         {
6930             // 3 seconds too slow for match countdowns
6931             waitTime = 0.5f;
6932         }
6933         else if (countdownActive && clOptions->timeLimit > 0.0f)
6934             waitTime = 1.0f;
6935 
6936         // get time for next flag drop
6937         float dropTime;
6938         while ((dropTime = FlagInfo::getNextDrop(tm)) <= 0.0f)
6939         {
6940             // if any flags were in the air, see if they've landed
6941             for (i = 0; i < numFlags; i++)
6942             {
6943                 FlagInfo *flag = FlagInfo::get(i);
6944                 if (!flag)
6945                     continue;
6946                 if (flag->landing(tm))
6947                 {
6948                     if (flag->flag.status == FlagOnGround)
6949                         sendFlagUpdate(*flag);
6950                     else
6951                         resetFlag(*flag);
6952                 }
6953             }
6954         }
6955         if (dropTime < waitTime)
6956             waitTime = dropTime;
6957 
6958         // get time for next Player internal action
6959         GameKeeper::Player::updateLatency(waitTime);
6960 
6961         // get time for the next world weapons shot
6962         if (world->getWorldWeapons().count() > 0)
6963         {
6964             float nextTime = world->getWorldWeapons().nextTime ();
6965             if (nextTime < waitTime)
6966                 waitTime = nextTime;
6967         }
6968 
6969         // get time for the next replay packet (if active)
6970         if (Replay::enabled())
6971         {
6972             float nextTime = Replay::nextTime ();
6973             if (nextTime < waitTime)
6974                 waitTime = nextTime;
6975         }
6976         else
6977         {
6978             // game time updates
6979             const float nextGT = nextGameTime();
6980             if (nextGT < waitTime)
6981                 waitTime = nextGT;
6982         }
6983 
6984         // minmal waitTime
6985         if (waitTime < 0.0f)
6986             waitTime = 0.0f;
6987 
6988         // if there are buffered UDP, no wait at all
6989         if (NetHandler::anyUDPPending())
6990             waitTime = 0.0f;
6991 
6992         // see if we are within the plug requested max wait time
6993 #ifdef BZ_PLUGINS
6994         const float pluginMaxWait = getPluginMinWaitTime();
6995         if (waitTime > pluginMaxWait)
6996             waitTime = pluginMaxWait;
6997 #endif
6998 
6999         if (!netConnectedPeers.empty())
7000         {
7001             std::map<int,NetConnectedPeer>::iterator itr = netConnectedPeers.begin();
7002             while (itr != netConnectedPeers.end())
7003             {
7004                 // don't wait if we have data to send out
7005                 if (itr->second.sendChunks.size())
7006                 {
7007                     waitTime = 0;
7008                     break;
7009                 }
7010                 ++itr;
7011             }
7012             if (waitTime > 0.1f)
7013                 waitTime = 0.1f;
7014         }
7015 
7016         // don't wait (used by CURL and MsgEnter)
7017         if (dontWait)
7018         {
7019             waitTime = 0.0f;
7020             dontWait = false;
7021         }
7022 
7023         /**************
7024          *  SELECT()  *
7025          **************/
7026 
7027         // wait for an incoming communication, a flag to hit the ground,
7028         // a game countdown to end, a world weapon needed to be fired,
7029         // or a replay packet waiting to be sent.
7030         struct timeval timeout;
7031         timeout.tv_sec = long(floorf(waitTime));
7032         timeout.tv_usec = long(1.0e+6f * (waitTime - floorf(waitTime)));
7033         nfound = select(maxFileDescriptor+1, (fd_set*)&read_set, (fd_set*)&write_set, 0, &timeout);
7034         //if (nfound)
7035         //  logDebugMessage(1,"nfound,read,write %i,%08lx,%08lx\n", nfound, read_set, write_set);
7036 
7037         // send replay packets
7038         // (this check and response should follow immediately after the select() call)
7039         if (Replay::playing())
7040             Replay::sendPackets ();
7041 
7042         // game time updates
7043         if (!Replay::enabled())
7044             sendPendingGameTime();
7045 
7046 
7047         // synchronize PlayerInfo
7048         tm = TimeKeeper::getCurrent();
7049         PlayerInfo::setCurrentTime(tm);
7050 
7051         // players see a countdown
7052         if (countdownDelay >= 0)
7053         {
7054             static TimeKeeper timePrevious = tm;
7055             if (readySetGo == -1)
7056                 readySetGo = countdownDelay;
7057 
7058             if (tm - timePrevious > 0.9f)
7059             {
7060                 timePrevious = tm;
7061                 if (readySetGo == 0)
7062                 {
7063                     sendMessage(ServerPlayer, AllPlayers, "The match has started!...Good Luck Teams!");
7064                     countdownDelay = -1; // reset back to "unset"
7065                     countdownResumeDelay = -1; // reset back to "unset"
7066                     readySetGo = -1; // reset back to "unset"
7067                     countdownActive = true;
7068                     gameOver = false;
7069 
7070                     // start server's clock
7071                     gameStartTime = tm;
7072                     clOptions->timeElapsed = 0.0f;
7073 
7074                     // start client's clock
7075                     void *msg = getDirectMessageBuffer();
7076                     nboPackInt(msg, (int32_t)(int)clOptions->timeLimit);
7077                     broadcastMessage(MsgTimeUpdate, sizeof(int32_t), msg);
7078 
7079                     // kill any players that are playing already
7080                     GameKeeper::Player *player;
7081                     if (clOptions->gameType == ClassicCTF)
7082                     {
7083                         for (int j = 0; j < curMaxPlayers; j++)
7084                         {
7085                             void *buf, *bufStart = getDirectMessageBuffer();
7086                             player = GameKeeper::Player::getPlayerByIndex(j);
7087                             if (!player || player->player.isObserver() || !player->player.isPlaying())
7088                                 continue;
7089 
7090                             // the server gets to capture the flag -- send some
7091                             // bogus player id
7092 
7093                             // curMaxPlayers should never exceed 255, so this should
7094                             // be a safe cast
7095                             TeamColor vteam = player->player.getTeam();
7096 
7097                             buf = nboPackUByte(bufStart, (uint8_t)curMaxPlayers);
7098                             buf = nboPackUShort(buf, uint16_t(FlagInfo::lookupFirstTeamFlag(vteam)));
7099                             buf = nboPackUShort(buf, uint16_t(1 + (int(vteam) % 4)));
7100                             directMessage(j, MsgCaptureFlag, (char*)buf - (char*)bufStart, bufStart);
7101 
7102                             // kick 'em while they're down
7103                             playerKilled(j, curMaxPlayers, 0, -1, Flags::Null, -1, true);
7104 
7105                             // be sure to reset the player!
7106                             player->player.setDead();
7107                             zapFlagByPlayer(j);
7108                             player->player.setPlayedEarly(false);
7109                             player->player.setRestartOnBase(true);
7110                         }
7111                     }
7112 
7113                     // reset all flags
7114                     for (int j = 0; j < numFlags; j++)
7115                         zapFlag(*FlagInfo::get(j));
7116 
7117                     // quietly reset team scores in case of a capture during the countdown
7118                     resetTeamScores();
7119 
7120                     // reset player scores
7121                     resetPlayerScores();
7122 
7123                     // fire off a game start event
7124                     bz_GameStartEndEventData_V2   gameData;
7125                     gameData.eventType = bz_eGameStartEvent;
7126                     gameData.duration = clOptions->timeLimit;
7127                     gameData.playerID = clOptions->countdownStarter;
7128                     worldEventManager.callEvents(bz_eGameStartEvent,&gameData);
7129 
7130                 }
7131                 else
7132                 {
7133                     if ((readySetGo == countdownDelay) && (countdownDelay > 0))
7134                         sendMessage(ServerPlayer, AllPlayers, "Start your engines!......");
7135 
7136                     sendMessage(ServerPlayer, AllPlayers, TextUtils::format("%i...", readySetGo).c_str());
7137                     --readySetGo;
7138                 }
7139             } // end check if second has elapsed
7140         } // end check if countdown delay is active
7141 
7142         // players see the announce of resuming the countdown
7143         if (countdownResumeDelay >= 0)
7144         {
7145             static TimeKeeper timePrevious = tm;
7146             if (tm - timePrevious > 0.9f)
7147             {
7148                 timePrevious = tm;
7149                 if (gameOver)
7150                     countdownResumeDelay = -1; // reset back to "unset"
7151                 else if (countdownResumeDelay == 0)
7152                 {
7153                     countdownResumeDelay = -1; // reset back to "unset"
7154                     clOptions->countdownPaused = false;
7155                     sendMessage(ServerPlayer, AllPlayers, "Countdown resumed");
7156 
7157                     // fire off a game resume event
7158                     bz_GamePauseResumeEventData_V1 resumeEventData;
7159                     resumeEventData.eventType = bz_eGameResumeEvent;
7160                     worldEventManager.callEvents(bz_eGameResumeEvent, &resumeEventData);
7161                 }
7162                 else
7163                 {
7164                     sendMessage(ServerPlayer, AllPlayers,TextUtils::format("%i...", countdownResumeDelay).c_str());
7165                     --countdownResumeDelay;
7166                 }
7167             } // end check if second has elapsed
7168         } // end check if countdown resuming delay is active
7169 
7170         // see if game time ran out or if we are paused
7171         if (!gameOver && countdownActive && clOptions->timeLimit > 0.0f)
7172         {
7173             float newTimeElapsed = (float)(tm - gameStartTime);
7174             float timeLeft = clOptions->timeLimit - newTimeElapsed;
7175             if (timeLeft <= 0.0f && !countdownPauseStart)
7176             {
7177                 timeLeft = 0.0f;
7178                 gameOver = true;
7179                 countdownActive = false;
7180                 countdownPauseStart = TimeKeeper::getNullTime ();
7181                 clOptions->countdownPaused = false;
7182 
7183                 // fire off a game end event
7184                 bz_GameStartEndEventData_V2 gameData;
7185                 gameData.eventType = bz_eGameEndEvent;
7186                 gameData.duration = clOptions->timeLimit;
7187                 worldEventManager.callEvents(bz_eGameEndEvent,&gameData);
7188 
7189                 cleanupGameOver();
7190             }
7191 
7192             if (countdownActive && clOptions->countdownPaused && !countdownPauseStart)
7193             {
7194                 // we have a new pause
7195                 countdownPauseStart = tm;
7196                 void *buf, *bufStart = getDirectMessageBuffer ();
7197                 buf = nboPackInt (bufStart, -1);
7198                 broadcastMessage (MsgTimeUpdate, (char *) buf - (char *) bufStart, bufStart);
7199             }
7200 
7201             if (countdownActive && !clOptions->countdownPaused && (countdownResumeDelay < 0) && countdownPauseStart)
7202             {
7203                 // resumed
7204                 gameStartTime += (tm - countdownPauseStart);
7205                 countdownPauseStart = TimeKeeper::getNullTime ();
7206                 newTimeElapsed = (float)(tm - gameStartTime);
7207                 timeLeft = clOptions->timeLimit - newTimeElapsed;
7208                 void *buf, *bufStart = getDirectMessageBuffer ();
7209                 buf = nboPackInt (bufStart, (int32_t) timeLeft);
7210                 broadcastMessage (MsgTimeUpdate, (char *) buf - (char *) bufStart, bufStart);
7211             }
7212 
7213             if ((timeLeft == 0.0f || newTimeElapsed - clOptions->timeElapsed >= 30.0f || clOptions->addedTime != 0.0f)
7214                     && !clOptions->countdownPaused && (countdownResumeDelay < 0))
7215             {
7216                 // send update every 30 seconds, when the game is over, or when time adjusted
7217                 if (clOptions->addedTime != 0.0f)
7218                 {
7219                     (timeLeft + clOptions->addedTime <= 0.0f) ? timeLeft = 0.0f : clOptions->timeLimit += clOptions->addedTime;
7220                     if (timeLeft > 0.0f)
7221                         timeLeft += clOptions->addedTime;
7222                     // inform visitors about the change
7223                     sendMessage(ServerPlayer, AdminPlayers,TextUtils::format("Adjusting the countdown by %f seconds",
7224                                 clOptions->addedTime).c_str());
7225                     clOptions->addedTime = 0.0f; //reset
7226                 }
7227 
7228                 void *buf, *bufStart = getDirectMessageBuffer ();
7229                 buf = nboPackInt (bufStart, (int32_t) timeLeft);
7230                 broadcastMessage (MsgTimeUpdate, (char *) buf - (char *) bufStart, bufStart);
7231                 clOptions->timeElapsed = newTimeElapsed;
7232                 if (clOptions->oneGameOnly && timeLeft == 0.0f)
7233                 {
7234                     done = true;
7235                     exitCode = 0;
7236                 }
7237             }
7238         }
7239 
7240         requestAuthentication = false;
7241         for (int p = 0; p < curMaxPlayers; ++p)
7242         {
7243             GameKeeper::Player* playerData = GameKeeper::Player::getPlayerByIndex(p);
7244             if (!playerData)
7245                 continue;
7246             doStuffOnPlayer(*playerData);
7247         }
7248         if (requestAuthentication)
7249         {
7250             // Request the listserver authentication
7251             listServerLink->queueMessage(ListServerLink::ADD);
7252         }
7253         GameKeeper::Player::setAllNeedHostbanChecked(false);
7254 
7255         // manage voting poll for collective kicks/bans/sets
7256         if ((clOptions->voteTime > 0) && (votingarbiter != NULL))
7257         {
7258             if (votingarbiter->knowsPoll())
7259             {
7260                 char message[MessageLen];
7261 
7262                 std::string target = votingarbiter->getPollTarget();
7263                 std::string action = votingarbiter->getPollAction();
7264                 std::string realIP = votingarbiter->getPollTargetIP();
7265 
7266                 static unsigned short int voteTime = 0;
7267 
7268                 /* flags to only blather once */
7269                 static bool announcedOpening = false;
7270                 static bool announcedClosure = false;
7271                 static bool announcedResults = false;
7272                 static bool apiEventCalled = false;
7273                 static bool calledCustomHandler = false;
7274 
7275                 /* once a poll begins, announce its commencement */
7276                 if (!announcedOpening)
7277                 {
7278                     voteTime = votingarbiter->getVoteTime();
7279                     snprintf(message, MessageLen, "A poll to %s %s has begun.  Players have up to %d seconds to vote.", action.c_str(),
7280                              target.c_str(), voteTime);
7281                     sendMessage(ServerPlayer, AllPlayers, message);
7282                     announcedOpening = true;
7283                 }
7284 
7285                 static TimeKeeper lastAnnounce = TimeKeeper::getNullTime();
7286 
7287                 /* make a heartbeat announcement every 15 seconds */
7288                 if (((voteTime - (int)(tm - votingarbiter->getStartTime()) - 1) % 15 == 0) &&
7289                         ((int)(tm - lastAnnounce) != 0) &&
7290                         (votingarbiter->timeRemaining() > 0))
7291                 {
7292                     snprintf(message, MessageLen, "%d seconds remain in the poll to %s %s.", votingarbiter->timeRemaining(), action.c_str(),
7293                              target.c_str());
7294                     sendMessage(ServerPlayer, AllPlayers, message);
7295                     lastAnnounce = tm;
7296                 }
7297 
7298                 if (votingarbiter->isPollClosed())
7299                 {
7300                     if (!apiEventCalled)
7301                     {
7302                         bz_PollEndEventData_V1 pollEndData;
7303                         pollEndData.successful = votingarbiter->isPollSuccessful();
7304                         pollEndData.yesCount = votingarbiter->getYesCount();
7305                         pollEndData.noCount  = votingarbiter->getNoCount();
7306                         pollEndData.abstentionCount = votingarbiter->getAbstentionCount();
7307 
7308                         worldEventManager.callEvents(bz_ePollEndEvent, &pollEndData);
7309 
7310                         apiEventCalled = true;
7311                     }
7312 
7313                     if (!calledCustomHandler && customPollTypes.find(votingarbiter->getPollAction()) != customPollTypes.end())
7314                     {
7315                         customPollTypes[votingarbiter->getPollAction()].pollHandler->PollClose(votingarbiter->getPollAction().c_str(),
7316                                 votingarbiter->getPollTarget().c_str(), votingarbiter->isPollSuccessful());
7317                         calledCustomHandler = true;
7318                     }
7319 
7320                     if (!announcedResults)
7321                     {
7322                         snprintf(message, MessageLen, "Poll Results: %ld in favor, %ld oppose, %ld abstain", votingarbiter->getYesCount(),
7323                                  votingarbiter->getNoCount(), votingarbiter->getAbstentionCount());
7324                         sendMessage(ServerPlayer, AllPlayers, message);
7325                         announcedResults = true;
7326                     }
7327 
7328                     if (votingarbiter->isPollSuccessful())
7329                     {
7330                         if (!announcedClosure)
7331                         {
7332                             std::string pollAction;
7333                             if (action == "ban")
7334                                 pollAction = "temporarily banned";
7335                             else if (action == "kick")
7336                                 pollAction = "kicked";
7337                             else if (action == "kill")
7338                                 pollAction = "killed";
7339                             else
7340                                 pollAction = action;
7341                             // a poll that exists and is closed has ended successfully
7342                             if (action != "flagreset")
7343                                 snprintf(message, MessageLen, "The poll is now closed and was successful.  %s is scheduled to be %s.", target.c_str(),
7344                                          pollAction.c_str());
7345                             else
7346                                 snprintf(message, MessageLen,
7347                                          "The poll is now closed and was successful.  Currently unused flags are scheduled to be reset.");
7348                             sendMessage(ServerPlayer, AllPlayers, message);
7349                             announcedClosure = true;
7350                         }
7351                     }
7352                     else
7353                     {
7354                         if (!announcedClosure)
7355                         {
7356                             snprintf(message, MessageLen, "The poll to %s %s was not successful", action.c_str(), target.c_str());
7357                             sendMessage(ServerPlayer, AllPlayers, message);
7358                             announcedClosure = true;
7359 
7360                             // go ahead and reset the poll (don't bother waiting for veto timeout)
7361                             votingarbiter->forgetPoll();
7362 
7363                             // reset all announcement flags
7364                             announcedOpening = false;
7365                             announcedClosure = false;
7366                             announcedResults = false;
7367                         }
7368                     }
7369 
7370                     /* the poll either terminates by itself or via a veto command */
7371                     if (votingarbiter->isPollExpired())
7372                     {
7373 
7374                         /* maybe successful, maybe not */
7375                         if (votingarbiter->isPollSuccessful())
7376                         {
7377                             // perform the action of the poll, if any
7378                             std::string pollAction;
7379                             if (action == "ban")
7380                             {
7381                                 int hours = 0;
7382                                 int minutes = clOptions->banTime % 60;
7383                                 if (clOptions->banTime > 60)
7384                                     hours = clOptions->banTime / 60;
7385                                 pollAction = std::string("banned for ");
7386                                 if (hours > 0)
7387                                 {
7388                                     pollAction += TextUtils::format("%d hour%s%s",
7389                                                                     hours,
7390                                                                     hours == 1 ? "." : "s",
7391                                                                     minutes > 0 ? " and " : "");
7392                                 }
7393                                 if (minutes > 0)
7394                                 {
7395                                     pollAction += TextUtils::format("%d minute%s",
7396                                                                     minutes,
7397                                                                     minutes > 1 ? "s" : "");
7398                                 }
7399                                 pollAction += ".";
7400                             }
7401                             else if (action == "kick")
7402                                 pollAction = std::string("kicked.");
7403                             else if (action == "kill")
7404                                 pollAction = std::string("killed.");
7405                             else
7406                                 pollAction = action;
7407                             if (action != "flagreset")
7408                                 snprintf(message, MessageLen, "%s has been %s", target.c_str(), pollAction.c_str());
7409                             else
7410                                 snprintf(message, MessageLen, "All unused flags have now been reset.");
7411                             sendMessage(ServerPlayer, AllPlayers, message);
7412 
7413                             /* regardless of whether or not the player was found, if the poll
7414                              * is a ban poll, ban the weenie
7415                              */
7416 
7417                             if (action == "ban")
7418                             {
7419                                 // reload the banlist in case anyone else has added
7420                                 clOptions->acl.load();
7421 
7422                                 clOptions->acl.ban(realIP.c_str(), target.c_str(), clOptions->banTime);
7423                                 clOptions->acl.save();
7424 
7425                             }
7426 
7427                             if ((action == "ban") || (action == "kick"))
7428                             {
7429                                 // lookup the player id
7430                                 bool foundPlayer = false;
7431                                 int v;
7432                                 for (v = 0; v < curMaxPlayers; v++)
7433                                 {
7434                                     GameKeeper::Player *otherData
7435                                         = GameKeeper::Player::getPlayerByIndex(v);
7436                                     if (otherData && (strncmp(otherData->player.getCallSign(),
7437                                                               target.c_str(), 256) == 0))
7438                                     {
7439                                         foundPlayer = true;
7440                                         break;
7441                                     }
7442                                 }
7443                                 // show the delinquent no mercy; make sure he is kicked even if he changed
7444                                 // his callsign by finding a corresponding IP and matching it to the saved one
7445                                 if (!foundPlayer)
7446                                 {
7447                                     v = NetHandler::whoIsAtIP(realIP);
7448                                     foundPlayer = (v >= 0);
7449                                 }
7450                                 if (foundPlayer)
7451                                 {
7452                                     // notify the player
7453                                     snprintf(message, MessageLen, "You have been %s due to sufficient votes to have you removed",
7454                                              action == "ban" ? "temporarily banned" : "kicked");
7455                                     sendMessage(ServerPlayer, v, message);
7456                                     snprintf(message,  MessageLen, "/poll %s", action.c_str());
7457                                     removePlayer(v, message);
7458                                 }
7459                             }
7460                             else if (action == "kill")
7461                             {
7462                                 // lookup the player id
7463                                 bool foundPlayer = false;
7464                                 int v;
7465                                 for (v = 0; v < curMaxPlayers; v++)
7466                                 {
7467                                     GameKeeper::Player *otherData
7468                                         = GameKeeper::Player::getPlayerByIndex(v);
7469                                     if (otherData && (strncmp(otherData->player.getCallSign(),
7470                                                               target.c_str(), 256) == 0))
7471                                     {
7472                                         foundPlayer = true;
7473                                         break;
7474                                     }
7475                                 }
7476 
7477                                 if (foundPlayer)
7478                                 {
7479                                     // notify the player
7480                                     sendMessage(ServerPlayer, v, "You have been killed due to sufficient votes");
7481                                     playerKilled(v, curMaxPlayers, 0, -1, Flags::Null, -1);
7482                                 }
7483                             }
7484 
7485                             else if (action == "set")
7486                             {
7487                                 std::vector<std::string> args = TextUtils::tokenize(target.c_str(), " ", 2, true);
7488                                 if ( args.size() < 2 )
7489                                     logDebugMessage(1,"Poll set taking action: no action taken, not enough parameters (%s).\n",
7490                                                     (!args.empty() ? args[0].c_str() : "No parameters."));
7491                                 else
7492                                 {
7493                                     StateDatabase::Permission permission = BZDB.getPermission(args[0]);
7494                                     if (!(BZDB.isSet(args[0]) && (permission == StateDatabase::ReadWrite || permission == StateDatabase::Locked)))
7495                                         logDebugMessage(1,"Poll set taking action: no action taken, variable cannot be set\n");
7496                                     else
7497                                     {
7498                                         logDebugMessage(1,"Poll set taking action: setting %s to %s\n", args[0].c_str(), args[1].c_str());
7499                                         BZDB.set(args[0], args[1], StateDatabase::Server);
7500                                     }
7501                                 }
7502                             }
7503                             else if (action == "reset")
7504                             {
7505                                 logDebugMessage(1,"Poll flagreset taking action: resetting unused flags.\n");
7506                                 for (int f = 0; f < numFlags; f++)
7507                                 {
7508                                     FlagInfo &flag = *FlagInfo::get(f);
7509                                     if (flag.player == -1)
7510                                         resetFlag(flag);
7511                                 }
7512                             }
7513                         } /* end if poll is successful */
7514 
7515                         // get ready for the next poll
7516                         votingarbiter->forgetPoll();
7517 
7518                         announcedClosure = false;
7519                         announcedOpening = false;
7520                         announcedResults = false;
7521 
7522                     } // the poll expired
7523 
7524                 }
7525                 else
7526                 {
7527                     // the poll may get enough votes early
7528                     if (votingarbiter->isPollSuccessful())
7529                     {
7530                         if (action != "flagreset")
7531                             snprintf(message,  MessageLen, "Enough votes were collected to %s %s early.", action.c_str(), target.c_str());
7532                         else
7533                             snprintf(message,  MessageLen, "Enough votes were collected to reset all unused flags early.");
7534 
7535                         sendMessage(ServerPlayer, AllPlayers, message);
7536 
7537                         // close the poll since we have enough votes (next loop will kick off notification)
7538                         votingarbiter->closePoll();
7539 
7540                     } // the poll is over
7541                 } // is the poll closed
7542             } // knows of a poll
7543         } // voting is allowed and an arbiter exists
7544 
7545 
7546         // periodic advertising broadcast
7547         static const std::vector<std::string>* adLines = clOptions->textChunker.getTextChunk("admsg");
7548         if ((clOptions->advertisemsg != "") || adLines != NULL)
7549         {
7550             static TimeKeeper lastbroadcast = tm;
7551             if (tm - lastbroadcast > 900)
7552             {
7553                 // every 15 minutes
7554                 char message[MessageLen];
7555                 if (clOptions->advertisemsg != "")
7556                 {
7557                     const std::string admsg = evaluateString(clOptions->advertisemsg);
7558                     // split the admsg into several lines if it contains '\n'
7559                     const char* c = admsg.c_str();
7560                     const char* j;
7561                     while ((j = strstr(c, "\\n")) != NULL)
7562                     {
7563                         int l = j - c < MessageLen - 1 ? j - c : MessageLen - 1;
7564                         strncpy(message, c, l);
7565                         message[l] = '\0';
7566                         sendMessage(ServerPlayer, AllPlayers, message);
7567                         c = j + 2;
7568                     }
7569                     strncpy(message, c, MessageLen - 1);
7570                     message[strlen(c) < MessageLen - 1 ? strlen(c) : MessageLen -1] = '\0';
7571                     sendMessage(ServerPlayer, AllPlayers, message);
7572                 }
7573                 // multi line from file advert
7574                 if (adLines != NULL)
7575                 {
7576                     for (int j = 0; j < (int)adLines->size(); j++)
7577                     {
7578                         const std::string admsg = evaluateString((*adLines)[j]);
7579                         sendMessage(ServerPlayer, AllPlayers, admsg.c_str());
7580                     }
7581                 }
7582                 lastbroadcast = tm;
7583             }
7584         }
7585 
7586         // check team flag timeouts
7587         if (clOptions->gameType == ClassicCTF)
7588         {
7589             for (i = RedTeam; i < CtfTeams; ++i)
7590             {
7591                 if (team[i].flagTimeout - tm < 0 && team[i].team.size == 0)
7592                 {
7593                     int flagid = FlagInfo::lookupFirstTeamFlag(i);
7594                     if (flagid >= 0)
7595                     {
7596                         for (int n = 0; n < clOptions->numTeamFlags[i]; n++)
7597                         {
7598                             FlagInfo &flag = *FlagInfo::get(flagid + n);
7599                             if (flag.exist() && flag.player == -1)
7600                             {
7601                                 logDebugMessage(1,"Flag timeout for team %d\n", i);
7602                                 zapFlag(flag);
7603                             }
7604                         }
7605                     }
7606                 }
7607             }
7608         }
7609 
7610         // maybe add a super flag (only if game isn't over)
7611         if (!gameOver && clOptions->numExtraFlags > 0 && nextSuperFlagInsertion<=tm)
7612         {
7613             // randomly choose next flag respawn time; halflife distribution
7614             float r = float(bzfrand() + 0.01); // small offset, we do not want to wait forever
7615             nextSuperFlagInsertion += -logf(r) / flagExp;
7616             for (i = numFlags - clOptions->numExtraFlags; i < numFlags; i++)
7617             {
7618                 FlagInfo &flag = *FlagInfo::get(i);
7619                 if (flag.flag.type == Flags::Null)
7620                 {
7621                     // flag in now entering game
7622                     flag.addFlag();
7623                     sendFlagUpdate(flag);
7624                     break;
7625                 }
7626             }
7627         }
7628 
7629         // occasionally add ourselves to the list again (in case we were
7630         // dropped for some reason).
7631         if (clOptions->publicizeServer)
7632             if (tm - listServerLink->lastAddTime > ListServerReAddTime)
7633             {
7634                 // if there are no list servers and nobody is playing then
7635                 // try publicizing again because we probably failed to get
7636                 // the list last time we published, and if we don't do it
7637                 // here then unless somebody stumbles onto this server then
7638                 // quits we'll never try publicizing ourself again.
7639                 if (listServerLinksCount == 0)
7640                 {
7641                     // if nobody playing then publicize
7642                     if (GameKeeper::Player::count() == 0)
7643                         publicize();
7644                 }
7645 
7646                 // send add request
7647                 listServerLink->queueMessage(ListServerLink::ADD);
7648             }
7649 
7650         // check messages
7651         if (nfound > 0)
7652         {
7653             //logDebugMessage(1,"chkmsg nfound,read,write %i,%08lx,%08lx\n", nfound, read_set, write_set);
7654             // first check initial contacts
7655             if (FD_ISSET(wksSocket, &read_set))
7656                 acceptClient();
7657 
7658             // check if we have any UDP packets pending
7659             if (NetHandler::isUdpFdSet(&read_set))
7660             {
7661                 TimeKeeper receiveTime = TimeKeeper::getCurrent();
7662                 while (true)
7663                 {
7664                     struct sockaddr_in uaddr;
7665                     unsigned char ubuf[MaxPacketLen];
7666                     bool     udpLinkRequest;
7667                     // interface to the UDP Receive routines
7668                     int id = NetHandler::udpReceive((char *) ubuf, &uaddr,
7669                                                     udpLinkRequest);
7670                     if (id == -1)
7671                         break;
7672                     else if (id == -2)
7673                     {
7674                         // if I'm ignoring pings
7675                         // then ignore the ping.
7676                         if (handlePings)
7677                         {
7678                             respondToPing(Address(uaddr));
7679                             pingReply.write(NetHandler::getUdpSocket(), &uaddr);
7680                         }
7681                         continue;
7682                     }
7683                     else
7684                     {
7685                         if (udpLinkRequest)
7686                             // send client the message that we are ready for him
7687                             sendUDPupdate(id);
7688 
7689                         // handle the command for UDP
7690                         handleCommand(id, ubuf, true);
7691 
7692                         // don't spend more than 250ms receiving udp
7693                         if (TimeKeeper::getCurrent() - receiveTime > 0.25f)
7694                         {
7695                             logDebugMessage(2,"Too much UDP traffic, will hope to catch up later\n");
7696                             break;
7697                         }
7698                     }
7699                 }
7700             }
7701 
7702             // process eventual resolver requests
7703             NetHandler::checkDNS(&read_set, &write_set);
7704 
7705             // now check messages from connected players and send queued messages
7706             GameKeeper::Player *playerData;
7707             NetHandler* netPlayer(0);
7708             for (int j = 0; j < curMaxPlayers; j++)
7709             {
7710                 playerData = GameKeeper::Player::getPlayerByIndex(j);
7711                 if (!playerData || !playerData->netHandler)
7712                     continue;
7713                 netPlayer = playerData->netHandler;
7714                 // send whatever we have ... if any
7715                 if (netPlayer->pflush(&write_set) == -1)
7716                 {
7717                     removePlayer(j, "ECONNRESET/EPIPE", false);
7718                     continue;
7719                 }
7720                 playerData->handleTcpPacket(&read_set);
7721             }
7722         }
7723         else if (nfound < 0)
7724         {
7725             if (getErrno() != EINTR)
7726             {
7727                 // test code - do not uncomment, will cause big stuttering
7728                 // TimeKeeper::sleep(1.0f);
7729             }
7730         }
7731         else
7732         {
7733             if (NetHandler::anyUDPPending())
7734                 NetHandler::flushAllUDP();
7735         }
7736 
7737 
7738         // check net connected peers
7739         // see if we have any thing from people won arn't players yet
7740         std::map<int,NetConnectedPeer>::iterator peerItr;
7741 
7742         // get a list of connections to purge, then purge them
7743         std::vector<int> toKill;
7744         for (peerItr  = netConnectedPeers.begin(); peerItr != netConnectedPeers.end(); ++peerItr)
7745         {
7746             if (peerItr->second.deleteMe)
7747             {
7748                 if (peerItr->second.netHandler)
7749                     peerItr->second.netHandler->closing();
7750 
7751                 toKill.push_back(peerItr->first);
7752             }
7753         }
7754 
7755         for (unsigned int j = 0; j < toKill.size(); j++)
7756         {
7757             if (netConnectedPeers.find(toKill[j]) != netConnectedPeers.end())
7758             {
7759                 NetConnectedPeer &peer = netConnectedPeers[toKill[j]];
7760                 if (peer.netHandler)
7761                     delete(peer.netHandler);
7762                 peer.netHandler = NULL;
7763                 netConnectedPeers.erase(netConnectedPeers.find(toKill[j]));
7764             }
7765         }
7766 
7767         // process the connections
7768         for (peerItr = netConnectedPeers.begin(); peerItr != netConnectedPeers.end(); ++peerItr)
7769             processConnectedPeer(peerItr->second, peerItr->first, read_set, write_set);
7770 
7771         // remove anyone that became a player since they will be handled by the rest of the code
7772         // there net handler was transfered to the player class
7773         toKill.clear();
7774         for (peerItr = netConnectedPeers.begin(); peerItr != netConnectedPeers.end(); ++peerItr)
7775         {
7776             if (peerItr->second.player != -1)
7777                 toKill.push_back(peerItr->first);
7778         }
7779         for (unsigned int j = 0; j < toKill.size(); j++)
7780         {
7781             if (netConnectedPeers.find(toKill[j]) != netConnectedPeers.end())
7782                 netConnectedPeers.erase(netConnectedPeers.find(toKill[j]));
7783         }
7784 
7785         // remove anyone that hasn't done anything in a long time
7786         toKill.clear();
7787         double timeoutNow = TimeKeeper::getCurrent().getSeconds();
7788 
7789         for (peerItr = netConnectedPeers.begin(); peerItr != netConnectedPeers.end(); ++peerItr)
7790         {
7791             if (timeoutNow > (peerItr->second.lastActivity.getSeconds() + peerItr->second.inactivityTimeout))
7792             {
7793                 peerItr->second.netHandler->closing();
7794                 toKill.push_back(peerItr->first);
7795             }
7796         }
7797         for (unsigned int j = 0; j < toKill.size(); j++)
7798         {
7799             NetConnectedPeer &peer = netConnectedPeers[toKill[j]];
7800             if (peer.netHandler)
7801                 delete(peer.netHandler);
7802             peer.netHandler = NULL;
7803             netConnectedPeers.erase(netConnectedPeers.find(toKill[j]));
7804         }
7805 
7806 
7807         // Fire world weapons
7808         world->getWorldWeapons().fire();
7809 
7810         // update all the shots we have tracked
7811         ShotManager.Update();
7812 
7813         // send out any pending chat messages
7814         std::list<PendingChatMessages>::iterator itr = pendingChatMessages.begin();
7815         while ( itr != pendingChatMessages.end() )
7816         {
7817             ::sendChatMessage(itr->from,itr->to,itr->text.c_str(),itr->type);
7818             ++itr;
7819         }
7820         pendingChatMessages.clear();
7821 
7822         // fire off a tick event
7823         bz_TickEventData_V1 tickData;
7824         worldEventManager.callEvents(bz_eTickEvent,&tickData);
7825 
7826         ApiTick();
7827 
7828         // Spawn waiting players
7829         doSpawns();
7830 
7831         // Clean pending players
7832         bool resetGame = GameKeeper::Player::clean();
7833 
7834         if (resetGame && playerHadWorld)
7835         {
7836             playerHadWorld = false;
7837             if ((clOptions->worldFile == "") && !Replay::enabled())
7838                 defineWorld();
7839         }
7840 
7841         // cURLperform should be called in any case as we could incur in timeout
7842         dontWait = dontWait || cURLManager::perform();
7843     }
7844 
7845     bzUPnP.stop();
7846 #ifdef BZ_PLUGINS
7847     unloadPlugins();
7848 #endif
7849 
7850     // print uptime
7851     logDebugMessage(1,"Shutting down server: uptime %s\n",
7852                     TimeKeeper::printTime(TimeKeeper::getCurrent() - TimeKeeper::getStartTime()).c_str());
7853 
7854     serverStop();
7855 
7856     // remove from list server and disconnect
7857     delete listServerLink;
7858 
7859     // free misc stuff
7860     AresHandler::globalShutdown();
7861 
7862     // free misc stuff
7863     delete clOptions;
7864     clOptions = NULL;
7865     FlagInfo::setSize(0);
7866     delete world;
7867     world = NULL;
7868     delete[] worldDatabase;
7869     worldDatabase = NULL;
7870     delete votingarbiter;
7871     votingarbiter = NULL;
7872 
7873     Record::kill();
7874     Replay::kill();
7875     Flags::kill();
7876 
7877 #if defined(_WIN32)
7878     WSACleanup();
7879 #endif /* defined(_WIN32) */
7880 
7881     // done
7882     return exitCode;
7883 }
7884 
playerStateToAPIState(bz_PlayerUpdateState & apiState,const PlayerState & playerState)7885 void playerStateToAPIState(bz_PlayerUpdateState &apiState, const PlayerState &playerState)
7886 {
7887     apiState.status = eAlive;
7888     if (playerState.status == PlayerState::DeadStatus) // DeadStatus is 0
7889         apiState.status = eDead;
7890     //FIXME  else if (playerState.status & PlayerState::Paused)
7891     //FIXME    apiState.status = ePaused;
7892     else if (playerState.status & PlayerState::Exploding)
7893         apiState.status = eExploding;
7894     else if (playerState.status & PlayerState::Teleporting)
7895         apiState.status = eTeleporting;
7896 //  else if (playerState.status & PlayerState::InBuilding)
7897 //   apiState.status = eInBuilding;
7898 
7899     apiState.inPhantomZone = false;//(playerState.status & PlayerState::PhantomZoned) != 0;
7900     apiState.falling = (playerState.status & PlayerState::Falling) != 0;
7901     apiState.crossingWall = (playerState.status & PlayerState::CrossingWall) != 0;
7902     apiState.phydrv = (playerState.status & PlayerState::OnDriver) ? playerState.phydrv : -1;
7903     apiState.rotation = playerState.azimuth;
7904     apiState.angVel = playerState.angVel;
7905     memcpy(apiState.pos,playerState.pos,sizeof(float)*3);
7906     memcpy(apiState.velocity,playerState.velocity,sizeof(float)*3);
7907 }
7908 
7909 
APIStateToplayerState(PlayerState & playerState,const bz_PlayerUpdateState & apiState)7910 void APIStateToplayerState(PlayerState &playerState, const bz_PlayerUpdateState &apiState)
7911 {
7912     playerState.status = 0;
7913     switch (apiState.status)
7914     {
7915     case eDead:
7916         playerState.status = PlayerState::DeadStatus; // DeadStatus = 0
7917         break;
7918     case eAlive:
7919         playerState.status |= PlayerState::Alive;
7920         break;
7921     case ePaused:
7922         playerState.status |= PlayerState::Paused;
7923         break;
7924     case eExploding:
7925         playerState.status |= PlayerState::Exploding;
7926         break;
7927     case eTeleporting:
7928         playerState.status |= PlayerState::Teleporting;
7929         break;
7930 
7931     default: // FIXME
7932         break;
7933         //  case eInBuilding:
7934         //    playerState.status |= PlayerState::InBuilding;
7935         //    break;
7936     }
7937 
7938 // if (apiState.inPhantomZone)
7939 //   playerState.status |=  PlayerState::PhantomZoned;
7940 
7941     if (apiState.falling)
7942         playerState.status |=  PlayerState::Falling;
7943 
7944     if (apiState.crossingWall)
7945         playerState.status |=  PlayerState::CrossingWall;
7946 
7947     if (apiState.phydrv != -1)
7948     {
7949         playerState.status |=  PlayerState::OnDriver;
7950         playerState.phydrv = apiState.phydrv;
7951     }
7952 
7953     playerState.azimuth = apiState.rotation;
7954     playerState.angVel = apiState.angVel;
7955     memcpy(playerState.pos,apiState.pos,sizeof(float)*3);
7956     memcpy(playerState.velocity,apiState.velocity,sizeof(float)*3);
7957 }
7958 
7959 
7960 // Local Variables: ***
7961 // mode: C++ ***
7962 // tab-width: 4 ***
7963 // c-basic-offset: 4 ***
7964 // indent-tabs-mode: nil ***
7965 // End: ***
7966 // ex: shiftwidth=4 tabstop=4
7967