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