· 8 years ago · Jan 28, 2018, 06:50 PM
1#include "r3dPCH.h"
2#include "r3d.h"
3#include "GameLevel.h"
4
5#include "ServerGameLogic.h"
6#include "MasterServerLogic.h"
7#include "GameObjects/ObjManag.h"
8
9#include "ObjectsCode/obj_ServerPlayer.h"
10#include "ObjectsCode/obj_ServerGravestone.h"
11#include "ObjectsCode/obj_ServerGrenade.h"
12#include "ObjectsCode/obj_ServerLightMesh.h"
13#include "ObjectsCode/sobj_SpawnedItem.h"
14#include "ObjectsCode/sobj_DroppedItem.h"
15#include "ObjectsCode/sobj_Note.h"
16#include "ObjectsCode/Zombies/sobj_Zombie.h"
17#include "ObjectsCode/Vehicles/obj_Vehicle.h"
18#include "ObjectsCode/obj_ServerPlayerSpawnPoint.h"
19#include "ObjectsCode/Zombies/sobj_ZombieSpawn.h"
20#include "ObjectsCode/obj_ServerBarricade.h"
21#include "ObjectsCode/obj_ServerLockbox.h"
22#include "ObjectsCode/obj_ServerFarmBlock.h"
23#include "ObjectsCode/Missions/MissionManager.h"
24#include "ObjectsCode/Missions/MissionTrigger.h"
25#include "ObjectsCode/obj_ServerItemSpawnPoint.h"//
26#include "ObjectsCode/obj_ServerGasZone.h"//
27#include "../EclipseStudio/Sources/ObjectsCode/Gameplay/BattleZone.h"//
28
29#include "../EclipseStudio/Sources/GameCode/UserProfile.h"
30#include "../EclipseStudio/Sources/ObjectsCode/weapons/WeaponArmory.h"
31#include "../EclipseStudio/Sources/ObjectsCode/Gameplay/ZombieStates.h"
32
33#include "ServerWeapons/ServerWeapon.h"
34
35#include "NetworkHelper.h"
36#include "AsyncFuncs.h"
37#include "Async_ServerObjects.h"
38#include "Async_ServerState.h"
39#include "TeamSpeakServer.h"
40
41#ifdef ENABLE_GAMEBLOCKS
42#include "GBClient/Inc/GBClient.h"
43#include "GBClient/Inc/GBReservedEvents.h"
44
45#pragma comment(lib, "../../../server/GameBlocksSDK/Lib/vc100/Win32/GBClient.lib")
46
47// PunkBuster SDK
48#ifdef __WITH_PB__
49#include "PunkBuster/pbcommon.h"
50#endif
51
52GameBlocks::GBClient* g_GameBlocks_Client = NULL;
53GameBlocks::GBPublicSourceId g_GameBlocks_ServerID=0;
54bool g_GameBlocks_SentServerInfo = false;
55#endif //ENABLE_GAMEBLOCKS
56
57ServerGameLogic gServerLogic;
58
59const float RESPAWN_TIME_AIRDROPBR = 5.0f * 60.0f;//
60const float RESPAWN_TIME_AIRDROP = 4.0f * 60.0f * 60.0f; // 4 hours
61CVAR_FLOAT( _glm_bzOutKillTime, 5.0f, "battle zone out kill time");//
62
63CVAR_FLOAT( _glm_SpawnRadius, 1.0f, "");
64
65extern __int64 cfg_sessionId;
66
67#include "../EclipseStudio/Sources/Gameplay_Params.h"
68 CGamePlayParams GPP_Data;
69 DWORD GPP_Seed = GetTickCount(); // seed used to xor CRC of gpp_data
70
71extern float getWaterDepthAtPos(const r3dPoint3D& pos);
72
73
74bool IsNullTerminated(const char* data, int size)
75{
76 for(int i=0; i<size; i++) {
77 if(data[i] == 0)
78 return true;
79 }
80
81 return false;
82}
83
84//
85//
86//
87//
88static void preparePacket(const GameObject* from, DefaultPacket* packetData)
89{
90 r3d_assert(packetData);
91 r3d_assert(packetData->EventID >= 0);
92
93 if(from && from->Class->Name != "obj_Building") {
94 r3d_assert(from->GetNetworkID());
95 //r3d_assert(from->NetworkLocal);
96
97 packetData->FromID = toP2pNetId(from->GetNetworkID());
98 } else {
99 packetData->FromID = 0; // world event
100 }
101
102 return;
103}
104
105ServerGameLogic::ServerGameLogic()
106{
107 curPlayers_ = 0;
108 maxLoggedPlayers_ = 0;
109 curPeersConnected = 0;
110
111 if(!ginfo_.IsGameBR())//
112 AirDropSpawnTime = r3dGetTime() + RESPAWN_TIME_AIRDROP;
113
114 // init index to players table
115 for(int i=0; i<MAX_NUM_PLAYERS; i++) {
116 plrToPeer_[i] = NULL;
117 }
118
119 // init peer to player table
120 for(int i=0; i<MAX_PEERS_COUNT; i++) {
121 peers_[i].Clear();
122 }
123
124 memset(&netRecvPktSize, 0, sizeof(netRecvPktSize));
125 memset(&netSentPktSize, 0, sizeof(netSentPktSize));
126
127 net_lastFreeId = NETID_OBJECTS_START;
128 net_mapLoaded_LastNetID = 0;
129
130 weaponStats_.reserve(128);
131}
132
133ServerGameLogic::~ServerGameLogic()
134{
135}
136
137void ServerGameLogic::Init(const GBGameInfo& ginfo, uint32_t creatorID)
138{
139 r3dOutToLog("Game: Initializing with %d players\n", ginfo.maxPlayers); CLOG_INDENT;
140 r3d_assert(curPlayers_ == 0);
141 r3d_assert(curPeersConnected == 0);
142
143 creatorID_ = creatorID;
144 ginfo_ = ginfo;
145 curPlayers_ = 0;
146 curPeersConnected = 0;
147
148 secsWithoutPlayers_ = 0;
149 hibernateStarted_ = false;
150 gameStartTimeAdminOffset = 0;
151
152 //
153 if(ginfo_.IsGameBR())
154 {
155 m_isGameHasStarted = false;
156 ForceStarGame = false;
157 isCountingDown = false;
158 canFinishMatch = false;
159 haveWinningPlayer = false;
160 playersTeleported = false;
161 isGasSuspended = true;
162 winningPlr = NULL;
163 suspendTimer = 0;
164 gasint = 0;
165 rewardBR = 100;//reward here. synch point server/client (obj_Player::OnCreate() || obj_Player::OnDestroy())
166 canCheckPlayers = r3dGetTime();
167 gastimer = r3dGetTime();
168 AirDropSpawnTime = r3dGetTime() + RESPAWN_TIME_AIRDROPBR;
169
170 r3dOutToLog("|=======================| Server game: Battle Royale |=======================|\n");
171 }
172 //
173
174 nextGroupID = 1;
175
176 nextLootboxUpdate_ = r3dGetTime() + u_GetRandom(10*60, 15*60); // spread lootbox update a bit across all servers
177
178 // init game time
179 gameStartTime_ = r3dGetTime();
180
181 __int64 utcTime = GetUtcGameTime();
182 struct tm* tm = _gmtime64(&utcTime);
183 r3d_assert(tm);
184
185 char buf[128];
186 asctime_s(buf, sizeof(buf), tm);
187 r3dOutToLog("Server time is %s", buf);
188
189 return;
190}
191
192void ServerGameLogic::SetNewGameInfo(DWORD flags, int gameTimeLimit)
193{
194 ginfo_.flags = flags;
195 ginfo_.gameTimeLimit = gameTimeLimit;
196
197 PKT_S2C_SetGameInfoFlags_s n;
198 n.gameInfoFlags = ginfo_.flags;
199 //
200 if(ginfo_.IsGameBR() && !isCountingDown)
201 n.GD = rewardBR*playersStarted;
202 else
203 n.GD =0;
204 //
205 p2pBroadcastToAll(&n, sizeof(n), true);
206}
207
208void ServerGameLogic::CreateHost(int port)
209{
210 r3dOutToLog("Starting server on port %d\n", port);
211
212 g_net.Initialize(this, "p2pNet");
213 g_net.CreateHost(port, MAX_PEERS_COUNT);
214 //g_net.dumpStats_ = 2;
215
216 return;
217}
218
219void ServerGameLogic::Disconnect()
220{
221 r3dOutToLog("Disconnect server\n");
222
223#ifdef ENABLE_GAMEBLOCKS
224 if(g_GameBlocks_Client)
225 {
226 g_GameBlocks_Client->Close();
227 delete g_GameBlocks_Client;
228 g_GameBlocks_Client = NULL;
229 }
230#endif //ENABLE_GAMEBLOCKS
231
232 g_net.Deinitialize();
233
234 return;
235}
236
237void ServerGameLogic::OnGameStart()
238{
239 /*
240 if(1)
241 {
242 r3dOutToLog("Main World objects\n"); CLOG_INDENT;
243 for(GameObject* obj=GameWorld().GetFirstObject(); obj; obj=GameWorld().GetNextObject(obj))
244 {
245 if(obj->isActive()) r3dOutToLog("obj: %s %s\n", obj->Class->Name.c_str(), obj->Name.c_str());
246 }
247 }
248
249 if(1)
250 {
251 extern ObjectManager ServerDummyWorld;
252 r3dOutToLog("Temporary World objects\n"); CLOG_INDENT;
253 for(GameObject* obj=ServerDummyWorld.GetFirstObject(); obj; obj=ServerDummyWorld.GetNextObject(obj))
254 {
255 if(obj->isActive()) r3dOutToLog("obj: %s %s\n", obj->Class->Name.c_str(), obj->Name.c_str());
256 }
257 }*/
258
259
260 // record last net id
261 net_mapLoaded_LastNetID = net_lastFreeId;
262 r3dOutToLog("net_mapLoaded_LastNetID: %d\n", net_mapLoaded_LastNetID);
263
264 // start getting server objects
265 g_AsyncApiMgr->AddJob(new CJobGetServerObjects());
266 // and saved state for objects
267 g_AsyncApiMgr->AddJob(new CJobGetSavedServerState());
268
269#ifdef ENABLE_GAMEBLOCKS
270// start gameblocks
271 bool startGameblocks = false; // THAI server
272 if(startGameblocks)
273 {
274 if(g_GameBlocks_Client)
275 {
276 r3dError("Gameblocks already initialized?!\n");
277 }
278 g_GameBlocks_SentServerInfo = false;
279 g_GameBlocks_Client = new GameBlocks::GBClient();
280 g_GameBlocks_ServerID = uint32_t(ginfo_.gameServerId);
281
282/* GameBlocks::ClientConfigData gameblocks_config;
283 gameblocks_config.gameblocksEnabled = true;
284 gameblocks_config.serverAddress = "216.152.129.156";
285 gameblocks_config.serverPort = 8730;
286 gameblocks_config.sourceId = g_GameBlocks_ServerID;
287 gameblocks_config.timeoutPeriod = 7;
288 g_GameBlocks_Client->SetConfig(gameblocks_config);
289
290 if(!g_GameBlocks_Client->Connect())*/
291
292 extern int cfg_uploadLogs;
293
294 const char* gbServerURL=NULL;
295 const char* gbServerIP=NULL;
296 int gbServerPort=0;
297 if(cfg_uploadLogs) // production environment (todo: need a different variable for that)
298 {
299 gbServerURL = "hammerpoint.infestation1.prod.gameblocks.info";
300 gbServerPort = 8730;
301 }
302 else
303 {
304 gbServerIP = "67.86.56.36";
305 gbServerPort = 8730;
306 }
307
308
309 r3dOutToLog("Connecting to gameblocks server '%s' at port %d\n", gbServerURL?gbServerURL:gbServerIP, gbServerPort);
310
311 GameBlocks::EGBClientError errCode;
312 if(gbServerURL)
313 errCode = g_GameBlocks_Client->ConnectUsingUrl(gbServerURL, gbServerPort, g_GameBlocks_ServerID);
314 else
315 errCode = g_GameBlocks_Client->Connect(gbServerIP, gbServerPort, g_GameBlocks_ServerID);
316
317 if(errCode != GameBlocks::GBCLIENT_ERROR_OK)
318 {
319 r3dOutToLog("Gameblocks failed to connect, code: %d\n", errCode);
320 }
321 g_GameBlocks_Client->SetTimeoutPeriod(30);
322
323 g_GameBlocks_Client->EnableAimBotDetector(8, 0.1f);
324 if(!cfg_uploadLogs) // dev env only
325 g_GameBlocks_Client->SetAimBotDetectorDebugPlayer("1641409");
326 g_GameBlocks_Client->EnableWeaponCheatDetector(300);
327 }
328#endif //ENABLE_GAMEBLOCKS
329}
330
331DWORD ServerGameLogic::GetFreeNetId()
332{
333 DWORD id = net_lastFreeId;
334
335 while(true)
336 {
337 if(GameWorld().GetNetworkObject(id) == NULL)
338 {
339 net_lastFreeId = id + 1;
340 if(net_lastFreeId > 0xFFF0)
341 net_lastFreeId = net_mapLoaded_LastNetID;
342
343 return id;
344 }
345
346 id++;
347 if(id > 0xFFF0)
348 id = net_mapLoaded_LastNetID;
349
350 // check if we looped back
351 if(id == net_lastFreeId)
352 r3dError("net_lastFreeId overflow, no free slots");
353 }
354}
355
356void ServerGameLogic::CheckPeerValidity()
357{
358 const float PEER_CHECK_DELAY = 0.2f; // do checks every N seconds
359 const float IDLE_PEERS_DELAY = 5.0f; // peer have this N seconds to validate itself
360 const float LOADING_PEER_PING = 60.0f; // loading peers must ping API every N seconds to indicate that player still in game
361 const float LOADING_MAX_TIME = 600.0f; // max loading time for client
362 const float SECREP1_DELAY = PKT_C2S_SecurityRep_s::REPORT_PERIOD*8; // x4 time of client reporting time (15sec) to receive security report
363
364 static float nextCheck = -1;
365 const float curTime = r3dGetTime();
366 if(curTime < nextCheck)
367 return;
368 nextCheck = curTime + PEER_CHECK_DELAY;
369
370 for(int peerId=0; peerId<MAX_PEERS_COUNT; peerId++)
371 {
372 peerInfo_s& peer = peers_[peerId];
373 if(peer.status_ == PEER_FREE)
374 continue;
375
376 // check againts not validated peers
377 if(peer.status_ == PEER_CONNECTED)
378 {
379 if(curTime < peer.startTime + IDLE_PEERS_DELAY)
380 continue;
381
382 DisconnectPeer(peerId, false, "no validation, last:%f/%d", peer.lastPacketTime, peer.lastPacketId);
383 continue;
384 }
385
386 // loading peers
387 if(peer.status_ == PEER_LOADING)
388 {
389 if(curTime > peer.startTime + LOADING_MAX_TIME)
390 {
391 DisconnectPeer(peerId, true, "too long loading");
392 continue;
393 }
394
395 // have profile and joined the game
396 if(peer.startGameAns == PKT_S2C_StartGameAns_s::RES_Ok)
397 {
398 if(peer.nextPingOnLoad < 0)
399 peer.nextPingOnLoad = curTime + LOADING_PEER_PING;
400
401 if(curTime > peer.nextPingOnLoad)
402 {
403 peer.nextPingOnLoad = curTime + LOADING_PEER_PING;
404 CJobUserPingGame* job = new CJobUserPingGame(peerId);
405 g_AsyncApiMgr->AddJob(job);
406 }
407 }
408 continue;
409 }
410
411 // for playing peers - check for receiveing security report
412 if(peer.player != NULL)
413 {
414 if(curTime > peer.secRepRecvTime + SECREP1_DELAY)
415 {
416 //DisconnectPeer(peerId, false, "no SecRep, last:%f/%d", peer.lastPacketTime, peer.lastPacketId);
417 continue;
418 }
419 if(curTime > m_LastConnectionRep + 120)
420 {
421 peer.player->isHighPing = false;
422 continue;
423 }
424 }
425
426 /*if(peer.player != NULL)//AlexRedd:: last connection
427 {
428 if(curTime > m_LastConnectionRep + 120)
429 {
430 peer.player->isHighPing = false;
431 continue;
432 }
433 }*/
434 }
435
436 return;
437}
438
439//Skin system
440void ServerGameLogic::ApiPlayerUpdateWeaponSkins(obj_ServerPlayer* plr)
441{
442 for( std::map< uint32_t, obj_ServerPlayer::WeaponSkins >::iterator Skinids = plr->WpnSKN.begin();Skinids != plr->WpnSKN.end(); ++Skinids )
443 {
444 obj_ServerPlayer::WeaponSkins& SkinWpn = Skinids->second;
445
446 if (SkinWpn.itemID != 0)
447 {
448 //r3dOutToLog("############ GUARDANDO EN LA DB\n");
449 for (int i = 0;i<MAX_SKINS_BY_WEAPON;i++)
450 {
451 if (SkinWpn.SkinsBought[i] != 99)
452 {
453 CJobChangeWeaponSkins* job = new CJobChangeWeaponSkins(plr);
454 job->ItemID = SkinWpn.itemID;
455 job->Skin = SkinWpn.SkinsBought[i];
456 job->LastSkinUsed = SkinWpn.LastSKINused;
457 g_AsyncApiMgr->AddJob(job);
458 }
459 }
460 }
461 }
462}
463/////
464
465void ServerGameLogic::ApiPlayerUpdateChar(obj_ServerPlayer* plr, bool disconnectAfter)
466{
467 r3d_assert(!plr->inventoryOpActive_);
468
469 // force current GameFlags update
470 plr->UpdateGameWorldFlags();
471
472 CJobUpdateChar* job = new CJobUpdateChar(plr);
473 job->CharData = *plr->loadout_;
474 job->OldData = plr->savedLoadout_;
475 job->Disconnect = disconnectAfter;
476 job->GD_Diff = plr->profile_.ProfileData.GameDollars - plr->savedGameDollars_;
477 job->ResWood = plr->profile_.ProfileData.ResWood;
478 job->ResStone = plr->profile_.ProfileData.ResStone;
479 job->ResMetal = plr->profile_.ProfileData.ResMetal;
480 job->SwearingCount = plr->profile_.ProfileData.SwearingCount;
481 // add character play time to update data
482 job->CharData.Stats.TimePlayed += (int)(r3dGetTime() - plr->startPlayTime_);
483 g_AsyncApiMgr->AddJob(job);
484
485 bool needPing=false;
486#ifdef DISABLE_GI_ACCESS_ON_PTE_MAP
487 if(gServerLogic.ginfo_.channel==6) // in PTE game, ping player
488 needPing=true;
489#endif
490#ifdef DISABLE_GI_ACCESS_ON_PTE_STRONGHOLD_MAP
491 if(gServerLogic.ginfo_.channel==6 && gServerLogic.ginfo_.mapId==GBGameInfo::MAPID_WZ_Cliffside) // in PTE game, ping player
492 needPing=true;
493#endif
494#ifdef DISABLE_GI_ACCESS_FOR_CALI_SERVER
495 if(gServerLogic.ginfo_.mapId==GBGameInfo::MAPID_WZ_California)
496 needPing=true;
497#endif
498#ifdef DISABLE_GI_ACCESS_FOR_DEV_EVENT_SERVER
499 if(gServerLogic.ginfo_.channel==6)
500 needPing=true;
501#endif
502
503 if(needPing && !disconnectAfter)
504 {
505 CJobUserPingGame* pingJob = new CJobUserPingGame(plr->peerId_);
506 g_AsyncApiMgr->AddJob(pingJob);
507 }
508 // replace character saved loadout. if update will fail, we'll disconnect player and keep everything at sync
509 plr->savedLoadout_ = *plr->loadout_;
510 plr->savedGameDollars_ = plr->profile_.ProfileData.GameDollars;
511}
512
513void ServerGameLogic::ApiPlayerLeftGame(DWORD peerId)
514{
515 const ServerGameLogic::peerInfo_s& peer = GetPeer(peerId);
516 r3d_assert(peer.status_ >= PEER_LOADING);
517
518 CJobUserLeftGame* job = new CJobUserLeftGame(peerId);
519 job->GameMapId = ginfo_.mapId;
520 job->GameServerId = ginfo_.gameServerId;
521 if(peer.player) {
522 job->TimePlayed = (int)(r3dGetTime() - peer.player->startPlayTime_);
523 }
524 g_AsyncApiMgr->AddJob(job);
525}
526
527void ServerGameLogic::LogInfo(DWORD peerId, const char* msg, const char* fmt, ...)
528{
529 char buf[4096];
530 va_list ap;
531 va_start(ap, fmt);
532 StringCbVPrintfA(buf, sizeof(buf), fmt, ap);
533 va_end(ap);
534
535 LogCheat(peerId, 0, false, msg, buf);
536}
537
538void ServerGameLogic::LogCheat(DWORD peerId, int LogID, int disconnect, const char* msg, const char* fmt, ...)
539{
540 char buf[4096];
541 va_list ap;
542 va_start(ap, fmt);
543 StringCbVPrintfA(buf, sizeof(buf), fmt, ap);
544 va_end(ap);
545
546 const peerInfo_s& peer = GetPeer(peerId);
547 DWORD IP = net_->GetPeerIp(peerId);
548
549 //extern int cfg_uploadLogs; // we don't use it anymore
550 if(LogID > 0) // always upload cheats
551 {
552 CJobAddLogInfo* job = new CJobAddLogInfo();
553 job->CheatID = LogID;
554 job->CustomerID = peer.CustomerID;
555 job->CharID = peer.CharID;
556 job->IP = IP;
557 r3dscpy(job->Gamertag, peer.temp_profile.ProfileData.ArmorySlots[0].Gamertag);
558 r3dscpy(job->Msg, msg);
559 r3dscpy(job->Data, buf);
560 g_AsyncApiMgr->AddJob(job);
561 }
562
563 const char* screenname = "<NOT_CONNECTED>";
564 if(peer.status_ == PEER_PLAYING)
565 screenname = peer.temp_profile.ProfileData.ArmorySlots[0].Gamertag;
566
567 r3dOutToLog("%s: peer%02d, r:%s %s, CID:%d [%s], ip:%s\n",
568 LogID > 0 ? "!!! cheat" : "LogInfo",
569 peerId,
570 msg, buf,
571 peer.CustomerID, screenname,
572 inet_ntoa(*(in_addr*)&IP));
573
574 if(disconnect && peer.player)
575 {
576 // tell client he's cheating.
577 // ptumik: no need to make it easier to hack
578 //PKT_S2C_CheatWarning_s n2;
579 //n2.cheatId = (BYTE)cheatId;
580 //p2pSendRawToPeer(peerId, NULL, &n2, sizeof(n2));
581
582 net_->DisconnectPeer(peerId);
583 // fire up disconnect event manually, enet might skip if if other peer disconnect as well
584 OnNetPeerDisconnected(peerId);
585 }
586
587 return;
588}
589
590void ServerGameLogic::DisconnectPeer(DWORD peerId, bool cheat, const char* fmt, ...)
591{
592 char buf[1024];
593 va_list ap;
594 va_start(ap, fmt);
595 StringCbVPrintfA(buf, sizeof(buf), fmt, ap);
596 va_end(ap);
597
598 LogCheat(peerId, cheat ? 99 : 0, false, "DisconnectPeer", buf);
599
600 net_->DisconnectPeer(peerId);
601
602 // fire up disconnect event manually, enet might skip if if other peer disconnect as well
603 OnNetPeerDisconnected(peerId);
604}
605
606void ServerGameLogic::OnNetPeerConnected(DWORD peerId)
607{
608 //
609 /*if (ginfo_.IsGameBR())
610 {
611 int startPlayers = (int)(ginfo_.maxPlayers * 0.5f);
612 if (curPlayers_ >= startPlayers && !m_isGameHasStarted && !isCountingDown)
613 {
614 r3dOutToLog("Minimum required players achieved, starting match\n");
615 StartCountdown();
616 }
617 }*/
618 //
619
620 // too many connections, do not allow more connects
621 if(peerId >= MAX_PEERS_COUNT)
622 {
623 r3dOutToLog("!!! peer%02d over MAX_PEERS_COUNT connected\n", peerId);
624 net_->DisconnectPeer(peerId);
625 return;
626 }
627
628 if(gMasterServerLogic.shuttingDown_ || gameFinished_)
629 {
630 r3dOutToLog("peer connected while game is finished or shutting down\n");
631 net_->DisconnectPeer(peerId);
632 return;
633 }
634
635 r3d_assert(ginfo_.maxPlayers > 0);
636
637 //r3dOutToLog("peer%02d connected\n", peerId); CLOG_INDENT;
638
639 peerInfo_s& peer = GetPeer(peerId);
640 peer.SetStatus(PEER_CONNECTED);
641
642 curPeersConnected++;
643 return;
644}
645
646void ServerGameLogic::OnNetPeerDisconnected(DWORD peerId)
647{
648 // too many connections, do not do anything
649 if(peerId >= MAX_PEERS_COUNT)
650 return;
651
652 //r3dOutToLog("peer%02d disconnected\n", peerId); CLOG_INDENT;
653
654 //
655 if (ginfo_.IsGameBR())
656 playersLeft--;
657 //
658
659 peerInfo_s& peer = GetPeer(peerId);
660
661 // debug validation
662 switch(peer.status_)
663 {
664 default:
665 r3dError("!!! Invalid status %d in disconnect !!!", peer.status_);
666 break;
667 case PEER_FREE:
668 break;
669
670 case PEER_CONNECTED:
671 case PEER_VALIDATED1:
672 r3d_assert(peer.player == NULL);
673 r3d_assert(peer.playerIdx == -1);
674 break;
675
676 case PEER_LOADING:
677 r3d_assert(peer.playerIdx != -1);
678 r3d_assert(plrToPeer_[peer.playerIdx] != NULL);
679 r3d_assert(peer.player == NULL);
680
681 // if we got profile and joined game we need to report that player left to server.
682 /*if(peer.startGameAns == PKT_S2C_StartGameAns_s::RES_Ok)
683 {
684 ApiPlayerLeftGame(peerId);
685 }*/
686
687 plrToPeer_[peer.playerIdx] = NULL;
688 break;
689
690 case PEER_PLAYING:
691 r3d_assert(peer.playerIdx != -1);
692 r3d_assert(plrToPeer_[peer.playerIdx] != NULL);
693 r3d_assert(plrToPeer_[peer.playerIdx] == &peer);
694 if(peer.player)
695 {
696#ifdef VEHICLES_ENABLED
697 // check if player is in vehicle, if is, remove player from vehicle
698 if (peer.player->IsInVehicle())
699 {
700 obj_Vehicle* vehicle = obj_Vehicle::GetVehicleById(peer.player->currentVehicleId);
701 if (vehicle)
702 {
703 peer.player->ExitVehicle(true, true, true);
704 }
705 }
706#endif
707 gMasterServerLogic.RemovePlayer(peer.playerIdx);
708
709 if(peer.player->loadout_->Alive && !peer.player->wasDisconnected_/* && !peer.player->inventoryOpActive_*/)
710 {
711 r3dOutToLog("peer%02d player %s is updating his data\n", peerId, peer.player->userName);
712 ApiPlayerUpdateChar(peer.player);
713 }
714
715 ApiPlayerLeftGame(peerId);
716
717 // report to users
718 //if(!(peer.player->profile_.ProfileData.isDevAccount & wiUserProfile::DAA_HIDDEN))
719 {
720 PKT_S2C_PlayerNameLeft_s n;
721 n.playerIdx = (WORD)peer.playerIdx;
722
723 // send to all, regardless visibility
724 for(int i=0; i<MAX_PEERS_COUNT; i++) {
725 if(peers_[i].status_ >= PEER_PLAYING && peers_[i].player && i != peerId) {
726 net_->SendToPeer(&n, sizeof(n), i, true);
727 }
728 }
729 }
730
731 DeletePlayer(peer.playerIdx, peer.player);
732 }
733
734 plrToPeer_[peer.playerIdx] = NULL;
735 break;
736 }
737
738 if(peer.status_ != PEER_FREE)
739 {
740 // OnNetPeerDisconnected can fire multiple times, because of forced disconnect
741 curPeersConnected--;
742 }
743
744 // clear peer status
745 peer.Clear();
746
747 return;
748}
749
750float ServerGameLogic::getInGameTime()
751{
752 __int64 gameUtcTime = GetUtcGameTime();
753 struct tm* tm = _gmtime64(&gameUtcTime);
754 r3d_assert(tm);
755 return ((float)tm->tm_hour + (float)tm->tm_min / 59.0f);
756}
757
758void ServerGameLogic::OnNetData(DWORD peerId, const r3dNetPacketHeader* packetData, int packetSize)
759{
760#ifdef __WITH_PB__
761 if ( memcmp ( packetData, "\xff\xff\xff\xffPB_", 7 ) == 0 ) {
762 PbSvAddEvent ( PB_EV_PACKET, (int)peerId, packetSize-4, (char*)packetData+4 ) ;
763 PbClAddEvent ( PB_EV_PACKET, packetSize-4, (char*)packetData+4 ) ;
764 return ;
765 }
766#endif
767
768 // too many connections, do not do anything
769 if(peerId >= MAX_PEERS_COUNT)
770 return;
771
772 // we can receive late packets from logically disconnected peer.
773 peerInfo_s& peer = GetPeer(peerId);
774 if(peer.status_ == PEER_FREE)
775 return;
776
777 if(packetSize < sizeof(DefaultPacket))
778 {
779 DisconnectPeer(peerId, true, "small packetSize %d", packetSize);
780 return;
781 }
782 const DefaultPacket* evt = static_cast<const DefaultPacket*>(packetData);
783
784 // store last packet data for debug
785 peer.lastPacketTime = r3dGetTime();
786 peer.lastPacketId = evt->EventID;
787
788 // store received sizes by packets
789 if(evt->EventID < 256)
790 netRecvPktSize[evt->EventID] += packetSize;
791
792 if(gameFinished_)
793 {
794 r3dOutToLog("!!! peer%02d got packet %d while game is finished\n", peerId, evt->EventID);
795 return;
796 }
797
798 GameObject* fromObj = GameWorld().GetNetworkObject(evt->FromID);
799
800
801 //r3dOutToLog("PACKET(%d): evtID:%d\n", peerId, evt->EventID);
802
803 // pass to world even processor first.
804 if(ProcessWorldEvent(fromObj, evt->EventID, peerId, packetData, packetSize)) {
805 return;
806 }
807
808 if(evt->FromID && fromObj == NULL) {
809 DisconnectPeer(peerId, true, "bad event %d from not registered object %d", evt->EventID, evt->FromID);
810 return;
811 }
812
813 if(fromObj)
814 {
815 if(IsServerPlayer(fromObj))
816 {
817 // make sure that sender of that packet is same player on server
818 if(((obj_ServerPlayer*)fromObj)->peerId_ != peerId)
819 {
820 LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_Network, false, "PlayerPeer",
821 "peerID: %d, player: %d, packetID: %d",
822 peerId, ((obj_ServerPlayer*)fromObj)->peerId_, evt->EventID);
823 return;
824 }
825 }
826
827 if(!fromObj->OnNetReceive(evt->EventID, packetData, packetSize))
828 {
829 LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_Network, true, "BadObjectEvent",
830 "%d for %s %d",
831 evt->EventID, fromObj->Name.c_str(), fromObj->GetNetworkID());
832 }
833 return;
834 }
835
836 LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_Network, true, "BadWorldEvent",
837 "event %d from %d, obj:%s",
838 evt->EventID, evt->FromID, fromObj ? fromObj->Name.c_str() : "NONE");
839 return;
840}
841
842int ServerGameLogic::getReputationKillEffect(int repFromPlr, int repKilledPlr)
843{
844 // Reputation system. Splits into three subsystems
845 // CIVILIAN LOGIC
846 if(repFromPlr > -20 && repFromPlr < 20)
847 {
848 if(repKilledPlr >= 1000)
849 repFromPlr -= 20;
850 else if(repKilledPlr >= 500)
851 repFromPlr -= 15;
852 else if(repKilledPlr >= 250)
853 repFromPlr -= 10;
854 else if(repKilledPlr >= 80)
855 repFromPlr -= 4;
856 else if(repKilledPlr >= 20)
857 repFromPlr -= 3;
858 else if(repKilledPlr >= 10)
859 repFromPlr -= 2;
860 else if(repKilledPlr <= -1000)
861 repFromPlr += 20;
862 else if(repKilledPlr <= -600)
863 repFromPlr += 15;
864 else if(repKilledPlr <= -300)
865 repFromPlr += 10;
866 else if(repKilledPlr <= -100)
867 repFromPlr += 4;
868 else if(repKilledPlr <= -20)
869 repFromPlr += 3;
870 else if(repKilledPlr <= -5)
871 repFromPlr += 2;
872 else
873 repFromPlr -= 1;
874
875 }
876 // BANDIT LOGIC (once you are in bandit section, you cannot come up to be neutral)
877 else if(repFromPlr <= -20)
878 {
879 if(repKilledPlr >= 1000)
880 repFromPlr -= 20;
881 else if(repKilledPlr >= 500)
882 repFromPlr -= 15;
883 else if(repKilledPlr >= 250)
884 repFromPlr -= 10;
885 else if(repKilledPlr >= 80)
886 repFromPlr -= 4;
887 else if(repKilledPlr >= 20)
888 repFromPlr -= 3;
889 else if(repKilledPlr >= 10)
890 repFromPlr -= 2;
891 else if(repKilledPlr <= -1000)
892 repFromPlr -= 20;
893 else if(repKilledPlr <= -600)
894 repFromPlr -= 15;
895 else if(repKilledPlr <= -300)
896 repFromPlr -= 10;
897 else if(repKilledPlr <= -100)
898 repFromPlr -= 4;
899 else if(repKilledPlr <= -20)
900 repFromPlr -= 3;
901 else if(repKilledPlr <= -5)
902 repFromPlr -= 2;
903 else
904 repFromPlr -= 1;
905 }
906 // LAWMAN LOGIC
907 else if(repFromPlr >= 20)
908 {
909 if(repKilledPlr <= -1000)
910 repFromPlr += 20;
911 else if(repKilledPlr <= -600)
912 repFromPlr += 15;
913 else if(repKilledPlr <= -300)
914 repFromPlr += 10;
915 else if(repKilledPlr <= -100)
916 repFromPlr += 4;
917 else if(repKilledPlr <= -20)
918 repFromPlr += 3;
919 else if(repKilledPlr <= -5)
920 repFromPlr += 2;
921 // lawman is killing civilian\lawmen, hard penalty depending on how much of a lawmen killer is
922 else if(repKilledPlr >= 1000)
923 repFromPlr -= 125;
924 else if(repKilledPlr >= 500)
925 repFromPlr -= 60;
926 else if(repKilledPlr >= 250)
927 repFromPlr -= 40;
928 else if(repKilledPlr >= 80)
929 repFromPlr -= 15;
930 else if(repKilledPlr >= 20)
931 repFromPlr -= 2;
932 else if(repKilledPlr >= 10)
933 repFromPlr -= 1;
934 else
935 repFromPlr -= 1;
936 }
937
938 return repFromPlr;
939}
940
941void ServerGameLogic::DoKillPlayer(GameObject* sourceObj, obj_ServerPlayer* targetPlr, STORE_CATEGORIES weaponCat, bool forced_by_server, bool fromPlayerInAir, bool targetPlayerInAir )
942{
943 r3dOutToLog("%s killed by %s, forced: %d\n", targetPlr->userName, sourceObj->Name.c_str(), (int)forced_by_server);
944
945 //AlexRedd:: KillFeed
946 /*============================================================================*/
947 //if (gServerLogic.ginfo_.channel == 6)
948 {
949 obj_ServerPlayer * killedByPlr = ((obj_ServerPlayer*)sourceObj);
950 if(ginfo_.IsGameBR() && !m_isGameHasStarted)//
951 killedByPlr->numKillWithoutDying = 0;
952 else
953 killedByPlr->numKillWithoutDying++;
954 char deadplayer[128] = { 0 };
955 char killerplayer[128] = { 0 };
956 sprintf(deadplayer, targetPlr->userName);
957 sprintf(killerplayer, killedByPlr->userName);
958
959 // sent kill packet
960 PKT_S2C_KillPlayer_s n;
961 if (IsServerPlayer(sourceObj))
962 {
963 if (targetPlr->profile_.CustomerID == killedByPlr->profile_.CustomerID &&
964 !haveWinningPlayer) //
965 {
966 n.KillFlag = 1;
967 r3dscpy(n.killer, deadplayer);
968 }
969 else if (targetPlr->lastHitBodyPart == 1)// By player headshot
970 n.KillFlag = 2;
971 else if (targetPlr->lastTimeHitItemID == WeaponConfig::ITEMID_UnarmedMelee) // By player Unarmed Melee
972 n.KillFlag = 3;
973 //
974 else if (ginfo_.IsGameBR() && m_isGameHasStarted && haveWinningPlayer &&
975 (targetPlr->profile_.CustomerID == killedByPlr->profile_.CustomerID))
976 n.KillFlag = 6;
977 //
978 else//By player
979 n.KillFlag = 4;
980 }
981 else if (sourceObj->isObjType(OBJTYPE_Zombie))//By zombie
982 n.KillFlag = 5;
983 else//Suicide
984 n.KillFlag = 1;
985 r3dscpy(n.victim, deadplayer);
986 r3dscpy(n.killer, killerplayer);
987 n.Killstreak = killedByPlr->numKillWithoutDying;
988 n.lastTimeHitItemID = targetPlr->lastTimeHitItemID;
989 n.targetId = toP2pNetId(targetPlr->GetNetworkID());
990 n.killerWeaponCat = (BYTE)weaponCat;
991 n.forced_by_server = forced_by_server;
992 p2pBroadcastToActive(sourceObj, &n, sizeof(n), true);
993 }
994 /*============================================================================*/
995
996 {
997#ifdef VEHICLES_ENABLED
998 if (targetPlr->IsInVehicle())
999 targetPlr->ExitVehicle(true, true);
1000#endif
1001 }
1002
1003 /*
1004 if(!forced_by_server && sourceObj != targetPlr) // do not reward suicides
1005 {
1006 DropLootBox(targetPlr);
1007 }*/
1008
1009 //
1010 if (ginfo_.IsGameBR() && m_isGameHasStarted)
1011 {
1012 playersLeft--;
1013 if(playersLeft>1)
1014 {
1015 PKT_C2C_ChatMessage_s n;
1016 char buffer[64] = { 0 };
1017 sprintf(buffer, "%i players left!", playersLeft);
1018 r3dscpy(n.gamertag, "<BattleRoyale>");
1019 r3dscpy(n.msg, buffer);
1020 n.msgChannel = 1; // global
1021 n.userFlag = 200; // battle royale
1022 p2pBroadcastToAll(&n, sizeof(n), true);
1023 }
1024 }
1025 //
1026
1027 // Initialize the player's aggressor for gravestone information
1028 if( sourceObj->isObjType( OBJTYPE_Human ) )
1029 {
1030 if( sourceObj != targetPlr )
1031 {
1032 r3dscpy( targetPlr->aggressor, ((obj_ServerPlayer*)sourceObj)->userName );
1033 targetPlr->killedBy = obj_ServerGravestone::KilledBy_Player;
1034 }
1035 else
1036 {
1037 r3dscpy( targetPlr->aggressor, "" );
1038 targetPlr->killedBy = obj_ServerGravestone::KilledBy_Self;
1039 }
1040 }
1041 else if( sourceObj->isObjType( OBJTYPE_Zombie ) )
1042 {
1043 r3dscpy( targetPlr->aggressor, "" );
1044 targetPlr->killedBy = obj_ServerGravestone::KilledBy_Zombie;
1045 }
1046#ifdef VEHICLES_ENABLED
1047 else if( sourceObj->isObjType( OBJTYPE_Vehicle ) )
1048 {
1049 obj_Vehicle* veh = (obj_Vehicle*)sourceObj;
1050 obj_ServerPlayer* p = veh->playersInVehicle[0];
1051 if( p )
1052 {
1053 r3dscpy( targetPlr->aggressor, p->userName );
1054 targetPlr->killedBy = obj_ServerGravestone::KilledBy_Player;
1055 }
1056 }
1057#endif
1058 targetPlr->DoDeath();
1059
1060 if(forced_by_server)
1061 return;
1062
1063#ifdef ENABLE_GAMEBLOCKS
1064 if(g_GameBlocks_Client && g_GameBlocks_Client->Connected())
1065 {
1066 if(IsServerPlayer(sourceObj))
1067 {
1068 g_GameBlocks_Client->PrepareEventForSending("Kill", g_GameBlocks_ServerID, GameBlocks::GBPublicPlayerId(uint32_t(IsServerPlayer(sourceObj)->profile_.CustomerID)));
1069 g_GameBlocks_Client->AddKeyValueInt("VictimID", targetPlr->profile_.CustomerID);
1070 g_GameBlocks_Client->AddKeyValueInt("WeaponType", weaponCat);
1071 g_GameBlocks_Client->AddKeyValueFloat("Distance", (targetPlr->GetPosition()-sourceObj->GetPosition()).Length());
1072 g_GameBlocks_Client->AddKeyValueVector3D("VictimPosition", targetPlr->GetPosition().x, targetPlr->GetPosition().y, targetPlr->GetPosition().z);
1073 g_GameBlocks_Client->AddKeyValueVector3D("KillerPosition", sourceObj->GetPosition().x, sourceObj->GetPosition().y, sourceObj->GetPosition().z);
1074 g_GameBlocks_Client->AddKeyValueInt("Headshot", targetPlr->lastHitBodyPart == 1);
1075 g_GameBlocks_Client->AddKeyValueInt("WeaponID", targetPlr->lastTimeHitItemID);
1076 g_GameBlocks_Client->AddKeyValueFloat("ServerTime", getInGameTime());
1077 {
1078 int hasNVG = 0;
1079 if( IsServerPlayer(sourceObj)->isNVGEquipped() )
1080 hasNVG = 1;
1081 g_GameBlocks_Client->AddKeyValueInt("hasNVG", hasNVG);
1082 }
1083 g_GameBlocks_Client->SendEvent();
1084 }
1085 else if(sourceObj && sourceObj->Class->Name == "obj_Zombie")
1086 {
1087 g_GameBlocks_Client->PrepareEventForSending("KillByZombie", g_GameBlocks_ServerID, GameBlocks::GBPublicPlayerId(uint32_t(targetPlr->profile_.CustomerID)));
1088 g_GameBlocks_Client->AddKeyValueVector3D("Position", targetPlr->GetPosition().x, targetPlr->GetPosition().y, targetPlr->GetPosition().z);
1089 g_GameBlocks_Client->SendEvent();
1090 }
1091 }
1092#endif
1093
1094 targetPlr->loadout_->Stats.Deaths++;
1095 //AddPlayerReward(targetPlr, RWD_Death, "RWD_Death");
1096
1097 // do not count suicide kills
1098 if(sourceObj == targetPlr)
1099 return;
1100
1101 if(IsServerPlayer(sourceObj))
1102 {
1103 // find spawn points within 500 meter radius of this kill and add "danger" to it
1104 for(int i=0; i<gCPMgr.numControlPoints_; i++)
1105 {
1106 BasePlayerSpawnPoint* spawn = gCPMgr.controlPoints_[i];
1107 for(int j=0; j<spawn->m_NumSpawnPoints; j++)
1108 {
1109 float dist = (targetPlr->GetPosition() - spawn->m_SpawnPoints[j].pos).Length();
1110 if(dist < 500.0f)
1111 {
1112 spawn->m_SpawnPoints[j].danger++;
1113 spawn->m_SpawnPoints[j].lastTimeDangerUpdated = r3dGetTime();
1114 }
1115 }
1116 }
1117 }
1118
1119 if(IsServerPlayer(sourceObj))
1120 {
1121 obj_ServerPlayer * fromPlr = ((obj_ServerPlayer*)sourceObj);
1122
1123 if(targetPlr->loadout_->Stats.Reputation > -5)
1124 fromPlr->loadout_->Stats.KilledSurvivors++;
1125 else
1126 fromPlr->loadout_->Stats.KilledBandits++;
1127
1128 int origRep = fromPlr->loadout_->Stats.Reputation;
1129
1130 int newRep = getReputationKillEffect(fromPlr->loadout_->Stats.Reputation, targetPlr->loadout_->Stats.Reputation);
1131 int diffRep = newRep - origRep;
1132 if(diffRep < 0 && (r3dGetTime() < targetPlr->m_AggressionTimeUntil))
1133 {
1134 // do not do anything to fromPlr reputation. Target player had aggression timer on him, so our player is OK killing him without penalty
1135 }
1136 else
1137 {
1138 fromPlr->loadout_->Stats.Reputation = newRep;
1139 if(diffRep != 0)
1140 {
1141 PKT_S2C_AddScore_s n;
1142 n.ID = 0;
1143 n.XP = 0;
1144 n.GD = 0;
1145 if(ginfo_.channel!=6 && //AlexRedd:: do not send reps for dev events servers
1146 !ginfo_.IsGameBR())// // alex
1147 n.Rep = diffRep;
1148 else
1149 n.Rep = 0;
1150 p2pSendToPeer(fromPlr->peerId_, fromPlr, &n, sizeof(n));
1151 }
1152 }
1153
1154 if(!ginfo_.IsGameBR() && ginfo_.channel!=6)//
1155 RewardKillPlayer(fromPlr, targetPlr, weaponCat);//AlexRedd:: kill rewards
1156 }
1157// else if(sourceObj->isObjType(OBJTYPE_GameplayItem) && sourceObj->Class->Name == "obj_ServerAutoTurret")
1158// {
1159// // award kill to owner of the turret
1160// obj_ServerPlayer* turretOwner = IsServerPlayer(GameWorld().GetObject(sourceObj->ownerID));
1161// if(turretOwner)
1162// {
1163// turretOwner->loadout_->Stats.Kills++;
1164// }
1165// }
1166
1167 return;
1168}
1169
1170void ServerGameLogic::RewardKillPlayer(obj_ServerPlayer* fromPlr, obj_ServerPlayer* targetPlr, STORE_CATEGORIES dmgCat)
1171{
1172 r3d_assert(fromPlr);
1173 r3d_assert(targetPlr);
1174 //r3dOutToLog("RewardKillPlayer %s->%s\n", fromPlr->userName, targetPlr->userName); CLOG_INDENT;
1175
1176 if (fromPlr->numKillWithoutDying == 15)
1177 AddPlayerReward(fromPlr, RWD_Kill15NotDying);
1178 else if (fromPlr->numKillWithoutDying == 25)
1179 AddPlayerReward(fromPlr, RWD_Kill25NotDying);
1180 else if (fromPlr->numKillWithoutDying == 50)
1181 {
1182 AddPlayerReward(fromPlr, RWD_Kill50NotDying);
1183 LogCheat(fromPlr->peerId_, PKT_S2C_CheatWarning_s::CHEAT_Kills, true, "HighKills 50");
1184 }
1185 else if (fromPlr->numKillWithoutDying == 100)
1186 {
1187 AddPlayerReward(fromPlr, RWD_Kill100NotDying);
1188 LogCheat(fromPlr->peerId_, PKT_S2C_CheatWarning_s::CHEAT_Kills, true, "HighKills 100");
1189 }
1190
1191 int gotKillReward = 0;
1192 if (targetPlr->loadout_->ClanID != fromPlr->loadout_->ClanID)
1193 {
1194 // count kill streaks
1195 if ((r3dGetTime() - fromPlr->LastEnemyKillTime) < GPP_Data.c_fKillStreakTimeout)
1196 fromPlr->Killstreaks++;
1197 else{
1198 fromPlr->Killstreaks = 1;
1199 if (dmgCat != storecat_GRENADE && (targetPlr->lastHitBodyPart == 1 && dmgCat != storecat_MELEE))
1200 AddPlayerReward(fromPlr, RWD_PlayerKill);
1201
1202 }
1203 if (fromPlr->Killstreaks == 2)
1204 {
1205 AddPlayerReward(fromPlr, RWD_Killstreak2);
1206 gotKillReward++;
1207 }
1208 else if (fromPlr->Killstreaks == 3)
1209 {
1210 AddPlayerReward(fromPlr, RWD_Killstreak3);
1211 gotKillReward++;
1212 }
1213 else if (fromPlr->Killstreaks == 4)
1214 {
1215 AddPlayerReward(fromPlr, RWD_Killstreak4);
1216 gotKillReward++;
1217 }
1218 else if (fromPlr->Killstreaks == 5)
1219 {
1220 AddPlayerReward(fromPlr, RWD_Killstreak5);
1221 gotKillReward++;
1222 }
1223 else if (fromPlr->Killstreaks == 6)
1224 {
1225 AddPlayerReward(fromPlr, RWD_Killstreak6);
1226 gotKillReward++;
1227 }
1228 else if (fromPlr->Killstreaks == 7)
1229 {
1230 AddPlayerReward(fromPlr, RWD_Killstreak7);
1231 gotKillReward++;
1232 }
1233 else if (fromPlr->Killstreaks == 8)
1234 {
1235 AddPlayerReward(fromPlr, RWD_Killstreak8);
1236 gotKillReward++;
1237 }
1238
1239 // update timer
1240 fromPlr->LastEnemyKillTime = r3dGetTime();
1241 }
1242
1243 if (dmgCat == storecat_GRENADE)
1244 {
1245 AddPlayerReward(fromPlr, RWD_ExplosionKill);
1246 }
1247 else if (targetPlr->lastHitBodyPart == 1 && dmgCat != storecat_MELEE) // headshot
1248 {
1249 AddPlayerReward(fromPlr, RWD_Headshot);
1250 }
1251}
1252
1253// make sure this function is the same on client: AI_Player.cpp bool canDamageTarget(const GameObject* obj)
1254bool ServerGameLogic::CanDamageThisObject(const GameObject* targetObj)
1255{
1256 if(IsServerPlayer(targetObj))
1257 {
1258 return true;
1259 }
1260 else if(targetObj->isObjType(OBJTYPE_Zombie))
1261 {
1262 return true;
1263 }
1264#ifdef VEHICLES_ENABLED
1265 else if (targetObj->isObjType(OBJTYPE_Vehicle))
1266 {
1267 return true;
1268 }
1269#endif
1270 else if(targetObj->Class->Name == "obj_LightMesh")
1271 {
1272 return true;
1273 }
1274 else if(targetObj->isObjType(OBJTYPE_Barricade))
1275 {
1276 return true;
1277 }
1278
1279 return false;
1280}
1281
1282void ServerGameLogic::ApplyDamage(GameObject* fromObj, GameObject* targetObj, const r3dPoint3D& dmgPos, float damage, bool force_damage, STORE_CATEGORIES damageSource, uint32_t dmgItemID, bool canApplyBleeding)
1283{
1284 r3d_assert(fromObj);
1285 r3d_assert(targetObj);
1286
1287 //
1288 if (ginfo_.IsGameBR() && isCountingDown)
1289 damage = 0;
1290 //
1291
1292 if(IsServerPlayer(fromObj))
1293 {
1294 obj_ServerPlayer* fromPlr = (obj_ServerPlayer*)fromObj;
1295 if(damageSource == storecat_MELEE)
1296 {
1297 if(fromPlr->loadout_->Skills[CUserSkills::SKILL_Weapons1])
1298 damage *= 1.05f;
1299 if(fromPlr->loadout_->Skills[CUserSkills::SKILL_Weapons3])
1300 damage *= 1.07f;
1301 if(fromPlr->loadout_->Skills[CUserSkills::SKILL_Weapons6])
1302 damage *= 1.10f;
1303 }
1304 }
1305
1306 if(IsServerPlayer(targetObj))
1307 {
1308 ApplyDamageToPlayer(fromObj, (obj_ServerPlayer*)targetObj, dmgPos, damage, -1, 0, force_damage, damageSource, dmgItemID, 0, canApplyBleeding);
1309 return;
1310 }
1311 else if(targetObj->isObjType(OBJTYPE_Zombie))
1312 {
1313 ApplyDamageToZombie(fromObj, targetObj, dmgPos, damage, -1, 0, force_damage, damageSource, dmgItemID);
1314 return;
1315 }
1316 else if(targetObj->Class->Name == "obj_LightMesh")
1317 {
1318 obj_ServerLightMesh* lightM = (obj_ServerLightMesh*)targetObj;
1319 if(lightM->bLightOn)
1320 {
1321 lightM->bLightOn = false;
1322 lightM->SyncLightStatus(-1);
1323 }
1324
1325 return;
1326 }
1327 else if(targetObj->isObjType(OBJTYPE_Barricade))
1328 {
1329 obj_ServerBarricade* shield = (obj_ServerBarricade*)targetObj;
1330 shield->DoDamage(damage);
1331 return;
1332 }
1333 else if (targetObj->isObjType(OBJTYPE_Vehicle))
1334 {
1335 if (!fromObj->isObjType(OBJTYPE_Vehicle))
1336 {
1337 obj_Vehicle* vehicle = (obj_Vehicle*)targetObj;
1338 vehicle->ApplyDamage(fromObj, damage, storecat_GRENADE);
1339 }
1340
1341 return;
1342 }
1343
1344 r3dOutToLog("!!! error !!! was trying to damage object %s [%s]\n", targetObj->Name.c_str(), targetObj->Class->Name.c_str());
1345}
1346
1347// return true if hit was registered, false otherwise
1348// state is grabbed from the dynamics. [0] is from player in the air, [1] is target player in the air
1349bool ServerGameLogic::ApplyDamageToPlayer(GameObject* fromObj, obj_ServerPlayer* targetPlr, const r3dPoint3D& dmgPos, float damage, int bodyBone, int bodyPart, bool force_damage, STORE_CATEGORIES damageSource, uint32_t dmgItemID, int airState, bool canApplyBleeding)
1350{
1351 r3d_assert(fromObj);
1352 r3d_assert(targetPlr);
1353
1354 if(targetPlr->loadout_->Alive == 0)
1355 return false;
1356
1357 if (ginfo_.IsGameBR() && isCountingDown) //
1358 return false;
1359
1360 //AlexRedd:: FriendlyFire system
1361 // disable dmg for same clan members
1362 if (targetPlr->loadout_->FriendlyFire == 0 && targetPlr->loadout_->ClanID != 0 && IsServerPlayer(fromObj) && IsServerPlayer(fromObj)->loadout_->ClanID == targetPlr->loadout_->ClanID && damageSource != storecat_GRENADE && fromObj != targetPlr)
1363 return false;
1364
1365 // can't damage players in safe zones (postbox now act like that)
1366 if((targetPlr->loadout_->GameFlags & wiCharDataFull::GAMEFLAG_NearPostBox) && !force_damage)
1367 return false;
1368 // double check that we can't kill player that operating with global inventory
1369 if(targetPlr->inventoryOpActive_)
1370 return false;
1371 // player's in the same group cannot damage each other (except for grenades)
1372 if(targetPlr->groupID!= 0 && IsServerPlayer(fromObj) && IsServerPlayer(fromObj)->groupID==targetPlr->groupID && damageSource!=storecat_GRENADE
1373 && fromObj!=targetPlr // if you are in group and damage is coming from player itself (for example falling damage) then ignore group
1374 )
1375 return false;
1376
1377 damage = targetPlr->ApplyDamage(damage, fromObj, bodyPart, damageSource, dmgItemID, canApplyBleeding);
1378
1379 if(fromObj->Class->Name == "obj_Zombie")
1380 {
1381 PKT_S2C_ZombieAttack_s n;
1382 n.targetId = toP2pNetId(targetPlr->GetNetworkID());
1383 p2pBroadcastToActive(fromObj, &n, sizeof(n));
1384
1385#ifdef VEHICLES_ENABLED
1386 if (!targetPlr->IsInVehicle())
1387 {
1388#endif
1389 extern float _zai_ChanceOfInfecting;
1390 extern float _zai_InfectStrength;
1391 if(u_GetRandom(0.0f, 100.0f) < _zai_ChanceOfInfecting) // chance of infecting
1392 {
1393 targetPlr->loadout_->Toxic += _zai_InfectStrength;
1394 targetPlr->loadout_->Toxic = R3D_CLAMP(targetPlr->loadout_->Toxic, 0.0f, 100.0f);
1395 }
1396#ifdef VEHICLES_ENABLED
1397 }
1398#endif
1399
1400
1401 }
1402 else
1403 {
1404 // send damage packet, originating from the firing dude
1405 PKT_S2C_Damage_s a;
1406 a.dmgPos = dmgPos;
1407 a.targetId = toP2pNetId(targetPlr->GetNetworkID());
1408 a.damage = R3D_MIN((BYTE)damage, (BYTE)255);
1409 a.dmgType = damageSource;
1410 a.bodyBone = bodyBone;
1411 p2pBroadcastToActive(fromObj, &a, sizeof(a));
1412 }
1413
1414 // check if we killed player
1415 if(targetPlr->loadout_->Health <= 0)
1416 {
1417 bool fromPlayerInAir = ((airState & 0x1) != 0);
1418 bool targetPlayerInAir = ((airState & 0x2) != 0);
1419
1420 DoKillPlayer(fromObj, targetPlr, damageSource, false, fromPlayerInAir, targetPlayerInAir);
1421#ifdef MISSIONS
1422 if( IsServerPlayer(fromObj) && ((obj_ServerPlayer*)fromObj)->m_MissionsProgress )
1423 ((obj_ServerPlayer*)fromObj)->m_MissionsProgress->AddKill( targetPlr, dmgItemID );
1424#endif
1425 }
1426
1427 return true;
1428}
1429
1430bool ServerGameLogic::ApplyDamageToZombie(GameObject* fromObj, GameObject* targetZombie, const r3dPoint3D& dmgPos, float damage, int bodyBone, int bodyPart, bool force_damage, STORE_CATEGORIES damageSource, uint32_t dmgItemID)
1431{
1432 r3d_assert(fromObj);
1433 r3d_assert(targetZombie && targetZombie->isObjType(OBJTYPE_Zombie));
1434
1435 // relay to zombie logic
1436 obj_Zombie* z = (obj_Zombie*)targetZombie;
1437 bool killed = z->ApplyDamage(fromObj, damage, bodyPart, damageSource);
1438
1439 if(IsServerPlayer(fromObj) && killed)
1440 {
1441 IsServerPlayer(fromObj)->loadout_->Stats.KilledZombies++;
1442#ifdef MISSIONS
1443 if( ((obj_ServerPlayer*)fromObj)->m_MissionsProgress )
1444 IsServerPlayer(fromObj)->m_MissionsProgress->AddKill( z, dmgItemID );
1445#endif
1446 }
1447
1448 return true;
1449}
1450
1451#ifdef VEHICLES_ENABLED
1452bool ServerGameLogic::ApplyDamageToVehicle(GameObject* fromObj, GameObject* targetVehicle, const r3dPoint3D& dmgPos, float damage, bool forceDamage, STORE_CATEGORIES damageSource, uint32_t damageItemId)
1453{
1454 r3d_assert(fromObj);
1455 r3d_assert(targetVehicle && targetVehicle->isObjType(OBJTYPE_Vehicle));
1456
1457 obj_Vehicle* vehicle = (obj_Vehicle*)targetVehicle;
1458 bool destroyed = vehicle->ApplyDamage(fromObj, damage, damageSource);
1459 if (IsServerPlayer(fromObj) && destroyed)
1460 {
1461 // todo - add player kill
1462 }
1463
1464 return true;
1465}
1466#endif
1467
1468void ServerGameLogic::RelayPacket(DWORD peerId, const GameObject* from, const DefaultPacket* packetData, int packetSize, bool guaranteedAndOrdered)
1469{
1470 if(!from)
1471 {
1472 r3dError("RelayPacket !from, event: %d", packetData->EventID);
1473 }
1474 const INetworkHelper* nh = const_cast<GameObject*>(from)->GetNetworkHelper();
1475
1476 for(int i=0; i<MAX_PEERS_COUNT; i++)
1477 {
1478 if(peers_[i].status_ >= PEER_PLAYING && i != peerId)
1479 {
1480 if(!nh->GetVisibility(i))
1481 {
1482 continue;
1483 }
1484
1485 net_->SendToPeer(packetData, packetSize, i, guaranteedAndOrdered);
1486 netSentPktSize[packetData->EventID] += packetSize;
1487 }
1488 }
1489
1490 return;
1491}
1492
1493void ServerGameLogic::p2pBroadcastToActive(const GameObject* from, DefaultPacket* packetData, int packetSize, bool guaranteedAndOrdered)
1494{
1495 if(!from)
1496 {
1497 r3dError("p2pBroadcastToActive !from, event: %d", packetData->EventID);
1498 }
1499 const INetworkHelper* nh = const_cast<GameObject*>(from)->GetNetworkHelper();
1500
1501 preparePacket(from, packetData);
1502
1503 for(int i=0; i<MAX_PEERS_COUNT; i++)
1504 {
1505 if(peers_[i].status_ >= PEER_PLAYING)
1506 {
1507 if(!nh->GetVisibility(i))
1508 {
1509 continue;
1510 }
1511 net_->SendToPeer(packetData, packetSize, i, guaranteedAndOrdered);
1512 netSentPktSize[packetData->EventID] += packetSize;
1513 }
1514 }
1515
1516 return;
1517}
1518
1519void ServerGameLogic::p2pBroadcastToAll(DefaultPacket* packetData, int packetSize, bool guaranteedAndOrdered)
1520{
1521 preparePacket(NULL, packetData);
1522
1523 for(int i=0; i<MAX_PEERS_COUNT; i++)
1524 {
1525 if(peers_[i].status_ >= PEER_VALIDATED1)
1526 {
1527 net_->SendToPeer(packetData, packetSize, i, guaranteedAndOrdered);
1528 netSentPktSize[packetData->EventID] += packetSize;
1529 }
1530 }
1531
1532 return;
1533}
1534
1535void ServerGameLogic::p2pSendToPeer(DWORD peerId, const GameObject* from, DefaultPacket* packetData, int packetSize, bool guaranteedAndOrdered)
1536{
1537 if(!from)
1538 {
1539 r3dError("p2pSendToPeer !from, event: %d", packetData->EventID);
1540 }
1541
1542 const peerInfo_s& peer = GetPeer(peerId);
1543 if(peer.status_ >= PEER_PLAYING)
1544 {
1545 const INetworkHelper* nh = const_cast<GameObject*>(from)->GetNetworkHelper();
1546 if(!nh->GetVisibility(peerId))
1547 {
1548 return;
1549 }
1550
1551 preparePacket(from, packetData);
1552 net_->SendToPeer(packetData, packetSize, peerId, guaranteedAndOrdered);
1553 netSentPktSize[packetData->EventID] += packetSize;
1554 }
1555}
1556
1557void ServerGameLogic::p2pSendRawToPeer(DWORD peerId, DefaultPacket* packetData, int packetSize, bool guaranteedAndOrdered)
1558{
1559 const peerInfo_s& peer = GetPeer(peerId);
1560 r3d_assert(peer.status_ != PEER_FREE);
1561
1562 preparePacket(NULL, packetData);
1563 net_->SendToPeer(packetData, packetSize, peerId, guaranteedAndOrdered);
1564 netSentPktSize[packetData->EventID] += packetSize;
1565}
1566
1567void ServerGameLogic::InformZombiesAboutSound(const obj_ServerPlayer* plr, const ServerWeapon* wpn)
1568{
1569 for(GameObject* obj = GameWorld().GetFirstObject(); obj; obj = GameWorld().GetNextObject(obj))
1570 {
1571 if(obj->isObjType(OBJTYPE_Zombie))
1572 ((obj_Zombie*)obj)->SenseWeaponFire(plr, wpn);
1573 }
1574}
1575
1576void ServerGameLogic::InformZombiesAboutGrenadeSound(const obj_ServerGrenade* grenade, bool isExplosion)
1577{
1578 for(std::list<obj_Zombie*>::iterator it = obj_Zombie::s_ListOfAllActiveZombies.begin(); it != obj_Zombie::s_ListOfAllActiveZombies.end(); ++it)
1579 {
1580 (*it)->SenseGrenadeSound(grenade, isExplosion);
1581 }
1582}
1583
1584#ifdef VEHICLES_ENABLED
1585void ServerGameLogic::InformZombiesAboutVehicleExplosion(const obj_Vehicle* vehicle)
1586{
1587 for(std::list<obj_Zombie*>::iterator it = obj_Zombie::s_ListOfAllActiveZombies.begin(); it != obj_Zombie::s_ListOfAllActiveZombies.end(); ++it)
1588 {
1589 (*it)->SenseVehicleExplosion(vehicle);
1590 }
1591}
1592#endif
1593
1594wiStatsTracking ServerGameLogic::GetRewardData(obj_ServerPlayer* plr, EPlayerRewardID rewardID)
1595{
1596 r3d_assert(g_GameRewards);
1597 const CGameRewards::rwd_s& rwd = g_GameRewards->GetRewardData(rewardID);
1598 if(!rwd.IsSet)
1599 {
1600 LogInfo(plr->peerId_, "GetReward", "%d not set", rewardID);
1601 return wiStatsTracking();
1602 }
1603
1604 wiStatsTracking stat;
1605 stat.RewardID = (int)rewardID;
1606 stat.GP = 0;
1607
1608 if(plr->loadout_->Hardcore)
1609 {
1610 stat.GD = rwd.GD_HARD;
1611 stat.XP = rwd.XP_HARD;
1612 }
1613 else
1614 {
1615 if(rwd.GD_SOFT>0)
1616 stat.GD = rwd.GD_SOFT;// + u_random(10);//AlexRedd:: random GD
1617 else
1618 stat.GD = rwd.GD_SOFT;
1619 if(rwd.XP_SOFT>0)
1620 stat.XP = rwd.XP_SOFT;// + u_random(10);//AlexRedd:: random XP
1621 else
1622 stat.XP = rwd.XP_SOFT;
1623 }
1624
1625 return stat;
1626}
1627
1628void ServerGameLogic::AddPlayerReward(obj_ServerPlayer* plr, EPlayerRewardID rewardID)
1629{
1630 wiStatsTracking stat = GetRewardData(plr, rewardID);
1631 if(stat.RewardID == 0)
1632 return;
1633
1634 const CGameRewards::rwd_s& rwd = g_GameRewards->GetRewardData(rewardID);
1635 AddDirectPlayerReward(plr, stat, rwd.Name.c_str());
1636}
1637
1638void ServerGameLogic::AddDirectPlayerReward(obj_ServerPlayer* plr, const wiStatsTracking& in_rwd, const char* rewardName)
1639{
1640 // add reward to player
1641 wiStatsTracking rwd2 = plr->AddReward(in_rwd);
1642 int xp = rwd2.XP;
1643 int gp = rwd2.GP;
1644 int gd = rwd2.GD;
1645 if(xp == 0 && gp == 0 && gd == 0)
1646 return;
1647
1648 r3dOutToLog("reward: %s got %dxp %dgp %dgd RWD_%s\n", plr->userName, xp, gp, gd, rewardName ? rewardName : "");
1649
1650 // send it to him
1651 PKT_S2C_AddScore_s n;
1652 n.ID = (WORD)in_rwd.RewardID;
1653 n.XP = R3D_CLAMP(xp, -30000, 30000);
1654 n.GD = (WORD)gd;
1655 n.Rep = 0;
1656 p2pSendToPeer(plr->peerId_, plr, &n, sizeof(n));
1657
1658 return;
1659}
1660
1661IMPL_PACKET_FUNC(ServerGameLogic, PKT_C2S_ValidateConnectingPeer)
1662{
1663 // reply back with our version
1664 PKT_C2S_ValidateConnectingPeer_s n1;
1665 n1.protocolVersion = P2PNET_VERSION;
1666 n1.sessionId = 0;
1667 p2pSendRawToPeer(peerId, &n1, sizeof(n1));
1668
1669 if(n.protocolVersion != P2PNET_VERSION)
1670 {
1671 DisconnectPeer(peerId, false, "Protocol Version mismatch");
1672 return;
1673 }
1674 extern __int64 cfg_sessionId;
1675 if(n.sessionId != cfg_sessionId)
1676 {
1677 DisconnectPeer(peerId, true, "Wrong sessionId");
1678 return;
1679 }
1680
1681 // switch to Validated state
1682 peerInfo_s& peer = GetPeer(peerId);
1683 peer.SetStatus(PEER_VALIDATED1);
1684
1685 return;
1686}
1687
1688#ifdef VEHICLES_ENABLED
1689IMPL_PACKET_FUNC(ServerGameLogic, PKT_C2S_VehicleEnter)
1690{
1691 obj_Vehicle* vehicle = obj_Vehicle::GetVehicleById(n.vehicleId);
1692
1693 if (!vehicle || !vehicle->isActive())
1694 return;
1695
1696 // does the vehicle exist or have room?
1697 PKT_S2C_VehicleEntered_s packet;
1698
1699 if (vehicle->GetDurability() <= 0 || vehicle->IsVehicleSeatsFull())
1700 packet.isSuccess = false;
1701 else
1702 packet.isSuccess = true;
1703
1704 // put the requesting player in the vehicle
1705 obj_ServerPlayer* player = IsServerPlayer(GetPeer(peerId).player);
1706 if (!player || player->loadout_->Alive == 0)
1707 return;
1708
1709 // make sure player is within 5 meters of vehicle
1710 if ((player->GetPosition() - vehicle->GetPosition()).Length() > 5.0f)
1711 packet.isSuccess = false;
1712
1713 if (packet.isSuccess)
1714 {
1715 player->EnterVehicle(vehicle);
1716
1717 packet.networkId = toP2pNetId(vehicle->GetNetworkID());
1718 packet.vehicleId = vehicle->vehicleId;
1719 packet.seat = (BYTE)player->seatPosition;
1720 packet.curFuel = vehicle->GetFuel();
1721
1722 // let all of the clients know, this player has entered a vehicle
1723 gServerLogic.p2pBroadcastToActive(GetPeer(peerId).player, &packet, sizeof(packet), true);
1724 }
1725 else // there is no available vehicle or seat
1726 {
1727 p2pSendToPeer(peerId, GetPeer(peerId).player, &packet, sizeof(packet));
1728 }
1729
1730 return;
1731}
1732
1733IMPL_PACKET_FUNC(ServerGameLogic, PKT_C2S_VehicleExit)
1734{
1735 obj_Vehicle* vehicle = obj_Vehicle::GetVehicleById(n.vehicleId);
1736 if (!vehicle)
1737 return;
1738
1739 obj_ServerPlayer* player = IsServerPlayer(GetPeer(peerId).player);
1740 if (!player)
1741 return;
1742
1743 player->ExitVehicle(true);
1744}
1745
1746IMPL_PACKET_FUNC(ServerGameLogic, PKT_C2S_VehicleHitTarget)
1747{
1748 if (!fromObj || !fromObj->isObjType(OBJTYPE_Vehicle))
1749 return;
1750
1751 obj_Vehicle* vehicle = (obj_Vehicle*)fromObj;
1752 if (!vehicle)
1753 return;
1754
1755 obj_ServerPlayer* player = vehicle->playersInVehicle[0];
1756 if (!player) // we have no driver and we have no last driver.. fail
1757 return;
1758
1759 GameObject* gameObj = GameWorld().GetNetworkObject(n.targetId);
1760 if (!gameObj)
1761 return;
1762
1763 float distanceCheck = (vehicle->GetSpeed() + 8.0f) * 2.0f; // temporary solution.
1764 float distance = (vehicle->GetPosition() - gameObj->GetPosition()).Length();
1765 if (distance > distanceCheck)
1766 {
1767 gServerLogic.LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_VehicleHitTarget, true, "VehicleHitTarget",
1768 "dist: %f; distCheck: %f", distance, distanceCheck);
1769 return;
1770 }
1771
1772 if (gameObj->isObjType(OBJTYPE_Zombie))
1773 {
1774 obj_Zombie* zombie = (obj_Zombie*)gameObj;
1775 if (zombie->ZombieHealth <= 0)
1776 return;
1777
1778 ApplyDamageToZombie(player, gameObj, vehicle->GetPosition(), zombie->ZombieHealth + 1, 1, 1, true, storecat_Vehicle, vehicle->vehicleId);
1779
1780 vehicle->SetDurability(-10);
1781
1782 if (zombie->ZombieHealth >= 0.0f)
1783 {
1784 PKT_S2C_VehicleHitTargetFail_s n;
1785 n.targetId = n.targetId;
1786 p2pBroadcastToActive(vehicle, &n, sizeof(n));
1787 }
1788 }
1789 else if (gameObj->isObjType(OBJTYPE_Human))
1790 {
1791 obj_ServerPlayer* hitPlayer = IsServerPlayer(gameObj);
1792 if (hitPlayer)
1793 {
1794 if (hitPlayer->loadout_->GameFlags & wiCharDataFull::GAMEFLAG_NearPostBox || hitPlayer->loadout_->Health <= 0)
1795 return;
1796
1797 ApplyDamageToPlayer(player, (obj_ServerPlayer*)gameObj, vehicle->GetPosition(), 200.0f, 1, 2, true, storecat_Vehicle, vehicle->vehicleId);
1798
1799 vehicle->SetDurability(-700, false);
1800
1801 if (hitPlayer->loadout_->Alive == 1)
1802 {
1803 PKT_S2C_VehicleHitTargetFail_s n;
1804 n.targetId = n.targetId;
1805 p2pBroadcastToActive(vehicle, &n, sizeof(n));
1806 }
1807 }
1808 }
1809}
1810
1811IMPL_PACKET_FUNC(ServerGameLogic, PKT_C2S_VehicleStopZombie)
1812{
1813 GameObject* gameObj = GameWorld().GetNetworkObject(n.targetId);
1814 if (!gameObj || !gameObj->isObjType(OBJTYPE_Zombie))
1815 return;
1816
1817 ((obj_Zombie*)gameObj)->StopNavAgent();
1818}
1819#endif
1820
1821obj_ServerPlayer* ServerGameLogic::CreateNewPlayer(DWORD peerId, const r3dPoint3D& spawnPos, float spawnDir, bool needsSpawnProtection, bool isFastLoad)
1822{
1823 peerInfo_s& peer = GetPeer(peerId);
1824 const int playerIdx = peer.playerIdx;
1825
1826 r3d_assert(playerIdx >= 0 && playerIdx < MAX_NUM_PLAYERS);
1827 r3d_assert(peer.startGameAns == PKT_S2C_StartGameAns_s::RES_Ok);
1828
1829 // store game session id
1830 peer.temp_profile.ProfileData.ArmorySlots[0].GameServerId = ginfo_.gameServerId;
1831 peer.temp_profile.ProfileData.ArmorySlots[0].GameMapId = ginfo_.mapId;
1832
1833 // client server sync point
1834 bool wipeInventory=false;
1835 bool giveLoadout = false;
1836 bool giveLoadoutSnipersMode = false;
1837 bool giveLoadoutNoSnipersMode = false;
1838 bool giveLoadoutBambi = false;
1839 bool giveLoadoutBR = false;//
1840
1841#ifdef DISABLE_GI_ACCESS_ON_PTE_MAP
1842 if(ginfo_.channel == 6) // PTE map, wipe inventory and give one flashlight only
1843 wipeInventory=true;
1844#endif
1845#ifdef DISABLE_GI_ACCESS_ON_PTE_STRONGHOLD_MAP
1846 if(ginfo_.channel == 6 && ginfo_.mapId==GBGameInfo::MAPID_WZ_Cliffside) // PTE map, wipe inventory and give one flashlight only
1847 wipeInventory=true;
1848#endif
1849#ifdef DISABLE_GI_ACCESS_FOR_CALI_SERVER
1850 if(gServerLogic.ginfo_.mapId==GBGameInfo::MAPID_WZ_California)
1851 wipeInventory=true;
1852#endif
1853#ifdef DISABLE_GI_ACCESS_FOR_DEV_EVENT_SERVER
1854 if (ginfo_.channel == 6 && ginfo_.gameServerId != 22000 && ginfo_.gameServerId != 23000 && ginfo_.mapId!=GBGameInfo::MAPID_WZ_Caliwood)// 22000, 23000 = no drop (sinch point client/server)
1855 {
1856 wipeInventory = true;
1857 if(gServerLogic.ginfo_.flags & GBGameInfo::SFLAGS_DisableASR)// Snipers Mode
1858 giveLoadoutSnipersMode = true;
1859 else if (gServerLogic.ginfo_.flags & GBGameInfo::SFLAGS_DisableSNP)// No Snipers Mode
1860 giveLoadoutNoSnipersMode = true;
1861 else
1862 giveLoadout = true;
1863 }
1864 if(ginfo_.channel == 6 && ginfo_.mapId==GBGameInfo::MAPID_WZ_Caliwood) // bambi mode. give one flashlight only
1865 {
1866 wipeInventory=true;
1867 giveLoadoutBambi = true;
1868 }
1869#endif
1870 //
1871 if(ginfo_.IsGameBR())
1872 {
1873 wipeInventory=true;
1874 giveLoadoutBR = true;
1875 }
1876 //
1877
1878 if(wipeInventory)
1879 {
1880 for(int i=0; i<wiCharDataFull::CHAR_MAX_BACKPACK_SIZE; ++i)
1881 peer.temp_profile.ProfileData.ArmorySlots[0].Items[i].Reset();
1882
1883 if (giveLoadout) // Normal Mode
1884 GiveDevEventLoadout(peerId);
1885
1886 if (giveLoadoutSnipersMode) // Snipers Mode
1887 GiveDevEventLoadoutSnipersMode(peerId);
1888
1889 if (giveLoadoutNoSnipersMode) // No Snipers Mode
1890 GiveDevEventLoadoutNoSnipersMode(peerId);
1891
1892 if (giveLoadoutBambi) // bambi mode
1893 {
1894 wiInventoryItem flashLightItem;
1895 flashLightItem.InventoryID = 0;
1896 flashLightItem.itemID = 101306;
1897 flashLightItem.quantity = 1;
1898 peer.temp_profile.ProfileData.ArmorySlots[0].Items[wiCharDataFull::CHAR_LOADOUT_WEAPON2] = flashLightItem;
1899 }
1900
1901 if (giveLoadoutBR) //
1902 GiveDevEventLoadoutBR(peerId);
1903
1904
1905 /*if (!giveLoadout) // bambi mode
1906 {
1907 wiInventoryItem flashLightItem;
1908 flashLightItem.InventoryID = 0;
1909 flashLightItem.itemID = 101306;
1910 flashLightItem.quantity = 1;
1911 peer.temp_profile.ProfileData.ArmorySlots[0].Items[wiCharDataFull::CHAR_LOADOUT_WEAPON2] = flashLightItem;
1912
1913 }
1914 else // dev event loadout mode
1915 {
1916 GiveDevEventLoadout(peerId);
1917 }*/
1918 }
1919 // end
1920
1921 // create player
1922 char name[128];
1923 //sprintf(name, "player%02d", playerIdx);
1924 sprintf(name, "%s", peer.temp_profile.ProfileData.ArmorySlots[0].Gamertag);
1925 obj_ServerPlayer* plr = (obj_ServerPlayer*)srv_CreateGameObject("obj_ServerPlayer", name, spawnPos);
1926
1927 // add to peer-to-player table (need to do before player creation, because of network objects visibility check)
1928 r3d_assert(plrToPeer_[playerIdx] != NULL);
1929 r3d_assert(plrToPeer_[playerIdx]->player == NULL);
1930 plrToPeer_[playerIdx]->player = plr;
1931 // mark that we're active
1932 peer.player = plr;
1933
1934 // fill player info
1935 plr->m_PlayerRotation = spawnDir;
1936 if(needsSpawnProtection)
1937 {
1938 if(ginfo_.mapId == GBGameInfo::MAPID_WZ_Hangar || //AlexRedd:: for map hangar we need half time spawn protection
1939 ginfo_.IsGameBR())//
1940 plr->m_SpawnProtectedUntil = r3dGetTime() + 15.0f; // 15 seconds of spawn protection
1941 else
1942 plr->m_SpawnProtectedUntil = r3dGetTime() + 30.0f; // 30 seconds of spawn protection
1943 }
1944
1945 plr->peerId_ = peerId;
1946 plr->SetNetworkID(playerIdx + NETID_PLAYERS_START);
1947 plr->NetworkLocal = false;
1948#ifdef MISSIONS
1949 peer.temp_profile.ProfileData.ArmorySlots[0].missionsProgress->m_player = plr;
1950#endif
1951 plr->SetProfile(peer.temp_profile);
1952 //Skin system
1953 plr->GetDBSkins(peer.temp_profile.ProfileData);// get Skins Bought
1954 plr->isFastLoad = isFastLoad;
1955 plr->OnCreate();
1956
1957 // from this point we do expect security report packets
1958 peer.secRepRecvTime = r3dGetTime();
1959 peer.secRepGameTime = -1;
1960 peer.secRepRecvAccum = 0;
1961
1962 m_LastConnectionRep = r3dGetTime();//AlexRedd:: for hight ping detect
1963
1964 plrList_[curPlayers_] = plr;
1965 curPlayers_++;
1966 maxLoggedPlayers_ = R3D_MAX(maxLoggedPlayers_, curPlayers_);
1967
1968 // report player to masterserver
1969 gMasterServerLogic.AddPlayer(peer.playerIdx, peer.CustomerID, plr->loadout_);
1970
1971 // report joined player name to all users
1972 //if(!(plr->profile_.ProfileData.isDevAccount & wiUserProfile::DAA_HIDDEN))
1973 //{
1974 PKT_S2C_PlayerNameJoined_s n;
1975 n.playerIdx = (WORD)playerIdx;
1976 r3dscpy(n.gamertag, plr->userName);
1977 n.reputation = plr->loadout_->Stats.Reputation;
1978 n.ClanID = plr->loadout_->ClanID;
1979 n.flags = 0;
1980 if(plr->profile_.ProfileData.AccountType == 0) // legend
1981 n.flags |= 1;
1982 if(plr->profile_.ProfileData.isDevAccount & wiUserProfile::DAA_DEV_ICON)
1983 n.flags |= 2;
1984 if(plr->profile_.ProfileData.AccountType == 54) // punisher
1985 n.flags |= 4;
1986 if(plr->profile_.ProfileData.PremiumAcc > 0)
1987 n.flags |= 8;
1988
1989 // send to all, regardless visibility, excluding us
1990 for(int i=0; i<MAX_PEERS_COUNT; i++) {
1991 if(peers_[i].status_ >= PEER_PLAYING && peers_[i].player && i != peerId) {
1992 net_->SendToPeer(&n, sizeof(n), i, true);
1993 }
1994 }
1995 //}
1996
1997 // report list of current players to user, including us
1998 {
1999 for(int i=0; i<MAX_PEERS_COUNT; i++) {
2000 if(peers_[i].status_ >= PEER_PLAYING && peers_[i].player) {
2001 //if(!(peers_[i].player->profile_.ProfileData.isDevAccount & wiUserProfile::DAA_HIDDEN))
2002 //{
2003 PKT_S2C_PlayerNameJoined_s n;
2004 n.playerIdx = (WORD)peers_[i].playerIdx;
2005 r3dscpy(n.gamertag, peers_[i].player->userName);
2006 n.reputation = peers_[i].player->loadout_->Stats.Reputation;
2007 n.ClanID = peers_[i].player->loadout_->ClanID;
2008 n.flags = 0;
2009 if(peers_[i].player->profile_.ProfileData.AccountType == 0) // legend
2010 n.flags |= 1;
2011 if(peers_[i].player->profile_.ProfileData.isDevAccount & wiUserProfile::DAA_DEV_ICON)
2012 n.flags |= 2;
2013 if(peers_[i].player->profile_.ProfileData.AccountType == 54) // punisher
2014 n.flags |= 4;
2015 if(peers_[i].player->profile_.ProfileData.PremiumAcc > 0)
2016 n.flags |= 8;
2017
2018 net_->SendToPeer(&n, sizeof(n), peerId, true);
2019
2020 // send call for help if any
2021 if(r3dGetTime() < (peers_[i].player->lastCallForHelp+600))
2022 {
2023 PKT_S2C_CallForHelpEvent_s n3;
2024 n3.playerIdx = (WORD)peers_[i].playerIdx;
2025 r3dscpy(n3.distress, peers_[i].player->CallForHelp_distress);
2026 r3dscpy(n3.reward, peers_[i].player->CallForHelp_reward);
2027 n3.timeLeft = (peers_[i].player->lastCallForHelp+600) - r3dGetTime();
2028 n3.locX = peers_[i].player->lastCallForHelpLocation.x;
2029 n3.locZ = peers_[i].player->lastCallForHelpLocation.z;
2030
2031 net_->SendToPeer(&n3, sizeof(n3), peerId, true);
2032 }
2033 //}
2034 }
2035 }
2036 }
2037
2038 return plr;
2039}
2040
2041void ServerGameLogic::DeletePlayer(int playerIdx, obj_ServerPlayer* plr)
2042{
2043 r3d_assert(plr);
2044
2045 r3d_assert(playerIdx == (plr->GetNetworkID() - NETID_PLAYERS_START));
2046 r3dOutToLog("DeletePlayer: %s, playerIdx: %d\n", plr->userName, playerIdx);
2047
2048#ifdef ENABLE_GAMEBLOCKS
2049 if(g_GameBlocks_Client && g_GameBlocks_Client->Connected())
2050 {
2051 GameBlocks::Event_PlayerLeave_Send(g_GameBlocks_Client, g_GameBlocks_ServerID, GameBlocks::GBPublicPlayerId(uint32_t(plr->profile_.CustomerID)), plr->userName);
2052 }
2053#endif //ENABLE_GAMEBLOCKS
2054 ResetNetObjVisData(plr);
2055
2056 // if player in group
2057 if(plr->groupID!=0)
2058 {
2059 leavePlayerFromGroup(plr);
2060 }
2061
2062 // remove player invites from other players
2063 cleanPlayerGroupInvites(plr);
2064
2065 // send player destroy
2066 {
2067 PKT_S2C_DestroyNetObject_s n;
2068 n.spawnID = gp2pnetid_t(plr->GetNetworkID());
2069 p2pBroadcastToActive(plr, &n, sizeof(n));
2070 }
2071
2072#if defined(MISSIONS)
2073 for(int i = 0; i < wiUserProfile::MAX_LOADOUT_SLOTS; ++i)
2074 {
2075 if( plr->profile_.ProfileData.ArmorySlots[ i ].missionsProgress )
2076 delete plr->profile_.ProfileData.ArmorySlots[ i ].missionsProgress;
2077 }
2078#endif
2079
2080 // mark for deletion
2081 plr->setActiveFlag(0);
2082 plr->wasDeleted = true;
2083
2084 // search for that player in flat array and remove it from there.
2085 for(int i=0; i<curPlayers_; ++i)
2086 {
2087 if(plrList_[i] == plr)
2088 {
2089 memmove(plrList_ + i, plrList_ + i + 1, (curPlayers_ - i - 1) * sizeof(plrList_[0]));
2090 break;
2091 }
2092 }
2093 r3d_assert(curPlayers_ > 0);
2094 curPlayers_--;
2095
2096 return;
2097}
2098
2099// sync with CMasterUserServer::IsAdmin(DWORD CustomerID)
2100bool ServerGameLogic::IsAdmin(DWORD CustomerID)
2101{
2102/*
2103 //@TH
2104 switch(CustomerID)
2105 {
2106 case 1651761:
2107 case 1651758:
2108 case 1000015:
2109 case 1000006:
2110 case 1241667:
2111 case 1000003:
2112 case 1000012:
2113 case 1000004:
2114 case 1000014:
2115 case 1000011:
2116 case 1288630:
2117 case 1125258:
2118 case 1731195:
2119 case 1320420:
2120 case 1731117:
2121 case 1731140:
2122 case 1182980:
2123 case 2280615: // askidmore@thewarz.com
2124 case 3099993: // eric koch
2125 return true;
2126 }
2127*/
2128 return false;
2129}
2130
2131IMPL_PACKET_FUNC(ServerGameLogic, PKT_C2S_JoinGameReq)
2132{
2133 DWORD ip = net_->GetPeerIp(peerId);
2134 r3dOutToLog("peer%02d PKT_C2S_JoinGameReq: CID:%d, ip:%s\n",
2135 peerId, n.CustomerID, inet_ntoa(*(in_addr*)&ip));
2136 CLOG_INDENT;
2137
2138 if(n.CustomerID == 0 || n.SessionID == 0 || n.CharID == 0)
2139 {
2140 gServerLogic.LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_Protocol, true, "JoinGame",
2141 "%d %d %d", n.CustomerID, n.SessionID, n.CharID);
2142 return;
2143 }
2144
2145 // check if that player was kicked before and ban time isn't expired
2146 {
2147 TKickedPlayers::iterator it = kickedPlayers_.find(n.CustomerID);
2148 if(it != kickedPlayers_.end() && r3dGetTime() < it->second)
2149 {
2150 PKT_S2C_CustomKickMsg_s n2;
2151 r3dscpy(n2.msg, "You are temporarily banned from this server");
2152 p2pSendRawToPeer(peerId, &n2, sizeof(n2));
2153
2154 PKT_S2C_JoinGameAns_s n;
2155 n.success = 0;
2156 n.playerIdx = 0;
2157 n.ownerCustomerID = 0;
2158 p2pSendRawToPeer(peerId, &n, sizeof(n));
2159
2160 DisconnectPeer(peerId, false, "temporary ban");
2161 return;
2162 }
2163 }
2164
2165 // GetFreePlayerSlot
2166 int playerIdx = -1;
2167 int slotsToScan = IsAdmin(n.CustomerID) ? MAX_NUM_PLAYERS : ginfo_.maxPlayers;
2168 for(int i=0; i<slotsToScan; i++)
2169 {
2170 if(plrToPeer_[i] == NULL)
2171 {
2172 playerIdx = i;
2173 break;
2174 }
2175 }
2176
2177 if(playerIdx == -1)
2178 {
2179 PKT_S2C_JoinGameAns_s n;
2180 n.success = 0;
2181 n.playerIdx = 0;
2182 n.ownerCustomerID = 0;
2183 p2pSendRawToPeer(peerId, &n, sizeof(n));
2184
2185 DisconnectPeer(peerId, false, "game is full"); //@ investigate why it's happening
2186 return;
2187 }
2188
2189 peerInfo_s& peer = GetPeer(peerId);
2190 r3d_assert(peer.player == NULL);
2191 peer.SetStatus(PEER_LOADING);
2192 peer.playerIdx = playerIdx;
2193 peer.CustomerID = n.CustomerID;
2194 peer.SessionID = n.SessionID;
2195 peer.CharID = n.CharID;
2196 peer.nextPingOnLoad = -1;
2197 peer.voicePeerId = (u_random(0xFFFFFFFE) & 0xFFFF0000) | playerIdx;
2198 peer.voiceClientId = 0;
2199
2200 // add to players table
2201 r3d_assert(plrToPeer_[playerIdx] == NULL);
2202 plrToPeer_[playerIdx] = &peer;
2203
2204 // send join answer to peer
2205 {
2206 PKT_S2C_JoinGameAns_s n;
2207 n.success = 1;
2208 n.playerIdx = playerIdx;
2209 n.ownerCustomerID = creatorID_;
2210 n.gameInfo = ginfo_;
2211 n.gameTime = GetUtcGameTime();
2212 n.voiceId = peer.voicePeerId;
2213 n.voicePwd = TSServer_GetPassword();
2214
2215 p2pSendRawToPeer(peerId, &n, sizeof(n));
2216 }
2217
2218 // send game parameters to peer
2219 {
2220 PKT_S2C_SetGamePlayParams_s n;
2221 n.GPP_Data = GPP_Data;
2222 n.GPP_Seed = GPP_Seed;
2223 p2pSendRawToPeer(peerId, &n, sizeof(n));
2224 }
2225
2226 // start thread for profile loading
2227 r3d_assert(peer.startGameAns == 0);
2228 g_AsyncApiMgr->AddJob(new CJobProcessUserJoin(peerId));
2229
2230 return;
2231}
2232
2233IMPL_PACKET_FUNC(ServerGameLogic, PKT_C2S_StartGameReq)
2234{
2235 peerInfo_s& peer = GetPeer(peerId);
2236 r3d_assert(peer.playerIdx != -1);
2237 r3d_assert(peer.player == NULL);
2238 r3d_assert(peer.status_ == PEER_LOADING);
2239
2240 r3dOutToLog("peer%02d PKT_C2S_StartGameReq, playerIdx:%d startGameAns: %d, lastNetID: %d\n", peerId, peer.playerIdx, peer.startGameAns, n.lastNetID); CLOG_INDENT;
2241
2242 /*if(n.lastNetID != net_mapLoaded_LastNetID)
2243 {
2244 PKT_S2C_StartGameAns_s n2;
2245 n2.result = PKT_S2C_StartGameAns_s::RES_UNSYNC;
2246 p2pSendRawToPeer(peerId, &n2, sizeof(n2));
2247 DisconnectPeer(peerId, true, "netID doesn't match %d vs %d", n.lastNetID, net_mapLoaded_LastNetID);
2248 return;
2249 }*/
2250
2251 // check for default values, just in case
2252 r3d_assert(0 == PKT_S2C_StartGameAns_s::RES_Unactive);
2253 r3d_assert(1 == PKT_S2C_StartGameAns_s::RES_Ok);
2254 switch(peer.startGameAns)
2255 {
2256 // we have profile, process
2257 case PKT_S2C_StartGameAns_s::RES_Ok:
2258 break;
2259
2260 // no profile loaded yet
2261 case PKT_S2C_StartGameAns_s::RES_Unactive:
2262 {
2263 // we give 60sec to finish getting profile per user
2264 if(r3dGetTime() > (peer.startTime + 60.0f))
2265 {
2266 PKT_S2C_StartGameAns_s n;
2267 n.result = PKT_S2C_StartGameAns_s::RES_Failed;
2268 p2pSendRawToPeer(peerId, &n, sizeof(n));
2269 DisconnectPeer(peerId, true, "timeout getting profile data");
2270 }
2271 else
2272 {
2273 // still pending
2274 PKT_S2C_StartGameAns_s n;
2275 n.result = PKT_S2C_StartGameAns_s::RES_Pending;
2276 p2pSendRawToPeer(peerId, &n, sizeof(n));
2277 }
2278 return;
2279 }
2280
2281 default:
2282 {
2283 PKT_S2C_StartGameAns_s n;
2284 n.result = (BYTE)peer.startGameAns;
2285 p2pSendRawToPeer(peerId, &n, sizeof(n));
2286 DisconnectPeer(peerId, true, "StarGameReq: %d", peer.startGameAns);
2287 return;
2288 }
2289 }
2290
2291 // we have player profile, put it in game
2292 r3d_assert(peer.startGameAns == PKT_S2C_StartGameAns_s::RES_Ok);
2293
2294 // we must have only one profile with correct charid
2295 if(peer.temp_profile.ProfileData.NumSlots != 1 ||
2296 peer.temp_profile.ProfileData.ArmorySlots[0].LoadoutID != peer.CharID)
2297 {
2298 PKT_S2C_StartGameAns_s n;
2299 n.result = PKT_S2C_StartGameAns_s::RES_Failed;
2300 p2pSendRawToPeer(peerId, &n, sizeof(n));
2301
2302 DisconnectPeer(peerId, true, "CharID mismatch %d vs %d", peer.CharID, peer.temp_profile.ProfileData.ArmorySlots[0].LoadoutID);
2303 return;
2304 }
2305
2306 // and it should be alive.
2307 wiCharDataFull& loadout = peer.temp_profile.ProfileData.ArmorySlots[0];
2308 if(loadout.Alive == 0)
2309 {
2310 PKT_S2C_StartGameAns_s n;
2311 n.result = PKT_S2C_StartGameAns_s::RES_Failed;
2312 p2pSendRawToPeer(peerId, &n, sizeof(n));
2313
2314 DisconnectPeer(peerId, true, "CharID %d is DEAD", peer.CharID);
2315 return;
2316 }
2317
2318 if(peer.temp_profile.ProfileData.isDevAccount==0) // no check for Devs
2319 {
2320 if(ginfo_.gameTimeLimit > int(peer.temp_profile.ProfileData.TimePlayed/60/60))
2321 {
2322 PKT_S2C_StartGameAns_s n;
2323 n.result = PKT_S2C_StartGameAns_s::RES_Failed;
2324 p2pSendRawToPeer(peerId, &n, sizeof(n));
2325
2326 DisconnectPeer(peerId, true, "Gametime limit on server");
2327 return;
2328 }
2329 }
2330
2331 // trial accs allowed to connect only to trial servers
2332 /*if(ginfo_.channel!=1 && peer.temp_profile.IsTrialAccount())
2333 {
2334 PKT_S2C_StartGameAns_s n;
2335 n.result = PKT_S2C_StartGameAns_s::RES_Failed;
2336 p2pSendRawToPeer(peerId, &n, sizeof(n));
2337
2338 DisconnectPeer(peerId, true, "Trial accs not allowed");
2339 return;
2340 }
2341
2342 if(ginfo_.channel==1 && !peer.temp_profile.IsTrialAccount())
2343 {
2344 if(peer.temp_profile.ProfileData.isDevAccount>0)
2345 {
2346 // allow devs to connect to trial servers
2347 }
2348 else
2349 {
2350 PKT_S2C_StartGameAns_s n;
2351 n.result = PKT_S2C_StartGameAns_s::RES_Failed;
2352 p2pSendRawToPeer(peerId, &n, sizeof(n));
2353
2354 DisconnectPeer(peerId, true, "Trial acc is required to join trial server");
2355 return;
2356 }
2357 }*/
2358
2359 if(ginfo_.channel==4 && peer.temp_profile.ProfileData.PremiumAcc == 0)
2360 {
2361 PKT_S2C_StartGameAns_s n;
2362 n.result = PKT_S2C_StartGameAns_s::RES_Failed;
2363 p2pSendRawToPeer(peerId, &n, sizeof(n));
2364
2365 DisconnectPeer(peerId, true, "Premium required");
2366 return;
2367 }
2368 if(peer.temp_profile.ProfileData.isDevAccount==0) // only for Devs
2369 {
2370 if(ginfo_.mapId == GBGameInfo::MAPID_ServerTest)
2371 {
2372 PKT_S2C_StartGameAns_s n;
2373 n.result = PKT_S2C_StartGameAns_s::RES_Failed;
2374 p2pSendRawToPeer(peerId, &n, sizeof(n));
2375
2376 DisconnectPeer(peerId, true, "This map only for DEV accounts");
2377 return;
2378 }
2379 }
2380
2381 //
2382 if (ginfo_.IsGameBR() && m_isGameHasStarted)
2383 {
2384 PKT_S2C_StartGameAns_s n;
2385 n.result = PKT_S2C_StartGameAns_s::RES_Failed;
2386 p2pSendRawToPeer(peerId, &n, sizeof(n));
2387 r3dOutToLog("peer connected while game has started!\n");
2388 DisconnectPeer(peerId, true, "game has started!");
2389 return;
2390 }
2391
2392 peer.SetStatus(PEER_PLAYING);
2393
2394 // if by some fucking unknown method you appeared at 0,0,0 - pretend he was dead, so it'll spawn at point
2395 if(loadout.Alive == 1 && loadout.GameMapId != GBGameInfo::MAPID_ServerTest && loadout.GamePos.Length() < 10 && !ginfo_.IsGameBR())//
2396 {
2397 LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_Data, false, "ZeroSpawn", "%f %f %f", loadout.GamePos.x, loadout.GamePos.y, loadout.GamePos.z);
2398 loadout.Alive = 2;
2399 }
2400
2401 // get spawn position
2402 r3dPoint3D spawnPos;
2403 float spawnDir;
2404 bool needsSpawnProtection = false;
2405 //r3dOutToLog("!!! Loading Pos: %.3f %.3f %.3f\n", loadout.GamePos.x, loadout.GamePos.y, loadout.GamePos.z);
2406 GetStartSpawnPosition(loadout, &spawnPos, &spawnDir, needsSpawnProtection);
2407 if(peer.isServerHop) // if player server hopped, then he will need spawnProtection no matter what, as he will spawn in random location
2408 needsSpawnProtection = true;
2409
2410 // adjust for positions that is under ground because of geometry change
2411 if(Terrain)
2412 {
2413 float y1 = Terrain->GetHeight(spawnPos);
2414 if(spawnPos.y <= y1)
2415 spawnPos.y = y1 + 0.1f;
2416 }
2417 //r3dOutToLog("!!! Final Pos: %.3f %.3f %.3f\n", spawnPos.x, spawnPos.y, spawnPos.z);
2418
2419 // create that player
2420 obj_ServerPlayer* plr = CreateNewPlayer(peerId, spawnPos, spawnDir, needsSpawnProtection, n.FastLoad);
2421
2422 // send current weapon info to player
2423 SendWeaponsInfoToPlayer(peerId);
2424
2425 // send answer to start game
2426 {
2427 PKT_S2C_StartGameAns_s n;
2428 n.result = PKT_S2C_StartGameAns_s::RES_Ok;
2429 p2pSendRawToPeer(peerId, &n, sizeof(n)); //net_->SendToPeer(&n, sizeof(n), peerId, true);
2430 }
2431
2432#ifdef ENABLE_GAMEBLOCKS
2433 if(g_GameBlocks_Client && g_GameBlocks_Client->Connected())
2434 {
2435 DWORD IP = net_->GetPeerIp(peerId);
2436 GameBlocks::Event_PlayerJoin_Send(g_GameBlocks_Client, g_GameBlocks_ServerID, GameBlocks::GBPublicPlayerId(uint32_t(peer.CustomerID)), loadout.Gamertag, inet_ntoa(*(in_addr*)&IP));
2437
2438 g_GameBlocks_Client->PrepareEventForSending("Player Information", g_GameBlocks_ServerID, GameBlocks::GBPublicPlayerId(uint32_t(peer.CustomerID)));
2439 g_GameBlocks_Client->AddKeyValueInt("charID", peer.CharID);
2440 g_GameBlocks_Client->AddKeyValueString("Name", loadout.Gamertag);
2441 g_GameBlocks_Client->AddKeyValueString("IP", inet_ntoa(*(in_addr*)&IP));
2442 g_GameBlocks_Client->AddKeyValueInt("KillCiv", loadout.Stats.KilledSurvivors);
2443 g_GameBlocks_Client->AddKeyValueInt("KillBand", loadout.Stats.KilledBandits);
2444 g_GameBlocks_Client->AddKeyValueInt("KillZomb", loadout.Stats.KilledZombies);
2445 g_GameBlocks_Client->AddKeyValueInt("TimeAlive", loadout.Stats.TimePlayed);
2446 g_GameBlocks_Client->AddKeyValueInt("Reputation", loadout.Stats.Reputation);
2447 g_GameBlocks_Client->AddKeyValueInt("XP", loadout.Stats.XP);
2448 g_GameBlocks_Client->AddKeyValueVector3D("Position", spawnPos.x, spawnPos.y, spawnPos.z);
2449 g_GameBlocks_Client->AddKeyValueInt("TrialAccount", peer.temp_profile.IsTrialAccount()?1:0);
2450 g_GameBlocks_Client->AddKeyValueInt("IsDeveloper", peer.temp_profile.ProfileData.isDevAccount);
2451 char buffer[20] = {0};
2452 sprintf(buffer, "%I64d", n.uniqueID);
2453 g_GameBlocks_Client->AddKeyValueString("NIC_ID", buffer);
2454 g_GameBlocks_Client->AddKeyValueInt("AccountType", peer.temp_profile.ProfileData.AccountType);
2455 g_GameBlocks_Client->SendEvent();
2456 }
2457#endif //ENABLE_GAMEBLOCKS
2458
2459 // send message to client that he was server hopped
2460 if(peer.isServerHop)
2461 {
2462 PKT_C2C_ChatMessage_s n2;
2463 n2.FromID = toP2pNetId(plr->GetNetworkID());
2464 n2.msgChannel = 1; // global
2465 n2.userFlag = 2; // mark as dev, so color is red
2466 r3dscpy(n2.gamertag, "<SYSTEM>");
2467 r3dscpy(n2.msg, "You were teleported to a safe location because of quick change of game servers");
2468 p2pSendToPeer(plr->peerId_, plr, &n2, sizeof(n2), 1);
2469 }
2470
2471#ifdef MISSIONS
2472 if( Mission::g_pMissionMgr )
2473 {
2474 Mission::g_pMissionMgr->SendActiveMissionsToPlayer( *plr );
2475
2476 // Sync client mission progress with what's been loaded, and now copied to the player on the server.
2477 Mission::MissionsProgress* mp = plr->profile_.ProfileData.ArmorySlots[0].missionsProgress;
2478 if( mp )
2479 {
2480 //r3dOutToLog("Syncing Remote Missions Progress for player %s\n", plr->Name);
2481 mp->SyncRemoteMissionsProgress();
2482 }
2483 else
2484 {
2485 r3dOutToLog("!!! Missions Progress not set, unable to sync Remote Missions Progress for player %s\n", plr->Name);
2486 }
2487 }
2488#endif
2489
2490 return;
2491}
2492
2493bool ServerGameLogic::CheckForPlayersAround(const r3dPoint3D& pos, float dist)
2494{
2495 float distSq = dist * dist;
2496 for(int i=0; i<curPlayers_; i++)
2497 {
2498 const obj_ServerPlayer* plr = plrList_[i];
2499 if((plr->GetPosition() - pos).LengthSq() < distSq)
2500 return true;
2501 }
2502
2503 return false;
2504}
2505
2506void ServerGameLogic::GetStartSpawnPosition(const wiCharDataFull& loadout, r3dPoint3D* pos, float* dir, bool& needsSpawnProtection)
2507{
2508 needsSpawnProtection = false;
2509
2510 // if no map assigned yet, or new map, or newly created character (alive == 3) or joining PTE map
2511 if((loadout.GameMapId == 0 || loadout.GameMapId != ginfo_.mapId || loadout.Alive == 3
2512#ifdef DISABLE_GI_ACCESS_ON_PTE_MAP
2513 || ginfo_.channel == 6
2514#endif
2515#ifdef DISABLE_GI_ACCESS_ON_PTE_STRONGHOLD_MAP
2516 || (ginfo_.channel == 6 && ginfo_.mapId==GBGameInfo::MAPID_WZ_Cliffside)
2517#endif
2518#ifdef DISABLE_GI_ACCESS_FOR_CALI_SERVER
2519 || (gServerLogic.ginfo_.mapId==GBGameInfo::MAPID_WZ_California)
2520#endif
2521#ifdef DISABLE_GI_ACCESS_FOR_DEV_EVENT_SERVER
2522 || ginfo_.channel == 6
2523#endif
2524 )&& !ginfo_.IsGameBR())//
2525 {
2526 needsSpawnProtection = true;
2527 GetSpawnPositionNewPlayer(loadout.GamePos, pos, dir);
2528 // move spawn pos at radius
2529 pos->x += u_GetRandom(-_glm_SpawnRadius, _glm_SpawnRadius);
2530 pos->z += u_GetRandom(-_glm_SpawnRadius, _glm_SpawnRadius);
2531 //r3dOutToLog("new spawn at position %f %f %f\n", pos->x, pos->y, pos->z);
2532 return;
2533 }
2534
2535 // alive at current map
2536 if(loadout.GameMapId && (loadout.GameMapId == ginfo_.mapId) && loadout.Alive == 1 &&
2537 !ginfo_.IsGameBR())
2538 {
2539 needsSpawnProtection = true;
2540 GetSpawnPositionAfterDeath(loadout.GamePos, pos, dir);
2541 // move spawn pos at radius
2542 pos->x += u_GetRandom(-_glm_SpawnRadius, _glm_SpawnRadius);
2543 pos->z += u_GetRandom(-_glm_SpawnRadius, _glm_SpawnRadius);
2544 //r3dOutToLog("revived at position %f %f %f\n", pos->x, pos->y, pos->z);
2545 return;
2546 /*needsSpawnProtection = false;
2547 PxRaycastHit hit;
2548 PxSceneQueryFilterData filter(PxFilterData(COLLIDABLE_STATIC_MASK,0,0,0), PxSceneQueryFilterFlags(PxSceneQueryFilterFlag::eSTATIC|PxSceneQueryFilterFlag::eDYNAMIC));
2549 bool hitResult = g_pPhysicsWorld->raycastSingle(PxVec3(loadout.GamePos.x, loadout.GamePos.y + 0.5f, loadout.GamePos.z), PxVec3(0, -1, 0), 500.0f, PxSceneQueryFlags(PxSceneQueryFlag::eIMPACT), hit, filter);
2550 r3dPoint3D posForWater = loadout.GamePos;
2551 if( hitResult )
2552 posForWater = r3dPoint3D(hit.impact.x, hit.impact.y, hit.impact.z); // This is the ground position underwater.
2553
2554 float waterDepth = getWaterDepthAtPos(posForWater);
2555
2556 //r3dOutToLog("!!! WaterCheck Hit: %d, Depth: %.3f, Impact: %.3f, %.3f, %.3f\n", (int)hitResult, waterDepth, posForWater.x, posForWater.y, posForWater.z);
2557
2558 const float allowedDepth = 1.5f;
2559 if(waterDepth > allowedDepth) // too deep, start swimming
2560 {
2561 *pos = r3dPoint3D(posForWater.x, posForWater.y + (waterDepth - allowedDepth), posForWater.z); // adjust to water, in case player logged out while swimming
2562 }
2563 else
2564 {
2565 *pos = AdjustPositionToFloor(loadout.GamePos); // adjust to floor, in case if player logged out while being on top of other player or anything like that
2566 }
2567 *dir = loadout.GameDir;
2568 //r3dOutToLog("alive at position %.3f %.3f %.3f\n", pos->x, pos->y, pos->z);
2569 return;*/
2570 }
2571
2572 // revived (alive == 2) - spawn to closest spawn point
2573 if(loadout.GameMapId && loadout.Alive == 2 &&
2574 !ginfo_.IsGameBR())//
2575 {
2576 needsSpawnProtection = true;
2577 GetSpawnPositionAfterDeath(loadout.GamePos, pos, dir);
2578 // move spawn pos at radius
2579 pos->x += u_GetRandom(-_glm_SpawnRadius, _glm_SpawnRadius);
2580 pos->z += u_GetRandom(-_glm_SpawnRadius, _glm_SpawnRadius);
2581 //r3dOutToLog("revived at position %f %f %f\n", pos->x, pos->y, pos->z);
2582 return;
2583 }
2584
2585 //
2586 if(ginfo_.IsGameBR())
2587 {
2588 needsSpawnProtection = true;
2589 GetSpawnPositionBattleRoyale(loadout.GamePos, pos, dir);
2590 pos->x += u_GetRandom(-_glm_SpawnRadius, _glm_SpawnRadius);
2591 pos->z += u_GetRandom(-_glm_SpawnRadius, _glm_SpawnRadius);
2592 return;
2593 }
2594
2595 r3dOutToLog("%d %d %d\n", loadout.GameMapId, loadout.Alive, ginfo_.mapId);
2596 r3d_assert(false && "GetStartSpawnPosition");
2597}
2598
2599void ServerGameLogic::GetSpawnPositionNewPlayer(const r3dPoint3D& GamePos, r3dPoint3D* pos, float* dir)
2600{
2601 if(gCPMgr.numControlPoints_ == 0)
2602 {
2603 r3dOutToLog("!!!!!!!!!!!! THERE IS NO CONTROL POINTS !!!!!!!\n");
2604 *pos = r3dPoint3D(0, 0, 0);
2605 *dir = 0;
2606 return;
2607 }
2608
2609 int idx1 = u_random(gCPMgr.numControlPoints_);
2610 r3d_assert(idx1 < gCPMgr.numControlPoints_);
2611 const BasePlayerSpawnPoint* spawn = gCPMgr.controlPoints_[idx1];
2612 spawn->getSpawnPoint(*pos, *dir);
2613 return;
2614}
2615
2616void ServerGameLogic::GetSpawnPositionAfterDeath(const r3dPoint3D& GamePos, r3dPoint3D* pos, float* dir)
2617{
2618 if(gCPMgr.numControlPoints_ == 0)
2619 {
2620 r3dError("!!!!!!!!!!!! THERE IS NO CONTROL POINT !!!!!!!\n");
2621 *pos = r3dPoint3D(0, 0, 0);
2622 *dir = 0;
2623 return;
2624 }
2625
2626 // set defaults in case if all spawnpoints is in 100m range (server test map)
2627 *pos = GamePos;
2628 *dir = 0;
2629
2630 // NOTE: this function is used from another thread from CJobProcessUserJoin::DetectServerHop. DO NOT add any statics here
2631 // spawn to closest point (but don't spawn player at almost exactly same spawn if he died to close to spawn location)
2632 float minDist = FLT_MAX;
2633 for(int i=0; i<gCPMgr.numControlPoints_; i++)
2634 {
2635 const BasePlayerSpawnPoint* spawn = gCPMgr.controlPoints_[i];
2636 for(int j=0; j<spawn->m_NumSpawnPoints; j++)
2637 {
2638 float dist = (GamePos - spawn->m_SpawnPoints[j].pos).Length();
2639 if(dist < 100.0f) // to make sure that player will not keep spawning at the same location
2640 continue;
2641 if(dist < minDist)
2642 {
2643 *pos = spawn->m_SpawnPoints[j].pos;
2644 *dir = spawn->m_SpawnPoints[j].dir;
2645 minDist = dist;
2646 }
2647 }
2648 }
2649
2650 return;
2651}
2652
2653void ServerGameLogic::GetSpawnPositionAfterDeath2(const r3dPoint3D& GamePos, r3dPoint3D* pos, float* dir)
2654{
2655 if(gCPMgr.numControlPoints_ == 0)
2656 {
2657 r3dError("!!!!!!!!!!!! THERE IS NO CONTROL POINT !!!!!!!\n");
2658 *pos = r3dPoint3D(0, 0, 0);
2659 *dir = 0;
2660 return;
2661 }
2662
2663 // find points in 1km radius and select one with least amount of "danger" in it
2664 static BasePlayerSpawnPoint::node_s listOfPoints[1000];
2665 int numPoints = 0;
2666
2667 for(int i=0; i<gCPMgr.numControlPoints_; i++)
2668 {
2669 const BasePlayerSpawnPoint* spawn = gCPMgr.controlPoints_[i];
2670 for(int j=0; j<spawn->m_NumSpawnPoints; j++)
2671 {
2672 float dist = (GamePos - spawn->m_SpawnPoints[j].pos).Length();
2673 if(dist < 1000.0f) // to make sure that player will not keep spawning at the same location
2674 {
2675 listOfPoints[numPoints++] = spawn->m_SpawnPoints[j];
2676 if(numPoints==1000) // reached maximum
2677 break;
2678 }
2679 }
2680 }
2681
2682 if(numPoints == 0) // fallback to old method
2683 {
2684 return GetSpawnPositionAfterDeath(GamePos, pos, dir);
2685 }
2686
2687 *pos = listOfPoints[0].pos;
2688 *dir = listOfPoints[0].dir;
2689
2690 // and now find closest point to our location
2691 int curDanger = 99999999;
2692 float minDist = FLT_MAX;
2693 for(int i=0; i<numPoints; ++i)
2694 {
2695 if(listOfPoints[i].danger > curDanger)
2696 continue;
2697
2698 float dist = (GamePos - listOfPoints[i].pos).Length();
2699 if(dist < 100.0f) // to make sure that player will not keep spawning at the same location
2700 continue;
2701 if(dist < minDist && listOfPoints[i].danger <= curDanger) // select smaller distance and lowest danger
2702 {
2703 *pos = listOfPoints[i].pos;
2704 *dir = listOfPoints[i].dir;
2705 minDist = dist;
2706 curDanger = listOfPoints[i].danger;
2707 }
2708 }
2709
2710 return;
2711}
2712
2713r3dPoint3D ServerGameLogic::AdjustPositionToFloor(const r3dPoint3D& pos)
2714{
2715 // do this in a couple of steps. firstly try +0.25, +1, then +5, then +50, then absolute +1000
2716 PxSweepHit hit;
2717 PxSceneQueryFilterData filter(PxFilterData(COLLIDABLE_STATIC_MASK, 0, 0, 0), PxSceneQueryFilterFlag::eSTATIC);
2718 PxSphereGeometry sphere(0.25f);
2719
2720 if (!g_pPhysicsWorld->PhysXScene->sweepSingle(sphere, PxTransform(PxVec3(pos.x, pos.y+0.5f, pos.z), PxQuat(0,0,0,1)), PxVec3(0, -1, 0), 2000.0f, PxSceneQueryFlag::eIMPACT, hit, filter))
2721 if (!g_pPhysicsWorld->PhysXScene->sweepSingle(sphere, PxTransform(PxVec3(pos.x, pos.y+1.0f, pos.z), PxQuat(0,0,0,1)), PxVec3(0, -1, 0), 2000.0f, PxSceneQueryFlag::eIMPACT, hit, filter))
2722 if (!g_pPhysicsWorld->PhysXScene->sweepSingle(sphere, PxTransform(PxVec3(pos.x, pos.y+5.0f, pos.z), PxQuat(0,0,0,1)), PxVec3(0, -1, 0), 2000.0f, PxSceneQueryFlag::eIMPACT, hit, filter))
2723 if (!g_pPhysicsWorld->PhysXScene->sweepSingle(sphere, PxTransform(PxVec3(pos.x, pos.y+50.0f, pos.z), PxQuat(0,0,0,1)), PxVec3(0, -1, 0), 2000.0f, PxSceneQueryFlag::eIMPACT, hit, filter))
2724 if (!g_pPhysicsWorld->PhysXScene->sweepSingle(sphere, PxTransform(PxVec3(pos.x, 1000.0f, pos.z), PxQuat(0,0,0,1)), PxVec3(0, -1, 0), 2000.0f, PxSceneQueryFlag::eIMPACT, hit, filter))
2725 {
2726 r3dOutToLog("!! there is no floor under %f %f %f\n", pos.x, pos.y, pos.z);
2727 return pos;
2728 }
2729
2730 return r3dPoint3D(hit.impact.x, hit.impact.y + 0.01f, hit.impact.z);
2731}
2732
2733//
2734// every server network object must call this function in their OnCreate() - TODO: think about better way to automatize that
2735//
2736void ServerGameLogic::NetRegisterObjectToPeers(GameObject* netObj)
2737{
2738 r3d_assert(netObj->GetNetworkID());
2739
2740 // scan for all peers and see if they within distance of this object
2741 INetworkHelper* nh = netObj->GetNetworkHelper();
2742 for(int peerId=0; peerId<MAX_PEERS_COUNT; peerId++)
2743 {
2744 const peerInfo_s& peer = peers_[peerId];
2745 if(peer.player == NULL)
2746 continue;
2747
2748 float dist = (peer.player->GetPosition() - netObj->GetPosition()).LengthSq();
2749 if(dist < nh->distToCreateSq)
2750 {
2751#ifdef _DEBUG
2752r3dOutToLog("NETHELPER: %s: on create - entered visibility of network object %d %s\n", peer.player->userName, netObj->GetNetworkID(), netObj->Name.c_str());
2753#endif
2754 r3d_assert(nh->PeerVisStatus[peerId] == 0);
2755 nh->PeerVisStatus[peerId] = 1;
2756
2757 int packetSize = 0;
2758 DefaultPacket* packetData = nh->NetGetCreatePacket(&packetSize);
2759 if(packetData)
2760 {
2761 preparePacket(netObj, packetData);
2762 net_->SendToPeer(packetData, packetSize, peerId, true);
2763 netSentPktSize[packetData->EventID] += packetSize;
2764 }
2765 }
2766 }
2767
2768}
2769
2770void ServerGameLogic::UpdateNetObjVisData(DWORD peerId, obj_ServerPlayer* pl, GameObject* netObj)
2771{
2772 if (netObj->Class->Name != "obj_Building")
2773 {
2774 if (!netObj->GetNetworkID()) return;
2775 r3d_assert(netObj->GetNetworkID());
2776 }
2777 r3d_assert(!(netObj->ObjFlags & OBJFLAG_JustCreated)); // object must be fully created at this moment
2778
2779 //const peerInfo_s& peer = GetPeer(peerId);
2780 float dist = (pl->GetPosition() - netObj->GetPosition()).LengthSq();
2781
2782 INetworkHelper* nh = netObj->GetNetworkHelper();
2783 if(nh->PeerVisStatus[peerId] == 0)
2784 {
2785 if (netObj->Class->Name != "obj_Building")
2786 {
2787 if(dist < nh->distToCreateSq)
2788 {
2789#ifdef _DEBUG
2790//r3dOutToLog("NETHELPER: %s: entered visibility of network object %d %s\n", pl->userName, netObj->GetNetworkID(), netObj->Name.c_str());
2791#endif
2792 if (netObj->Class->Name != "obj_Building")
2793 {
2794 nh->PeerVisStatus[peerId] = 1;
2795
2796 int packetSize = 0;
2797 DefaultPacket* packetData = nh->NetGetCreatePacket(&packetSize);
2798 if(packetData)
2799 {
2800 if (netObj->Class->Name == "obj_Building")
2801 {
2802 PKT_S2C_CreateBuilding_s n;
2803 n.angle = netObj->GetRotationVector();
2804 n.col = false;
2805 if(netObj->ObjFlags & OBJFLAG_PlayerCollisionOnly)
2806 n.col = true;
2807
2808 n.pos = netObj->GetPosition();
2809 r3dscpy(n.fname,netObj->FileName.c_str());
2810 n.spawnID = 0;
2811 net_->SendToPeer(&n, sizeof(n), peerId, false);
2812 }
2813 else
2814 {
2815 preparePacket(netObj, packetData);
2816 net_->SendToPeer(packetData, packetSize, peerId, true);
2817 netSentPktSize[packetData->EventID] += packetSize;
2818 }
2819 }
2820 }
2821 else
2822 {
2823 nh->PeerVisStatus[peerId] = 1;
2824
2825 int packetSize = 0;
2826 DefaultPacket* packetData = nh->NetGetCreatePacket(&packetSize);
2827 if(packetData)
2828 {
2829 preparePacket(netObj, packetData);
2830 net_->SendToPeer(packetData, packetSize, peerId, false);
2831 netSentPktSize[packetData->EventID] += packetSize;
2832 }
2833 return;
2834 }
2835 }
2836 }
2837 else
2838 {
2839#ifdef _DEBUG
2840//r3dOutToLog("NETHELPER: %s: entered visibility of network object %d %s\n", pl->userName, netObj->GetNetworkID(), netObj->Name.c_str());
2841#endif
2842 if ((r3dGetTime() - pl->startPlayTime_) > 10.0f)
2843 {
2844 nh->PeerVisStatus[peerId] = 1;
2845
2846 int packetSize = 0;
2847 DefaultPacket* packetData = nh->NetGetCreatePacket(&packetSize);
2848 if(packetData)
2849 {
2850 // r3dOutToLog("SEND PACKET CREATEBUILDING TO PEER%d NAME %s FNAME %s\n",peerId,peer.player->userName,netObj->FileName.c_str());
2851 preparePacket(netObj, packetData);
2852 net_->SendToPeer(packetData, packetSize, peerId, false);
2853 netSentPktSize[packetData->EventID] += packetSize;
2854 }
2855 return;
2856 }
2857 else
2858 {
2859 nh->PeerVisStatus[peerId] = 1;
2860
2861 int packetSize = 0;
2862 DefaultPacket* packetData = nh->NetGetCreatePacket(&packetSize);
2863 if(packetData)
2864 {
2865 // r3dOutToLog("SEND PACKET CREATEBUILDING TO PEER%d NAME %s FNAME %s\n",peerId,peer.player->userName,netObj->FileName.c_str());
2866 preparePacket(netObj, packetData);
2867 net_->SendToPeer(packetData, packetSize, peerId, false);
2868 netSentPktSize[packetData->EventID] += packetSize;
2869 }
2870 return;
2871 }
2872 }
2873 }
2874 else
2875 {
2876 if(netObj && netObj->isObjType(OBJTYPE_Human))
2877 {
2878 /* @PZDEV: Just for test */
2879 obj_ServerPlayer* plr = (obj_ServerPlayer*)netObj;
2880 /*if (plr) // Made by Pakorn ^^
2881 {
2882 PKT_S2C_DestroyNetObject_s n;
2883 n.spawnID = toP2pNetId(netObj->GetNetworkID());
2884
2885 // send only to that peer!
2886 preparePacket(netObj, &n);
2887 net_->SendToPeer(&n, sizeof(n), peerId, true);
2888 netSentPktSize[n.EventID] += sizeof(n);
2889
2890 nh->PeerVisStatus[peerId] = 0;
2891 }*/
2892 }
2893 else
2894 {
2895 // already visible
2896 if(dist > nh->distToDeleteSq)
2897 {
2898#ifdef _DEBUG
2899//r3dOutToLog("NETHELPER: %s: left visibility of network object %d %s\n", pl->userName, netObj->GetNetworkID(), netObj->Name.c_str());
2900#endif
2901 PKT_S2C_DestroyNetObject_s n;
2902 n.spawnID = toP2pNetId(netObj->GetNetworkID());
2903
2904 // send only to that peer!
2905 preparePacket(netObj, &n);
2906 net_->SendToPeer(&n, sizeof(n), peerId, true);
2907 netSentPktSize[n.EventID] += sizeof(n);
2908
2909 nh->PeerVisStatus[peerId] = 0;
2910 }
2911 }
2912 }
2913}
2914
2915void ServerGameLogic::UpdateNetObjVisData(obj_ServerPlayer* plr)
2916{
2917 DWORD peerId = plr->peerId_;
2918
2919 if (!plr) return;
2920
2921 // scan for all objects and create/destroy them based on distance
2922 for(GameObject* obj=GameWorld().GetFirstObject(); obj; obj=GameWorld().GetNextObject(obj))
2923 {
2924 if (!plr) return;
2925
2926 if(obj->GetNetworkID() == 0)
2927 continue;
2928 if(obj->ObjFlags & OBJFLAG_JustCreated)
2929 continue;
2930 if(!obj->isActive())
2931 continue;
2932
2933 INetworkHelper* nh = obj->GetNetworkHelper();
2934
2935 if ((plr->GetPosition() - obj->GetPosition()).LengthSq() < nh->distToCreateSq && nh->PeerVisStatus[peerId] == 0)
2936 UpdateNetObjVisData(peerId, plr, obj);
2937 else if ((plr->GetPosition() - obj->GetPosition()).LengthSq() > nh->distToDeleteSq && nh->PeerVisStatus[peerId] == 1)
2938 UpdateNetObjVisData(peerId, plr, obj);
2939 }
2940 if (plr->isFastLoad)
2941 {
2942 extern ObjectManager ServerDummyWorld;
2943 for(GameObject* obj=ServerDummyWorld.GetFirstObject(); obj; obj=ServerDummyWorld.GetNextObject(obj))
2944 {
2945 if (!plr) return;
2946
2947 if(obj->ObjFlags & OBJFLAG_JustCreated)
2948 continue;
2949 if(!obj->isActive())
2950 continue;
2951
2952 INetworkHelper* nh = obj->GetNetworkHelper();
2953 if ((plr->GetPosition() - obj->GetPosition()).LengthSq() < nh->distToCreateSq && nh->PeerVisStatus[peerId] == 0/* && u_GetRandom() >= 0.8f*/)
2954 UpdateNetObjVisData(peerId, plr, obj);
2955 }
2956 }
2957}
2958
2959void ServerGameLogic::ResetNetObjVisData(const obj_ServerPlayer* plr)
2960{
2961 DWORD peerId = plr->peerId_;
2962
2963 // scan for all objects and reset their visibility of player
2964 for(GameObject* obj=GameWorld().GetFirstObject(); obj; obj=GameWorld().GetNextObject(obj))
2965 {
2966 if(obj->GetNetworkID() == 0)
2967 continue;
2968
2969 INetworkHelper* nh = obj->GetNetworkHelper();
2970 if (nh)
2971 {
2972 nh->PeerVisStatus[peerId] = 0;
2973 }
2974 }
2975
2976 extern ObjectManager ServerDummyWorld;
2977 for(GameObject* obj=ServerDummyWorld.GetFirstObject(); obj; obj=ServerDummyWorld.GetNextObject(obj))
2978 {
2979 INetworkHelper* nh = obj->GetNetworkHelper();
2980 if (nh)
2981 {
2982 nh->PeerVisStatus[peerId] = 0;
2983 }
2984 }
2985}
2986
2987void ServerGameLogic::DoExplosion(GameObject* fromObj, GameObject* sourceObj, r3dVector& forwVector, r3dVector& lastCollisionNormal, float direction, float damageArea, float damageAmount, STORE_CATEGORIES damageCategory, uint32_t damageItemId, bool isFromVehicle) // const WeaponConfig* wpnConfig)
2988{
2989 r3d_assert(sourceObj);
2990
2991 r3dVector explosionPos = sourceObj->GetPosition();
2992 if (!isFromVehicle)
2993 {
2994 r3dOutToLog("Explosion for grenade %d\n", sourceObj->GetSafeID()); CLOG_INDENT;
2995 PKT_S2C_Explosion_s n;
2996 n.spawnID = toP2pNetId(sourceObj->GetNetworkID());
2997 n.explosion_pos = explosionPos;
2998 n.forwVector = forwVector;
2999 n.lastCollisionNormal = lastCollisionNormal;
3000 n.direction = direction;
3001
3002 gServerLogic.p2pBroadcastToActive(sourceObj, &n, sizeof(n));
3003 }
3004
3005 // apply damage within a radius
3006 ObjectManager& GW = GameWorld();
3007 for(GameObject* obj = GW.GetFirstObject(); obj; obj = GW.GetNextObject(obj))
3008 {
3009 if(obj->GetNetworkID() == 0)
3010 continue;
3011 if(obj->ObjFlags & OBJFLAG_JustCreated)
3012 continue;
3013 if(!obj->isActive())
3014 continue;
3015
3016 if(!CanDamageThisObject(obj))
3017 continue;
3018
3019 float dist_to_obj = (obj->GetPosition() - explosionPos).Length();
3020 if(dist_to_obj > damageArea) // wpnConfig->m_AmmoArea)
3021 continue;
3022
3023 // raycast to make sure that player isn't behind a wall
3024 r3dPoint3D orig = r3dPoint3D(obj->GetPosition().x, obj->GetPosition().y+1.6f, obj->GetPosition().z);
3025 r3dPoint3D dir = r3dPoint3D(explosionPos.x-obj->GetPosition().x, explosionPos.y-(obj->GetPosition().y+1.6f), explosionPos.z - obj->GetPosition().z);
3026 float rayLen = dir.Length();
3027 if(rayLen < 0.0001f)
3028 continue;
3029 dir.Normalize();
3030
3031 BYTE damagePercentage = 100;
3032 float minDotDirection = cos( R3D_DEG2RAD( direction ) );
3033 const float FULL_AREA_EXPLOSION = 360; // from client
3034 if(direction == FULL_AREA_EXPLOSION || dir.Dot( forwVector ) > minDotDirection)
3035 {
3036 PxRaycastHit hit;
3037 PxSceneQueryFilterData filter(PxFilterData(COLLIDABLE_STATIC_MASK,0,0,0), PxSceneQueryFilterFlag::eDYNAMIC|PxSceneQueryFilterFlag::eSTATIC);
3038 if(g_pPhysicsWorld->raycastSingle(PxVec3(orig.x, orig.y, orig.z), PxVec3(dir.x, dir.y, dir.z), rayLen, PxSceneQueryFlag::eIMPACT, hit, filter))
3039 {
3040 // check distance to collision
3041 float len = r3dPoint3D(hit.impact.x-obj->GetPosition().x, hit.impact.y-(obj->GetPosition().y+2.0f), hit.impact.z-obj->GetPosition().z).Length();
3042 if((len+0.01f) < rayLen)
3043 {
3044 // human is behind a wall
3045 PhysicsCallbackObject* target;
3046 if( hit.shape && (target = static_cast<PhysicsCallbackObject*>(hit.shape->getActor().userData)))
3047 {
3048 // this currently only handles one piercable object between the player and explosion. More complexity might be valid here.
3049 GameObject* hitobj = target->isGameObject();
3050 if ( hitobj )
3051 {
3052 damagePercentage = hitobj->m_BulletPierceable;
3053 }
3054 }
3055 }
3056 }
3057 // wpnConfig->m_AmmoDamage
3058 float damage = damageAmount * (1.0f - (dist_to_obj / damageArea)); // wpnConfig->m_AmmoArea));
3059 damage *= damagePercentage / 100.0f; // damage through wall
3060
3061 //r3dOutToLog("Explosion from %s to %s, damage=%.2f\n", fromObj->Name.c_str(), obj->Name.c_str(), damage); CLOG_INDENT;
3062
3063 ApplyDamage(fromObj, obj, explosionPos, damage, false, damageCategory, damageItemId); // wpnConfig->category, wpnConfig->m_itemID);
3064 }
3065 }
3066}
3067
3068int ServerGameLogic::ProcessChatCommand(obj_ServerPlayer* plr, const char* cmd)
3069{
3070 r3dOutToLog("cmd: %s admin:%d\n", cmd, plr->profile_.ProfileData.isDevAccount);
3071 if(strncmp(cmd, "/tp", 3) == 0 && (plr->profile_.ProfileData.isDevAccount & wiUserProfile::DAA_TELEPORT))
3072 return Cmd_Teleport(plr, cmd);
3073
3074 if(strncmp(cmd, "/gi", 3) == 0 && (plr->profile_.ProfileData.isDevAccount & wiUserProfile::DAA_SPAWN_ITEM))
3075 return Cmd_GiveItem(plr, cmd);
3076
3077 if(strncmp(cmd, "/ammo", 5) == 0 && (plr->profile_.ProfileData.isDevAccount & wiUserProfile::DAA_SPAWN_ITEM))//AlexRedd:: Give ammo
3078 return Cmd_Ammo(plr, cmd);
3079
3080 if(strncmp(cmd, "/sv", 3) == 0 && plr->profile_.ProfileData.isDevAccount)
3081 return Cmd_SetVitals(plr, cmd);
3082
3083 if(strncmp(cmd, "/kick", 5) == 0 && ((plr->profile_.ProfileData.isDevAccount & wiUserProfile::DAA_KICK) || plr->profile_.CustomerID == creatorID_))
3084 return Cmd_Kick(plr, cmd);
3085
3086 if(strncmp(cmd, "/ban", 4) == 0 && plr->profile_.ProfileData.isDevAccount & wiUserProfile::DAA_BAN)
3087 return Cmd_Ban(plr, cmd);
3088
3089 if(strncmp(cmd, "/chatban", 8) == 0 && plr->profile_.ProfileData.isDevAccount & wiUserProfile::DAA_BAN)//AlexRedd:: Ban chat
3090 return Cmd_BanChat(plr, cmd);
3091
3092 if(strncmp(cmd, "/ttp", 4) == 0 && plr->profile_.ProfileData.isDevAccount & wiUserProfile::DAA_TELEPORT)
3093 return Cmd_TeleportToPlayer(plr, cmd);
3094
3095 if(strncmp(cmd, "/ttyl", 5) == 0 &&
3096 (plr->profile_.ProfileData.isDevAccount & wiUserProfile::DAA_TELEPORT))
3097 return Cmd_TeleportPlayerToDev(plr, cmd);
3098
3099 if(strncmp(cmd, "/loc", 4) == 0 && plr->profile_.ProfileData.isDevAccount & wiUserProfile::DAA_TELEPORT)
3100 return Cmd_Loc(plr, cmd);
3101
3102 /*if (strncmp(cmd, "/airdrop", 8) == 0 && plr->profile_.ProfileData.isDevAccount)
3103 return Cmd_AirDrop(plr, cmd);*/
3104
3105 if(strncmp(cmd, "/god", 4) == 0 && plr->profile_.ProfileData.isDevAccount & wiUserProfile::DAA_GOD)
3106 {
3107 plr->m_isAdmin_GodMode = !plr->m_isAdmin_GodMode; // once turned on, you cannot disable it (to prevent abuse)
3108
3109 PKT_C2C_ChatMessage_s n2;
3110 n2.userFlag = 0;
3111 n2.msgChannel = 1;
3112 if (plr->m_isAdmin_GodMode)
3113 {
3114 sprintf(n2.msg, "God Mode ON");
3115 }
3116 else {
3117 sprintf(n2.msg, "God Mode OFF");
3118 }
3119 r3dscpy(n2.gamertag, "[System]");
3120 p2pSendToPeer(plr->peerId_, plr, &n2, sizeof(n2));
3121
3122 return 0;
3123 }
3124#ifdef MISSIONS
3125 if(strncmp(cmd, "/resetmd", 8) == 0 && plr->profile_.ProfileData.isDevAccount)
3126 return Cmd_ResetMissionData(plr, cmd);
3127#endif
3128
3129#ifdef VEHICLES_ENABLED
3130 if (strncmp(cmd, "/vspawn", 7) == 0 && plr->profile_.ProfileData.isDevAccount)
3131 return Cmd_VehicleSpawn(plr, cmd);
3132#endif
3133 if (strncmp(cmd, "/zspawn", 7) == 0 && plr->profile_.ProfileData.isDevAccount)
3134 return Cmd_ZombieSpawn(plr, cmd, false);
3135 if (strncmp(cmd, "/szspawn", 8) == 0 && plr->profile_.ProfileData.isDevAccount)
3136 return Cmd_ZombieSpawn(plr, cmd, true);
3137 //
3138 if (strncmp(cmd, "/start", 8) == 0 && plr->profile_.ProfileData.isDevAccount & wiUserProfile::DAA_DEV_ICON && ginfo_.IsGameBR() && !m_isGameHasStarted)
3139 return Cmd_StartBR(plr, cmd);
3140
3141 return 1;
3142}
3143
3144int ServerGameLogic::admin_TeleportPlayer(obj_ServerPlayer* plr, float x, float z)
3145{
3146 r3d_assert(plr);
3147
3148 // cast ray down and find where we should place mine. should be in front of character, facing away from him
3149 PxRaycastHit hit;
3150 PxSceneQueryFilterData filter(PxFilterData(COLLIDABLE_STATIC_MASK, 0, 0, 0), PxSceneQueryFilterFlag::eSTATIC);
3151 if(!g_pPhysicsWorld->raycastSingle(PxVec3(x, 5000.0f, z), PxVec3(0, -1, 0), 10000.0f, PxSceneQueryFlag::eIMPACT, hit, filter))
3152 {
3153 r3dOutToLog("unable to teleport - no collision\n");
3154 return 2;
3155 }
3156
3157 r3dPoint3D pos = AdjustPositionToFloor(r3dPoint3D(x, 0, z));
3158
3159 PKT_S2C_MoveTeleport_s n;
3160 n.teleport_pos = pos;
3161 p2pBroadcastToActive(plr, &n, sizeof(n));
3162 plr->SetLatePacketsBarrier("teleport");
3163 plr->TeleportPlayer(pos);
3164 //r3dOutToLog("%s teleported to %f, %f, %f\n", plr->userName, pos.x, pos.y, pos.z);
3165
3166 return 0;
3167}
3168
3169int ServerGameLogic::admin_TeleportPlayer(obj_ServerPlayer* plr, float x, float y, float z)
3170{
3171 r3d_assert(plr);
3172
3173 r3dPoint3D pos(x, y+0.1f, z);
3174 PKT_S2C_MoveTeleport_s n;
3175 n.teleport_pos = pos;
3176 p2pBroadcastToActive(plr, &n, sizeof(n));
3177 plr->SetLatePacketsBarrier("teleport");
3178 plr->TeleportPlayer(pos);
3179 r3dOutToLog("%s teleported to %f, %f, %f\n", plr->userName, pos.x, pos.y, pos.z);
3180
3181 return 0;
3182}
3183
3184
3185int ServerGameLogic::Cmd_Teleport(obj_ServerPlayer* plr, const char* cmd)
3186{
3187 char buf[128];
3188 float x, z;
3189
3190 if(3 != sscanf(cmd, "%s %f %f", buf, &x, &z))
3191 return 2;
3192
3193 return admin_TeleportPlayer(plr, x, z);
3194}
3195
3196int ServerGameLogic::Cmd_TeleportToPlayer(obj_ServerPlayer* plr, const char* cmd)
3197{
3198 char buf[64]={0};
3199 char plrName[64]={0};
3200
3201 const char* beginNameStr = strchr(cmd, '"');
3202 const char* endNameStr = strrchr(cmd, '"');
3203 if(!beginNameStr || !endNameStr || beginNameStr==endNameStr)
3204 return 2;
3205
3206 memcpy(plrName, beginNameStr+1, int((endNameStr)-(beginNameStr))-1);
3207
3208 for(int i=0; i<curPlayers_; ++i)
3209 {
3210 obj_ServerPlayer* pl = plrList_[i];
3211 if(stricmp(pl->userName, plrName)==0)
3212 {
3213 float distanceToPlayer = 1.0f;
3214#ifdef VEHICLES_ENABLED
3215 if (pl->IsInVehicle())
3216 distanceToPlayer = 3.0f;
3217#endif
3218 return admin_TeleportPlayer(plr, pl->GetPosition().x-distanceToPlayer, pl->GetPosition().y, pl->GetPosition().z);
3219 }
3220 }
3221
3222 return 1;
3223}
3224
3225int ServerGameLogic::Cmd_TeleportPlayerToDev(obj_ServerPlayer* plr, const char* cmd)
3226{
3227 char buf[64]={0};
3228 char plrName[64]={0};
3229
3230 const char* beginNameStr = strchr(cmd, '"');
3231 const char* endNameStr = strrchr(cmd, '"');
3232 if(!beginNameStr || !endNameStr || beginNameStr==endNameStr)
3233 return 2;
3234
3235 memcpy(plrName, beginNameStr+1, int((endNameStr)-(beginNameStr))-1);
3236
3237 for(int i=0; i<curPlayers_; ++i)
3238 {
3239 obj_ServerPlayer* pl = plrList_[i];
3240 if(stricmp(pl->userName, plrName)==0)
3241 {
3242#ifdef VEHICLES_ENABLED
3243 if (pl->IsInVehicle())
3244 pl->ExitVehicle(true, false);
3245#endif
3246
3247#ifdef ENABLE_GAMEBLOCKS
3248 if(g_GameBlocks_Client && g_GameBlocks_Client->Connected())
3249 {
3250 g_GameBlocks_Client->PrepareEventForSending("DevIssuedTeleport", g_GameBlocks_ServerID, GameBlocks::GBPublicPlayerId(uint32_t(pl->profile_.CustomerID)));
3251 g_GameBlocks_Client->SendEvent();
3252 }
3253#endif
3254 return admin_TeleportPlayer(pl, plr->GetPosition().x-1, plr->GetPosition().y, plr->GetPosition().z);
3255 }
3256 }
3257
3258 return 1;
3259}
3260
3261int ServerGameLogic::Cmd_GiveItem(obj_ServerPlayer* plr, const char* cmd)//AlexRedd:: Give ammo
3262{
3263 char buf[128];
3264 int itemid;
3265 int count = 1;
3266
3267 if(2 > sscanf(cmd, "%s %d %d", buf, &itemid, &count))
3268 return 2;
3269
3270 if(g_pWeaponArmory->getConfig(itemid) == NULL) {
3271 r3dOutToLog("Cmd_GiveItem: no item %d\n", itemid);
3272 return 3;
3273 }
3274
3275 bool logGiveItem=true;
3276
3277#ifdef DISABLE_GI_ACCESS_ON_PTE_MAP
3278 if(ginfo_.channel==6) // don't care about PTE maps, as nothing there is saved
3279 logGiveItem=false;
3280#endif
3281#ifdef DISABLE_GI_ACCESS_ON_PTE_STRONGHOLD_MAP
3282 if(ginfo_.channel==6 && ginfo_.mapId==GBGameInfo::MAPID_WZ_Cliffside) // don't care about PTE maps, as nothing there is saved
3283 logGiveItem=false;
3284#endif
3285#ifdef DISABLE_GI_ACCESS_FOR_CALI_SERVER
3286 if(gServerLogic.ginfo_.mapId==GBGameInfo::MAPID_WZ_California)
3287 logGiveItem=false;
3288#endif
3289#ifdef DISABLE_GI_ACCESS_FOR_DEV_EVENT_SERVER
3290 if(ginfo_.channel==6)
3291 logGiveItem=false;
3292#endif
3293
3294 if(ginfo_.IsGameBR())//
3295 logGiveItem=false;
3296
3297
3298 if(logGiveItem)
3299 LogCheat(plr->peerId_, PKT_S2C_CheatWarning_s::CHEAT_AdminGiveItem, 0, "Admin Spawn Item", "%d (%s) spawned %d with quantity %d on server %s\n", plr->profile_.CustomerID, plr->userName, itemid, count, ginfo_.name);
3300
3301 wiInventoryItem wi;
3302 wi.itemID = itemid;
3303 wi.quantity = count;
3304 plr->BackpackAddItem(wi);
3305
3306 return 0;
3307}
3308
3309int ServerGameLogic::Cmd_Ammo(obj_ServerPlayer* plr, const char* cmd)
3310{
3311 //lets get the weapon config from the selected weapon
3312 ServerWeapon* wpn = plr->m_WeaponArray[plr->m_SelectedWeapon];
3313
3314 //statements, you might want to add more?
3315 if(wpn && wpn->getCategory() != storecat_MELEE)
3316 {
3317 //get clip config from wpn
3318 const WeaponAttachmentConfig* clip = wpn->getClipConfig();
3319
3320 //basicly lets just take the weapon item for our clip item
3321 wiInventoryItem wi;
3322 wi = plr->loadout_->Items[plr->loadout_->CHAR_LOADOUT_WEAPON1];
3323
3324
3325 wi.itemID = clip->m_itemID; //now overrides with clip item id
3326 wi.quantity = 3; //set how many mags per command you want!
3327 wi.Var1 = clip->m_Clipsize; //set max clipsize from the clip config
3328
3329 //add it to the backpack
3330 plr->BackpackAddItem(wi);
3331
3332 }
3333
3334
3335 return 0;
3336}
3337
3338int ServerGameLogic::Cmd_SetVitals(obj_ServerPlayer* plr, const char* cmd)
3339{
3340 char buf[128];
3341 int v1, v2, v3, v4;
3342
3343 if(5 != sscanf(cmd, "%s %d %d %d %d", buf, &v1, &v2, &v3, &v4))
3344 return 2;
3345
3346 plr->loadout_->Health = (float)v1;
3347 plr->loadout_->Hunger = (float)v2;
3348 plr->loadout_->Thirst = (float)v3;
3349 plr->loadout_->Toxic = (float)v4;
3350 plr->loadout_->MedBleeding = 0.0f;
3351 plr->loadout_->MedBloodInfection = 0.0f;
3352
3353 return 0;
3354}
3355
3356int ServerGameLogic::Cmd_Kick(obj_ServerPlayer* plr, const char* cmd)
3357{
3358 char buf[64]={0};
3359 char plrName[64]={0};
3360 char reason[256]={0};
3361
3362 const char* beginNameStr = strchr(cmd, '"');
3363 const char* endNameStr = strrchr(cmd, '"');
3364 if(!beginNameStr || !endNameStr || beginNameStr==endNameStr)
3365 return 2;
3366
3367 memcpy(plrName, beginNameStr+1, int((endNameStr)-(beginNameStr))-1);
3368 memcpy(reason, endNameStr+1, strlen(endNameStr+1));
3369
3370 if(strlen(reason) < 5)
3371 return 0;
3372
3373 for(int i=0; i<curPlayers_; ++i)
3374 {
3375 obj_ServerPlayer* pl = plrList_[i];
3376 if(stricmp(pl->userName, plrName)==0)
3377 {
3378 // Kicking players from private servers should give that player a 30 minute ban
3379 //if(ginfo_.IsRentedGame() && plr->profile_.CustomerID == creatorID_)
3380 //{
3381 float banTime = r3dGetTime() + 30*60;
3382 TKickedPlayers::iterator it = kickedPlayers_.find(pl->profile_.CustomerID);
3383 if(it == kickedPlayers_.end())
3384 {
3385 kickedPlayers_.insert(std::pair<DWORD, float>(pl->profile_.CustomerID, banTime));
3386 }
3387 else
3388 {
3389 it->second = banTime;
3390 }
3391 //}
3392
3393 // info player that he was kicked
3394 PKT_S2C_CustomKickMsg_s n;
3395 r3dscpy(n.msg, "You were kicked from server");
3396 p2pSendToPeer(pl->peerId_, pl, &n, sizeof(n));
3397
3398#ifdef VEHICLES_ENABLED
3399 if (pl->IsInVehicle())
3400 pl->ExitVehicle(true, true, true);
3401#endif
3402 LogInfo(pl->peerId_, "kicked by dev", "kicked by dev '%s' (custID:%d) for reason '%s'", plr->userName, plr->profile_.CustomerID, reason);
3403 DisconnectPeer(pl->peerId_, false, "disconnected by developer");
3404 return 0;
3405 }
3406 }
3407
3408 return 1;
3409}
3410
3411int ServerGameLogic::Cmd_Ban(obj_ServerPlayer* plr, const char* cmd)
3412{
3413 char buf[64]={0};
3414 char plrName[64]={0};
3415 char reason[256]={0};
3416
3417 const char* beginNameStr = strchr(cmd, '"');
3418 const char* endNameStr = strrchr(cmd, '"');
3419 if(!beginNameStr || !endNameStr || beginNameStr==endNameStr)
3420 return 2;
3421
3422 memcpy(plrName, beginNameStr+1, int((endNameStr)-(beginNameStr))-1);
3423 memcpy(reason, endNameStr+1, strlen(endNameStr+1));
3424
3425 if(strlen(reason) < 5)
3426 return 0;
3427
3428 for(int i=0; i<curPlayers_; ++i)
3429 {
3430 obj_ServerPlayer* pl = plrList_[i];
3431 if(stricmp(pl->userName, plrName)==0)
3432 {
3433 CJobBanUser* job = new CJobBanUser(pl);
3434 char tmpStr[256];
3435 sprintf(tmpStr, "Banned from in-game by dev %s for reason: %s", plr->userName, reason);
3436 LogInfo(pl->peerId_, "banned by dev", tmpStr);
3437 r3dscpy(job->BanReason, tmpStr);
3438 g_AsyncApiMgr->AddJob(job);
3439 return 0;
3440 }
3441 }
3442
3443 return 1;
3444}
3445
3446int ServerGameLogic::Cmd_BanChat(obj_ServerPlayer* plr, const char* cmd)//AlexRedd:: Ban chat
3447{
3448 char buf[64]={0};
3449 char plrName[64]={0};
3450 char reason[256]={0};
3451
3452 const char* beginNameStr = strchr(cmd, '"');
3453 const char* endNameStr = strrchr(cmd, '"');
3454 if(!beginNameStr || !endNameStr || beginNameStr==endNameStr)
3455 return 2;
3456
3457 memcpy(plrName, beginNameStr+1, int((endNameStr)-(beginNameStr))-1);
3458 memcpy(reason, endNameStr+1, strlen(endNameStr+1));
3459
3460 if(strlen(reason) < 5)
3461 return 0;
3462
3463 for(int i=0; i<curPlayers_; ++i)
3464 {
3465 obj_ServerPlayer* pl = plrList_[i];
3466 if(stricmp(pl->userName, plrName)==0)
3467 {
3468 if(pl->profile_.ProfileData.isDevAccount == 0)//skip for dev
3469 {
3470 CJobBanChatUser* job = new CJobBanChatUser(pl);
3471 char tmpStr[256];
3472 sprintf(tmpStr, "Banned chat in-game by dev %s for reason: %s", plr->userName, reason);
3473 LogInfo(pl->peerId_, "banned chat by dev", tmpStr);
3474 r3dscpy(job->BanReason, tmpStr);
3475 g_AsyncApiMgr->AddJob(job);
3476#ifdef VEHICLES_ENABLED
3477 if (pl->IsInVehicle())
3478 pl->ExitVehicle(true, true, true);
3479#endif
3480 // info player that he was banned from chat
3481 PKT_S2C_CustomKickMsg_s n;
3482 if(strlen(reason) > 8)
3483 sprintf(n.msg, "You are banned from chat\n%s", reason);
3484 else
3485 r3dscpy(n.msg, "You are banned from chat");
3486 p2pSendToPeer(pl->peerId_, pl, &n, sizeof(n));
3487
3488 LogCheat(pl->peerId_, PKT_S2C_CheatWarning_s::CHEAT_Data, 0, "Admin banned chat in-game", "User: '%s' chat banned by DEV: '%s' (CustomerID: %d) for reason: '%s'", pl->userName, plr->userName, plr->profile_.CustomerID, reason);
3489 return 0;
3490 }
3491 }
3492 }
3493 return 1;
3494}
3495
3496int ServerGameLogic::Cmd_Loc(obj_ServerPlayer* plr, const char* cmd)
3497{
3498 char buf[64]={0};
3499 char plrName[64]={0};
3500
3501 const char* beginNameStr = strchr(cmd, '"');
3502 const char* endNameStr = strrchr(cmd, '"');
3503 if(!beginNameStr || !endNameStr || beginNameStr==endNameStr)
3504 return 2;
3505
3506 memcpy(plrName, beginNameStr+1, int((endNameStr)-(beginNameStr))-1);
3507
3508 for(int i=0; i<curPlayers_; ++i)
3509 {
3510 obj_ServerPlayer* pl = plrList_[i];
3511 if(stricmp(pl->userName, plrName)==0)
3512 {
3513 // send message to dev
3514 PKT_C2C_ChatMessage_s locN;
3515 locN.FromID = toP2pNetId(plr->GetNetworkID());
3516 locN.msgChannel = 1;
3517 locN.userFlag = 2;
3518 r3dscpy(locN.gamertag, "<SYSTEM>");
3519 char tmpStr[64];
3520 sprintf(tmpStr, "Player %s location: x:%.2f, y:%.2f, z:%.2f", pl->userName, pl->GetPosition().x, pl->GetPosition().y, pl->GetPosition().z);
3521 r3dscpy(locN.msg, tmpStr);
3522 p2pSendToPeer(plr->peerId_, plr, &locN, sizeof(locN), 1);
3523 return 0;
3524 }
3525 }
3526
3527 return 1;
3528}
3529
3530int ServerGameLogic::Cmd_AirDrop(obj_ServerPlayer* plr, const char* cmd)
3531{
3532 if (AirDropsPos.size() < 1)
3533 return 5;
3534
3535 char buf[128];
3536 char option[128];
3537 bool ToMyPosition = false;
3538
3539 if(2 == sscanf(cmd, "%s %s %d %d %d", buf, &option))
3540 {
3541 if (strcmp(option,"me") == 0)
3542 ToMyPosition = true;
3543 else
3544 ToMyPosition = false;
3545 }
3546
3547 wiInventoryItem wi;
3548 wi.itemID = 'ARDR';
3549 wi.quantity = 1;
3550 // create network object
3551 r3dPoint3D AirDropSpawn(0,0,0);
3552 uint32_t AirDropRand = 1;
3553
3554 if (ToMyPosition == false)
3555 {
3556 if (Terrain3)
3557 {
3558 AirDropSpawn.y = Terrain3->GetHeight(AirDropSpawn)+300.0f;
3559 }
3560
3561 if (AirDropsPos.size()>1)
3562 {
3563 AirDropRand = rand() % AirDropsPos.size()+1;
3564 }
3565
3566 AirDropSpawn.x = AirDropsPos[AirDropRand].m_location.x + u_GetRandom(-AirDropsPos[AirDropRand].m_radius,AirDropsPos[AirDropRand].m_radius);
3567 AirDropSpawn.z = AirDropsPos[AirDropRand].m_location.z+ + u_GetRandom(-AirDropsPos[AirDropRand].m_radius,AirDropsPos[AirDropRand].m_radius);
3568
3569 }
3570 else {
3571 AirDropSpawn.x = plr->GetPosition().x;
3572 AirDropSpawn.y = plr->GetPosition().y+300.0f;
3573 AirDropSpawn.z = plr->GetPosition().z;
3574 }
3575
3576 obj_DroppedItem* obj = (obj_DroppedItem*)srv_CreateGameObject("obj_DroppedItem", "obj_DroppedItem", AirDropSpawn);
3577 obj->AirDropPos = AirDropSpawn;
3578 obj->m_FirstTime = 1;
3579 obj->ExpireFirstTime= r3dGetTime() + 5.0f;
3580 obj->SetPosition(AirDropSpawn);
3581 plr->SetupPlayerNetworkItem(obj);
3582 // vars
3583 obj->m_Item = wi;
3584 obj->m_Item.quantity = 1;
3585
3586 char msg[512]="";
3587
3588 sprintf(msg,"Spawned at position %.2f %.2f",AirDropSpawn.x,AirDropSpawn.z);
3589 PKT_C2C_ChatMessage_s n;
3590 n.userFlag = 0;
3591 n.msgChannel = 1;
3592 r3dscpy(n.msg, msg);
3593 r3dscpy(n.gamertag, "[AIRDROP]");
3594 p2pBroadcastToAll(&n, sizeof(n), true);
3595
3596 //r3dOutToLog("#######Position X:%f Y:%f Z:%f\n",AirDropSpawn.x,AirDropSpawn.y,AirDropSpawn.z);
3597 return 0;
3598}
3599
3600#if MISSIONS
3601int ServerGameLogic::Cmd_ResetMissionData(obj_ServerPlayer* plr, const char* cmd)
3602{
3603 char plrName[64]={0};
3604
3605 const char* beginNameStr = strchr(cmd, '"');
3606 const char* endNameStr = strrchr(cmd, '"');
3607 if(!beginNameStr || !endNameStr || beginNameStr==endNameStr)
3608 return 2;
3609
3610 memcpy(plrName, beginNameStr+1, int((endNameStr)-(beginNameStr))-1);
3611
3612 for(int i=0; i<curPlayers_; ++i)
3613 {
3614 obj_ServerPlayer* pl = plrList_[i];
3615 if(stricmp(pl->userName, plrName)==0)
3616 {
3617 Mission::g_pMissionMgr->ResetMissionData( pl );
3618 return 0;
3619 }
3620 }
3621 r3dOutToLog("!!! FAILED to Reset Mission Data for '%s'.\n", plrName);
3622 return 1;
3623}
3624#endif
3625
3626#ifdef VEHICLES_ENABLED
3627int ServerGameLogic::Cmd_VehicleSpawn(obj_ServerPlayer* plr, const char* cmd)
3628{
3629 char buf[128];
3630 int vehicleType = 0;
3631
3632 if(2 != sscanf(cmd, "%s %d", buf, &vehicleType))
3633 return 1;
3634
3635 if (vehicleType < 0 || vehicleType > 7)
3636 return 1;
3637
3638 r3dVector position = plr->GetPosition();
3639 position.x += 3.0f;
3640
3641 char name[28];
3642 sprintf(name, "Vehicle_%d_%p", obj_Vehicle::s_ListOfAllActiveVehicles.size() + 1, this);
3643
3644 obj_Vehicle* vehicle = (obj_Vehicle*)srv_CreateGameObject("obj_Vehicle", name, position);
3645 vehicle->SetNetworkID(gServerLogic.GetFreeNetId());
3646 vehicle->NetworkLocal = true;
3647 vehicle->spawnObject = 0;
3648 vehicle->spawnIndex = -1;
3649 vehicle->SetVehicleType((obj_Vehicle::VehicleTypes)vehicleType);
3650 vehicle->SetRotationVector(plr->GetRotationVector());
3651 vehicle->OnCreate();
3652
3653 r3dOutToLog("[%s] Admin has spawned a vehicle.\n", plr->userName);
3654
3655 return 0;
3656}
3657#endif
3658
3659//
3660int ServerGameLogic::Cmd_StartBR(obj_ServerPlayer* plr, const char* cmd)
3661{
3662 if (!m_isGameHasStarted)
3663 {
3664 char msg[128]="";
3665 ForceStarGame = true;
3666 sprintf(msg,"The Match is started by DEV: %s",plr->loadout_->Gamertag);
3667 r3dOutToLog("[BattleRoyale] -- The Match is started by DEV: %s\n", plr->loadout_->Gamertag);
3668 PKT_C2C_ChatMessage_s n;
3669 n.userFlag = 200; // battle royale
3670 n.msgChannel = 1; // global
3671 r3dscpy(n.msg, msg);
3672 r3dscpy(n.gamertag, "<BattleRoyale>");
3673 p2pBroadcastToAll(&n, sizeof(n), true);
3674 StartCountdown();//Start counting down
3675 }
3676 return 0;
3677}
3678
3679int ServerGameLogic::Cmd_ZombieSpawn(obj_ServerPlayer* plr, const char* cmd, bool isSuperZombie)
3680{
3681 char buf[128];
3682 int zombieCount = 0;
3683
3684 if(sscanf(cmd, "%s %d", buf, &zombieCount) != 2)
3685 zombieCount = 1;
3686
3687 if (zombieCount > 50)
3688 zombieCount = 50;
3689 else if (zombieCount < 1)
3690 zombieCount = 1;
3691
3692 if (isSuperZombie)
3693 zombieCount = 1;
3694
3695 obj_ZombieSpawn* spawnObject = obj_ZombieSpawn::GetClosestToPosition(plr->GetPosition());
3696
3697 r3dVector centerPosition = plr->GetPosition();
3698 r3dVector position = centerPosition;
3699 position.x += 3.0f;
3700
3701 for (int i = 0; i < zombieCount; ++i)
3702 {
3703 if (zombieCount > 1)
3704 {
3705 position.x = u_GetRandom(centerPosition.x - 5.0f, centerPosition.x + 5.0f);
3706 position.z = u_GetRandom(centerPosition.z - 5.0f, centerPosition.z + 5.0f);
3707
3708 if (Terrain3)
3709 position.y = Terrain3->GetHeight(position);
3710 }
3711
3712 char name[28];
3713 sprintf(name, "Zombie_%d_%p", spawnObject->numSpawnedZombies++, this);
3714
3715 obj_Zombie* z = (obj_Zombie*)srv_CreateGameObject("obj_Zombie", name, position);
3716 z->SetNetworkID(gServerLogic.GetFreeNetId());
3717 z->NetworkLocal = true;
3718 z->spawnObject = spawnObject;
3719 z->DetectRadius = 0;
3720 z->SetRotationVector(r3dVector(u_GetRandom(0, 360), 0, 0));
3721 z->CanCallForHelp = false;
3722 z->isSuperZombieForced = isSuperZombie;
3723
3724 spawnObject->zombies.push_back(z);
3725 }
3726
3727 LogCheat(plr->peerId_, PKT_S2C_CheatWarning_s::CHEAT_AdminGiveItem, 0, "Admin Spawn Zombie", "%d (%s) spawned %d zombies\n", plr->profile_.CustomerID, plr->userName, zombieCount);
3728
3729 return 0;
3730}
3731
3732void ServerGameLogic::SendSystemChatMessageToPeer(DWORD peerId, const obj_ServerPlayer* fromPlr, const char* msg)
3733{
3734 r3d_assert(msg);
3735
3736 PKT_C2C_ChatMessage_s n2;
3737 n2.userFlag = 0;
3738 n2.msgChannel = 1;
3739 r3dscpy(n2.msg, msg);
3740 r3dscpy(n2.gamertag, "<system>");
3741 p2pSendToPeer(peerId, fromPlr, &n2, sizeof(n2));
3742}
3743
3744IMPL_PACKET_FUNC(ServerGameLogic, PKT_C2C_ChatMessage)
3745{
3746 if(!IsNullTerminated(n.gamertag, sizeof(n.gamertag))) {
3747 DisconnectPeer(peerId, true, "invalid PKT_C2C_ChatMessage #1");
3748 return;
3749 }
3750 if(!IsNullTerminated(n.msg, sizeof(n.msg))) {
3751 DisconnectPeer(peerId, true, "invalid PKT_C2C_ChatMessage #1");
3752 return;
3753 }
3754 if(n.userFlag != 0) {
3755 DisconnectPeer(peerId, true, "invalid PKT_C2C_ChatMessage #1 - flags");
3756 return;
3757 }
3758
3759 // get player from peer, not from fromObj - more secure, no need to check for peer faking (player peer != p2p peer)
3760 obj_ServerPlayer* fromPlr = GetPeer(peerId).player;
3761 if(!fromPlr) {
3762 return;
3763 }
3764
3765 PKT_C2C_ChatMessage_s* n_non_const = const_cast<PKT_C2C_ChatMessage_s*>(&n);
3766
3767 // overwrite gamertag in packet, as hacker can send any crap he wants and post messages as someone else
3768 r3dscpy(n_non_const->gamertag, fromPlr->userName);
3769
3770 // trim new lines
3771 {
3772 char tmpMsg[sizeof(n.msg) + 1 ] = {0};
3773 size_t strLen = strlen(n.msg);
3774 int c=0;
3775 for(size_t i=0; i<strLen; ++i)
3776 {
3777 if(n.msg[i]=='\t')
3778 tmpMsg[c++]=' ';
3779 else if(n.msg[i]=='\n' || n.msg[i]=='\r')
3780 continue;
3781 else
3782 tmpMsg[c++]=n.msg[i];
3783 }
3784 tmpMsg[c++] = 0;
3785 r3dscpy(n_non_const->msg, tmpMsg);
3786 }
3787
3788 // set userflag
3789 n_non_const->userFlag = 0;
3790 if(fromPlr->profile_.ProfileData.AccountType == 0)
3791 {
3792 n_non_const->userFlag |= 1;
3793 }
3794 if(fromPlr->profile_.ProfileData.isDevAccount & wiUserProfile::DAA_DEV_ICON)
3795 {
3796 n_non_const->userFlag |= 2;
3797 }
3798
3799 const float curTime = r3dGetTime();
3800
3801 if(n.msg[0] == '/')
3802 {
3803 if(int res = ProcessChatCommand(fromPlr, n.msg) == 0)
3804 {
3805 SendSystemChatMessageToPeer(peerId, fromPlr, "command executed");
3806 }
3807 else if(res == 2)
3808 {
3809 SendSystemChatMessageToPeer(peerId, fromPlr, "Player Reported");
3810 }
3811 else if(res == 4)
3812 {
3813 SendSystemChatMessageToPeer(peerId, fromPlr, "Player not found");
3814 }
3815 else if(res == 5)
3816 {
3817 SendSystemChatMessageToPeer(peerId, fromPlr, "AirDropSpawn not found on this server");
3818 }
3819 else
3820 {
3821 PKT_C2C_ChatMessage_s n2;
3822 n2.userFlag = 0;
3823 n2.msgChannel = 1;
3824 sprintf(n2.msg, "no such command, %d", res);
3825 r3dscpy(n2.gamertag, "<system>");
3826 p2pSendToPeer(peerId, fromPlr, &n2, sizeof(n2));
3827 }
3828 return;
3829 }
3830
3831 // check for chat spamming
3832 const float CHAT_DELAY_BETWEEN_MSG = 1.0f; // expected delay between message
3833 const int CHAT_NUMBER_TO_SPAM = 60; // number of messages below delay time to be considered spam
3834 float diff = curTime - fromPlr->lastChatTime_;
3835
3836 if(diff > CHAT_DELAY_BETWEEN_MSG)
3837 {
3838 fromPlr->numChatMessages_ = 0;
3839 fromPlr->lastChatTime_ = curTime;
3840 }
3841 else
3842 {
3843 fromPlr->numChatMessages_++;
3844 if(fromPlr->numChatMessages_ >= CHAT_NUMBER_TO_SPAM)
3845 {
3846 DisconnectPeer(peerId, true, "invalid PKT_C2C_ChatMessage #3 - spam");
3847 return;
3848 }
3849 }
3850
3851
3852#ifdef ENABLE_GAMEBLOCKS
3853 if(g_GameBlocks_Client && g_GameBlocks_Client->Connected())
3854 {
3855 g_GameBlocks_Client->PrepareEventForSending("Chat", g_GameBlocks_ServerID, GameBlocks::GBPublicPlayerId(uint32_t(fromPlr->profile_.CustomerID)));
3856 g_GameBlocks_Client->AddKeyValueString("PlayerName", fromPlr->loadout_->Gamertag);
3857 g_GameBlocks_Client->AddKeyValueInt("Channel", n.msgChannel);
3858 g_GameBlocks_Client->AddKeyValueString("Text", n.msg);
3859 g_GameBlocks_Client->SendEvent();
3860 }
3861#endif
3862
3863 // do not broadcast fairfight reporting messages
3864/* if(strncmp(n.msg, "FairFight ", 10) == 0)
3865 {
3866#ifdef ENABLE_GAMEBLOCKS
3867 // those strings should be big enough to hold whole n.msg!!! otherwise attacker can construct such a message that will cause buffer overrun.
3868 char buf[256]={0};
3869 char plrName[256]={0};
3870
3871 const char* beginNameStr = strchr(n.msg, '"');
3872 const char* endNameStr = strrchr(n.msg, '"');
3873 if(!beginNameStr || !endNameStr || beginNameStr==endNameStr)
3874 return;
3875
3876 memcpy(plrName, beginNameStr+1, int((endNameStr)-(beginNameStr))-1);
3877
3878 for(int i=0; i<curPlayers_; ++i)
3879 {
3880 obj_ServerPlayer* pl = plrList_[i];
3881 if(stricmp(pl->userName, plrName)==0)
3882 {
3883 g_GameBlocks_Client->PrepareEventForSending("ReportPlayer", g_GameBlocks_ServerID, GameBlocks::GBPublicPlayerId(uint32_t(pl->profile_.CustomerID)));
3884 g_GameBlocks_Client->AddKeyValueInt("ReportedByID", fromPlr->profile_.CustomerID);
3885 g_GameBlocks_Client->SendEvent();
3886 break;
3887 }
3888 }
3889#endif
3890 return;
3891 }*/
3892
3893 // note
3894 // do not use p2p function here as they're visibility based now
3895
3896 switch( n.msgChannel )
3897 {
3898 case 3: // group
3899 {
3900 if(fromPlr->groupID != 0)
3901 {
3902 for(int i=0; i<MAX_PEERS_COUNT; i++) {
3903 if(peers_[i].status_ >= PEER_PLAYING && i != peerId && peers_[i].player) {
3904 if(fromPlr->groupID == peers_[i].player->groupID)
3905 net_->SendToPeer(&n, sizeof(n), i, true);
3906 }
3907 }
3908 }
3909 }
3910 break;
3911
3912 case 2: // clan
3913 {
3914 if(fromPlr->loadout_->ClanID != 0)
3915 {
3916 for(int i=0; i<MAX_PEERS_COUNT; i++) {
3917 if(peers_[i].status_ >= PEER_PLAYING && i != peerId && peers_[i].player) {
3918 if(fromPlr->loadout_->ClanID == peers_[i].player->loadout_->ClanID)
3919 net_->SendToPeer(&n, sizeof(n), i, true);
3920 }
3921 }
3922 }
3923 }
3924 break;
3925
3926 case 1: // global
3927 {
3928 for(int i=0; i<MAX_PEERS_COUNT; i++) {
3929 if(peers_[i].status_ >= PEER_PLAYING && i != peerId && peers_[i].player) {
3930 net_->SendToPeer(&n, sizeof(n), i, true);
3931 }
3932 }
3933 }
3934 break;
3935
3936 case 0: // proximity
3937 for(int i=0; i<MAX_PEERS_COUNT; i++) {
3938 if(peers_[i].status_ >= PEER_PLAYING && i != peerId && peers_[i].player) {
3939 if((peers_[i].player->GetPosition() - fromPlr->GetPosition()).Length() < 200.0f)
3940 net_->SendToPeer(&n, sizeof(n), i, true);
3941 }
3942 }
3943 break;
3944 default:
3945 {
3946 DisconnectPeer(peerId, true, "invalid PKT_C2C_ChatMessage #4 - wrong msgChannel");
3947 return;
3948 }
3949 break;
3950
3951 }
3952}
3953
3954IMPL_PACKET_FUNC(ServerGameLogic, PKT_C2S_DataUpdateReq)
3955{
3956 r3dOutToLog("got PKT_C2S_DataUpdateReq\n");
3957
3958 // relay that event to master server.
3959 //gMasterServerLogic.RequestDataUpdate();
3960}
3961
3962IMPL_PACKET_FUNC(ServerGameLogic, PKT_C2S_Admin_GiveItem)
3963{
3964 peerInfo_s& peer = GetPeer(peerId);
3965
3966 // check if received from legitimate admin account
3967 if(!peer.player || !(peer.temp_profile.ProfileData.isDevAccount & wiUserProfile::DAA_SPAWN_ITEM))
3968 return;
3969
3970 if(g_pWeaponArmory->getConfig(n.Item.itemID) == NULL) {
3971 r3dOutToLog("PKT_C2S_Admin_GiveItem: no item %d\n", n.Item.itemID);
3972 return;
3973 }
3974
3975 peer.player->BackpackAddItem(n.Item);
3976}
3977
3978IMPL_PACKET_FUNC(ServerGameLogic, PKT_C2S_ClientConnection)
3979{
3980 const float curTime = r3dGetTime();
3981 peerInfo_s& peer = GetPeer(peerId);
3982 if(peer.player == NULL)
3983 return;
3984
3985 if(n.isHightPing == true)
3986 {
3987 peer.player->isHighPing = true;
3988 }
3989
3990 m_LastConnectionRep = curTime;
3991 return;
3992}
3993
3994IMPL_PACKET_FUNC(ServerGameLogic, PKT_C2S_SecurityRep)
3995{
3996 const float curTime = r3dGetTime();
3997 peerInfo_s& peer = GetPeer(peerId);
3998 if(peer.player==NULL) // cheat?? ptumik: keep in mind that after calling LogCheat below that peer.player will be null again!
3999 return;
4000
4001 if(!r3d_float_isFinite(n.EnvironmentCurTime) || !r3d_float_isFinite(n.gameTime) )
4002 {
4003#ifdef ENABLE_GAMEBLOCKS
4004 if(g_GameBlocks_Client && g_GameBlocks_Client->Connected())
4005 {
4006 g_GameBlocks_Client->PrepareEventForSending("GodModeCheatAttempt", g_GameBlocks_ServerID, GameBlocks::GBPublicPlayerId(uint32_t(peer.player->profile_.CustomerID)));
4007 g_GameBlocks_Client->SendEvent();
4008 }
4009#endif
4010 return;
4011 }
4012
4013
4014 if(peer.secRepGameTime < 0)
4015 {
4016 // first call.
4017 peer.secRepRecvTime = curTime;
4018 peer.secRepGameTime = n.gameTime;
4019 //r3dOutToLog("peer%02d, CustomerID:%d SecRep started\n");
4020 return;
4021 }
4022
4023 float delta1 = n.gameTime - peer.secRepGameTime;
4024 float delta2 = curTime - peer.secRepRecvTime;
4025
4026 //@ ignore small values for now, until we resolve how that can happens without cheating.
4027 if(delta2 > ((float)PKT_C2S_SecurityRep_s::REPORT_PERIOD - 0.3f) && delta2 < PKT_C2S_SecurityRep_s::REPORT_PERIOD)
4028 delta2 = PKT_C2S_SecurityRep_s::REPORT_PERIOD;
4029
4030 // account for late packets
4031 peer.secRepRecvAccum -= (delta2 - PKT_C2S_SecurityRep_s::REPORT_PERIOD);
4032
4033 float k = delta1 - delta2;
4034 bool isLag = (k > 1.0f || k < -1.0f);
4035
4036 /*
4037 r3dOutToLog("peer%02d, CID:%d SecRep: %f %f %f %f %s\n",
4038 peerId, peer.CustomerID, delta1, delta2, k, peer.secRepRecvAccum,
4039 isLag ? "net_lag" : "");*/
4040
4041 // check for client timer
4042 if(fabs(delta1 - PKT_C2S_SecurityRep_s::REPORT_PERIOD) > 1.0f)
4043 {
4044 LogInfo(peerId, "client_lag?", "%f, %f, %f", delta1, delta2, peer.secRepRecvAccum);
4045 }
4046
4047 // check if client was sending packets faster that he should, 20% limit
4048 if(peer.secRepRecvAccum > ((float)PKT_C2S_SecurityRep_s::REPORT_PERIOD * 0.2f))
4049 {
4050 LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_SpeedHack, true, "speedhack",
4051 "%f, %f, %f", delta1, delta2, peer.secRepRecvAccum
4052 );
4053
4054 peer.secRepRecvAccum = 0;
4055 }
4056
4057 if((GPP_Data.GetCrc32() ^ GPP_Seed) != n.GPP_Crc32)
4058 {
4059 LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_GPP, true, "GPP cheat");
4060 }
4061
4062 // check for gametime manipulation
4063 if(peer.player && peer.player->profile_.ProfileData.isDevAccount == 0 && gServerLogic.ginfo_.channel!=7) // do not log gametime cheat on Trade server
4064 {
4065 __int64 gameTimeDelta = R3D_ABS(GetUtcGameTime() - n.gameUtcTime);
4066
4067 if(gameTimeDelta > 1000 && !peer.player->security_utcGameTimeSent)
4068 {
4069 //r3dOutToLog("!!! @@@ Utc delta = %d\n", int(gameTimeDelta));
4070 peer.player->security_utcGameTimeSent = true;
4071 LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_GametimeCheat, false, "utc gametime", "delta=%d", int(gameTimeDelta));
4072 }
4073
4074 // @@@ Environment Cur Time Sync Point @@@
4075 if(peer.player)
4076 {
4077 float EnvCurTime = getInGameTime();
4078
4079 float envtimeDelta = R3D_ABS(EnvCurTime-n.EnvironmentCurTime);
4080
4081 if(envtimeDelta > 2.0f && envtimeDelta < 22.0f && !peer.player->security_GameTimeSent) // sometimes delta could be equal 24, which is ok. check for it by <23.
4082 {
4083 //r3dOutToLog("!!! @@@@ Env time diff: %.2f\n", envtimeDelta);
4084 peer.player->security_GameTimeSent = true;
4085 LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_GametimeCheat, false, "gametime", "delta=%.2f", envtimeDelta);
4086#ifdef ENABLE_GAMEBLOCKS
4087 if(g_GameBlocks_Client && g_GameBlocks_Client->Connected())
4088 {
4089 g_GameBlocks_Client->PrepareEventForSending("GametimeCheat", g_GameBlocks_ServerID, GameBlocks::GBPublicPlayerId(uint32_t(peer.CustomerID)));
4090 g_GameBlocks_Client->SendEvent();
4091 }
4092#endif
4093 }
4094 }
4095 }
4096
4097 if(n.InsertKeyPressedNumber)
4098 {
4099 LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_MaybeEnabledCheatMenu, false, "insert key", "pressed=%d", n.InsertKeyPressedNumber);
4100#ifdef ENABLE_GAMEBLOCKS
4101 if(g_GameBlocks_Client && g_GameBlocks_Client->Connected())
4102 {
4103 g_GameBlocks_Client->PrepareEventForSending("InsertKeyPressed", g_GameBlocks_ServerID, GameBlocks::GBPublicPlayerId(uint32_t(peer.CustomerID)));
4104 g_GameBlocks_Client->SendEvent();
4105 }
4106#endif
4107 }
4108 if(n.DeleteKeyPressedNumber)
4109 {
4110 LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_MaybeEnabledCheatMenu, false, "delete key", "pressed=%d", n.DeleteKeyPressedNumber);
4111#ifdef ENABLE_GAMEBLOCKS
4112 if(g_GameBlocks_Client && g_GameBlocks_Client->Connected())
4113 {
4114 g_GameBlocks_Client->PrepareEventForSending("DeleteKeyPressed", g_GameBlocks_ServerID, GameBlocks::GBPublicPlayerId(uint32_t(peer.CustomerID)));
4115 g_GameBlocks_Client->SendEvent();
4116 }
4117#endif
4118 }
4119 if(n.MinusKeyPressedNumber)
4120 {
4121 LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_MaybeEnabledCheatMenu, false, "minus key", "pressed=%d", n.MinusKeyPressedNumber);
4122#ifdef ENABLE_GAMEBLOCKS
4123 if(g_GameBlocks_Client && g_GameBlocks_Client->Connected())
4124 {
4125 g_GameBlocks_Client->PrepareEventForSending("MinusKeyPressed", g_GameBlocks_ServerID, GameBlocks::GBPublicPlayerId(uint32_t(peer.CustomerID)));
4126 g_GameBlocks_Client->SendEvent();
4127 }
4128#endif
4129 }
4130
4131 if(n.NVGHack && peer.player)
4132 {
4133 if(!peer.player->security_NVGSent)
4134 {
4135 peer.player->security_NVGSent = true;
4136 LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_NVG, false, "nvg", "");
4137#ifdef ENABLE_GAMEBLOCKS
4138 if(g_GameBlocks_Client && g_GameBlocks_Client->Connected())
4139 {
4140 g_GameBlocks_Client->PrepareEventForSending("NVGCheatDetected", g_GameBlocks_ServerID, GameBlocks::GBPublicPlayerId(uint32_t(peer.CustomerID)));
4141 g_GameBlocks_Client->SendEvent();
4142 }
4143#endif
4144 }
4145 }
4146
4147
4148 peer.secRepRecvTime = curTime;
4149 peer.secRepGameTime = n.gameTime;
4150 return;
4151}
4152
4153IMPL_PACKET_FUNC(ServerGameLogic, PKT_C2S_CameraPos)
4154{
4155 const float curTime = r3dGetTime();
4156 peerInfo_s& peer = GetPeer(peerId);
4157 if(peer.player==NULL) // cheat?? ptumik: keep in mind that after calling LogCheat below that peer.player will be null again!
4158 return;
4159
4160 if(!r3d_vector_isFinite(n.camPos))
4161 {
4162#ifdef ENABLE_GAMEBLOCKS
4163 if(g_GameBlocks_Client && g_GameBlocks_Client->Connected())
4164 {
4165 g_GameBlocks_Client->PrepareEventForSending("GodModeCheatAttempt", g_GameBlocks_ServerID, GameBlocks::GBPublicPlayerId(uint32_t(peer.player->profile_.CustomerID)));
4166 g_GameBlocks_Client->SendEvent();
4167 }
4168#endif
4169 return;
4170 }
4171
4172
4173 float dist = (peer.player->GetPosition() - n.camPos).Length();
4174 bool cheated = false;
4175
4176 float maxDistance = 10.0f;
4177#ifdef VEHICLES_ENABLED
4178 if (peer.player->IsInVehicle())
4179 maxDistance = 45.0f;
4180#endif
4181
4182 if(peer.player->loadout_->Health > 0 && dist > maxDistance)
4183 {
4184 LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_CameraHack, 0, "camerahack",
4185 "%f", dist);
4186 cheated = true;
4187 }
4188 if(peer.player->loadout_->Health == 0 && dist > 50.0f)
4189 {
4190 LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_CameraHack, 0, "camerahack2",
4191 "%f", dist);
4192 cheated = true;
4193 }
4194
4195#ifdef ENABLE_GAMEBLOCKS
4196 if(g_GameBlocks_Client && g_GameBlocks_Client->Connected() && cheated)
4197 {
4198 g_GameBlocks_Client->PrepareEventForSending("CheatCameraPos", g_GameBlocks_ServerID, GameBlocks::GBPublicPlayerId(uint32_t(peer.CustomerID)));
4199 g_GameBlocks_Client->SendEvent();
4200 }
4201#endif
4202
4203 return;
4204}
4205
4206IMPL_PACKET_FUNC(ServerGameLogic, PKT_C2S_UseNetObject)
4207{
4208 //LogInfo(peerId, "PKT_C2S_UseNetObject", "%d", n.spawnID); CLOG_INDENT;
4209
4210 // get player from peer, not from fromObj - more secure, no need to check for peer faking (player peer != p2p peer)
4211 obj_ServerPlayer* fromPlr = GetPeer(peerId).player;
4212 if(!fromPlr) {
4213 return;
4214 }
4215
4216 if(fromPlr->loadout_->Alive == 0) {
4217 // he might be dead on server, but still didn't know that on client
4218 return;
4219 }
4220
4221 if(fromPlr->inventoryOpActive_) {
4222 return;
4223 }
4224
4225 GameObject* base = GameWorld().GetNetworkObject(n.spawnID);
4226 if(!base) {
4227 // this is valid situation, as network item might be already despawned
4228 return;
4229 }
4230
4231 // multiple players can try to activate it
4232 if(!base->isActive())
4233 return;
4234
4235// r3dOutToLog("UseNetObject(%d): plrPos: %.2f, %.2f, %.2f\n", peerId, fromPlr->GetPosition().x, fromPlr->GetPosition().y, fromPlr->GetPosition().z);
4236
4237 // validate range (without Y)
4238 {
4239 r3dPoint3D bpos = base->GetPosition(); bpos.y = 0.0f;
4240 r3dPoint3D ppos = fromPlr->GetPosition(); ppos.y = 0.0f;
4241 float dist = (bpos - ppos).Length();
4242 //r3dOutToLog("UseNetObject(%d): dist=%.2f\n", peerId, dist);
4243 if(dist > 5.0f)
4244 {
4245 gServerLogic.LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_Protocol, true, "UseNetObject",
4246 "dist %f", dist);
4247 return;
4248 }
4249 }
4250
4251#ifdef ENABLE_GAMEBLOCKS
4252 if(g_GameBlocks_Client && g_GameBlocks_Client->Connected())
4253 {
4254 uint32_t itemID = 0;
4255 int spawned = 0;
4256 int isHackerDecoy = 0;
4257 if(base->Class->Name == "obj_SpawnedItem")
4258 {
4259 itemID = ((obj_SpawnedItem*)base)->m_Item.itemID;
4260 spawned = 1;
4261 isHackerDecoy = ((obj_SpawnedItem*)base)->m_isHackerDecoy?1:0;
4262 }
4263 else if(base->Class->Name == "obj_DroppedItem")
4264 itemID = ((obj_DroppedItem*)base)->m_Item.itemID;
4265 else if(base->Class->Name == "obj_ServerLockbox")
4266 itemID = 0; // do not send anything to FF
4267 else if(base->Class->Name == "obj_ServerFarmBlock")
4268 itemID = 0; // do not send anything to FF
4269
4270 if(itemID)
4271 {
4272 g_GameBlocks_Client->PrepareEventForSending("ItemPickup", g_GameBlocks_ServerID, GameBlocks::GBPublicPlayerId(uint32_t(fromPlr->profile_.CustomerID)));
4273 g_GameBlocks_Client->AddKeyValueInt("SpawnedItem", spawned);
4274 g_GameBlocks_Client->AddKeyValueInt("HackerDecoy", isHackerDecoy);
4275 g_GameBlocks_Client->AddKeyValueInt("ItemID", itemID);
4276 g_GameBlocks_Client->AddKeyValueVector3D("Position", base->GetPosition().x, base->GetPosition().y, base->GetPosition().z);
4277 const BaseItemConfig* cfg = g_pWeaponArmory->getConfig(itemID);
4278 int cat = storecat_INVALID;
4279 if(cfg)
4280 cat = cfg->category;
4281 g_GameBlocks_Client->AddKeyValueInt("Category", cat);
4282 g_GameBlocks_Client->SendEvent();
4283
4284 // log into our DB too
4285 if(isHackerDecoy)
4286 LogCheat(fromPlr->peerId_, PKT_S2C_CheatWarning_s::CHEAT_HackerDecoyPickup, false, "player picked up decoy item, using ESP 100%",
4287 "item: %d, loc: %.2f,%.2f,%.2f", itemID, base->GetPosition().x, base->GetPosition().y, base->GetPosition().z);
4288 }
4289 }
4290#endif
4291
4292 // check notify time
4293 float timeSinceNotify = n.clientTime - fromPlr->lastPickupNotifyTime;
4294 //r3dOutToLog("!!! @@@@ notifyTime = %.3f\n", timeSinceNotify);
4295 if(timeSinceNotify < 0.3f) // client side check, so increased tolerance. client sends pickup only 1 second after fromPlr->lastPickupNotifyTime
4296 {
4297 // collect data for now
4298 LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_QuickPickupItem, false, "Improved detection: item was picked up too fast, time should be more than 1.0", "time=%.2f", timeSinceNotify);
4299#ifdef ENABLE_GAMEBLOCKS
4300 if(g_GameBlocks_Client && g_GameBlocks_Client->Connected())
4301 {
4302 g_GameBlocks_Client->PrepareEventForSending("ItemPickupTooFast", g_GameBlocks_ServerID, GameBlocks::GBPublicPlayerId(uint32_t(fromPlr->profile_.CustomerID)));
4303 g_GameBlocks_Client->SendEvent();
4304 }
4305#endif
4306 }
4307
4308 if(base->Class->Name == "obj_SpawnedItem")
4309 {
4310 obj_SpawnedItem* obj = (obj_SpawnedItem*)base;
4311 if(fromPlr->BackpackAddItem(obj->m_Item))
4312 {
4313#ifdef MISSIONS
4314 if( fromPlr->m_MissionsProgress )
4315 fromPlr->m_MissionsProgress->PerformItemAction( Mission::ITEM_Collect, obj->m_Item.itemID, obj->GetHashID() );
4316#endif
4317 obj->setActiveFlag(0);
4318 }
4319 }
4320 else if(base->Class->Name == "obj_DroppedItem")
4321 {
4322 obj_DroppedItem* obj = (obj_DroppedItem*)base;
4323 if(fromPlr->BackpackAddItem(obj->m_Item))
4324 {
4325#ifdef MISSIONS
4326 if( fromPlr->m_MissionsProgress )
4327 fromPlr->m_MissionsProgress->PerformItemAction( Mission::ITEM_Collect, obj->m_Item.itemID, obj->GetHashID() );
4328#endif
4329 obj->setActiveFlag(0);
4330 }
4331 }
4332#if defined(MISSIONS) && defined(MISSION_TRIGGERS)
4333 else if(base->Class->Name == "obj_MissionTrigger")
4334 {
4335 r3d_assert( false && "Mission Triggers are disabled, since no UI exists for them atm." );
4336 Mission::obj_MissionTrigger* obj = (Mission::obj_MissionTrigger*)base;
4337 obj->NetShowMissionTrigger(peerId);
4338 }
4339#endif
4340 else if(base->Class->Name == "obj_Note")
4341 {
4342 obj_Note* obj = (obj_Note*)base;
4343 obj->NetSendNoteData(peerId);
4344 }
4345 else if(base->Class->Name == "obj_ServerFarmBlock")
4346 {
4347 obj_ServerFarmBlock* farm = (obj_ServerFarmBlock*)base;
4348 farm->TryToHarvest(fromPlr);
4349 }
4350 else if(base->Class->Name == "obj_ServerLockbox")
4351 {
4352 obj_ServerLockbox* lockbox = TryToOpenLockbox(fromPlr, toP2pNetId(base->GetNetworkID()), "");
4353 if (lockbox)
4354 {
4355 lockbox->UpdateServerData();
4356 lockbox->SendContentToPlayer(fromPlr);
4357 }
4358
4359 // disabled - only owner can access lock box now
4360 /*
4361 if(!lockbox->m_IsLocked)
4362 {
4363 PKT_S2C_LockboxOpReq_s n2;
4364 n2.op = PKT_S2C_LockboxOpReq_s::LBOR_SetNewCode;
4365 n2.lockboxID = toP2pNetId(lockbox->GetNetworkID());
4366 gServerLogic.p2pSendToPeer(fromPlr->peerId_, fromPlr, &n2, sizeof(n2));
4367 }
4368 else
4369 {
4370 if(lockbox->IsLockdownActive(fromPlr))
4371 {
4372 n2.op = PKT_S2C_LockboxOpReq_s::LBOR_SecurityLockdown;
4373 n2.lockboxID = 0;
4374 }
4375 else
4376 {
4377 n2.op = PKT_S2C_LockboxOpReq_s::LBOR_AskForCode;
4378 n2.lockboxID = toP2pNetId(lockbox->GetNetworkID());
4379 }
4380 gServerLogic.p2pSendToPeer(fromPlr->peerId_, fromPlr, &n2, sizeof(n2));
4381 }
4382 */
4383 }
4384 else
4385 {
4386 LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_Protocol, false, "UseNetObject",
4387 "obj %s", base->Class->Name.c_str());
4388 }
4389
4390 return;
4391}
4392
4393obj_ServerLockbox* ServerGameLogic::TryToOpenLockbox(obj_ServerPlayer* fromPlr, gp2pnetid_t lockboxID, const char* AccessCodeS)
4394{
4395 if(fromPlr->loadout_->Alive == 0) {
4396 // he might be dead on server, but still didn't know that on client
4397 return NULL;
4398 }
4399
4400 GameObject* obj = GameWorld().GetNetworkObject(lockboxID);
4401 if(obj == NULL)
4402 return NULL;
4403 if(obj->Class->Name != "obj_ServerLockbox")
4404 return NULL;
4405 obj_ServerLockbox* lockbox = (obj_ServerLockbox*)obj;
4406
4407 if((fromPlr->GetPosition() - lockbox->GetPosition()).Length() > 5.0f)
4408 return NULL; // cheat?
4409
4410 // important- first access to lockbox will set it code
4411 //if(!lockbox->m_IsLocked)
4412 // lockbox->setAccessCode(AccessCodeS);
4413
4414 //if(lockbox->IsLockdownActive(fromPlr))
4415 //{
4416 // PKT_S2C_LockboxOpReq_s n2;
4417 // n2.op = PKT_S2C_LockboxOpReq_s::LBOR_SecurityLockdown;
4418 // n2.lockboxID = 0;
4419 // gServerLogic.p2pSendToPeer(fromPlr->peerId_, fromPlr, &n2, sizeof(n2));
4420 // return NULL;
4421 //}
4422
4423 //if(strcmp(lockbox->m_AccessCodeS, AccessCodeS)==0)
4424 if (lockbox->lockboxOwnerId == fromPlr->profile_.CustomerID || fromPlr->profile_.ProfileData.isDevAccount)
4425 {
4426 return lockbox;
4427 }
4428 else
4429 {
4430 //lockbox->SetLockdown(fromPlr->profile_.CustomerID);
4431
4432 PKT_S2C_LockboxOpReq_s n2;
4433 n2.op = PKT_S2C_LockboxOpReq_s::LBOR_NotOwner;
4434 n2.lockboxID = 0;
4435 gServerLogic.p2pSendToPeer(fromPlr->peerId_, fromPlr, &n2, sizeof(n2));
4436 return NULL;
4437 }
4438}
4439
4440IMPL_PACKET_FUNC(ServerGameLogic, PKT_C2S_LockboxOpAns)
4441{
4442 if(!IsNullTerminated(n.AccessCodeS, sizeof(n.AccessCodeS))) {
4443 gServerLogic.DisconnectPeer(peerId, true, "invalid PKT_C2S_LockboxOpAns");
4444 return;
4445 }
4446
4447 // get player from peer, not from fromObj - more secure, no need to check for peer faking (player peer != p2p peer)
4448 obj_ServerPlayer* fromPlr = GetPeer(peerId).player;
4449 if(!fromPlr) {
4450 return;
4451 }
4452
4453 obj_ServerLockbox* lockbox = TryToOpenLockbox(fromPlr, n.lockboxID, n.AccessCodeS);
4454 if(lockbox)
4455 {
4456 lockbox->UpdateServerData();
4457 // send lockbox content
4458 lockbox->SendContentToPlayer(fromPlr);
4459 }
4460}
4461
4462IMPL_PACKET_FUNC(ServerGameLogic, PKT_C2S_LockboxKeyReset)
4463{
4464 if(!IsNullTerminated(n.old_AccessCodeS, sizeof(n.old_AccessCodeS))) {
4465 gServerLogic.DisconnectPeer(peerId, true, "invalid PKT_C2S_LockboxItemBackpackToLockbox_s");
4466 return;
4467 }
4468 if(!IsNullTerminated(n.new_AccessCodeS, sizeof(n.new_AccessCodeS))) {
4469 gServerLogic.DisconnectPeer(peerId, true, "invalid PKT_C2S_LockboxItemBackpackToLockbox_s");
4470 return;
4471 }
4472
4473 // get player from peer, not from fromObj - more secure, no need to check for peer faking (player peer != p2p peer)
4474 obj_ServerPlayer* fromPlr = GetPeer(peerId).player;
4475 if(!fromPlr) {
4476 return;
4477 }
4478
4479 obj_ServerLockbox* lockbox = TryToOpenLockbox(fromPlr, n.lockboxID, n.old_AccessCodeS);
4480 if(lockbox)
4481 {
4482 lockbox->setAccessCode(n.new_AccessCodeS);
4483 PKT_S2C_LockboxOpReq_s n2;
4484 n2.op = PKT_S2C_LockboxOpReq_s::LBOR_CodeChanged;
4485 n2.lockboxID = 0;
4486 gServerLogic.p2pSendToPeer(fromPlr->peerId_, fromPlr, &n2, sizeof(n2));
4487 }
4488}
4489
4490IMPL_PACKET_FUNC(ServerGameLogic, PKT_C2S_PreparingUseNetObject)
4491{
4492 // get player from peer, not from fromObj - more secure, no need to check for peer faking (player peer != p2p peer)
4493 obj_ServerPlayer* fromPlr = GetPeer(peerId).player;
4494 if(!fromPlr) {
4495 return;
4496 }
4497
4498 if(fromPlr->loadout_->Alive == 0) {
4499 // he might be dead on server, but still didn't know that on client
4500 return;
4501 }
4502
4503 fromPlr->lastPickupNotifyTime = n.clientTime; // yes, this is hackable, but otherwise server side check is not going to work due to latency in packet arrival
4504
4505 return;
4506}
4507
4508IMPL_PACKET_FUNC(ServerGameLogic, PKT_C2S_CreateNote)
4509{
4510 // get player from peer, not from fromObj - more secure, no need to check for peer faking (player peer != p2p peer)
4511 obj_ServerPlayer* fromPlr = GetPeer(peerId).player;
4512 if(!fromPlr) {
4513 return;
4514 }
4515
4516 if(!IsNullTerminated(n.TextFrom, sizeof(n.TextFrom)) || !IsNullTerminated(n.TextSubj, sizeof(n.TextSubj))) {
4517 gServerLogic.LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_Protocol, true, "PKT_C2S_CreateNote",
4518 "no null in text");
4519 return;
4520 }
4521
4522 // relay logic to player
4523 fromPlr->UseItem_CreateNote(n);
4524 return;
4525}
4526
4527IMPL_PACKET_FUNC(ServerGameLogic, PKT_C2S_TEST_SpawnDummyReq)
4528{
4529 r3dOutToLog("!!!!!!! NOPE: dummies not implemented\n");
4530 r3d_assert(fromObj);
4531 r3d_assert(IsServerPlayer(fromObj));
4532
4533 return;
4534}
4535
4536#ifdef MISSIONS
4537IMPL_PACKET_FUNC(ServerGameLogic, PKT_C2S_AcceptMission)
4538{
4539 // get player from peer, not from fromObj - more secure, no need to check for peer faking (player peer != p2p peer)
4540 obj_ServerPlayer* fromPlr = GetPeer(peerId).player;
4541 if(!fromPlr) {
4542 return;
4543 }
4544
4545 if( Mission::g_pMissionMgr )
4546 {
4547 if( !Mission::g_pMissionMgr->IsMissionActive( n.missionID ) ) {
4548 gServerLogic.LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_Protocol, true, "PKT_C2S_AcceptMission", "Mission(%d): Invalid mission accepted", n.missionID);
4549 return;
4550 }
4551
4552 if( fromPlr->m_MissionsProgress )
4553 if( fromPlr->m_MissionsProgress->AddMission( n.missionID ) )
4554 g_AsyncApiMgr->AddJob(new CJobUpdateMissionsData( fromPlr ));
4555 }
4556}
4557
4558IMPL_PACKET_FUNC(ServerGameLogic, PKT_C2S_AbandonMission)
4559{
4560 // get player from peer, not from fromObj - more secure, no need to check for peer faking (player peer != p2p peer)
4561 obj_ServerPlayer* fromPlr = GetPeer(peerId).player;
4562 if(!fromPlr) {
4563 return;
4564 }
4565
4566 if( Mission::g_pMissionMgr )
4567 {
4568 if( !Mission::g_pMissionMgr->IsMissionActive( n.missionID ) ) {
4569 gServerLogic.LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_Protocol, true, "PKT_C2S_AbandonMission", "invalid mission abandoned");
4570 return;
4571 }
4572
4573 if( fromPlr->m_MissionsProgress )
4574 if( fromPlr->m_MissionsProgress->RemoveMission( n.missionID, Mission::RMV_MissionAbandoned ) )
4575 g_AsyncApiMgr->AddJob(new CJobUpdateMissionsData( fromPlr ));
4576 }
4577}
4578
4579IMPL_PACKET_FUNC(ServerGameLogic, PKT_C2S_MissionStateObjectUse)
4580{
4581 // get player from peer, not from fromObj - more secure, no need to check for peer faking (player peer != p2p peer)
4582 obj_ServerPlayer* fromPlr = GetPeer(peerId).player;
4583 if(!fromPlr)
4584 return;
4585
4586 if( Mission::g_pMissionMgr )
4587 {
4588 GameObject* obj = GameWorld().GetObjectByHash( n.objHash );
4589 if( !obj )
4590 {
4591 gServerLogic.LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_Protocol, true, "PKT_C2S_MissionStateObjectUse", "invalid object hash");
4592 return;
4593 }
4594
4595 float distance = (obj->GetPosition() - fromPlr->GetPosition()).Length();
4596 if( distance > 5.0f )
4597 {
4598 gServerLogic.LogCheat(peerId, PKT_S2C_CheatWarning_s::CHEAT_Protocol, false, "PKT_C2S_MissionStateObjectUse", "player %s is standing too far (%.2f m) from Mission State Object with HashID %d", fromPlr->Name, distance, n.objHash);
4599 return;
4600 }
4601
4602 if( fromPlr->m_MissionsProgress )
4603 {
4604 fromPlr->m_MissionsProgress->IncState( obj );
4605 }
4606 }
4607}
4608#endif
4609
4610IMPL_PACKET_FUNC(ServerGameLogic, PKT_C2S_DBG_LogMessage)
4611{
4612 // get player from peer, not from fromObj - more secure, no need to check for peer faking (player peer != p2p peer)
4613 obj_ServerPlayer* fromPlr = GetPeer(peerId).player;
4614 if(!fromPlr) {
4615 return;
4616 }
4617
4618 // log that packet with temp cheat code
4619 LogCheat(fromPlr->peerId_, 98, false, "clientlog",
4620 "%s",
4621 n.msg
4622 );
4623 return;
4624}
4625
4626void ServerGameLogic::OnPKT_C2S_ScreenshotData(DWORD peerId, const int size, const char* data)
4627{
4628 //char fname[MAX_PATH];
4629
4630 const peerInfo_s& peer = GetPeer(peerId);
4631 if(peer.player == NULL)
4632 {
4633 return;
4634 }
4635 /*else
4636 {
4637 sprintf(fname, "logss\\JPG_%d_%d_%d_%x.jpg", ginfo_.gameServerId, peer.player->profile_.CustomerID, peer.player->loadout_->LoadoutID, GetTickCount());
4638 }
4639
4640 r3dOutToLog("peer%02d received screenshot, fname:%s\n", peerId, fname);
4641
4642 FILE* f = fopen(fname, "wb");
4643 if(f == NULL) {
4644 LogInfo(peerId, "SaveScreenshot", "unable to save fname:%s", fname);
4645 }
4646 else
4647 {
4648 fwrite(data, 1, size, f);
4649 fclose(f);
4650 }*/
4651
4652 peer.player->security_screenshotRequestSentAt = 0; // reset
4653
4654#ifdef ENABLE_GAMEBLOCKS
4655 if(g_GameBlocks_Client && g_GameBlocks_Client->Connected())
4656 {
4657 GameBlocks::Event_PlayerScreenShotJpg_Send(g_GameBlocks_Client, g_GameBlocks_ServerID, GameBlocks::GBPublicPlayerId(uint32_t(peer.player->profile_.CustomerID)), data, size);
4658 }
4659#endif
4660 return;
4661}
4662
4663void ServerGameLogic::OnPKT_C2S_PunkBuster(DWORD peerId, const int size, const char* data)
4664{
4665 const peerInfo_s& peer = GetPeer(peerId);
4666 if(peer.player == NULL) {
4667 return;
4668 }
4669
4670#ifdef __WITH_PB__
4671 if ( memcmp ( data, "\xff\xff\xff\xffPB_", 7 ) )
4672 return ;
4673
4674 PbSvAddEvent ( PB_EV_PACKET, peer.playerIdx, size-4, (char*)data+4 ) ;
4675#endif
4676
4677 return;
4678}
4679
4680
4681int ServerGameLogic::ProcessWorldEvent(GameObject* fromObj, DWORD eventId, DWORD peerId, const void* packetData, int packetSize)
4682{
4683 // do version check and game join request
4684 peerInfo_s& peer = GetPeer(peerId);
4685
4686 switch(peer.status_)
4687 {
4688 // check version in connected state
4689 case PEER_CONNECTED:
4690 switch(eventId)
4691 {
4692 DEFINE_PACKET_HANDLER(PKT_C2S_ValidateConnectingPeer);
4693 }
4694 DisconnectPeer(peerId, true, "bad packet ID %d in connected state", eventId);
4695 return TRUE;
4696
4697 // process join request in validated state
4698 case PEER_VALIDATED1:
4699 switch(eventId)
4700 {
4701 DEFINE_PACKET_HANDLER(PKT_C2S_JoinGameReq);
4702 }
4703 DisconnectPeer(peerId, true, "bad packet ID %d in validated1 state", eventId);
4704 return TRUE;
4705
4706 case PEER_LOADING:
4707 switch(eventId)
4708 {
4709 DEFINE_PACKET_HANDLER(PKT_C2S_StartGameReq);
4710 }
4711 DisconnectPeer(peerId, true, "bad packet ID %d in loading state", eventId);
4712 return TRUE;
4713 }
4714
4715 r3d_assert(peer.status_ == PEER_PLAYING);
4716
4717 // validation and relay client code
4718 switch(eventId)
4719 {
4720 DEFINE_PACKET_HANDLER(PKT_C2C_ChatMessage);
4721 DEFINE_PACKET_HANDLER(PKT_C2S_DataUpdateReq);
4722 DEFINE_PACKET_HANDLER(PKT_C2S_ClientConnection);
4723 DEFINE_PACKET_HANDLER(PKT_C2S_SecurityRep);
4724 DEFINE_PACKET_HANDLER(PKT_C2S_CameraPos);
4725 DEFINE_PACKET_HANDLER(PKT_C2S_Admin_GiveItem);
4726 DEFINE_PACKET_HANDLER(PKT_C2S_UseNetObject);
4727 DEFINE_PACKET_HANDLER(PKT_C2S_PreparingUseNetObject);
4728 DEFINE_PACKET_HANDLER(PKT_C2S_CreateNote);
4729 DEFINE_PACKET_HANDLER(PKT_C2S_LockboxOpAns);
4730 DEFINE_PACKET_HANDLER(PKT_C2S_LockboxKeyReset);
4731 DEFINE_PACKET_HANDLER(PKT_C2S_TEST_SpawnDummyReq);
4732#ifdef MISSIONS
4733 DEFINE_PACKET_HANDLER(PKT_C2S_AcceptMission);
4734 DEFINE_PACKET_HANDLER(PKT_C2S_AbandonMission);
4735 DEFINE_PACKET_HANDLER(PKT_C2S_MissionStateObjectUse);
4736#endif
4737
4738 DEFINE_PACKET_HANDLER(PKT_C2S_DBG_LogMessage);
4739#ifdef VEHICLES_ENABLED
4740 DEFINE_PACKET_HANDLER(PKT_C2S_VehicleEnter);
4741 DEFINE_PACKET_HANDLER(PKT_C2S_VehicleExit);
4742 DEFINE_PACKET_HANDLER(PKT_C2S_VehicleHitTarget);
4743 DEFINE_PACKET_HANDLER(PKT_C2S_VehicleStopZombie);
4744#endif
4745 // special packet case with variable length
4746 case PKT_C2S_ScreenshotData:
4747 {
4748 const PKT_C2S_ScreenshotData_s& n = *(PKT_C2S_ScreenshotData_s*)packetData;
4749 if(packetSize < sizeof(n)) {
4750 LogInfo(peerId, "PKT_C2S_ScreenshotData", "packetSize %d < %d", packetSize, sizeof(n));
4751 return TRUE;
4752 }
4753 if(n.errorCode != 0)
4754 {
4755 LogInfo(peerId, "PKT_C2S_ScreenshotData", "screenshot grab failed: %d", n.errorCode);
4756 return TRUE;
4757 }
4758
4759 if(packetSize != sizeof(n) + n.dataSize) {
4760 LogInfo(peerId, "PKT_C2S_ScreenshotData", "dataSize %d != %d+%d", packetSize, sizeof(n), n.dataSize);
4761 return TRUE;
4762 }
4763
4764 OnPKT_C2S_ScreenshotData(peerId, n.dataSize, (char*)packetData + sizeof(n));
4765 return TRUE;
4766 }
4767
4768 // special packet case with variable length
4769 case PKT_C2S_PunkBuster:
4770 {
4771 const PKT_C2S_PunkBuster_s& n = *(PKT_C2S_PunkBuster_s*)packetData;
4772 if(packetSize < sizeof(n)) {
4773 LogInfo(peerId, "PKT_C2S_PunkBuster", "packetSize %d < %d", packetSize, sizeof(n));
4774 return TRUE;
4775 }
4776 if(n.errorCode != 0)
4777 {
4778 LogInfo(peerId, "PKT_C2S_PunkBuster", "screenshot grab failed: %d", n.errorCode);
4779 return TRUE;
4780 }
4781
4782 if(packetSize != sizeof(n) + n.dataSize) {
4783 LogInfo(peerId, "PKT_C2S_PunkBuster", "dataSize %d != %d+%d", packetSize, sizeof(n), n.dataSize);
4784 return TRUE;
4785 }
4786
4787 OnPKT_C2S_PunkBuster(peerId, n.dataSize, (char*)packetData + sizeof(n));
4788 return TRUE;
4789 }
4790 }
4791
4792 return FALSE;
4793}
4794
4795void ServerGameLogic::TrackWeaponUsage(uint32_t ItemID, int ShotsFired, int ShotsHits, int Kills)
4796{
4797 WeaponStats_s* ws = NULL;
4798 for(size_t i = 0, size = weaponStats_.size(); i < size; ++i)
4799 {
4800 if(weaponStats_[i].ItemID == ItemID)
4801 {
4802 ws = &weaponStats_[i];
4803 break;
4804 }
4805 }
4806
4807 if(ws == NULL)
4808 {
4809 weaponStats_.push_back(WeaponStats_s());
4810 ws = &weaponStats_.back();
4811 ws->ItemID = ItemID;
4812 }
4813
4814 r3d_assert(ws);
4815 ws->ShotsFired += ShotsFired;
4816 ws->ShotsHits += ShotsHits;
4817 ws->Kills += Kills;
4818 return;
4819}
4820
4821void ServerGameLogic::StartHibernate()
4822{
4823 r3d_assert(!hibernateStarted_);
4824 hibernateStarted_ = true;
4825
4826 r3dOutToLog("Hibernate starting\n"); CLOG_INDENT;
4827
4828 CJobHibernate* job = new CJobHibernate();
4829
4830 // server state
4831 job->jobs.push_back(new CJobSetSavedServerState());
4832
4833 // dynamic objects
4834 ObjectManager& GW = GameWorld();
4835 for(GameObject* obj = GW.GetFirstObject(); obj; obj = GW.GetNextObject(obj))
4836 {
4837 if(obj->GetNetworkID() == 0)
4838 continue;
4839 if(obj->ObjFlags & OBJFLAG_JustCreated)
4840 continue;
4841 if(!obj->isActive())
4842 continue;
4843
4844 INetworkHelper* nh = obj->GetNetworkHelper();
4845 switch(nh->GetServerObjectSerializationType())
4846 {
4847 case 0:
4848 break;
4849
4850 case 1: // static object
4851 {
4852 if(!nh->srvObjParams_.IsDirty)
4853 break;
4854
4855 CJobUpdateServerObject* job2 = new CJobUpdateServerObject(obj);
4856 job->jobs.push_back(job2);
4857 break;
4858 }
4859
4860 case 2: // hibernated object
4861 {
4862 CJobAddServerObject* job2 = new CJobAddServerObject(obj);
4863 job->jobs.push_back(job2);
4864 break;
4865 }
4866 }
4867 }
4868 r3dOutToLog("hibernating %d objects\n", job->jobs.size());
4869
4870 g_AsyncApiMgr->AddJob(job);
4871}
4872
4873void ServerGameLogic::UpdateHibernate()
4874{
4875 if(curPeersConnected == 0) secsWithoutPlayers_ += r3dGetFrameTime();
4876 else secsWithoutPlayers_ = 0;
4877
4878 if(!ginfo_.IsRentedGame())
4879 return;
4880
4881 if(hibernateStarted_)
4882 return;
4883
4884 // 5 min on gameservers, 60 sec on strongholds.
4885 float secToWait = ginfo_.IsGameworld() ? (5 * 60.0f) : 60;
4886
4887 // start hibernate
4888 if(secsWithoutPlayers_ > secToWait)
4889 {
4890 int jobs = g_AsyncApiMgr->GetActiveJobs();
4891 if(jobs > 0)
4892 {
4893 // delay hibernate to 30 secs, report about that
4894 secsWithoutPlayers_ = secToWait - 30;
4895 r3dOutToLog("!!! warning: JobsOnHibernate: %d\n", jobs);
4896
4897 AsyncJobAddServerLogInfo(PKT_S2C_CheatWarning_s::CHEAT_Jobs, 0, 0,
4898 "JobsOnHibernate", "n: %d",
4899 jobs);
4900
4901 g_AsyncApiMgr->DumpJobs();
4902 return;
4903 }
4904
4905
4906 StartHibernate();
4907 }
4908}
4909
4910void ServerGameLogic::Tick()
4911{
4912 //r3dOutToLog("ServerGameLogic::Tick\n");
4913 //CLOG_INDENT;
4914 r3d_assert(ginfo_.maxPlayers > 0);
4915
4916 //
4917 if (ginfo_.IsGameBR())
4918 BattleRoyaleTick();
4919 //
4920
4921 {
4922 const float t1 = r3dGetTime();
4923 net_->Update();
4924 const float t2 = r3dGetTime() - t1;
4925 //r3dOutToLog("net update %f sec\n", t2);
4926 }
4927
4928 {
4929 const float t1 = r3dGetTime();
4930#ifdef ENABLE_GAMEBLOCKS
4931 if(g_GameBlocks_Client)
4932 {
4933 {
4934 const float t3 = r3dGetTime();
4935 g_GameBlocks_Client->Tick();
4936 const float t4 = r3dGetTime() - t3;
4937 //r3dOutToLog("FF tick: %f\n", t4);
4938 }
4939
4940 // aim bot detection. YO! :D
4941 {
4942 const float t3 = r3dGetTime();
4943 {
4944 g_GameBlocks_Client->AimBotDetector_BeginFrame();
4945 {
4946 const float t5 = r3dGetTime();
4947 for(int i=0; i<curPlayers_; ++i)
4948 {
4949 obj_ServerPlayer* pl = plrList_[i];
4950 uint32_t currentWeaponID = 0;
4951 if(pl->m_WeaponArray[pl->m_SelectedWeapon])
4952 currentWeaponID = pl->m_WeaponArray[pl->m_SelectedWeapon]->getConfig()->m_itemID;
4953 g_GameBlocks_Client->AimBotDetector_Add(GameBlocks::GBPublicPlayerId(uint32_t(pl->profile_.CustomerID)), currentWeaponID, 0, pl->GetPosition().x, pl->GetPosition().y+0.9f, pl->GetPosition().z,
4954 pl->lastCamPos.x, pl->lastCamPos.y, pl->lastCamPos.z,
4955 pl->lastCamDir.x, pl->lastCamDir.y, pl->lastCamDir.z);
4956 }
4957 const float t6 = r3dGetTime() - t5;
4958 //r3dOutToLog("FF aimbot 1: %f\n", t6);
4959 }
4960
4961 {
4962 const float t5 = r3dGetTime();
4963 int c=0;
4964 for(std::list<obj_Zombie*>::iterator it = obj_Zombie::s_ListOfAllActiveZombies.begin(); it!=obj_Zombie::s_ListOfAllActiveZombies.end(); ++it)
4965 {
4966 obj_Zombie* z = *it;
4967 if(z->ZombieState != EZombieStates::ZState_Dead)
4968 {
4969 c++;
4970 g_GameBlocks_Client->AimBotDetector_Add(GameBlocks::GBPublicPlayerId(uint32_t(z->ZombieUniqueIDForFF)), 0, 0, z->GetPosition().x, z->GetPosition().y+0.9f, z->GetPosition().z,
4971 0, 0, 0,
4972 0, 0, 0);
4973 }
4974 }
4975 const float t6 = r3dGetTime() - t5;
4976 //r3dOutToLog("FF aimbot 2: %f, z:%d\n", t6, c);
4977 }
4978
4979 g_GameBlocks_Client->AimBotDetector_EndFrame();
4980 }
4981 const float t4 = r3dGetTime() - t3;
4982 //r3dOutToLog("FF aimbot: %f\n", t4);
4983 }
4984
4985 if(g_GameBlocks_Client->Connected())
4986 {
4987 static bool logConnectedToGB = true;
4988 if(logConnectedToGB)
4989 {
4990 r3dOutToLog("Connected to Gameblocks server!\n");
4991 logConnectedToGB = false;
4992 }
4993
4994 {
4995 const float t3 = r3dGetTime();
4996 if(!g_GameBlocks_SentServerInfo)
4997 {
4998 g_GameBlocks_SentServerInfo = true;
4999 g_GameBlocks_Client->Source_SetProperty(g_GameBlocks_ServerID, GB_RESERVED_EVENT_SOURCEINFO_KEY_PROP_MAP_NEW, ginfo_.mapId);
5000 g_GameBlocks_Client->Source_SetProperty(g_GameBlocks_ServerID, "ServerName", ginfo_.name);
5001 }
5002 const float t4 = r3dGetTime() - t3;
5003 //r3dOutToLog("FF1 tick: %f\n", t4);
5004 }
5005 {
5006 const float t3 = r3dGetTime();
5007 if (g_GameBlocks_Client->Incoming_EventCount() > 0) // process one message at a time
5008 {
5009 g_GameBlocks_Client->Incoming_PrepareEventForReading();
5010
5011 //get incoming message information
5012 const char* messageName = g_GameBlocks_Client->Incoming_GetName();
5013 const GameBlocks::GBPublicPlayerId playerID = g_GameBlocks_Client->Incoming_GetPlayerId();
5014 uint32_t plrID = 0;
5015 if(playerID.IsValid())
5016 plrID = playerID;
5017
5018 r3dOutToLog("Received message from Gameblocks: '%s' for playerID:%d, numPairValues: %d\n", messageName?messageName:"none", plrID, g_GameBlocks_Client->Incoming_GetNumPairs());
5019
5020 char reason[512] = {0};
5021 if(g_GameBlocks_Client->Incoming_HasPairValueString(0))
5022 g_GameBlocks_Client->Incoming_GetPairValueString(reason, 512, 0);
5023
5024 if(strcmp(messageName, "Kick")==0 && reason[0])
5025 {
5026 for(int i=0; i<curPlayers_; ++i)
5027 {
5028 obj_ServerPlayer* pl = plrList_[i];
5029 if(pl->profile_.CustomerID == plrID)
5030 {
5031 LogInfo(pl->peerId_, "kicked by gameblocks", "kicked by gameblocks, reason %s", reason);
5032 DisconnectPeer(pl->peerId_, false, "disconnected by gameblocks");
5033 break;
5034 }
5035 }
5036 }
5037 else if(strcmp(messageName, "KickMsg")==0 && reason[0])
5038 {
5039 for(int i=0; i<curPlayers_; ++i)
5040 {
5041 obj_ServerPlayer* pl = plrList_[i];
5042 if(pl->profile_.CustomerID == plrID)
5043 {
5044 PKT_S2C_CustomKickMsg_s n;
5045 r3dscpy(n.msg, reason);
5046 p2pSendToPeer(pl->peerId_, pl, &n, sizeof(n));
5047 break;
5048 }
5049 }
5050 }
5051 else if(strcmp(messageName, "Message")==0 && reason[0])
5052 {
5053 for(int i=0; i<curPlayers_; ++i)
5054 {
5055 obj_ServerPlayer* pl = plrList_[i];
5056 if(pl->profile_.CustomerID == plrID)
5057 {
5058 PKT_C2C_ChatMessage_s chatPacket;
5059 chatPacket.FromID = toP2pNetId(pl->GetNetworkID());
5060 r3dscpy(chatPacket.gamertag, "<SYSTEM>");
5061 chatPacket.msgChannel = 1; // global
5062 chatPacket.userFlag = 2; // mark as dev, so color is red
5063 r3dscpy(chatPacket.msg, reason);
5064 net_->SendToPeer(&chatPacket, sizeof(chatPacket), pl->peerId_, true);
5065 break;
5066 }
5067 }
5068 }
5069 else if(strcmp(messageName, "GlobalMessage")==0 && reason[0])
5070 {
5071 for(int i=0; i<curPlayers_; ++i)
5072 {
5073 obj_ServerPlayer* pl = plrList_[i];
5074
5075 PKT_C2C_ChatMessage_s chatPacket;
5076 chatPacket.FromID = toP2pNetId(pl->GetNetworkID());
5077 r3dscpy(chatPacket.gamertag, "<SYSTEM>");
5078 chatPacket.msgChannel = 1; // global
5079 chatPacket.userFlag = 2; // mark as dev, so color is red
5080 r3dscpy(chatPacket.msg, reason);
5081 net_->SendToPeer(&chatPacket, sizeof(chatPacket), pl->peerId_, true);
5082 }
5083 }
5084 else if(strcmp(messageName, "Kill")==0 && reason[0])
5085 {
5086 for(int i=0; i<curPlayers_; ++i)
5087 {
5088 obj_ServerPlayer* pl = plrList_[i];
5089 if(pl->profile_.CustomerID == plrID)
5090 {
5091 LogInfo(pl->peerId_, "killed by gameblocks", "killed by gameblocks, reason %s", reason);
5092 DoKillPlayer(pl, pl, storecat_INVALID, true);
5093 break;
5094 }
5095 }
5096 }
5097 else if(strcmp(messageName, "Screenshot")==0)
5098 {
5099 for(int i=0; i<curPlayers_; ++i)
5100 {
5101 obj_ServerPlayer* pl = plrList_[i];
5102 if(pl->profile_.CustomerID == plrID)
5103 {
5104 pl->security_screenshotRequestSentAt = r3dGetTime();
5105 PKT_S2C_CheatWarning_s sn;
5106 sn.cheatId = 255;
5107 p2pSendToPeer(pl->peerId_, pl, &sn, sizeof(sn),true);
5108 break;
5109 }
5110 }
5111 }
5112 else if(strcmp(messageName, "ScreenshotFront")==0)
5113 {
5114 for(int i=0; i<curPlayers_; ++i)
5115 {
5116 obj_ServerPlayer* pl = plrList_[i];
5117 if(pl->profile_.CustomerID == plrID)
5118 {
5119 pl->security_screenshotRequestSentAt = r3dGetTime();
5120 PKT_S2C_CheatWarning_s sn;
5121 sn.cheatId = 254;
5122 p2pSendToPeer(pl->peerId_, pl, &sn, sizeof(sn),true);
5123 break;
5124 }
5125 }
5126 }
5127 else if(strcmp(messageName, "ScreenshotBack")==0)
5128 {
5129 for(int i=0; i<curPlayers_; ++i)
5130 {
5131 obj_ServerPlayer* pl = plrList_[i];
5132 if(pl->profile_.CustomerID == plrID)
5133 {
5134 pl->security_screenshotRequestSentAt = r3dGetTime();
5135 PKT_S2C_CheatWarning_s sn;
5136 sn.cheatId = 253;
5137 p2pSendToPeer(pl->peerId_, pl, &sn, sizeof(sn),true);
5138 break;
5139 }
5140 }
5141 }
5142 else if(strcmp(messageName, "Teleport")==0 && g_GameBlocks_Client->Incoming_HasPairValueFloat(0)
5143 && g_GameBlocks_Client->Incoming_HasPairValueFloat(1)
5144 && g_GameBlocks_Client->Incoming_HasPairValueFloat(2)) // should be used by gameblocks guys only
5145 {
5146 float tx, ty, tz;
5147 g_GameBlocks_Client->Incoming_GetPairValueFloat(tx, 0);
5148 g_GameBlocks_Client->Incoming_GetPairValueFloat(ty, 1);
5149 g_GameBlocks_Client->Incoming_GetPairValueFloat(tz, 2);
5150
5151 for(int i=0; i<curPlayers_; ++i)
5152 {
5153 obj_ServerPlayer* pl = plrList_[i];
5154 if(pl->profile_.CustomerID == plrID)
5155 {
5156 admin_TeleportPlayer(pl, tx, tz);
5157 break;
5158 }
5159 }
5160 }
5161 //remove this message from the queue
5162 g_GameBlocks_Client->Incoming_PopMessage();
5163 }
5164 const float t4 = r3dGetTime() - t3;
5165 //r3dOutToLog("FF2 tick: %f\n", t4);
5166 }
5167
5168 {
5169 const float t3 = r3dGetTime();
5170 static float nextPlayerListUpdate = 0.0f;
5171 if(r3dGetTime() > nextPlayerListUpdate)
5172 {
5173 nextPlayerListUpdate = r3dGetTime() + 30.0f; // update every 30 seconds as recommended by SDK
5174
5175 GameBlocks::Event_PlayerList_Prepare(g_GameBlocks_Client, g_GameBlocks_ServerID);
5176 for(int i=0; i<curPlayers_; ++i)
5177 {
5178 obj_ServerPlayer* plr = plrList_[i];
5179
5180 DWORD ip = net_->GetPeerIp(plr->peerId_);
5181 GameBlocks::Event_PlayerList_Push(g_GameBlocks_Client, GameBlocks::GBPublicPlayerId(uint32_t(plr->profile_.CustomerID)), plr->userName, inet_ntoa(*(in_addr*)&ip), 0, int(r3dGetTime()-plr->startPlayTime_), 0);
5182 }
5183 GameBlocks::Event_PlayerList_Send(g_GameBlocks_Client);
5184 }
5185 const float t4 = r3dGetTime() - t3;
5186 //r3dOutToLog("FF3 tick: %f\n", t4);
5187 }
5188
5189 {
5190 const float t3 = r3dGetTime();
5191 static float nextPlayerCountUpdate = 0.0f;
5192 if(r3dGetTime() > nextPlayerCountUpdate)
5193 {
5194 nextPlayerCountUpdate = r3dGetTime() + 120.0f;
5195 GameBlocks::Event_PlayerCount_Send(g_GameBlocks_Client, g_GameBlocks_ServerID, curPlayers_);
5196
5197 g_GameBlocks_Client->PrepareEventForSending("GameTime", g_GameBlocks_ServerID, 0);
5198 g_GameBlocks_Client->AddKeyValueFloat("ServerTime", getInGameTime());
5199 g_GameBlocks_Client->SendEvent();
5200 }
5201 const float t4 = r3dGetTime() - t3;
5202 //r3dOutToLog("FF4 tick: %f\n", t4);
5203 }
5204
5205 {
5206 const float t3 = r3dGetTime();
5207 static float nextMaxPlayerCountUpdate = 0.0f;
5208 if(r3dGetTime() > nextMaxPlayerCountUpdate)
5209 {
5210 nextMaxPlayerCountUpdate = r3dGetTime() + 600.0f;
5211 GameBlocks::Event_MaxPlayerCount_Send(g_GameBlocks_Client, g_GameBlocks_ServerID, maxLoggedPlayers_);
5212 }
5213
5214 static float nextPlayerMoveUpdate = 0.0f;
5215 if(r3dGetTime() > nextPlayerMoveUpdate)
5216 {
5217 nextPlayerMoveUpdate = r3dGetTime() + 1.0f; // update every 1 second
5218
5219 GameBlocks::Event_PlayerLocationList_Prepare(g_GameBlocks_Client, g_GameBlocks_ServerID);
5220 for(int i=0; i<curPlayers_; ++i)
5221 {
5222 obj_ServerPlayer* plr = plrList_[i];
5223 r3dPoint3D pos = plr->GetPosition();
5224 GameBlocks::Event_PlayerLocationList_Push(g_GameBlocks_Client, GameBlocks::GBPublicPlayerId(uint32_t(plr->profile_.CustomerID)),
5225 pos.x, pos.y, pos.z,
5226 0, 0, 0,
5227#ifdef VEHICLES_ENABLED
5228 plr->IsInVehicle()?(plr->GetVehicleType()+1):0, //+1 because vehicle type is 0 based
5229 plr->IsInVehicle()?(plr->seatPosition==0?1:2):0 // 0-on foot, 1-driver, 2-passenger
5230#else
5231 0,
5232 0
5233#endif
5234 );
5235 }
5236 GameBlocks::Event_PlayerLocationList_Send(g_GameBlocks_Client);
5237 }
5238 const float t4 = r3dGetTime() - t3;
5239 //r3dOutToLog("FF5 tick: %f\n", t4);
5240 }
5241 }
5242 }
5243#endif //ENABLE_GAMEBLOCKS
5244 const float t2 = r3dGetTime() - t1;
5245 //r3dOutToLog("FF update %f sec\n", t2);
5246 }
5247 const float curTime = r3dGetTime();
5248
5249 {
5250 const float t1 = r3dGetTime();
5251#ifdef MISSIONS
5252 // update mission data
5253 {
5254 static float nextTimeUpdateMissions = 0.0f;
5255 if( Mission::g_pMissionMgr && curTime > nextTimeUpdateMissions )
5256 {
5257 nextTimeUpdateMissions = r3dGetTime() + 2.0f;
5258 Mission::g_pMissionMgr->Update();
5259 }
5260 }
5261#endif
5262 const float t2 = r3dGetTime() - t1;
5263 //r3dOutToLog("mission update %f sec\n", t2);
5264 }
5265
5266 {
5267 const float t1 = r3dGetTime();
5268 // send player status updates
5269 {
5270 static float nextTimeUpdatePlayerStatus = 0.0f;
5271 if(curTime > nextTimeUpdatePlayerStatus)
5272 {
5273 nextTimeUpdatePlayerStatus = r3dGetTime() + 30.0f;
5274 // go through all players, and send info to them about players that are in the same group\clan as them
5275 for(int i=0; i<curPlayers_; ++i)
5276 {
5277 obj_ServerPlayer* srcPlr = plrList_[i];
5278
5279 for(int k=0; k<curPlayers_; ++k)
5280 {
5281 obj_ServerPlayer* pl = plrList_[k];
5282 if(pl!=srcPlr && ((pl->groupID==srcPlr->groupID && pl->groupID!=0) || (pl->loadout_->ClanID==srcPlr->loadout_->ClanID && pl->loadout_->ClanID!=0)))
5283 {
5284 if(!(pl->profile_.ProfileData.isDevAccount & wiUserProfile::DAA_HIDDEN))
5285 {
5286 PKT_S2C_PlayerStatusUpdate_s n;
5287 n.playerIdx = (WORD)(pl->GetNetworkID() - NETID_PLAYERS_START);
5288 n.posX = pl->GetPosition().x;
5289 n.posZ = pl->GetPosition().z;
5290 p2pSendToPeer(srcPlr->peerId_, srcPlr, &n, sizeof(n));
5291 }
5292 }
5293 }
5294 }
5295
5296 }
5297 }
5298 const float t2 = r3dGetTime() - t1;
5299 //r3dOutToLog("player status update %f sec\n", t2);
5300 }
5301
5302
5303 {
5304 const float t1 = r3dGetTime();
5305 // update spawn points danger indicator
5306 {
5307 static float nextTimeUpdateSpawnPointsDanger = 0.0f;
5308 if(curTime > nextTimeUpdateSpawnPointsDanger)
5309 {
5310 nextTimeUpdateSpawnPointsDanger = r3dGetTime() + 60.0f; // once a minute
5311
5312 for(int i=0; i<gCPMgr.numControlPoints_; i++)
5313 {
5314 BasePlayerSpawnPoint* spawn = gCPMgr.controlPoints_[i];
5315 for(int j=0; j<spawn->m_NumSpawnPoints; j++)
5316 {
5317 if(spawn->m_SpawnPoints[j].danger>0)
5318 {
5319 if(curTime > (spawn->m_SpawnPoints[j].lastTimeDangerUpdated+900))
5320 {
5321 spawn->m_SpawnPoints[j].danger--;
5322 spawn->m_SpawnPoints[j].lastTimeDangerUpdated = curTime;
5323 }
5324 }
5325 }
5326 }
5327 }
5328 }
5329 const float t2 = r3dGetTime() - t1;
5330 //r3dOutToLog("spawn point update %f sec\n", t2);
5331 }
5332
5333 // shutdown notify logic
5334 if(gMasterServerLogic.shuttingDown_)
5335 {
5336 if(curPeersConnected == 0)
5337 {
5338 r3dOutToLog("Shutting down gracefully\n");
5339 gameFinished_ = 1;
5340 return;
5341 }
5342
5343 static float lastSent = 999999;
5344 // send note every X sec
5345 float sentEachSec=1.0f;
5346 if(gMasterServerLogic.shutdownLeft_ > 60)
5347 sentEachSec = 15.0f; //send every 15 sec to prevent spam
5348 else
5349 sentEachSec = 5.0f; // send every 5 sec
5350
5351 if(fabs(lastSent - gMasterServerLogic.shutdownLeft_) > sentEachSec)
5352 {
5353 lastSent = gMasterServerLogic.shutdownLeft_;
5354 r3dOutToLog("sent shutdown note\n");
5355
5356 PKT_S2C_ShutdownNote_s n;
5357 n.reason = gMasterServerLogic.shuttingDown_;
5358 n.timeLeft = gMasterServerLogic.shutdownLeft_;
5359 p2pBroadcastToAll(&n, sizeof(n), true);
5360 }
5361
5362 // close game when shutdown
5363 if(gMasterServerLogic.shutdownLeft_ < 0)
5364 throw "shutting down....";
5365 }
5366
5367 {
5368 const float t1 = r3dGetTime();
5369 CheckPeerValidity();
5370 const float t2 = r3dGetTime() - t1;
5371 //r3dOutToLog("peer valid update %f sec\n", t2);
5372 }
5373
5374 {
5375 const float t1 = r3dGetTime();
5376 g_AsyncApiMgr->Tick();
5377 const float t2 = r3dGetTime() - t1;
5378 //r3dOutToLog("async manager update %f sec\n", t2);
5379 }
5380
5381 if(gameFinished_)
5382 return;
5383
5384 // AUTOMATIC AIRDROP ON SERVER
5385 if(ginfo_.IsGameBR() && m_isGameHasStarted)//
5386 {
5387 if (AirDropsPos.size()>0)
5388 {
5389 if(r3dGetTime() > AirDropSpawnTime)
5390 {
5391 AirDropSpawnTime = r3dGetTime() + RESPAWN_TIME_AIRDROPBR;
5392
5393 wiInventoryItem wi;
5394 wi.itemID = 'ARDR';
5395 wi.quantity = 1;
5396
5397 // create network object
5398 r3dPoint3D AirDropSpawn(0,0,0);
5399
5400 if (Terrain3)
5401 {
5402 AirDropSpawn.y = Terrain3->GetHeight(AirDropSpawn)+300.0f;
5403 }
5404
5405 uint32_t AirDropRand = 1;
5406
5407 if (AirDropsPos.size()>1)
5408 {
5409 AirDropRand = rand() % AirDropsPos.size()+1;
5410 }
5411
5412 AirDropSpawn.x = AirDropsPos[AirDropRand].m_location.x + u_GetRandom(-AirDropsPos[AirDropRand].m_radius,AirDropsPos[AirDropRand].m_radius);
5413 AirDropSpawn.z = AirDropsPos[AirDropRand].m_location.z+ + u_GetRandom(-AirDropsPos[AirDropRand].m_radius,AirDropsPos[AirDropRand].m_radius);
5414
5415 obj_DroppedItem* obj = (obj_DroppedItem*)srv_CreateGameObject("obj_DroppedItem", "obj_DroppedItem", AirDropSpawn);
5416 obj->AirDropPos = AirDropSpawn;
5417 obj->m_FirstTime = 1;
5418 obj->ExpireFirstTime= r3dGetTime() + 5.0f;
5419 obj->SetPosition(AirDropSpawn);
5420 obj->SetNetworkID(gServerLogic.GetFreeNetId());
5421 obj->NetworkLocal = true;
5422 // vars
5423 obj->m_Item = wi;
5424 obj->m_Item.quantity = 1;
5425
5426 char msg[512]="";
5427 sprintf(msg,"Spawned, the next Spawn is on 5 minutes");
5428 PKT_C2C_ChatMessage_s n;
5429 n.userFlag = 0;
5430 n.msgChannel = 1;
5431 r3dscpy(n.msg, msg);
5432 r3dscpy(n.gamertag, "[AIRDROP]");
5433 p2pBroadcastToAll(&n, sizeof(n), true);
5434 }
5435 }
5436 }
5437 /*else if(!ginfo_.IsGameBR())
5438 {
5439 if (AirDropsPos.size()>0)
5440 {
5441 if(r3dGetTime() > AirDropSpawnTime)
5442 {
5443 AirDropSpawnTime = r3dGetTime() + RESPAWN_TIME_AIRDROP;
5444
5445 wiInventoryItem wi;
5446 wi.itemID = 'ARDR';
5447 wi.quantity = 1;
5448
5449 // create network object
5450 r3dPoint3D AirDropSpawn(0,0,0);
5451
5452 if (Terrain3)
5453 {
5454 AirDropSpawn.y = Terrain3->GetHeight(AirDropSpawn)+300.0f;
5455 }
5456
5457 uint32_t AirDropRand = 1;
5458
5459 if (AirDropsPos.size()>1)
5460 {
5461 AirDropRand = rand() % AirDropsPos.size()+1;
5462 }
5463
5464 AirDropSpawn.x = AirDropsPos[AirDropRand].m_location.x + u_GetRandom(-AirDropsPos[AirDropRand].m_radius,AirDropsPos[AirDropRand].m_radius);
5465 AirDropSpawn.z = AirDropsPos[AirDropRand].m_location.z+ + u_GetRandom(-AirDropsPos[AirDropRand].m_radius,AirDropsPos[AirDropRand].m_radius);
5466
5467 obj_DroppedItem* obj = (obj_DroppedItem*)srv_CreateGameObject("obj_DroppedItem", "obj_DroppedItem", AirDropSpawn);
5468 obj->AirDropPos = AirDropSpawn;
5469 obj->m_FirstTime = 1;
5470 obj->ExpireFirstTime= r3dGetTime() + 5.0f;
5471 obj->SetPosition(AirDropSpawn);
5472 obj->SetNetworkID(gServerLogic.GetFreeNetId());
5473 obj->NetworkLocal = true;
5474 // vars
5475 obj->m_Item = wi;
5476 obj->m_Item.quantity = 1;
5477
5478 char msg[512]="";
5479 sprintf(msg,"Spawned, the next Spawn is on 4 hours");
5480 PKT_C2C_ChatMessage_s n;
5481 n.userFlag = 0;
5482 n.msgChannel = 1;
5483 r3dscpy(n.msg, msg);
5484 r3dscpy(n.gamertag, "[AIRDROP]");
5485 p2pBroadcastToAll(&n, sizeof(n), true);
5486 }
5487 }
5488 }*/
5489
5490 /*DISABLED, as it complicate things a bit.
5491 if(gMasterServerLogic.gotWeaponUpdate_)
5492 {
5493 gMasterServerLogic.gotWeaponUpdate_ = false;
5494
5495 weaponDataUpdates_++;
5496 SendWeaponsInfoToPlayer(true, 0);
5497 }*/
5498
5499 if(gMasterServerLogic.kickReqCharID_)
5500 {
5501 DWORD CharID = gMasterServerLogic.kickReqCharID_;
5502 gMasterServerLogic.kickReqCharID_ = 0;
5503
5504 for(int i=0; i<curPlayers_; i++)
5505 {
5506 obj_ServerPlayer* plr = plrList_[i];
5507 if(plr->loadout_->LoadoutID == CharID)
5508 {
5509 if(plr->profile_.ProfileData.isDevAccount == 0) // do not kick admins
5510 DisconnectPeer(plr->peerId_, false, "kicked by server owner");
5511 break;
5512 }
5513 }
5514 }
5515
5516 // check for jobs overflow
5517 {
5518 static float lastReport = -999;
5519 static int JOBS_THRESHOLD_TO_REPORT = 200;
5520
5521 int jobs = g_AsyncApiMgr->GetActiveJobs();
5522 if(jobs > JOBS_THRESHOLD_TO_REPORT && curTime > lastReport + 60.0f)
5523 {
5524 lastReport = curTime;
5525 r3dOutToLog("!!! warning: JobsOverflow: %d\n", jobs);
5526
5527 AsyncJobAddServerLogInfo(PKT_S2C_CheatWarning_s::CHEAT_Jobs, 0, 0,
5528 "JobsOverflow", "n: %d",
5529 jobs);
5530
5531 g_AsyncApiMgr->DumpJobs();
5532 return;
5533 }
5534 }
5535
5536 // debug keys
5537 static float lastKeyPress = -999;
5538 if(curTime > lastKeyPress + 0.3f)
5539 {
5540 //@@@ kill all players
5541 /*if(GetAsyncKeyState(VK_F11) & 0x8000)
5542 {
5543 lastKeyPress = curTime;
5544
5545 r3dOutToLog("trying to kill all players\n");
5546 for(int i=0; i<curPlayers_; i++) {
5547 obj_ServerPlayer* plr = plrList_[i];
5548 if(plr->loadout_->Alive == 0)
5549 continue;
5550
5551 DoKillPlayer(plr, plr, storecat_INVALID, true);
5552 }
5553 }*/
5554
5555 if(GetAsyncKeyState(VK_F12) & 0x8000)
5556 {
5557 lastKeyPress = curTime;
5558 }
5559 }
5560
5561 //CheckBattleZone();//
5562
5563 {
5564 const float t1 = r3dGetTime();
5565 // pereodically update loot box content
5566 if(curTime > nextLootboxUpdate_)
5567 {
5568 nextLootboxUpdate_ = curTime + u_GetRandom(10*60, 15*60); // spread lootbox update a bit across all servers
5569 g_AsyncApiMgr->AddJob(new CJobGetLootboxData());
5570 }
5571 const float t2 = r3dGetTime() - t1;
5572 //r3dOutToLog("loot update %f sec\n", t2);
5573 }
5574
5575 {
5576 const float t1 = r3dGetTime();
5577 // hibernate logic
5578 UpdateHibernate();
5579 const float t2 = r3dGetTime() - t1;
5580 //r3dOutToLog("hiber update %f sec\n", t2);
5581 }
5582
5583 static float nextDebugLog_ = 0;
5584 if(curTime > nextDebugLog_)
5585 {
5586 char apiStatus[128];
5587 g_AsyncApiMgr->GetStatus(apiStatus);
5588
5589 nextDebugLog_ = curTime + 60.0f;
5590 r3dOutToLog("time: %.0f, plrs:%d/%d, net_lastFreeId: %d, objects: %d, async:%s\n",
5591 r3dGetTime() - gameStartTime_,
5592 curPlayers_, ginfo_.maxPlayers,
5593 net_lastFreeId,
5594 GameWorld().GetNumObjects(),
5595 apiStatus);
5596
5597 //
5598 if(ginfo_.IsGameBR() && curPeersConnected == 0)// reset all stats if no players
5599 {
5600 m_isGameHasStarted = false;
5601 ForceStarGame = false;
5602 isCountingDown = false;
5603 canFinishMatch = false;
5604 haveWinningPlayer = false;
5605 playersTeleported = false;
5606 isGasSuspended = true;
5607 winningPlr = NULL;
5608 suspendTimer = 0;
5609 gasint = 0;
5610 canCheckPlayers = r3dGetTime();
5611 gastimer = r3dGetTime();
5612 SetNewGameInfo(60, 0);
5613 ResetGasAreaBattleRoyale();// reset gas radius to default
5614 r3dOutToLog("[BattleRoyale] -- Match stats resetted.\n");
5615 }
5616 }
5617
5618 return;
5619}
5620
5621void ServerGameLogic::DumpPacketStatistics()
5622{
5623 __int64 totsent = 0;
5624 __int64 totrecv = 0;
5625
5626 for(int i=0; i<R3D_ARRAYSIZE(netRecvPktSize); i++) {
5627 totsent += netSentPktSize[i];
5628 totrecv += netRecvPktSize[i];
5629 }
5630
5631 r3dOutToLog("Packet Statistics: out:%I64d in:%I64d, k:%f\n", totsent, totrecv, (float)totsent/(float)totrecv);
5632 CLOG_INDENT;
5633
5634 for(int i=0; i<R3D_ARRAYSIZE(netRecvPktSize); i++) {
5635 if(netSentPktSize[i] == 0 && netRecvPktSize[i] == 0)
5636 continue;
5637
5638 r3dOutToLog("%3d: out:%10I64d in:%10I64d out%%:%.1f%%\n",
5639 i,
5640 netSentPktSize[i],
5641 netRecvPktSize[i],
5642 (float)netSentPktSize[i] * 100.0f / float(totsent));
5643 }
5644
5645}
5646
5647__int64 ServerGameLogic::GetUtcGameTime()
5648{
5649 // "world time start" offset, so gametime at 1st sep 2012 will be in 2018 range
5650 struct tm toff = {0};
5651 toff.tm_year = 2011-1900;
5652 toff.tm_mon = 6;
5653 toff.tm_mday = 1;
5654 toff.tm_isdst = -1; // A value less than zero to have the C run-time library code compute whether standard time or daylight saving time is in effect.
5655 __int64 secs0 = _mkgmtime64(&toff); // world start time
5656 __int64 secs1 = _time64(&secs1); // current UTC time
5657
5658 // reassemble time, with speedup factor
5659 return secs0 + (secs1 - secs0) * (__int64)GPP_Data.c_iGameTimeCompression + gameStartTimeAdminOffset;
5660}
5661
5662void ServerGameLogic::SendWeaponsInfoToPlayer(DWORD peerId)
5663{
5664 //r3dOutToLog("sending weapon info to peer %d\n", peerId);
5665
5666 const peerInfo_s& peer = GetPeer(peerId);
5667
5668 g_pWeaponArmory->startItemSearch();
5669 while(g_pWeaponArmory->searchNextItem())
5670 {
5671 uint32_t itemID = g_pWeaponArmory->getCurrentSearchItemID();
5672 const WeaponConfig* weaponConfig = g_pWeaponArmory->getWeaponConfig(itemID);
5673 if(weaponConfig)
5674 {
5675 PKT_S2C_UpdateWeaponData_s n;
5676 n.itemId = weaponConfig->m_itemID;
5677 weaponConfig->copyParametersTo(n.wi);
5678 p2pSendRawToPeer(peerId, &n, sizeof(n), true);
5679 }
5680
5681 const WeaponAttachmentConfig* attmConfig = g_pWeaponArmory->getAttachmentConfig(itemID);
5682 if(attmConfig)
5683 {
5684 PKT_S2C_UpdateAttmData_s n;
5685 n.itemId = attmConfig->m_itemID;
5686 attmConfig->copyParametersTo(n.ai);
5687 p2pSendRawToPeer(peerId, &n, sizeof(n), true);
5688 }
5689
5690 const GearConfig* gearConfig = g_pWeaponArmory->getGearConfig(itemID);
5691 if(gearConfig)
5692 {
5693 PKT_S2C_UpdateGearData_s n;
5694 n.itemId = gearConfig->m_itemID;
5695 gearConfig->copyParametersTo(n.gi);
5696 p2pSendRawToPeer(peerId, &n, sizeof(n), true);
5697 }
5698 }
5699
5700 return;
5701}
5702
5703uint32_t ServerGameLogic::getNumPlayersInGroup(uint32_t groupID)
5704{
5705 r3d_assert(groupID);
5706
5707 uint32_t numPl = 0;
5708 for(int i=0; i<curPlayers_; ++i)
5709 {
5710 obj_ServerPlayer* pl = plrList_[i];
5711 if(pl->groupID == groupID)
5712 {
5713 ++numPl;
5714 }
5715 }
5716 return numPl;
5717}
5718
5719void ServerGameLogic::joinPlayerToGroup(obj_ServerPlayer* plr, uint32_t groupID)
5720{
5721 r3d_assert(plr);
5722 r3d_assert(groupID);
5723 r3d_assert(plr->groupID==0);
5724
5725 PKT_S2C_GroupAddMember_s n2;
5726 n2.groupID = groupID;
5727 n2.isLeader = 0;
5728 r3dscpy(n2.gamertag, plr->userName);
5729
5730 for(int i=0; i<curPlayers_; ++i)
5731 {
5732 obj_ServerPlayer* pl = plrList_[i];
5733 if(pl->groupID == groupID)
5734 {
5735 // send current group info to new member
5736 PKT_S2C_GroupAddMember_s n;
5737 n.groupID = groupID;
5738 n.isLeader = pl->isGroupLeader?1:0;
5739 r3dscpy(n.gamertag, pl->userName);
5740 p2pSendToPeer(plr->peerId_, plr, &n, sizeof(n));
5741
5742 // send new member to the group
5743 p2pSendToPeer(pl->peerId_, pl, &n2, sizeof(n2));
5744 }
5745 }
5746
5747 plr->groupID = groupID;
5748 plr->isGroupLeader = false;
5749 // send yourself info about us being in new group
5750 p2pSendToPeer(plr->peerId_, plr, &n2, sizeof(n2));
5751
5752 cleanPlayerGroupInvites(plr); // remove any invites that he had
5753}
5754
5755void ServerGameLogic::createNewPlayerGroup(obj_ServerPlayer* leader, obj_ServerPlayer* plr, uint32_t groupID)
5756{
5757 r3d_assert(leader);
5758 r3d_assert(plr);
5759 r3d_assert(plr->groupID==0);
5760 r3d_assert(leader->groupID==0);
5761 r3d_assert(groupID);
5762
5763 leader->groupID = groupID;
5764 leader->isGroupLeader = true;
5765
5766 plr->groupID = groupID;
5767 plr->isGroupLeader = false;
5768
5769 cleanPlayerGroupInvites(plr); // remove any outstanding invites from other player
5770
5771 // send into to each other
5772 PKT_S2C_GroupAddMember_s n;
5773 n.groupID = groupID;
5774 n.isLeader = 1;
5775 r3dscpy(n.gamertag, leader->userName);
5776 p2pSendToPeer(leader->peerId_, leader, &n, sizeof(n));
5777 p2pSendToPeer(plr->peerId_, plr, &n, sizeof(n));
5778
5779 n.isLeader = 0;
5780 r3dscpy(n.gamertag, plr->userName);
5781 p2pSendToPeer(leader->peerId_, leader, &n, sizeof(n));
5782 p2pSendToPeer(plr->peerId_, plr, &n, sizeof(n));
5783}
5784
5785bool ServerGameLogic::SetAirDrop( const AirDropPositions& AirDrop )
5786{
5787 AirDropsPos[ AirDropsPos.size() + 1 ] = AirDrop;
5788 r3dOutToLog("AirDropSystem - DB Spawn Number: %d\n",AirDropsPos.size());
5789 return true;
5790}
5791
5792void ServerGameLogic::leavePlayerFromGroup(obj_ServerPlayer* plr)
5793{
5794 r3d_assert(plr);
5795
5796 plr->m_LeaveGroupAtTime = -1;
5797
5798 if(plr->groupID == 0) // not sure how it happens
5799 {
5800 return;
5801 }
5802
5803 uint32_t curNumPlInGroup = getNumPlayersInGroup(plr->groupID);
5804 r3d_assert(curNumPlInGroup>=2); // group should only exist with at least two players in it
5805
5806 bool deleteGroup = curNumPlInGroup == 2; // if in group of two someone leaves, then group deletes itself
5807 bool reassignLeader = !deleteGroup && plr->isGroupLeader;
5808
5809 // remove everyone from group for plr
5810 for(int i=0; i<curPlayers_; ++i)
5811 {
5812 obj_ServerPlayer* pl = plrList_[i];
5813 if(pl->groupID == plr->groupID)
5814 {
5815 PKT_S2C_GroupRemoveMember_s n;
5816 r3dscpy(n.gamertag, pl->userName);
5817 p2pSendToPeer(plr->peerId_, plr, &n, sizeof(n));
5818 }
5819 }
5820 uint32_t groupID = plr->groupID;
5821 plr->groupID = 0;
5822 plr->isGroupLeader = false;
5823 PKT_S2C_GroupRemoveMember_s n;
5824 r3dscpy(n.gamertag, plr->userName);
5825
5826 // now remove that player for rest of the group
5827 for(int i=0; i<curPlayers_; ++i)
5828 {
5829 obj_ServerPlayer* pl = plrList_[i];
5830 if(pl->groupID == groupID)
5831 {
5832 p2pSendToPeer(pl->peerId_, pl, &n, sizeof(n));
5833 if(deleteGroup)
5834 {
5835 pl->groupID = 0;
5836 pl->isGroupLeader = false;
5837 PKT_S2C_GroupRemoveMember_s n2;
5838 r3dscpy(n2.gamertag, pl->userName);
5839 p2pSendToPeer(pl->peerId_, pl, &n2, sizeof(n2));
5840 }
5841 }
5842 }
5843
5844 // also remove any outstanding invites
5845 cleanPlayerGroupInvites(plr);
5846
5847 if(reassignLeader)
5848 {
5849 PKT_S2C_GroupNewLeader_s n2;
5850 for(int i=0; i<curPlayers_; ++i)
5851 {
5852 obj_ServerPlayer* pl = plrList_[i];
5853 if(pl && pl->groupID == groupID)
5854 {
5855 if(reassignLeader)
5856 {
5857 // assign leadership to first player in the list
5858 r3d_assert(pl->isGroupLeader==false);
5859 pl->isGroupLeader = true;
5860 r3dscpy(n2.gamertag, pl->userName);
5861 reassignLeader = false;
5862 }
5863 p2pSendToPeer(pl->peerId_, pl, &n2, sizeof(n2));
5864 }
5865 }
5866 }
5867}
5868
5869void ServerGameLogic::cleanPlayerGroupInvites(obj_ServerPlayer* plr)
5870{
5871 // remove player invites from other players
5872 {
5873 for(int i=0; i<curPlayers_; ++i)
5874 {
5875 obj_ServerPlayer* pl = plrList_[i];
5876 if(pl!=plr)
5877 {
5878 // look for invite from plr
5879 std::vector<obj_ServerPlayer::GroupInviteStruct>::iterator it;
5880 for(it=pl->groupInvitesFrom.begin(); it!=pl->groupInvitesFrom.end(); )
5881 {
5882 if(it->fromID == plr->GetNetworkID())
5883 {
5884 it = pl->groupInvitesFrom.erase(it);
5885 continue;
5886 }
5887 ++it;
5888 }
5889 }
5890 }
5891 }
5892}
5893
5894void ServerGameLogic::PreloadDevEventLoadout()
5895{
5896 devEventItemsCount = 0;
5897 devEventBackpackId = 0;
5898
5899 const char* xmlPath = "Data\\Weapons\\DevEventLoadout.xml";
5900
5901 r3dFile* file = r3d_open(xmlPath, "rb");
5902 if (!file)
5903 {
5904 r3dOutToLog("Failed to open Dev Event Loadout configuration file: %s\n", xmlPath);
5905 return;
5906 }
5907
5908 char* buffer = game_new char[file->size + 1];
5909 fread(buffer, file->size, 1, file);
5910 buffer[file->size] = 0;
5911
5912 pugi::xml_document xmlDoc;
5913 pugi::xml_parse_result parseResult = xmlDoc.load_buffer_inplace(buffer, file->size);
5914 fclose(file);
5915
5916 if (!parseResult)
5917 r3dError("Failed to parse Dev Event Loadout XML file, error: %s", parseResult.description());
5918
5919 pugi::xml_node loadout = xmlDoc.child("loadout");
5920 devEventBackpackId = loadout.attribute("backpackId").as_int();
5921
5922 pugi::xml_node item = loadout.child("item");
5923
5924 devEventItemsCount = -1;
5925 while (item)
5926 {
5927 wiInventoryItem tempItem;
5928 tempItem.InventoryID = 0;
5929 tempItem.itemID = item.attribute("id").as_uint();
5930 tempItem.quantity = item.attribute("qty").as_int();
5931 tempItem.Var2 = item.attribute("slot").as_int();
5932
5933 devEventItems[++devEventItemsCount] = tempItem;
5934
5935 item = item.next_sibling();
5936 }
5937
5938 delete [] buffer;
5939
5940 r3dOutToLog("Dev Event Loadout configuration has been loaded with: %d items.\n", devEventItemsCount);
5941}
5942
5943void ServerGameLogic::GiveDevEventLoadout(DWORD peerId)
5944{
5945 peerInfo_s& peer = GetPeer(peerId);
5946
5947 // give player backpack
5948 const BackpackConfig* cfg = g_pWeaponArmory->getBackpackConfig(devEventBackpackId);
5949 if (cfg == NULL)
5950 {
5951 r3dOutToLog("The specified dev event backpack is not valid. itemId: %d\n", devEventBackpackId);
5952 return;
5953 }
5954
5955 peer.temp_profile.ProfileData.ArmorySlots[0].BackpackID = cfg->m_itemID;
5956 peer.temp_profile.ProfileData.ArmorySlots[0].BackpackSize = cfg->m_maxSlots;
5957
5958 int useIndex = 0;
5959 for (int i = 0; i < devEventItemsCount+1; ++i)
5960 {
5961 wiInventoryItem tempItem;
5962 tempItem.InventoryID = useIndex+1;
5963 tempItem.itemID = devEventItems[i].itemID;
5964 tempItem.quantity = devEventItems[i].quantity;
5965 tempItem.Var2 = 0;
5966
5967 if (devEventItems[i].Var2 == -1)
5968 {
5969 peer.temp_profile.ProfileData.ArmorySlots[0].Items[useIndex+8] = tempItem;
5970 useIndex++;
5971 }
5972 else
5973 peer.temp_profile.ProfileData.ArmorySlots[0].Items[devEventItems[i].Var2] = tempItem;
5974 }
5975}
5976
5977void ServerGameLogic::PreloadDevEventLoadoutSnipersMode()
5978{
5979 devEventItemsCount2 = 0;
5980 devEventBackpackId2 = 0;
5981
5982 const char* xmlPath2 = "Data\\Weapons\\DevEventLoadoutSnipersMode.xml";
5983
5984 r3dFile* file2 = r3d_open(xmlPath2, "rb");
5985 if (!file2)
5986 {
5987 r3dOutToLog("Failed to open Dev Event Loadout Snipers Mode configuration file: %s\n", xmlPath2);
5988 return;
5989 }
5990
5991 char* buffer = game_new char[file2->size + 1];
5992 fread(buffer, file2->size, 1, file2);
5993 buffer[file2->size] = 0;
5994
5995 pugi::xml_document xmlDoc;
5996 pugi::xml_parse_result parseResult = xmlDoc.load_buffer_inplace(buffer, file2->size);
5997 fclose(file2);
5998
5999 if (!parseResult)
6000 r3dError("Failed to parse Dev Event Loadout Snipers Mode XML file, error: %s", parseResult.description());
6001
6002 pugi::xml_node loadout = xmlDoc.child("loadout");
6003 devEventBackpackId2 = loadout.attribute("backpackId").as_int();
6004
6005 pugi::xml_node item2 = loadout.child("item");
6006
6007 devEventItemsCount2 = -1;
6008 while (item2)
6009 {
6010 wiInventoryItem tempItem;
6011 tempItem.InventoryID = 0;
6012 tempItem.itemID = item2.attribute("id").as_uint();
6013 tempItem.quantity = item2.attribute("qty").as_int();
6014 tempItem.Var2 = item2.attribute("slot").as_int();
6015
6016 devEventItems2[++devEventItemsCount2] = tempItem;
6017
6018 item2 = item2.next_sibling();
6019 }
6020
6021 delete [] buffer;
6022
6023 r3dOutToLog("Dev Event Loadout Snipers Mode configuration has been loaded with: %d items.\n", devEventItemsCount2);
6024}
6025
6026void ServerGameLogic::GiveDevEventLoadoutSnipersMode(DWORD peerId)
6027{
6028 peerInfo_s& peer2 = GetPeer(peerId);
6029
6030 // give player backpack
6031 const BackpackConfig* cfg = g_pWeaponArmory->getBackpackConfig(devEventBackpackId2);
6032 if (cfg == NULL)
6033 {
6034 r3dOutToLog("The specified dev event Snipers Mode backpack is not valid. itemId: %d\n", devEventBackpackId2);
6035 return;
6036 }
6037
6038 peer2.temp_profile.ProfileData.ArmorySlots[0].BackpackID = cfg->m_itemID;
6039 peer2.temp_profile.ProfileData.ArmorySlots[0].BackpackSize = cfg->m_maxSlots;
6040
6041 int useIndex = 0;
6042 for (int i = 0; i < devEventItemsCount2+1; ++i)
6043 {
6044 wiInventoryItem tempItem;
6045 tempItem.InventoryID = useIndex+1;
6046 tempItem.itemID = devEventItems2[i].itemID;
6047 tempItem.quantity = devEventItems2[i].quantity;
6048 tempItem.Var2 = 0;
6049
6050 if (devEventItems2[i].Var2 == -1)
6051 {
6052 peer2.temp_profile.ProfileData.ArmorySlots[0].Items[useIndex+8] = tempItem;
6053 useIndex++;
6054 }
6055 else
6056 peer2.temp_profile.ProfileData.ArmorySlots[0].Items[devEventItems2[i].Var2] = tempItem;
6057 }
6058}
6059
6060void ServerGameLogic::PreloadDevEventLoadoutNoSnipersMode()
6061{
6062 devEventItemsCount3 = 0;
6063 devEventBackpackId3 = 0;
6064
6065 const char* xmlPath3 = "Data\\Weapons\\DevEventLoadoutNoSnipersMode.xml";
6066
6067 r3dFile* file3 = r3d_open(xmlPath3, "rb");
6068 if (!file3)
6069 {
6070 r3dOutToLog("Failed to open Dev Event Loadout No Snipers Mode configuration file: %s\n", xmlPath3);
6071 return;
6072 }
6073
6074 char* buffer = game_new char[file3->size + 1];
6075 fread(buffer, file3->size, 1, file3);
6076 buffer[file3->size] = 0;
6077
6078 pugi::xml_document xmlDoc;
6079 pugi::xml_parse_result parseResult = xmlDoc.load_buffer_inplace(buffer, file3->size);
6080 fclose(file3);
6081
6082 if (!parseResult)
6083 r3dError("Failed to parse Dev Event Loadout No Snipers Mode XML file, error: %s", parseResult.description());
6084
6085 pugi::xml_node loadout = xmlDoc.child("loadout");
6086 devEventBackpackId3 = loadout.attribute("backpackId").as_int();
6087
6088 pugi::xml_node item3 = loadout.child("item");
6089
6090 devEventItemsCount3 = -1;
6091 while (item3)
6092 {
6093 wiInventoryItem tempItem;
6094 tempItem.InventoryID = 0;
6095 tempItem.itemID = item3.attribute("id").as_uint();
6096 tempItem.quantity = item3.attribute("qty").as_int();
6097 tempItem.Var2 = item3.attribute("slot").as_int();
6098
6099 devEventItems3[++devEventItemsCount3] = tempItem;
6100
6101 item3 = item3.next_sibling();
6102 }
6103
6104 delete[] buffer;
6105
6106 r3dOutToLog("Dev Event Loadout No Snipers Mode configuration has been loaded with: %d items.\n", devEventItemsCount3);
6107}
6108
6109void ServerGameLogic::GiveDevEventLoadoutNoSnipersMode(DWORD peerId)
6110{
6111 peerInfo_s& peer3 = GetPeer(peerId);
6112
6113 // give player backpack
6114 const BackpackConfig* cfg = g_pWeaponArmory->getBackpackConfig(devEventBackpackId3);
6115 if (cfg == NULL)
6116 {
6117 r3dOutToLog("The specified dev event No Snipers Mode backpack is not valid. itemId: %d\n", devEventBackpackId3);
6118 return;
6119 }
6120
6121 peer3.temp_profile.ProfileData.ArmorySlots[0].BackpackID = cfg->m_itemID;
6122 peer3.temp_profile.ProfileData.ArmorySlots[0].BackpackSize = cfg->m_maxSlots;
6123
6124 int useIndex = 0;
6125 for (int i = 0; i < devEventItemsCount3 + 1; ++i)
6126 {
6127 wiInventoryItem tempItem;
6128 tempItem.InventoryID = useIndex + 1;
6129 tempItem.itemID = devEventItems3[i].itemID;
6130 tempItem.quantity = devEventItems3[i].quantity;
6131 tempItem.Var2 = 0;
6132
6133 if (devEventItems3[i].Var2 == -1)
6134 {
6135 peer3.temp_profile.ProfileData.ArmorySlots[0].Items[useIndex + 8] = tempItem;
6136 useIndex++;
6137 }
6138 else
6139 peer3.temp_profile.ProfileData.ArmorySlots[0].Items[devEventItems3[i].Var2] = tempItem;
6140 }
6141}
6142
6143// // need debug all stuff
6144/*===============================================================================*/
6145bool isOccupied;
6146void ServerGameLogic::PreloadDevEventLoadoutBR()
6147{
6148 BRItemsCount = 0;
6149 BRBackpackId = 0;
6150
6151 const char* xmlPathBR = "Data\\Weapons\\DevEventLoadoutBR.xml";
6152
6153 r3dFile* file4 = r3d_open(xmlPathBR, "rb");
6154 if (!file4)
6155 {
6156 r3dOutToLog("[BattleRoyale] -- Failed to open Loadout configuration file: %s\n", xmlPathBR);
6157 return;
6158 }
6159
6160 char* buffer = game_new char[file4->size + 1];
6161 fread(buffer, file4->size, 1, file4);
6162 buffer[file4->size] = 0;
6163
6164 pugi::xml_document xmlDoc;
6165 pugi::xml_parse_result parseResult = xmlDoc.load_buffer_inplace(buffer, file4->size);
6166 fclose(file4);
6167
6168 if (!parseResult)
6169 r3dError("[BattleRoyale] -- Failed to parse Loadout XML file, error: %s", parseResult.description());
6170
6171 pugi::xml_node loadout = xmlDoc.child("loadout");
6172 BRBackpackId = loadout.attribute("backpackId").as_int();
6173
6174 pugi::xml_node item4 = loadout.child("item");
6175
6176 BRItemsCount = -1;
6177 while (item4)
6178 {
6179 wiInventoryItem tempItem;
6180 tempItem.InventoryID = 0;
6181 tempItem.itemID = item4.attribute("id").as_uint();
6182 tempItem.quantity = item4.attribute("qty").as_int();
6183 tempItem.Var2 = item4.attribute("slot").as_int();
6184
6185 BRItems[++BRItemsCount] = tempItem;
6186
6187 item4 = item4.next_sibling();
6188 }
6189
6190 delete[] buffer;
6191
6192 r3dOutToLog("[BattleRoyale] -- Loadout configuration has been loaded with: %d items.\n", BRItemsCount);
6193}
6194
6195void ServerGameLogic::GiveDevEventLoadoutBR(DWORD peerId)
6196{
6197 peerInfo_s& peer = GetPeer(peerId);
6198
6199 // give player backpack
6200 const BackpackConfig* cfg = g_pWeaponArmory->getBackpackConfig(BRBackpackId);
6201 if (cfg == NULL)
6202 {
6203 r3dOutToLog("[BattleRoyale] -- The specified backpack is not valid. itemId: %d\n", BRBackpackId);
6204 return;
6205 }
6206
6207 peer.temp_profile.ProfileData.ArmorySlots[0].BackpackID = cfg->m_itemID;
6208 peer.temp_profile.ProfileData.ArmorySlots[0].BackpackSize = cfg->m_maxSlots;
6209
6210 obj_ServerPlayer* plr = IsServerPlayer(GetPeer(peerId).player);
6211 int useIndex = 0;
6212 for (int i = 0; i < BRItemsCount + 1; ++i)
6213 {
6214 wiInventoryItem tempItem;
6215 tempItem.InventoryID = useIndex + 1;
6216 tempItem.itemID = BRItems[i].itemID;
6217 tempItem.quantity = BRItems[i].quantity;
6218 tempItem.Var2 = 0;
6219
6220 if (BRItems[i].Var2 == -1)
6221 {
6222 peer.temp_profile.ProfileData.ArmorySlots[0].Items[useIndex + 8] = tempItem;
6223 useIndex++;
6224 }
6225 else
6226 peer.temp_profile.ProfileData.ArmorySlots[0].Items[BRItems[i].Var2] = tempItem;
6227 }
6228 r3dOutToLog("[BattleRoyale] -- Loadout configuration has been added with: %d items.\n", BRItemsCount);
6229}
6230
6231void ServerGameLogic::BattleRoyaleTick()
6232{
6233 for (int i = 0; i < curPlayers_; i++)
6234 {
6235 obj_ServerPlayer* pl = plrList_[i];
6236 if(curPlayers_ == ginfo_.maxPlayers && !isCountingDown && !m_isGameHasStarted)
6237 {
6238 r3dOutToLog("Minimum required players achieved, starting match\n");
6239 StartCountdown();
6240 }
6241
6242 if (isCountingDown)
6243 {
6244 float diff = preGameTime - r3dGetTime();
6245 int iDiff = (int)diff; // Time left util we start the game
6246 if (/*iDiff == 60 || iDiff == 30 || iDiff == 15 ||*/ iDiff <= 5)
6247 {
6248 // Chat message to all players
6249 if (diff > 1)
6250 {
6251 if (lastNumber != iDiff) // Check so that we send this message once every second
6252 {
6253 // Tell the players how much time we have left before we start the match
6254 PKT_C2C_ChatMessage_s n2;
6255 n2.msgChannel = 1; // global
6256 n2.userFlag = 200; // battle royale
6257 r3dscpy(n2.gamertag, "<BattleRoyale>");
6258 char buffer[64];
6259 sprintf_s(buffer, "Battle Royale will start in %i!", iDiff);
6260 r3dscpy(n2.msg, buffer);
6261 p2pBroadcastToAll(&n2, sizeof(n2), 1);
6262 lastNumber = iDiff;
6263 }
6264 }
6265 else if (iDiff == 0) // If the time left to start the match is smaller than or equal to 0
6266 {
6267 // We start the match
6268 StartMatch();
6269
6270 // And send a message to all players that the match has started
6271 PKT_C2C_ChatMessage_s n2;
6272 n2.msgChannel = 1; // global
6273 n2.userFlag = 200; // battle royale
6274 r3dscpy(n2.gamertag, "<BattleRoyale>");
6275 char buffer[64];
6276 sprintf_s(buffer, "The Battle Royale has started, good luck!", diff);
6277 r3dscpy(n2.msg, buffer);
6278 p2pBroadcastToAll(&n2, sizeof(n2), 1);
6279 }
6280 }
6281 }
6282
6283 // Check battle zone
6284 if(!m_isGameHasStarted)
6285 CheckBattleZone();//
6286
6287 // Gas scaner
6288 if(m_isGameHasStarted && curPlayers_ > 0 && !isCountingDown && !haveWinningPlayer)
6289 GasScanner();
6290
6291 //check winner
6292 if (m_isGameHasStarted)
6293 CheckBattleRoyaleWinner();
6294
6295 // we need reset all shit if match is finished
6296 if (canFinishMatch && curPlayers_<1)
6297 FinishMatch();
6298 }
6299}
6300
6301void ServerGameLogic::GetSpawnPositionBattleRoyale(const r3dPoint3D& GamePos, r3dPoint3D* pos, float* dir)
6302{
6303 if(gCPMgr.numControlPoints_ == 0)
6304 {
6305 r3dError("!!!!!!!!!!!! THERE IS NO CONTROL POINT !!!!!!!\n");
6306 *pos = r3dPoint3D(0, 0, 0);
6307 *dir = 0;
6308 return;
6309 }
6310
6311 //Get random lobby spawn point
6312 for(int i=0; i<gCPMgr.numControlPoints_; i++)
6313 {
6314 const BasePlayerSpawnPoint* spawn = gCPMgr.controlPoints_[i];
6315 for(int j=0; j<spawn->m_NumSpawnPoints; j++)
6316 {
6317 int idx1 = u_random(j);// we need random spawn point
6318 if(spawn->spawnType_!=1)//when player spawns first time, he spawns at those points
6319 {
6320 *pos = spawn->m_SpawnPoints[idx1].pos;
6321 *dir = spawn->m_SpawnPoints[idx1].dir;
6322 }
6323 }
6324 }
6325
6326 return;
6327}
6328
6329void ServerGameLogic::ResetPositionBattleRoyale(obj_ServerPlayer* plr, int lbNum)// alex
6330{
6331 //Get random lobby spawn point
6332 r3dPoint3D pos;
6333
6334 for(int i=0; i<gCPMgr.numControlPoints_; i++)
6335 {
6336 const BasePlayerSpawnPoint* spawn = gCPMgr.controlPoints_[i];
6337 for(int j=0; j<spawn->m_NumSpawnPoints; j++)
6338 {
6339 int idx1 = u_random(j);// we need random spawn point
6340 if(spawn->spawnType_!=1)//when player spawns first time, he spawns at those points
6341 pos = spawn->m_SpawnPoints[idx1].pos;
6342 }
6343 }
6344 admin_TeleportPlayer(plr, pos.x, pos.z);
6345}
6346void ServerGameLogic::StartCountdown()
6347{
6348 if(!gMasterServerLogic.shuttingDown_)// do not start match if server shutting down
6349 {
6350 isCountingDown = true;
6351 ForceStarGame = false;
6352 preGameTime = r3dGetTime() + 5.0f;
6353 lastNumber = 0;
6354 winningPlr = NULL;
6355
6356 //Report client about preparing for start the match
6357 SetNewGameInfo(gServerLogic.ginfo_.flags |= GBGameInfo::SFLAGS_isAboutStartedBR, 0);
6358 gMasterServerLogic.SetGameBRinfo(1);//send info about status to masterserver
6359
6360 PKT_C2C_ChatMessage_s n2;
6361 n2.msgChannel = 1; // global
6362 n2.userFlag = 200; // battle royale
6363 r3dscpy(n2.gamertag, "<BattleRoyale>");
6364 char buffer[64];
6365 sprintf_s(buffer, "Battle Royale will start in 5 seconds");
6366 r3dscpy(n2.msg, buffer);
6367 p2pBroadcastToAll(&n2, sizeof(n2), 1);
6368 r3dOutToLog("[BattleRoyale] -- Start counting down\n");
6369 }
6370}
6371
6372void ServerGameLogic::StartMatch()
6373{
6374 isCountingDown = false;
6375 m_isGameHasStarted = true;
6376 gMasterServerLogic.SetGameBRinfo(1);//send info about status to masterserver
6377 canFinishMatch = false;
6378 bool DarSpawnProtection = false;
6379 haveWinningPlayer = false;
6380 isGasSuspended = true;
6381 winningPlr = NULL;
6382 suspendTimer = 0;
6383 gasint = 0;
6384 playersLeft = curPlayers_;
6385 playersStarted = curPlayers_;
6386 AirDropSpawnTime = r3dGetTime() + RESPAWN_TIME_AIRDROPBR;
6387
6388 // Set occupied false for all spawns
6389 BasePlayerSpawnPoint* curSpawn;
6390 int numSpawns = 0;
6391
6392 for (int i = 0; i < gCPMgr.numControlPoints_; i++)
6393 {
6394 curSpawn = gCPMgr.controlPoints_[i];
6395 for (int j = 0; j < curSpawn->m_NumSpawnPoints; j++)
6396 {
6397 curSpawn->m_SpawnPoints[j].isOccupied = false;
6398 numSpawns++;
6399 }
6400 }
6401 r3dOutToLog("[BattleRoyale] -- Resetted %i spawns\n", numSpawns);
6402
6403 // Teleport players to their start positions
6404 for (int i = 0; i < curPlayers_; ++i)
6405 {
6406 DarSpawnProtection = true;
6407 obj_ServerPlayer* pl = plrList_[i];
6408 pl->m_SpawnProtectedUntil = r3dGetTime() + 30.0f;
6409 pl->loadout_->Health = 100.0f;
6410 pl->loadout_->Hunger = 0.0f;
6411 pl->loadout_->Thirst = 0.0f;
6412 pl->loadout_->Toxic = 0.0f;
6413 pl->loadout_->MedBleeding = 0.0f;
6414 pl->loadout_->MedBloodInfection = 0.0f;
6415 pl->m_Stamina = GPP_Data.c_fSprintMaxEnergy;
6416 //GetStartPositionBattleRoyale(pl);
6417 GetStartPositionBattleRoyale2(pl);
6418 playersTeleported = true;
6419 teleportTimer = r3dGetTime()+ 0.3f;//0.3f; // from 2.5
6420 pl->GiveBattleRoyaleLoadout();//give loadout items
6421 }
6422
6423 //Report client about already started the match
6424 //@MrAcoustic: Just for tested.
6425 //PKT_S2C_SetGameInfoFlags_s n;
6426 //n.gameInfoFlags = ginfo_.flags |= GBGameInfo::SFLAGS_isStartedBR;
6427 //p2pBroadcastToAll(&n, sizeof(n), true);
6428 SetNewGameInfo(gServerLogic.ginfo_.flags |= GBGameInfo::SFLAGS_isStartedBR, 0);
6429
6430 //Get gas area radius
6431 for (int l = 0; l < gGasAreaMngr.numGasArea_; l++)
6432 {
6433 obj_ServerGasArea* rbox = gGasAreaMngr.GasArea_[l];
6434 // inform client about gas radius
6435 PKT_S2C_SetGasRadius_s n;
6436 n.gasRadius = rbox->useRadius;
6437 n.gasStatus = 0;
6438 isGasSuspended = true;
6439 p2pBroadcastToAll(&n, sizeof(n));
6440 break;
6441 }
6442 DestroyBarricadesBattleRoyale();// destroy all barricade // alex
6443
6444 r3dOutToLog("[BattleRoyale] -- Teleported and equipped %i players\n", curPlayers_);
6445 r3dOutToLog("[BattleRoyale] -- The match has started!\n");
6446}
6447
6448void ServerGameLogic::GetStartPositionBattleRoyale(obj_ServerPlayer* plr)
6449{
6450 float posX, posZ;
6451
6452 // Right now we teleport players to the first empty spawn
6453 BasePlayerSpawnPoint* curSpawn;
6454
6455 while (true)
6456 {
6457 srand((unsigned int)r3dGetTime());
6458 int i = rand() % gCPMgr.numControlPoints_;
6459 curSpawn = gCPMgr.controlPoints_[i];
6460
6461 int j = rand() % curSpawn->m_NumSpawnPoints;
6462
6463 if (!curSpawn->m_SpawnPoints[j].isOccupied)
6464 {
6465 curSpawn->m_SpawnPoints[j].isOccupied = true;
6466 if(curSpawn->spawnType_!=0)// not spawn in lobby spawn points
6467 {
6468 posX = curSpawn->m_SpawnPoints[j].pos.x;
6469 posZ = curSpawn->m_SpawnPoints[j].pos.z;
6470 admin_TeleportPlayer(plr, posX, posZ);
6471 break;
6472 }
6473 }
6474 }
6475 return;
6476}
6477
6478void ServerGameLogic::GetStartPositionBattleRoyale2(obj_ServerPlayer* plr)
6479{
6480 // Made by @MrAcoustic | Mr.Pakorn from TH xD.
6481 r3dPoint3D pos;
6482
6483 for(int i=0; i<gCPMgr.numControlPoints_; i++)
6484 {
6485 const BasePlayerSpawnPoint* spawn = gCPMgr.controlPoints_[i];
6486 for(int j=0; j<spawn->m_NumSpawnPoints; j++)
6487 {
6488 int idx1 = u_random(j);// we need random spawn point
6489 if(spawn->spawnType_!=0)//when player spawns first time, he spawns at those points
6490 pos = spawn->m_SpawnPoints[idx1].pos;
6491 }
6492 }
6493 admin_TeleportPlayer(plr, pos.x, pos.z);
6494}
6495
6496
6497
6498void ServerGameLogic::GasScanner()
6499{
6500 const int timeToPrepare = 120;// 2 Minutes to start getting close before the Radiactive gas arrives
6501
6502 if (r3dGetTime() > gastimer)
6503 {
6504 if (m_isGameHasStarted && curPlayers_ > 0 && !haveWinningPlayer) // check for someone living
6505 {
6506 if(gasint == 0)
6507 {
6508 PKT_C2C_ChatMessage_s n2;
6509 n2.msgChannel = 1; // global
6510 n2.userFlag = 200; // battle royale
6511 r3dscpy(n2.gamertag, "<BattleRoyale>");
6512 char buffer[128];
6513
6514 sprintf_s(buffer, "The Safezone has been indicated on the map. You have 2 Minutes to start getting close before the Radiactive gas arrives.");
6515 r3dscpy(n2.msg, buffer);
6516 p2pBroadcastToAll(&n2, sizeof(n2));
6517 }
6518 else if(gasint == timeToPrepare)
6519 {
6520 PKT_C2C_ChatMessage_s n2;
6521 n2.msgChannel = 1; // global
6522 n2.userFlag = 200; // battle royale
6523 r3dscpy(n2.gamertag, "<BattleRoyale>");
6524 char buffer[64];
6525 sprintf_s(buffer, "Radioactive gas released! Good Luck Survivors!");
6526 r3dscpy(n2.msg, buffer);
6527 p2pBroadcastToAll(&n2, sizeof(n2));
6528 }
6529
6530 gasint++;
6531 gastimer = r3dGetTime() + 1.0f;
6532
6533 if(gasint > timeToPrepare)
6534 {
6535 if(suspendTimer%30 == 0) // 30 + 60- 90+ 120-
6536 {
6537 isGasSuspended=!isGasSuspended;
6538 ChangeGasAreaBattleRoyale();
6539 }
6540 suspendTimer++;
6541
6542 if(!isGasSuspended)
6543 ChangeGasAreaBattleRoyale();
6544 }
6545 }
6546 }
6547}
6548
6549void ServerGameLogic::ChangeGasAreaBattleRoyale()
6550{
6551 //Set new gas area radius
6552 for (int l = 0; l < gGasAreaMngr.numGasArea_; l++)
6553 {
6554 obj_ServerGasArea* rbox = gGasAreaMngr.GasArea_[l];
6555 if(rbox->useRadius>0)//Stop using the gas
6556 {
6557 if(!isGasSuspended)
6558 rbox->useRadius -= 0.5f;// change radius
6559
6560 // inform client about changed gas radius
6561 PKT_S2C_SetGasRadius_s n;
6562 n.gasRadius = rbox->useRadius;
6563 n.gasStatus = 1;
6564 n.isGasSuspended = isGasSuspended;
6565 p2pBroadcastToAll(&n, sizeof(n));
6566 }
6567 break;
6568 }
6569}
6570
6571void ServerGameLogic::ResetGasAreaBattleRoyale()
6572{
6573 // Reset radius to default
6574 float radius = 0;
6575 for (int l = 0; l < gGasAreaMngr.numGasArea_; l++)
6576 {
6577 obj_ServerGasArea* rbox = gGasAreaMngr.GasArea_[l];
6578 rbox->useRadius = rbox->defaultRadius;
6579 radius = rbox->useRadius;
6580 break;
6581 }
6582 //r3dOutToLog("[BattleRoyale] -- Gas area resetted! radius is : %.f\n", radius);
6583}
6584
6585void ServerGameLogic::CheckBattleRoyaleWinner()
6586{
6587 //scan alive players
6588 int LivePlayers = 0;
6589 int LiveID = 0;
6590 for (GameObject* obj = GameWorld().GetFirstObject(); obj; obj = GameWorld().GetNextObject(obj))
6591 {
6592 if (obj->isObjType(OBJTYPE_Human))
6593 {
6594 obj_ServerPlayer* Player = (obj_ServerPlayer*)obj;
6595 if (Player->loadout_->Alive > 0)
6596 {
6597 LivePlayers++;
6598 LiveID = Player->GetNetworkID();
6599 }
6600 }
6601 }
6602
6603 // detect winner player and give him GD bonus
6604 if (LivePlayers == 1 && !haveWinningPlayer && m_isGameHasStarted)
6605 {
6606 winningPlr = (obj_ServerPlayer*)GameWorld().GetNetworkObject(LiveID);
6607 if (winningPlr)
6608 {
6609 haveWinningPlayer = true;
6610 winningPlr->profile_.ProfileData.GameDollars += rewardBR*playersStarted;
6611 winningPlr->loadout_->Stats.VictorysBattleRoyale++;
6612 ApiPlayerUpdateChar(winningPlr);
6613 SetNewGameInfo(60, 0);
6614
6615 //add message to chat
6616 char msg[512] = "";
6617 sprintf(msg, "The winner is %s!!!\n and has won %d GD - Congratulations!", winningPlr->loadout_->Gamertag, rewardBR*playersStarted);
6618 PKT_C2C_ChatMessage_s n;
6619 n.userFlag = 200;// battle royale
6620 n.msgChannel = 1;// global
6621 r3dscpy(n.msg, msg);
6622 r3dscpy(n.gamertag, "<BattleRoyale>");
6623 p2pBroadcastToAll(&n, sizeof(n), true);
6624
6625 DoKillPlayer(winningPlr, winningPlr, storecat_INVALID, true);// we need kill winner player for finish match
6626 canFinishMatch = true;
6627
6628 r3dOutToLog("[BattleRoyale] -- The winner is %s!!! he won %d GD\n", winningPlr->loadout_->Gamertag, rewardBR*playersStarted);
6629 }
6630 else
6631 r3dOutToLog("[BattleRoyale] -- Something wrong happen WE NOT HAVE WINNER\n");
6632 }
6633}
6634
6635void ServerGameLogic::FinishMatch()
6636{
6637 m_isGameHasStarted = false;
6638 ForceStarGame = false;
6639 isCountingDown = false;
6640 canFinishMatch = false;
6641 haveWinningPlayer = false;
6642 playersTeleported = false;
6643 isGasSuspended = true;
6644 suspendTimer = 0;
6645 winningPlr = NULL;
6646 gasint = 0;
6647 canCheckPlayers = r3dGetTime();
6648 gastimer = r3dGetTime();
6649 SetNewGameInfo(60, 0);
6650 DestroyBarricadesBattleRoyale();// destroy all barricade
6651 ReSpawnLootBattleRoyale(); //respawn all lootboxes
6652 ResetGasAreaBattleRoyale();// reset gas radius to default
6653 AirDropSpawnTime = r3dGetTime() + RESPAWN_TIME_AIRDROPBR;
6654
6655 r3dOutToLog("[BattleRoyale] -- Match is finished!\n");
6656}
6657
6658void ServerGameLogic::DestroyBarricadesBattleRoyale()
6659{
6660 //Destroy all barricades from BR map
6661 for (GameObject* obj = GameWorld().GetFirstObject(); obj; obj = GameWorld().GetNextObject(obj))
6662 {
6663 if (obj->isObjType(OBJTYPE_Barricade))
6664 {
6665 obj_ServerBarricade* shield = (obj_ServerBarricade*)obj;
6666 shield->DestroyBarricade();
6667 }
6668 }
6669 r3dOutToLog("[BattleRoyale] -- barricades destroyed!\n");
6670}
6671
6672void ServerGameLogic::ReSpawnLootBattleRoyale()
6673{
6674 //Respawn loot from BR map
6675 for (GameObject* obj = GameWorld().GetFirstObject(); obj; obj = GameWorld().GetNextObject(obj))
6676 {
6677 if (obj->isObjType(OBJTYPE_ItemSpawnPoint))
6678 {
6679 obj_ServerItemSpawnPoint* spawn = (obj_ServerItemSpawnPoint*)obj;
6680 spawn->RespawnLootBattleRoyale();
6681 }
6682 }
6683 r3dOutToLog("[BattleRoyale] -- Dropped item removed!\n");// log from obj_DroppedItem::Update()
6684 r3dOutToLog("[BattleRoyale] -- loot respawned!\n");
6685}
6686
6687void ServerGameLogic::RespawnPlayer(obj_ServerPlayer* plr)
6688{
6689 //Get random respawn point
6690 r3dPoint3D pos;
6691 float dir;
6692 if(ginfo_.IsGameBR())//BR MODE
6693 {
6694 for(int i=0; i<gCPMgr.numControlPoints_; i++)
6695 {
6696 const BasePlayerSpawnPoint* spawn = gCPMgr.controlPoints_[i];
6697 if(spawn->spawnType_!=1)//when player spawns first time, he spawns at those points
6698 {
6699 for(int j=0; j<spawn->m_NumSpawnPoints; j++)
6700 {
6701 int idx1 = u_random(j);// get random spawn point
6702 pos = spawn->m_SpawnPoints[idx1].pos;
6703 dir = spawn->m_SpawnPoints[idx1].dir;
6704 }
6705 }
6706 }
6707 }
6708 else if(ginfo_.channel==6)//DEV EVENT MODE
6709 {
6710 for(int i=0; i<gCPMgr.numControlPoints_; i++)
6711 {
6712 int idx = u_random(i);// get random control point
6713 const BasePlayerSpawnPoint* spawn = gCPMgr.controlPoints_[idx];
6714 for(int j=0; j<spawn->m_NumSpawnPoints; j++)
6715 {
6716 int idx1 = u_random(j);// get random spawn point
6717 pos = spawn->m_SpawnPoints[idx1].pos;
6718 dir = spawn->m_SpawnPoints[idx1].dir;
6719 }
6720 }
6721 }
6722
6723 float respawnProtection = 10.0f;
6724 plr->DoRespawn(pos, dir, respawnProtection);
6725
6726 PKT_S2C_RespawnPlayer_s n;
6727 n.pos = plr->GetPosition();
6728 n.dir = plr->m_PlayerRotation;
6729 n.spawnProtection = respawnProtection;
6730 p2pBroadcastToActive(plr, &n, sizeof(n));
6731 plr->packetBarrierReason = "";
6732
6733 //Give loadout items
6734 int useIndex = 0;
6735 if(ginfo_.IsGameBR())//Battle Royale
6736 {
6737 for (int i = 0; i < BRItemsCount + 1; ++i)
6738 {
6739 wiInventoryItem tempItem;
6740 tempItem.InventoryID = useIndex + 1;
6741 tempItem.itemID = BRItems[i].itemID;
6742 tempItem.quantity = BRItems[i].quantity;
6743 tempItem.Var2 = 0;
6744 plr->BackpackAddItem(tempItem);
6745 }
6746 }
6747 else if(ginfo_.channel==6)//DEV EVENT MODE
6748 {
6749 if(gServerLogic.ginfo_.flags & GBGameInfo::SFLAGS_DisableASR)// Snipers Mode
6750 {
6751 for (int i = 0; i < devEventItemsCount2 + 1; ++i)
6752 {
6753 wiInventoryItem tempItem;
6754 tempItem.InventoryID = useIndex + 1;
6755 tempItem.itemID = devEventItems2[i].itemID;
6756 tempItem.quantity = devEventItems2[i].quantity;
6757 tempItem.Var2 = 0;
6758 plr->BackpackAddItem(tempItem);
6759 }
6760 }
6761 else if (gServerLogic.ginfo_.flags & GBGameInfo::SFLAGS_DisableSNP)// No Snipers Mode
6762 {
6763 for (int i = 0; i < devEventItemsCount3 + 1; ++i)
6764 {
6765 wiInventoryItem tempItem;
6766 tempItem.InventoryID = useIndex + 1;
6767 tempItem.itemID = devEventItems3[i].itemID;
6768 tempItem.quantity = devEventItems3[i].quantity;
6769 tempItem.Var2 = 0;
6770 plr->BackpackAddItem(tempItem);
6771 }
6772 }
6773 else // Normal Mode
6774 {
6775 for (int i = 0; i < devEventItemsCount + 1; ++i)
6776 {
6777 wiInventoryItem tempItem;
6778 tempItem.InventoryID = useIndex + 1;
6779 tempItem.itemID = devEventItems[i].itemID;
6780 tempItem.quantity = devEventItems[i].quantity;
6781 tempItem.Var2 = 0;
6782 plr->BackpackAddItem(tempItem);
6783 }
6784 }
6785 }
6786
6787 r3dOutToLog("Player: %s -- respawning\n", plr->userName); CLOG_INDENT;
6788 return;
6789}
6790
6791void ServerGameLogic::CheckBattleZone()
6792{
6793 const float curTime = r3dGetTime();
6794
6795 for(int i=0; i<curPlayers_; i++)
6796 {
6797 obj_ServerPlayer* plr = plrList_[i];
6798 if(!plr || plr->loadout_->Alive == 0 || m_isGameHasStarted)
6799 continue;
6800
6801 if(g_BattleZone.IsInBattle(plr->GetPosition())) {
6802 if(plr->bzOutTime > 0) {
6803 //r3dOutToLog("%s entered battle zone\n", plr->Name.c_str());
6804 plr->bzOutTime = -1;
6805 }
6806 continue;
6807 }
6808
6809 if(plr->bzOutTime < 0) {
6810 plr->bzOutTime = curTime;
6811 //r3dOutToLog("%s left battle zone\n", plr->Name.c_str());
6812 continue;
6813 }
6814
6815 if(curTime > (plr->bzOutTime + _glm_bzOutKillTime)) {
6816 //r3dOutToLog("%s disconnect outside battle zone\n", plr->Name.c_str());
6817 DoKillPlayer(plr, plr, storecat_INVALID, true);
6818 //DisconnectPeer(plr->peerId_, false, "Player: %s disconnect! Reason: outside battle zone", plr->Name.c_str());
6819 continue;
6820 }
6821 }
6822
6823 return;
6824}
6825
6826/*===============================================================================*/