the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
1#include "stdafx.h"
2#include "PlayerList.h"
3#include "PlayerChunkMap.h"
4#include "MinecraftServer.h"
5#include "Settings.h"
6#include "ServerLevel.h"
7#include "ServerChunkCache.h"
8#include "ServerPlayer.h"
9#include "ServerPlayerGameMode.h"
10#include "ServerConnection.h"
11#include "PendingConnection.h"
12#include "PlayerConnection.h"
13#include "EntityTracker.h"
14#include "..\Minecraft.World\net.minecraft.world.level.storage.h"
15#include "..\Minecraft.World\net.minecraft.world.level.dimension.h"
16#include "..\Minecraft.World\ArrayWithLength.h"
17#include "..\Minecraft.World\net.minecraft.network.packet.h"
18#include "..\Minecraft.World\net.minecraft.network.h"
19#include "..\Minecraft.World\Pos.h"
20#include "..\Minecraft.World\ProgressListener.h"
21#include "..\Minecraft.World\HellRandomLevelSource.h"
22#include "..\Minecraft.World\net.minecraft.world.phys.h"
23#include "..\Minecraft.World\net.minecraft.world.item.h"
24#include "..\Minecraft.World\net.minecraft.world.level.storage.h"
25#include "..\Minecraft.World\net.minecraft.world.level.saveddata.h"
26#include "..\Minecraft.World\JavaMath.h"
27#include "..\Minecraft.World\EntityIO.h"
28#if defined(_XBOX) || defined(_WINDOWS64)
29#include "Xbox\Network\NetworkPlayerXbox.h"
30#elif defined(__PS3__) || defined(__ORBIS__)
31#include "Common\Network\Sony\NetworkPlayerSony.h"
32#endif
33
34// 4J - this class is fairly substantially altered as there didn't seem any point in porting code for banning, whitelisting, ops etc.
35
36PlayerList::PlayerList(MinecraftServer *server)
37{
38 playerIo = NULL;
39
40 this->server = server;
41
42 sendAllPlayerInfoIn = 0;
43 overrideGameMode = NULL;
44 allowCheatsForAllPlayers = false;
45
46#ifdef __PSVITA__
47 viewDistance = 3;
48#elif defined _LARGE_WORLDS
49 viewDistance = 16;
50#else
51 viewDistance = 10;
52#endif
53
54 //int viewDistance = server->settings->getInt(L"view-distance", 10);
55
56 maxPlayers = server->settings->getInt(L"max-players", 20);
57 doWhiteList = false;
58
59#ifdef _WINDOWS64
60 maxPlayers = MINECRAFT_NET_MAX_PLAYERS;
61#else
62 maxPlayers = server->settings->getInt(L"max-players", 20);
63#endif
64 InitializeCriticalSection(&m_kickPlayersCS);
65 InitializeCriticalSection(&m_closePlayersCS);
66}
67
68PlayerList::~PlayerList()
69{
70 for( AUTO_VAR(it, players.begin()); it < players.end(); it++ )
71 {
72 (*it)->connection = nullptr; // Must remove reference to connection, or else there is a circular dependency
73 delete (*it)->gameMode; // Gamemode also needs deleted as it references back to this player
74 (*it)->gameMode = NULL;
75 }
76
77 DeleteCriticalSection(&m_kickPlayersCS);
78 DeleteCriticalSection(&m_closePlayersCS);
79}
80
81void PlayerList::placeNewPlayer(Connection *connection, shared_ptr<ServerPlayer> player, shared_ptr<LoginPacket> packet)
82{
83 CompoundTag *playerTag = load(player);
84
85 bool newPlayer = playerTag == NULL;
86
87 player->setLevel(server->getLevel(player->dimension));
88 player->gameMode->setLevel((ServerLevel *)player->level);
89
90 // Make sure these privileges are always turned off for the host player
91 INetworkPlayer *networkPlayer = connection->getSocket()->getPlayer();
92 if(networkPlayer != NULL && networkPlayer->IsHost())
93 {
94 player->enableAllPlayerPrivileges(true);
95 player->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_HOST,1);
96 }
97
98#if defined(__PS3__) || defined(__ORBIS__)
99 // PS3 networking library doesn't automatically assign PlayerUIDs to the network players for anything remote, so need to tell it what to set from the data in this packet now
100 if( !g_NetworkManager.IsLocalGame() )
101 {
102 if( networkPlayer != NULL )
103 {
104 ((NetworkPlayerSony *)networkPlayer)->SetUID( packet->m_onlineXuid );
105 }
106 }
107#endif
108#ifdef _WINDOWS64
109 if (networkPlayer != NULL && !networkPlayer->IsLocal())
110 {
111 NetworkPlayerXbox* nxp = (NetworkPlayerXbox*)networkPlayer;
112 IQNetPlayer* qnp = nxp->GetQNetPlayer();
113 wcsncpy_s(qnp->m_gamertag, 32, player->name.c_str(), _TRUNCATE);
114 }
115#endif
116 // 4J Stu - TU-1 hotfix
117 // Fix for #13150 - When a player loads/joins a game after saving/leaving in the nether, sometimes they are spawned on top of the nether and cannot mine down
118 validatePlayerSpawnPosition(player);
119
120 // logger.info(getName() + " logged in with entity id " + playerEntity.entityId + " at (" + playerEntity.x + ", " + playerEntity.y + ", " + playerEntity.z + ")");
121
122 ServerLevel *level = server->getLevel(player->dimension);
123
124 DWORD playerIndex = 0;
125 {
126 bool usedIndexes[MINECRAFT_NET_MAX_PLAYERS];
127 ZeroMemory( &usedIndexes, MINECRAFT_NET_MAX_PLAYERS * sizeof(bool) );
128 for(AUTO_VAR(it, players.begin()); it < players.end(); ++it)
129 {
130 usedIndexes[ (int)(*it)->getPlayerIndex() ] = true;
131 }
132 for(unsigned int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i)
133 {
134 if(!usedIndexes[i])
135 {
136 playerIndex = i;
137 break;
138 }
139 }
140 }
141 player->setPlayerIndex( playerIndex );
142 player->setCustomSkin( packet->m_playerSkinId );
143 player->setCustomCape( packet->m_playerCapeId );
144
145 // 4J-JEV: Moved this here so we can send player-model texture and geometry data.
146 shared_ptr<PlayerConnection> playerConnection = shared_ptr<PlayerConnection>(new PlayerConnection(server, connection, player));
147 //player->connection = playerConnection; // Used to be assigned in PlayerConnection ctor but moved out so we can use shared_ptr
148
149 if(newPlayer)
150 {
151 int mapScale = 3;
152#ifdef _LARGE_WORLDS
153 int scale = MapItemSavedData::MAP_SIZE * 2 * (1 << mapScale);
154 int centreXC = (int) (Math::round(player->x / scale) * scale);
155 int centreZC = (int) (Math::round(player->z / scale) * scale);
156#else
157 // 4J-PB - for Xbox maps, we'll centre them on the origin of the world, since we can fit the whole world in our map
158 int centreXC = 0;
159 int centreZC = 0;
160#endif
161 // 4J Added - Give every player a map the first time they join a server
162 player->inventory->setItem( 9, shared_ptr<ItemInstance>( new ItemInstance(Item::map_Id, 1, level->getAuxValueForMap(player->getXuid(),0,centreXC, centreZC, mapScale ) ) ) );
163 if(app.getGameRuleDefinitions() != NULL)
164 {
165 app.getGameRuleDefinitions()->postProcessPlayer(player);
166 }
167 }
168
169 if(!player->customTextureUrl.empty() && player->customTextureUrl.substr(0,3).compare(L"def") != 0 && !app.IsFileInMemoryTextures(player->customTextureUrl))
170 {
171 if( server->getConnection()->addPendingTextureRequest(player->customTextureUrl))
172 {
173#ifndef _CONTENT_PACKAGE
174 wprintf(L"Sending texture packet to get custom skin %ls from player %ls\n",player->customTextureUrl.c_str(), player->name.c_str());
175#endif
176 playerConnection->send(shared_ptr<TextureAndGeometryPacket>( new TextureAndGeometryPacket(player->customTextureUrl,NULL,0) ) );
177 }
178 }
179 else if(!player->customTextureUrl.empty() && app.IsFileInMemoryTextures(player->customTextureUrl))
180 {
181 // Update the ref count on the memory texture data
182 app.AddMemoryTextureFile(player->customTextureUrl,NULL,0);
183 }
184
185 if(!player->customTextureUrl2.empty() && player->customTextureUrl2.substr(0,3).compare(L"def") != 0 && !app.IsFileInMemoryTextures(player->customTextureUrl2))
186 {
187 if( server->getConnection()->addPendingTextureRequest(player->customTextureUrl2))
188 {
189#ifndef _CONTENT_PACKAGE
190 wprintf(L"Sending texture packet to get custom skin %ls from player %ls\n",player->customTextureUrl2.c_str(), player->name.c_str());
191#endif
192 playerConnection->send(shared_ptr<TexturePacket>( new TexturePacket(player->customTextureUrl2,NULL,0) ) );
193 }
194 }
195 else if(!player->customTextureUrl2.empty() && app.IsFileInMemoryTextures(player->customTextureUrl2))
196 {
197 // Update the ref count on the memory texture data
198 app.AddMemoryTextureFile(player->customTextureUrl2,NULL,0);
199 }
200
201 player->setIsGuest( packet->m_isGuest );
202
203 Pos *spawnPos = level->getSharedSpawnPos();
204
205 updatePlayerGameMode(player, nullptr, level);
206
207 // Update the privileges with the correct game mode
208 GameType *gameType = Player::getPlayerGamePrivilege(player->getAllPlayerGamePrivileges(),Player::ePlayerGamePrivilege_CreativeMode) ? GameType::CREATIVE : GameType::SURVIVAL;
209 gameType = LevelSettings::validateGameType(gameType->getId());
210 if (player->gameMode->getGameModeForPlayer() != gameType)
211 {
212 player->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_CreativeMode,player->gameMode->getGameModeForPlayer()->getId() );
213 }
214
215 //shared_ptr<PlayerConnection> playerConnection = shared_ptr<PlayerConnection>(new PlayerConnection(server, connection, player));
216 player->connection = playerConnection; // Used to be assigned in PlayerConnection ctor but moved out so we can use shared_ptr
217
218 // 4J Added to store UGC settings
219 playerConnection->m_friendsOnlyUGC = packet->m_friendsOnlyUGC;
220 playerConnection->m_offlineXUID = packet->m_offlineXuid;
221 playerConnection->m_onlineXUID = packet->m_onlineXuid;
222
223 // This player is now added to the list, so incrementing this value invalidates all previous PreLogin packets
224 if(packet->m_friendsOnlyUGC) ++server->m_ugcPlayersVersion;
225
226 addPlayerToReceiving( player );
227
228 playerConnection->send( shared_ptr<LoginPacket>( new LoginPacket(L"", player->entityId, level->getLevelData()->getGenerator(), level->getSeed(), player->gameMode->getGameModeForPlayer()->getId(),
229 (byte) level->dimension->id, (byte) level->getMaxBuildHeight(), (byte) getMaxPlayers(),
230 level->difficulty, TelemetryManager->GetMultiplayerInstanceID(), (BYTE)playerIndex, level->useNewSeaLevel(), player->getAllPlayerGamePrivileges(),
231 level->getLevelData()->getXZSize(), level->getLevelData()->getHellScale() ) ) );
232 playerConnection->send( shared_ptr<SetSpawnPositionPacket>( new SetSpawnPositionPacket(spawnPos->x, spawnPos->y, spawnPos->z) ) );
233 playerConnection->send( shared_ptr<PlayerAbilitiesPacket>( new PlayerAbilitiesPacket(&player->abilities)) );
234 playerConnection->send( shared_ptr<SetCarriedItemPacket>( new SetCarriedItemPacket(player->inventory->selected)));
235 delete spawnPos;
236
237 updateEntireScoreboard((ServerScoreboard *) level->getScoreboard(), player);
238
239 sendLevelInfo(player, level);
240
241 // 4J-PB - removed, since it needs to be localised in the language the client is in
242 //server->players->broadcastAll( shared_ptr<ChatPacket>( new ChatPacket(L"�e" + playerEntity->name + L" joined the game.") ) );
243 broadcastAll( shared_ptr<ChatPacket>( new ChatPacket(player->name, ChatPacket::e_ChatPlayerJoinedGame) ) );
244
245 MemSect(14);
246 add(player);
247 MemSect(0);
248
249 player->doTick(true, true, false); // 4J - added - force sending of the nearest chunk before the player is teleported, so we have somewhere to arrive on...
250 playerConnection->teleport(player->x, player->y, player->z, player->yRot, player->xRot);
251
252 server->getConnection()->addPlayerConnection(playerConnection);
253 playerConnection->send( shared_ptr<SetTimePacket>( new SetTimePacket(level->getGameTime(), level->getDayTime(), level->getGameRules()->getBoolean(GameRules::RULE_DAYLIGHT)) ) );
254
255 AUTO_VAR(activeEffects, player->getActiveEffects());
256 for(AUTO_VAR(it, activeEffects->begin()); it != activeEffects->end(); ++it)
257 {
258 MobEffectInstance *effect = *it;
259 playerConnection->send(shared_ptr<UpdateMobEffectPacket>( new UpdateMobEffectPacket(player->entityId, effect) ) );
260 }
261
262 player->initMenu();
263
264 if (playerTag != NULL && playerTag->contains(Entity::RIDING_TAG))
265 {
266 // this player has been saved with a mount tag
267 shared_ptr<Entity> mount = EntityIO::loadStatic(playerTag->getCompound(Entity::RIDING_TAG), level);
268 if (mount != NULL)
269 {
270 mount->forcedLoading = true;
271 level->addEntity(mount);
272 player->ride(mount);
273 mount->forcedLoading = false;
274 }
275 }
276
277 // If we are joining at the same time as someone in the end on this system is travelling through the win portal,
278 // then we should set our wonGame flag to true so that respawning works when the EndPoem is closed
279 INetworkPlayer *thisPlayer = player->connection->getNetworkPlayer();
280 if( thisPlayer != NULL )
281 {
282 for(AUTO_VAR(it, players.begin()); it != players.end(); ++it)
283 {
284 shared_ptr<ServerPlayer> servPlayer = *it;
285 INetworkPlayer *checkPlayer = servPlayer->connection->getNetworkPlayer();
286 if(thisPlayer != checkPlayer && checkPlayer != NULL && thisPlayer->IsSameSystem( checkPlayer ) && servPlayer->wonGame )
287 {
288 player->wonGame = true;
289 break;
290 }
291 }
292 }
293}
294
295void PlayerList::updateEntireScoreboard(ServerScoreboard *scoreboard, shared_ptr<ServerPlayer> player)
296{
297 //unordered_set<Objective *> objectives;
298
299 //for (PlayerTeam team : scoreboard->getPlayerTeams())
300 //{
301 // player->connection->send( shared_ptr<SetPlayerTeamPacket>(new SetPlayerTeamPacket(team, SetPlayerTeamPacket::METHOD_ADD)));
302 //}
303
304 //for (int slot = 0; slot < Scoreboard::DISPLAY_SLOTS; slot++)
305 //{
306 // Objective objective = scoreboard->getDisplayObjective(slot);
307
308 // if (objective != NULL && !objectives->contains(objective))
309 // {
310 // vector<shared_ptr<Packet> > *packets = scoreboard->getStartTrackingPackets(objective);
311
312 // for (Packet packet : packets)
313 // {
314 // player->connection->send(packet);
315 // }
316
317 // objectives->add(objective);
318 // }
319 //}
320}
321
322void PlayerList::setLevel(ServerLevelArray levels)
323{
324 playerIo = levels[0]->getLevelStorage()->getPlayerIO();
325}
326
327void PlayerList::changeDimension(shared_ptr<ServerPlayer> player, ServerLevel *from)
328{
329 ServerLevel *to = player->getLevel();
330
331 if (from != NULL) from->getChunkMap()->remove(player);
332 to->getChunkMap()->add(player);
333
334 to->cache->create(((int) player->x) >> 4, ((int) player->z) >> 4);
335}
336
337int PlayerList::getMaxRange()
338{
339 return PlayerChunkMap::convertChunkRangeToBlock(getViewDistance());
340}
341
342CompoundTag *PlayerList::load(shared_ptr<ServerPlayer> player)
343{
344 return playerIo->load(player);
345}
346
347void PlayerList::save(shared_ptr<ServerPlayer> player)
348{
349 playerIo->save(player);
350}
351
352// 4J Stu - TU-1 hotifx
353// Add this function to take some of the code from the PlayerList::add function with the fixes
354// for checking spawn area, especially in the nether. These needed to be done in a different order from before
355// Fix for #13150 - When a player loads/joins a game after saving/leaving in the nether, sometimes they are spawned on top of the nether and cannot mine down
356void PlayerList::validatePlayerSpawnPosition(shared_ptr<ServerPlayer> player)
357{
358 // 4J Stu - Some adjustments to make sure the current players position is correct
359 // Make sure that the player is on the ground, and in the centre x/z of the current column
360 app.DebugPrintf("Original pos is %f, %f, %f in dimension %d\n", player->x, player->y, player->z, player->dimension);
361
362 bool spawnForced = player->isRespawnForced();
363
364 double targetX = 0;
365 if(player->x < 0) targetX = Mth::ceil(player->x) - 0.5;
366 else targetX = Mth::floor(player->x) + 0.5;
367
368 double targetY = floor(player->y);
369
370 double targetZ = 0;
371 if(player->z < 0) targetZ = Mth::ceil(player->z) - 0.5;
372 else targetZ = Mth::floor(player->z) + 0.5;
373
374 player->setPos(targetX, targetY, targetZ);
375
376 app.DebugPrintf("New pos is %f, %f, %f in dimension %d\n", player->x, player->y, player->z, player->dimension);
377
378 ServerLevel *level = server->getLevel(player->dimension);
379 while (level->getCubes(player, player->bb)->size() != 0)
380 {
381 player->setPos(player->x, player->y + 1, player->z);
382 }
383 app.DebugPrintf("Final pos is %f, %f, %f in dimension %d\n", player->x, player->y, player->z, player->dimension);
384
385 // 4J Stu - If we are in the nether and the above while loop has put us above the nether then we have a problem
386 // Finding a valid, safe spawn point is potentially computationally expensive (may have to hunt through a large part
387 // of the nether) so move the player to their spawn position in the overworld so that they do not lose their inventory
388 // 4J Stu - We also use this mechanism to force a spawn point in the overworld for players who were in the save when the reset nether option was applied
389 if(level->dimension->id == -1 && player->y > 125)
390 {
391 app.DebugPrintf("Player in the nether tried to spawn at y = %f, moving to overworld\n", player->y);
392 player->setLevel(server->getLevel(0));
393 player->gameMode->setLevel(server->getLevel(0));
394 player->dimension = 0;
395
396 level = server->getLevel(player->dimension);
397
398 Pos *levelSpawn = level->getSharedSpawnPos();
399 player->setPos(levelSpawn->x, levelSpawn->y, levelSpawn->z);
400 delete levelSpawn;
401
402 Pos *bedPosition = player->getRespawnPosition();
403 if (bedPosition != NULL)
404 {
405 Pos *respawnPosition = Player::checkBedValidRespawnPosition(server->getLevel(player->dimension), bedPosition, spawnForced);
406 if (respawnPosition != NULL)
407 {
408 player->moveTo(respawnPosition->x + 0.5f, respawnPosition->y + 0.1f, respawnPosition->z + 0.5f, 0, 0);
409 player->setRespawnPosition(bedPosition, spawnForced);
410 }
411 delete bedPosition;
412 }
413 while (level->getCubes(player, player->bb)->size() != 0)
414 {
415 player->setPos(player->x, player->y + 1, player->z);
416 }
417
418 app.DebugPrintf("Updated pos is %f, %f, %f in dimension %d\n", player->x, player->y, player->z, player->dimension);
419 }
420}
421
422void PlayerList::add(shared_ptr<ServerPlayer> player)
423{
424 //broadcastAll(shared_ptr<PlayerInfoPacket>( new PlayerInfoPacket(player->name, true, 1000) ) );
425 if( player->connection->getNetworkPlayer() )
426 {
427 broadcastAll(shared_ptr<PlayerInfoPacket>( new PlayerInfoPacket( player ) ) );
428 }
429
430 players.push_back(player);
431
432 // 4J Added
433 addPlayerToReceiving(player);
434
435 // Ensure the area the player is spawning in is loaded!
436 ServerLevel *level = server->getLevel(player->dimension);
437
438 // 4J Stu - TU-1 hotfix
439 // Fix for #13150 - When a player loads/joins a game after saving/leaving in the nether, sometimes they are spawned on top of the nether and cannot mine down
440 // Some code from here has been moved to the above validatePlayerSpawnPosition function
441
442 // 4J Stu - Swapped these lines about so that we get the chunk visiblity packet way ahead of all the add tracked entity packets
443 // Fix for #9169 - ART : Sign text is replaced with the words �Awaiting approval�.
444 changeDimension(player, NULL);
445 level->addEntity(player);
446
447 for (int i = 0; i < players.size(); i++)
448 {
449 shared_ptr<ServerPlayer> op = players.at(i);
450 //player->connection->send(shared_ptr<PlayerInfoPacket>( new PlayerInfoPacket(op->name, true, op->latency) ) );
451 if( op->connection->getNetworkPlayer() )
452 {
453 player->connection->send(shared_ptr<PlayerInfoPacket>( new PlayerInfoPacket( op ) ) );
454 }
455 }
456
457 if(level->isAtLeastOnePlayerSleeping())
458 {
459 shared_ptr<ServerPlayer> firstSleepingPlayer = nullptr;
460 for (unsigned int i = 0; i < players.size(); i++)
461 {
462 shared_ptr<ServerPlayer> thisPlayer = players[i];
463 if(thisPlayer->isSleeping())
464 {
465 if(firstSleepingPlayer == NULL) firstSleepingPlayer = thisPlayer;
466 thisPlayer->connection->send(shared_ptr<ChatPacket>( new ChatPacket(thisPlayer->name, ChatPacket::e_ChatBedMeSleep)));
467 }
468 }
469 player->connection->send(shared_ptr<ChatPacket>( new ChatPacket(firstSleepingPlayer->name, ChatPacket::e_ChatBedPlayerSleep)));
470 }
471}
472
473void PlayerList::move(shared_ptr<ServerPlayer> player)
474{
475 player->getLevel()->getChunkMap()->move(player);
476}
477
478void PlayerList::remove(shared_ptr<ServerPlayer> player)
479{
480 save(player);
481 //4J Stu - We don't want to save the map data for guests, so when we are sure that the player is gone delete the map
482 if(player->isGuest()) playerIo->deleteMapFilesForPlayer(player);
483 ServerLevel *level = player->getLevel();
484if (player->riding != NULL)
485 {
486 level->removeEntityImmediately(player->riding);
487 app.DebugPrintf("removing player mount");
488 }
489 level->getTracker()->removeEntity(player);
490 level->removeEntity(player);
491 level->getChunkMap()->remove(player);
492 AUTO_VAR(it, find(players.begin(),players.end(),player));
493 if( it != players.end() )
494 {
495 players.erase(it);
496 }
497 //broadcastAll(shared_ptr<PlayerInfoPacket>( new PlayerInfoPacket(player->name, false, 9999) ) );
498
499 removePlayerFromReceiving(player);
500 player->connection = nullptr; // Must remove reference to connection, or else there is a circular dependency
501 delete player->gameMode; // Gamemode also needs deleted as it references back to this player
502 player->gameMode = NULL;
503
504 // 4J Stu - Save all the players currently in the game, which will also free up unused map id slots if required, and remove old players
505 saveAll(NULL,false);
506}
507
508shared_ptr<ServerPlayer> PlayerList::getPlayerForLogin(PendingConnection *pendingConnection, const wstring& userName, PlayerUID xuid, PlayerUID onlineXuid)
509{
510#ifdef _WINDOWS64
511 if (players.size() >= (unsigned int)MINECRAFT_NET_MAX_PLAYERS)
512#else
513 if (players.size() >= (unsigned int)maxPlayers)
514#endif
515 {
516 pendingConnection->disconnect(DisconnectPacket::eDisconnect_ServerFull);
517 return shared_ptr<ServerPlayer>();
518 }
519 shared_ptr<ServerPlayer> player = shared_ptr<ServerPlayer>(new ServerPlayer(server, server->getLevel(0), userName, new ServerPlayerGameMode(server->getLevel(0)) ));
520 player->gameMode->player = player; // 4J added as had to remove this assignment from ServerPlayer ctor
521 player->setXuid( xuid ); // 4J Added
522 player->setOnlineXuid( onlineXuid ); // 4J Added
523#ifdef _WINDOWS64
524 {
525 INetworkPlayer* np = pendingConnection->connection->getSocket()->getPlayer();
526 if (np != NULL)
527 {
528 PlayerUID realXuid = np->GetUID();
529 player->setXuid(realXuid);
530 player->setOnlineXuid(realXuid);
531 }
532 }
533#endif
534 // Work out the base server player settings
535 INetworkPlayer *networkPlayer = pendingConnection->connection->getSocket()->getPlayer();
536 if(networkPlayer != NULL && !networkPlayer->IsHost())
537 {
538 player->enableAllPlayerPrivileges( app.GetGameHostOption(eGameHostOption_TrustPlayers)>0 );
539 }
540
541 // 4J Added
542 LevelRuleset *serverRuleDefs = app.getGameRuleDefinitions();
543 if(serverRuleDefs != NULL)
544 {
545 player->gameMode->setGameRules( GameRuleDefinition::generateNewGameRulesInstance(GameRulesInstance::eGameRulesInstanceType_ServerPlayer, serverRuleDefs, pendingConnection->connection) );
546 }
547
548 return player;
549}
550
551shared_ptr<ServerPlayer> PlayerList::respawn(shared_ptr<ServerPlayer> serverPlayer, int targetDimension, bool keepAllPlayerData)
552{
553 // How we handle the entity tracker depends on whether we are the primary player currently, and whether there will be any player in the same system in the same dimension once we finish respawning.
554 bool isPrimary = canReceiveAllPackets(serverPlayer); // Is this the primary player in its current dimension?
555 int oldDimension = serverPlayer->dimension;
556 bool isEmptying = ( targetDimension != oldDimension); // We're not emptying this dimension on this machine if this player is going back into the same dimension
557
558 // Also consider if there is another player on this machine which is in the same dimension and can take over as primary player
559 if( isEmptying )
560 {
561 INetworkPlayer *thisPlayer = serverPlayer->connection->getNetworkPlayer();
562
563 for( unsigned int i = 0; i < players.size(); i++ )
564 {
565 shared_ptr<ServerPlayer> ep = players[i];
566 if( ep == serverPlayer ) continue;
567 if( ep->dimension != oldDimension ) continue;
568
569 INetworkPlayer * otherPlayer = ep->connection->getNetworkPlayer();
570 if( otherPlayer != NULL && thisPlayer->IsSameSystem(otherPlayer) )
571 {
572 // There's another player here in the same dimension - we're not the last one out
573 isEmptying = false;
574 }
575 }
576 }
577
578 // Now we know where we stand, the actions to take are as follows:
579 // (1) if this isn't the primary player, then we just need to remove it from the entity tracker
580 // (2) if this Is the primary player then:
581 // (a) if isEmptying is true, then remove the player from the tracker, and send "remove entity" packets for anything seen (this is the original behaviour of the code)
582 // (b) if isEmptying is false, then we'll be transferring control of entity tracking to another player
583
584 if( isPrimary )
585 {
586 if( isEmptying )
587 {
588 app.DebugPrintf("Emptying this dimension\n");
589 serverPlayer->getLevel()->getTracker()->clear(serverPlayer);
590 }
591 else
592 {
593 app.DebugPrintf("Transferring... storing flags\n");
594 serverPlayer->getLevel()->getTracker()->removeEntity(serverPlayer);
595 }
596 }
597 else
598 {
599 app.DebugPrintf("Not primary player\n");
600 serverPlayer->getLevel()->getTracker()->removeEntity(serverPlayer);
601 }
602
603 serverPlayer->getLevel()->getChunkMap()->remove(serverPlayer);
604 AUTO_VAR(it, find(players.begin(),players.end(),serverPlayer));
605 if( it != players.end() )
606 {
607 players.erase(it);
608 }
609 server->getLevel(serverPlayer->dimension)->removeEntityImmediately(serverPlayer);
610
611 Pos *bedPosition = serverPlayer->getRespawnPosition();
612 bool spawnForced = serverPlayer->isRespawnForced();
613
614 removePlayerFromReceiving(serverPlayer);
615 serverPlayer->dimension = targetDimension;
616
617 EDefaultSkins skin = serverPlayer->getPlayerDefaultSkin();
618 DWORD playerIndex = serverPlayer->getPlayerIndex();
619
620 PlayerUID playerXuid = serverPlayer->getXuid();
621 PlayerUID playerOnlineXuid = serverPlayer->getOnlineXuid();
622
623 shared_ptr<ServerPlayer> player = shared_ptr<ServerPlayer>(new ServerPlayer(server, server->getLevel(serverPlayer->dimension), serverPlayer->getName(), new ServerPlayerGameMode(server->getLevel(serverPlayer->dimension))));
624 player->connection = serverPlayer->connection;
625 player->restoreFrom(serverPlayer, keepAllPlayerData);
626 if (keepAllPlayerData)
627 {
628 // Fix for #81759 - TU9: Content: Gameplay: Entering The End Exit Portal replaces the Player's currently held item with the first one from the Quickbar
629 player->inventory->selected = serverPlayer->inventory->selected;
630 }
631 player->gameMode->player = player; // 4J added as had to remove this assignment from ServerPlayer ctor
632 player->setXuid( playerXuid ); // 4J Added
633 player->setOnlineXuid( playerOnlineXuid ); // 4J Added
634
635 // 4J Stu - Don't reuse the id. If we do, then the player can be re-added after being removed, but the add packet gets sent before the remove packet
636 //player->entityId = serverPlayer->entityId;
637
638 player->setPlayerDefaultSkin( skin );
639 player->setIsGuest( serverPlayer->isGuest() );
640 player->setPlayerIndex( playerIndex );
641 player->setCustomSkin( serverPlayer->getCustomSkin() );
642 player->setCustomCape( serverPlayer->getCustomCape() );
643 player->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_All, serverPlayer->getAllPlayerGamePrivileges());
644 player->gameMode->setGameRules( serverPlayer->gameMode->getGameRules() );
645 player->dimension = targetDimension;
646
647 // 4J Stu - Added this as we need to know earlier if the player is the player for this connection so that
648 // we can work out if they are the primary for the system and can receive all packets
649 player->connection->setPlayer( player );
650
651 addPlayerToReceiving(player);
652
653 ServerLevel *level = server->getLevel(serverPlayer->dimension);
654
655 // reset the player's game mode (first pick from old, then copy level if
656 // necessary)
657 updatePlayerGameMode(player, serverPlayer, level);
658
659 if(serverPlayer->wonGame && targetDimension == oldDimension && serverPlayer->getHealth() > 0)
660 {
661 // If the player is still alive and respawning to the same dimension, they are just being added back from someone else viewing the Win screen
662 player->moveTo(serverPlayer->x, serverPlayer->y, serverPlayer->z, serverPlayer->yRot, serverPlayer->xRot);
663 if(bedPosition != NULL)
664 {
665 player->setRespawnPosition(bedPosition, spawnForced);
666 delete bedPosition;
667 }
668 // Fix for #81759 - TU9: Content: Gameplay: Entering The End Exit Portal replaces the Player's currently held item with the first one from the Quickbar
669 player->inventory->selected = serverPlayer->inventory->selected;
670 }
671 else if (bedPosition != NULL)
672 {
673 Pos *respawnPosition = Player::checkBedValidRespawnPosition(server->getLevel(serverPlayer->dimension), bedPosition, spawnForced);
674 if (respawnPosition != NULL)
675 {
676 player->moveTo(respawnPosition->x + 0.5f, respawnPosition->y + 0.1f, respawnPosition->z + 0.5f, 0, 0);
677 player->setRespawnPosition(bedPosition, spawnForced);
678 }
679 else
680 {
681 player->connection->send( shared_ptr<GameEventPacket>( new GameEventPacket(GameEventPacket::NO_RESPAWN_BED_AVAILABLE, 0) ) );
682 }
683 delete bedPosition;
684 }
685
686 // Ensure the area the player is spawning in is loaded!
687 level->cache->create(((int) player->x) >> 4, ((int) player->z) >> 4);
688
689 while (!level->getCubes(player, player->bb)->empty())
690 {
691 player->setPos(player->x, player->y + 1, player->z);
692 }
693
694 player->connection->send( shared_ptr<RespawnPacket>( new RespawnPacket((char) player->dimension, player->level->getSeed(), player->level->getMaxBuildHeight(),
695 player->gameMode->getGameModeForPlayer(), level->difficulty, level->getLevelData()->getGenerator(),
696 player->level->useNewSeaLevel(), player->entityId, level->getLevelData()->getXZSize(), level->getLevelData()->getHellScale()) ) );
697 player->connection->teleport(player->x, player->y, player->z, player->yRot, player->xRot);
698 player->connection->send( shared_ptr<SetExperiencePacket>( new SetExperiencePacket(player->experienceProgress, player->totalExperience, player->experienceLevel)) );
699
700 if(keepAllPlayerData)
701 {
702 vector<MobEffectInstance *> *activeEffects = player->getActiveEffects();
703 for(AUTO_VAR(it, activeEffects->begin()); it != activeEffects->end(); ++it)
704 {
705 MobEffectInstance *effect = *it;
706
707 player->connection->send(shared_ptr<UpdateMobEffectPacket>( new UpdateMobEffectPacket(player->entityId, effect) ) );
708 }
709 delete activeEffects;
710 player->getEntityData()->markDirty(Mob::DATA_EFFECT_COLOR_ID);
711 }
712
713 sendLevelInfo(player, level);
714
715 level->getChunkMap()->add(player);
716 level->addEntity(player);
717 players.push_back(player);
718
719 player->initMenu();
720 player->setHealth(player->getHealth());
721
722 // 4J-JEV - Dying before this point in the tutorial is pretty annoying,
723 // making sure to remove health/hunger and give you back your meat.
724 if( Minecraft::GetInstance()->isTutorial()
725 && (!Minecraft::GetInstance()->gameMode->getTutorial()->isStateCompleted(e_Tutorial_State_Food_Bar)) )
726 {
727 app.getGameRuleDefinitions()->postProcessPlayer(player);
728 }
729
730 if( oldDimension == 1 && player->dimension != 1 )
731 {
732 player->displayClientMessage(IDS_PLAYER_LEFT_END);
733 }
734
735 return player;
736
737}
738
739void PlayerList::toggleDimension(shared_ptr<ServerPlayer> player, int targetDimension)
740{
741 int lastDimension = player->dimension;
742 // How we handle the entity tracker depends on whether we are the primary player currently, and whether there will be any player in the same system in the same dimension once we finish respawning.
743 bool isPrimary = canReceiveAllPackets(player); // Is this the primary player in its current dimension?
744 bool isEmptying = true;
745
746 // Also consider if there is another player on this machine which is in the same dimension and can take over as primary player
747 INetworkPlayer *thisPlayer = player->connection->getNetworkPlayer();
748
749 for( unsigned int i = 0; i < players.size(); i++ )
750 {
751 shared_ptr<ServerPlayer> ep = players[i];
752 if( ep == player ) continue;
753 if( ep->dimension != lastDimension ) continue;
754
755 INetworkPlayer * otherPlayer = ep->connection->getNetworkPlayer();
756 if( otherPlayer != NULL && thisPlayer->IsSameSystem(otherPlayer) )
757 {
758 // There's another player here in the same dimension - we're not the last one out
759 isEmptying = false;
760 }
761 }
762
763
764 // Now we know where we stand, the actions to take are as follows:
765 // (1) if this isn't the primary player, then we just need to remove it from the entity tracker
766 // (2) if this Is the primary player then:
767 // (a) if isEmptying is true, then remove the player from the tracker, and send "remove entity" packets for anything seen (this is the original behaviour of the code)
768 // (b) if isEmptying is false, then we'll be transferring control of entity tracking to another player
769
770 if( isPrimary )
771 {
772 if( isEmptying )
773 {
774 app.DebugPrintf("Toggle... Emptying this dimension\n");
775 player->getLevel()->getTracker()->clear(player);
776 }
777 else
778 {
779 app.DebugPrintf("Toggle... transferring\n");
780 player->getLevel()->getTracker()->removeEntity(player);
781 }
782 }
783 else
784 {
785 app.DebugPrintf("Toggle... Not primary player\n");
786 player->getLevel()->getTracker()->removeEntity(player);
787 }
788
789 ServerLevel *oldLevel = server->getLevel(player->dimension);
790
791 // 4J Stu - Do this much earlier so we don't end up unloading chunks in the wrong dimension
792 player->getLevel()->getChunkMap()->remove(player);
793
794 if(player->dimension != 1 && targetDimension == 1)
795 {
796 player->displayClientMessage(IDS_PLAYER_ENTERED_END);
797 }
798 else if( player->dimension == 1 )
799 {
800 player->displayClientMessage(IDS_PLAYER_LEFT_END);
801 }
802
803 player->dimension = targetDimension;
804
805 ServerLevel *newLevel = server->getLevel(player->dimension);
806
807 // 4J Stu - Fix for #46423 - TU5: Art: Code: No burning animation visible after entering The Nether while burning
808 player->clearFire(); // Stop burning if travelling through a portal
809
810 // 4J Stu Added so that we remove entities from the correct level, after the respawn packet we will be in the wrong level
811 player->flushEntitiesToRemove();
812
813 player->connection->send( shared_ptr<RespawnPacket>( new RespawnPacket((char) player->dimension, newLevel->getSeed(), newLevel->getMaxBuildHeight(),
814 player->gameMode->getGameModeForPlayer(), newLevel->difficulty, newLevel->getLevelData()->getGenerator(),
815 newLevel->useNewSeaLevel(), player->entityId, newLevel->getLevelData()->getXZSize(), newLevel->getLevelData()->getHellScale()) ) );
816
817 oldLevel->removeEntityImmediately(player);
818 player->removed = false;
819
820 repositionAcrossDimension(player, lastDimension, oldLevel, newLevel);
821 changeDimension(player, oldLevel);
822
823 player->gameMode->setLevel(newLevel);
824
825 // Resend the teleport if we haven't yet sent the chunk they will land on
826 if( !g_NetworkManager.SystemFlagGet(player->connection->getNetworkPlayer(),ServerPlayer::getFlagIndexForChunk( ChunkPos(player->xChunk,player->zChunk), player->level->dimension->id ) ) )
827 {
828 player->connection->teleport(player->x, player->y, player->z, player->yRot, player->xRot, false);
829 // Force sending of the current chunk
830 player->doTick(true, true, true);
831 }
832
833 player->connection->teleport(player->x, player->y, player->z, player->yRot, player->xRot);
834
835 // 4J Stu - Fix for #64683 - Customer Encountered: TU7: Content: Gameplay: Potion effects are removed after using the Nether Portal
836 vector<MobEffectInstance *> *activeEffects = player->getActiveEffects();
837 for(AUTO_VAR(it, activeEffects->begin()); it != activeEffects->end(); ++it)
838 {
839 MobEffectInstance *effect = *it;
840
841 player->connection->send(shared_ptr<UpdateMobEffectPacket>( new UpdateMobEffectPacket(player->entityId, effect) ) );
842 }
843 delete activeEffects;
844 player->getEntityData()->markDirty(Mob::DATA_EFFECT_COLOR_ID);
845
846 sendLevelInfo(player, newLevel);
847 sendAllPlayerInfo(player);
848}
849
850void PlayerList::repositionAcrossDimension(shared_ptr<Entity> entity, int lastDimension, ServerLevel *oldLevel, ServerLevel *newLevel)
851{
852 double xt = entity->x;
853 double zt = entity->z;
854 double xOriginal = entity->x;
855 double yOriginal = entity->y;
856 double zOriginal = entity->z;
857 float yRotOriginal = entity->yRot;
858 double scale = newLevel->getLevelData()->getHellScale(); // 4J Scale was 8 but this is all we can fit in
859 if (entity->dimension == -1)
860 {
861 xt /= scale;
862 zt /= scale;
863 entity->moveTo(xt, entity->y, zt, entity->yRot, entity->xRot);
864 if (entity->isAlive())
865 {
866 oldLevel->tick(entity, false);
867 }
868 }
869 else if (entity->dimension == 0)
870 {
871 xt *= scale;
872 zt *= scale;
873 entity->moveTo(xt, entity->y, zt, entity->yRot, entity->xRot);
874 if (entity->isAlive())
875 {
876 oldLevel->tick(entity, false);
877 }
878 }
879 else
880 {
881 Pos *p;
882
883 if (lastDimension == 1)
884 {
885 // Coming from the end
886 p = newLevel->getSharedSpawnPos();
887 }
888 else
889 {
890 // Going to the end
891 p = newLevel->getDimensionSpecificSpawn();
892 }
893
894 xt = p->x;
895 entity->y = p->y;
896 zt = p->z;
897 delete p;
898 entity->moveTo(xt, entity->y, zt, 90, 0);
899 if (entity->isAlive())
900 {
901 oldLevel->tick(entity, false);
902 }
903 }
904
905 if(entity->GetType() == eTYPE_SERVERPLAYER)
906 {
907 shared_ptr<ServerPlayer> player = dynamic_pointer_cast<ServerPlayer>(entity);
908 removePlayerFromReceiving(player, false, lastDimension);
909 addPlayerToReceiving(player);
910 }
911
912 if (lastDimension != 1)
913 {
914 xt = (double) Mth::clamp((int) xt, -Level::MAX_LEVEL_SIZE + 128, Level::MAX_LEVEL_SIZE - 128);
915 zt = (double) Mth::clamp((int) zt, -Level::MAX_LEVEL_SIZE + 128, Level::MAX_LEVEL_SIZE - 128);
916 if (entity->isAlive())
917 {
918 newLevel->addEntity(entity);
919 entity->moveTo(xt, entity->y, zt, entity->yRot, entity->xRot);
920 newLevel->tick(entity, false);
921 newLevel->cache->autoCreate = true;
922 newLevel->getPortalForcer()->force(entity, xOriginal, yOriginal, zOriginal, yRotOriginal);
923 newLevel->cache->autoCreate = false;
924 }
925 }
926
927 entity->setLevel(newLevel);
928}
929
930void PlayerList::tick()
931{
932 // 4J - brought changes to how often this is sent forward from 1.2.3
933 if (++sendAllPlayerInfoIn > SEND_PLAYER_INFO_INTERVAL)
934 {
935 sendAllPlayerInfoIn = 0;
936 }
937
938 if (sendAllPlayerInfoIn < players.size())
939 {
940 shared_ptr<ServerPlayer> op = players[sendAllPlayerInfoIn];
941 //broadcastAll(shared_ptr<PlayerInfoPacket>( new PlayerInfoPacket(op->name, true, op->latency) ) );
942 if( op->connection->getNetworkPlayer() )
943 {
944 broadcastAll(shared_ptr<PlayerInfoPacket>( new PlayerInfoPacket( op ) ) );
945 }
946 }
947
948 EnterCriticalSection(&m_closePlayersCS);
949 while(!m_smallIdsToClose.empty())
950 {
951 BYTE smallId = m_smallIdsToClose.front();
952 m_smallIdsToClose.pop_front();
953
954 shared_ptr<ServerPlayer> player = nullptr;
955
956 for(unsigned int i = 0; i < players.size(); i++)
957 {
958 shared_ptr<ServerPlayer> p = players.at(i);
959 // 4J Stu - May be being a bit overprotective with all the NULL checks, but adding late in TU7 so want to be safe
960 if (p != NULL && p->connection != NULL && p->connection->connection != NULL && p->connection->connection->getSocket() != NULL && p->connection->connection->getSocket()->getSmallId() == smallId )
961 {
962 player = p;
963 break;
964 }
965 }
966
967 if (player != NULL)
968 {
969 player->connection->disconnect( DisconnectPacket::eDisconnect_Closed );
970 }
971 }
972 LeaveCriticalSection(&m_closePlayersCS);
973
974 EnterCriticalSection(&m_kickPlayersCS);
975 while(!m_smallIdsToKick.empty())
976 {
977 BYTE smallId = m_smallIdsToKick.front();
978 m_smallIdsToKick.pop_front();
979 INetworkPlayer *selectedPlayer = g_NetworkManager.GetPlayerBySmallId(smallId);
980 if( selectedPlayer != NULL )
981 {
982 if( selectedPlayer->IsLocal() != TRUE )
983 {
984 //#ifdef _XBOX
985 PlayerUID xuid = selectedPlayer->GetUID();
986 // Kick this player from the game
987 shared_ptr<ServerPlayer> player = nullptr;
988
989 for(unsigned int i = 0; i < players.size(); i++)
990 {
991 shared_ptr<ServerPlayer> p = players.at(i);
992 PlayerUID playersXuid = p->getOnlineXuid();
993 if (p != NULL && ProfileManager.AreXUIDSEqual(playersXuid, xuid ) )
994 {
995 player = p;
996 break;
997 }
998 }
999
1000 if (player != NULL)
1001 {
1002 m_bannedXuids.push_back( player->getOnlineXuid() );
1003 // 4J Stu - If we have kicked a player, make sure that they have no privileges if they later try to join the world when trust players is off
1004 player->enableAllPlayerPrivileges( false );
1005 player->connection->setWasKicked();
1006 player->connection->send( shared_ptr<DisconnectPacket>( new DisconnectPacket(DisconnectPacket::eDisconnect_Kicked) ));
1007 }
1008 //#endif
1009 }
1010 }
1011 }
1012 LeaveCriticalSection(&m_kickPlayersCS);
1013
1014 // Check our receiving players, and if they are dead see if we can replace them
1015 for(unsigned int dim = 0; dim < 2; ++dim)
1016 {
1017 for(unsigned int i = 0; i < receiveAllPlayers[dim].size(); ++i)
1018 {
1019 shared_ptr<ServerPlayer> currentPlayer = receiveAllPlayers[dim][i];
1020 if(currentPlayer->removed)
1021 {
1022 shared_ptr<ServerPlayer> newPlayer = findAlivePlayerOnSystem(currentPlayer);
1023 if(newPlayer != NULL)
1024 {
1025 receiveAllPlayers[dim][i] = newPlayer;
1026 app.DebugPrintf("Replacing primary player %ls with %ls in dimension %d\n", currentPlayer->name.c_str(), newPlayer->name.c_str(), dim);
1027 }
1028 }
1029 }
1030 }
1031}
1032
1033bool PlayerList::isTrackingTile(int x, int y, int z, int dimension)
1034{
1035 return server->getLevel(dimension)->getChunkMap()->isTrackingTile(x, y, z);
1036}
1037
1038// 4J added - make sure that any tile updates for the chunk at this location get prioritised for sending
1039void PlayerList::prioritiseTileChanges(int x, int y, int z, int dimension)
1040{
1041 server->getLevel(dimension)->getChunkMap()->prioritiseTileChanges(x, y, z);
1042}
1043
1044void PlayerList::broadcastAll(shared_ptr<Packet> packet)
1045{
1046 for (unsigned int i = 0; i < players.size(); i++)
1047 {
1048 shared_ptr<ServerPlayer> player = players[i];
1049 player->connection->send(packet);
1050 }
1051}
1052
1053void PlayerList::broadcastAll(shared_ptr<Packet> packet, int dimension)
1054{
1055 for (unsigned int i = 0; i < players.size(); i++)
1056 {
1057 shared_ptr<ServerPlayer> player = players[i];
1058 if (player->dimension == dimension) player->connection->send(packet);
1059 }
1060}
1061
1062wstring PlayerList::getPlayerNames()
1063{
1064 wstring msg;
1065 for (unsigned int i = 0; i < players.size(); i++)
1066 {
1067 if (i > 0) msg += L", ";
1068 msg += players[i]->name;
1069 }
1070 return msg;
1071}
1072
1073bool PlayerList::isWhiteListed(const wstring& name)
1074{
1075 return true;
1076}
1077
1078bool PlayerList::isOp(const wstring& name)
1079{
1080 return false;
1081}
1082
1083bool PlayerList::isOp(shared_ptr<ServerPlayer> player)
1084{
1085 bool cheatsEnabled = app.GetGameHostOption(eGameHostOption_CheatsEnabled);
1086#ifdef _DEBUG_MENUS_ENABLED
1087 cheatsEnabled = cheatsEnabled || app.GetUseDPadForDebug();
1088#endif
1089 INetworkPlayer *networkPlayer = player->connection->getNetworkPlayer();
1090 bool isOp = cheatsEnabled && (player->isModerator() || (networkPlayer != NULL && networkPlayer->IsHost()));
1091 return isOp;
1092}
1093
1094shared_ptr<ServerPlayer> PlayerList::getPlayer(const wstring& name)
1095{
1096 for (unsigned int i = 0; i < players.size(); i++)
1097 {
1098 shared_ptr<ServerPlayer> p = players[i];
1099 if (p->name == name) // 4J - used to be case insensitive (using equalsIgnoreCase) - imagine we'll be shifting to XUIDs anyway
1100 {
1101 return p;
1102 }
1103 }
1104 return nullptr;
1105}
1106
1107// 4J Added
1108shared_ptr<ServerPlayer> PlayerList::getPlayer(PlayerUID uid)
1109{
1110 for (unsigned int i = 0; i < players.size(); i++)
1111 {
1112 shared_ptr<ServerPlayer> p = players[i];
1113 if (p->getXuid() == uid || p->getOnlineXuid() == uid) // 4J - used to be case insensitive (using equalsIgnoreCase) - imagine we'll be shifting to XUIDs anyway
1114 {
1115 return p;
1116 }
1117 }
1118 return nullptr;
1119}
1120
1121shared_ptr<ServerPlayer> PlayerList::getNearestPlayer(Pos *position, int range)
1122{
1123 if (players.empty()) return nullptr;
1124 if (position == NULL) return players.at(0);
1125 shared_ptr<ServerPlayer> current = nullptr;
1126 double dist = -1;
1127 int rangeSqr = range * range;
1128
1129 for (int i = 0; i < players.size(); i++)
1130 {
1131 shared_ptr<ServerPlayer> next = players.at(i);
1132 double newDist = position->distSqr(next->getCommandSenderWorldPosition());
1133
1134 if ((dist == -1 || newDist < dist) && (range <= 0 || newDist <= rangeSqr))
1135 {
1136 dist = newDist;
1137 current = next;
1138 }
1139 }
1140
1141 return current;
1142}
1143
1144vector<ServerPlayer> *PlayerList::getPlayers(Pos *position, int rangeMin, int rangeMax, int count, int mode, int levelMin, int levelMax, unordered_map<wstring, int> *scoreRequirements, const wstring &playerName, const wstring &teamName, Level *level)
1145{
1146 app.DebugPrintf("getPlayers NOT IMPLEMENTED!");
1147 return NULL;
1148
1149 /*if (players.empty()) return NULL;
1150 vector<shared_ptr<ServerPlayer> > result = new vector<shared_ptr<ServerPlayer> >();
1151 bool reverse = count < 0;
1152 bool playerNameNot = !playerName.empty() && playerName.startsWith("!");
1153 bool teamNameNot = !teamName.empty() && teamName.startsWith("!");
1154 int rangeMinSqr = rangeMin * rangeMin;
1155 int rangeMaxSqr = rangeMax * rangeMax;
1156 count = Mth.abs(count);
1157
1158 if (playerNameNot) playerName = playerName.substring(1);
1159 if (teamNameNot) teamName = teamName.substring(1);
1160
1161 for (int i = 0; i < players.size(); i++) {
1162 ServerPlayer player = players.get(i);
1163
1164 if (level != null && player.level != level) continue;
1165 if (playerName != null) {
1166 if (playerNameNot == playerName.equalsIgnoreCase(player.getAName())) continue;
1167 }
1168 if (teamName != null) {
1169 Team team = player.getTeam();
1170 String actualName = team == null ? "" : team.getName();
1171 if (teamNameNot == teamName.equalsIgnoreCase(actualName)) continue;
1172 }
1173
1174 if (position != null && (rangeMin > 0 || rangeMax > 0)) {
1175 float distance = position.distSqr(player.getCommandSenderWorldPosition());
1176 if (rangeMin > 0 && distance < rangeMinSqr) continue;
1177 if (rangeMax > 0 && distance > rangeMaxSqr) continue;
1178 }
1179
1180 if (!meetsScoreRequirements(player, scoreRequirements)) continue;
1181
1182 if (mode != GameType.NOT_SET.getId() && mode != player.gameMode.getGameModeForPlayer().getId()) continue;
1183 if (levelMin > 0 && player.experienceLevel < levelMin) continue;
1184 if (player.experienceLevel > levelMax) continue;
1185
1186 result.add(player);
1187 }
1188
1189 if (position != null) Collections.sort(result, new PlayerDistanceComparator(position));
1190 if (reverse) Collections.reverse(result);
1191 if (count > 0) result = result.subList(0, Math.min(count, result.size()));
1192
1193 return result;*/
1194}
1195
1196bool PlayerList::meetsScoreRequirements(shared_ptr<Player> player, unordered_map<wstring, int> scoreRequirements)
1197{
1198 app.DebugPrintf("meetsScoreRequirements NOT IMPLEMENTED!");
1199 return false;
1200
1201 //if (scoreRequirements == null || scoreRequirements.size() == 0) return true;
1202
1203 //for (Map.Entry<String, Integer> requirement : scoreRequirements.entrySet()) {
1204 // String name = requirement.getKey();
1205 // boolean min = false;
1206
1207 // if (name.endsWith("_min") && name.length() > 4) {
1208 // min = true;
1209 // name = name.substring(0, name.length() - 4);
1210 // }
1211
1212 // Scoreboard scoreboard = player.getScoreboard();
1213 // Objective objective = scoreboard.getObjective(name);
1214 // if (objective == null) return false;
1215 // Score score = player.getScoreboard().getPlayerScore(player.getAName(), objective);
1216 // int value = score.getScore();
1217
1218 // if (value < requirement.getValue() && min) {
1219 // return false;
1220 // } else if (value > requirement.getValue() && !min) {
1221 // return false;
1222 // }
1223 //}
1224
1225 //return true;
1226}
1227
1228void PlayerList::sendMessage(const wstring& name, const wstring& message)
1229{
1230 shared_ptr<ServerPlayer> player = getPlayer(name);
1231 if (player != NULL)
1232 {
1233 player->connection->send( shared_ptr<ChatPacket>( new ChatPacket(message) ) );
1234 }
1235}
1236
1237void PlayerList::broadcast(double x, double y, double z, double range, int dimension, shared_ptr<Packet> packet)
1238{
1239 broadcast(nullptr, x, y, z, range, dimension, packet);
1240}
1241
1242void PlayerList::broadcast(shared_ptr<Player> except, double x, double y, double z, double range, int dimension, shared_ptr<Packet> packet)
1243{
1244 // 4J - altered so that we don't send to the same machine more than once. Add the source player to the machines we have "sent" to as it doesn't need to go to that
1245 // machine either
1246 vector< shared_ptr<ServerPlayer> > sentTo;
1247 if( except != NULL )
1248 {
1249 sentTo.push_back(dynamic_pointer_cast<ServerPlayer>(except));
1250 }
1251
1252 for (unsigned int i = 0; i < players.size(); i++)
1253 {
1254 shared_ptr<ServerPlayer> p = players[i];
1255 if (p == except) continue;
1256 if (p->dimension != dimension) continue;
1257
1258 // 4J - don't send to the same machine more than once
1259 bool dontSend = false;
1260 if( sentTo.size() )
1261 {
1262 INetworkPlayer *thisPlayer = p->connection->getNetworkPlayer();
1263 if( thisPlayer == NULL )
1264 {
1265 dontSend = true;
1266 }
1267 else
1268 {
1269 for(unsigned int j = 0; j < sentTo.size(); j++ )
1270 {
1271 shared_ptr<ServerPlayer> player2 = sentTo[j];
1272 INetworkPlayer *otherPlayer = player2->connection->getNetworkPlayer();
1273 if( otherPlayer != NULL && thisPlayer->IsSameSystem(otherPlayer) )
1274 {
1275 dontSend = true;
1276 }
1277 }
1278 }
1279 }
1280 if( dontSend )
1281 {
1282 continue;
1283 }
1284
1285
1286 double xd = x - p->x;
1287 double yd = y - p->y;
1288 double zd = z - p->z;
1289 if (xd * xd + yd * yd + zd * zd < range * range)
1290 {
1291#if 0 // _DEBUG
1292 shared_ptr<LevelSoundPacket> SoundPacket= dynamic_pointer_cast<LevelSoundPacket>(packet);
1293
1294 if(SoundPacket)
1295 {
1296
1297 app.DebugPrintf("---broadcast - eSoundType_[%d] ",SoundPacket->getSound());
1298 OutputDebugStringW(ConsoleSoundEngine::wchSoundNames[SoundPacket->getSound()]);
1299 app.DebugPrintf("\n");
1300 }
1301#endif
1302 p->connection->send(packet);
1303 sentTo.push_back( p );
1304 }
1305 }
1306
1307}
1308
1309void PlayerList::saveAll(ProgressListener *progressListener, bool bDeleteGuestMaps /*= false*/)
1310{
1311 if(progressListener != NULL) progressListener->progressStart(IDS_PROGRESS_SAVING_PLAYERS);
1312 // 4J - playerIo can be NULL if we have have to exit a game really early on due to network failure
1313 if(playerIo)
1314 {
1315 playerIo->saveAllCachedData();
1316 for (unsigned int i = 0; i < players.size(); i++)
1317 {
1318 playerIo->save(players[i]);
1319
1320 //4J Stu - We don't want to save the map data for guests, so when we are sure that the player is gone delete the map
1321 if(bDeleteGuestMaps && players[i]->isGuest()) playerIo->deleteMapFilesForPlayer(players[i]);
1322
1323 if(progressListener != NULL) progressListener->progressStagePercentage((i * 100)/ ((int)players.size()));
1324 }
1325 playerIo->clearOldPlayerFiles();
1326 playerIo->saveMapIdLookup();
1327 }
1328}
1329
1330void PlayerList::whiteList(const wstring& playerName)
1331{
1332}
1333
1334void PlayerList::blackList(const wstring& playerName)
1335{
1336}
1337
1338void PlayerList::reloadWhitelist()
1339{
1340}
1341
1342void PlayerList::sendLevelInfo(shared_ptr<ServerPlayer> player, ServerLevel *level)
1343{
1344 player->connection->send( shared_ptr<SetTimePacket>( new SetTimePacket(level->getGameTime(), level->getDayTime(), level->getGameRules()->getBoolean(GameRules::RULE_DAYLIGHT)) ) );
1345 if (level->isRaining())
1346 {
1347 player->connection->send( shared_ptr<GameEventPacket>( new GameEventPacket(GameEventPacket::START_RAINING, 0) ) );
1348 }
1349 else
1350 {
1351 // 4J Stu - Fix for #44836 - Customer Encountered: Out of Sync Weather [A-10]
1352 // If it was raining when the player left the level, and is now not raining we need to make sure that state is updated
1353 player->connection->send( shared_ptr<GameEventPacket>( new GameEventPacket(GameEventPacket::STOP_RAINING, 0) ) );
1354 }
1355
1356 // send the stronghold position if there is one
1357 if((level->dimension->id==0) && level->getLevelData()->getHasStronghold())
1358 {
1359 player->connection->send( shared_ptr<XZPacket>( new XZPacket(XZPacket::STRONGHOLD,level->getLevelData()->getXStronghold(),level->getLevelData()->getZStronghold()) ) );
1360 }
1361}
1362
1363void PlayerList::sendAllPlayerInfo(shared_ptr<ServerPlayer> player)
1364{
1365 player->refreshContainer(player->inventoryMenu);
1366 player->resetSentInfo();
1367 player->connection->send( shared_ptr<SetCarriedItemPacket>( new SetCarriedItemPacket(player->inventory->selected)) );
1368}
1369
1370int PlayerList::getPlayerCount()
1371{
1372 return (int)players.size();
1373}
1374
1375int PlayerList::getPlayerCount(ServerLevel *level)
1376{
1377 int count = 0;
1378
1379 for(AUTO_VAR(it, players.begin()); it != players.end(); ++it)
1380 {
1381 if( (*it)->level == level ) ++count;
1382 }
1383
1384 return count;
1385}
1386
1387int PlayerList::getMaxPlayers()
1388{
1389 return maxPlayers;
1390}
1391
1392MinecraftServer *PlayerList::getServer()
1393{
1394 return server;
1395}
1396
1397int PlayerList::getViewDistance()
1398{
1399 return viewDistance;
1400}
1401
1402void PlayerList::setOverrideGameMode(GameType *gameMode)
1403{
1404 overrideGameMode = gameMode;
1405}
1406
1407void PlayerList::updatePlayerGameMode(shared_ptr<ServerPlayer> newPlayer, shared_ptr<ServerPlayer> oldPlayer, Level *level)
1408{
1409
1410 // reset the player's game mode (first pick from old, then copy level if
1411 // necessary)
1412 if (oldPlayer != NULL)
1413 {
1414 newPlayer->gameMode->setGameModeForPlayer(oldPlayer->gameMode->getGameModeForPlayer());
1415 }
1416 else if (overrideGameMode != NULL)
1417 {
1418 newPlayer->gameMode->setGameModeForPlayer(overrideGameMode);
1419 }
1420 newPlayer->gameMode->updateGameMode(level->getLevelData()->getGameType());
1421}
1422
1423void PlayerList::setAllowCheatsForAllPlayers(bool allowCommands)
1424{
1425 this->allowCheatsForAllPlayers = allowCommands;
1426}
1427
1428shared_ptr<ServerPlayer> PlayerList::findAlivePlayerOnSystem(shared_ptr<ServerPlayer> player)
1429{
1430 int dimIndex, playerDim;
1431 dimIndex = playerDim = player->dimension;
1432 if( dimIndex == -1 ) dimIndex = 1;
1433 else if( dimIndex == 1) dimIndex = 2;
1434
1435 INetworkPlayer *thisPlayer = player->connection->getNetworkPlayer();
1436 if( thisPlayer != NULL )
1437 {
1438 for(AUTO_VAR(itP, players.begin()); itP != players.end(); ++itP)
1439 {
1440 shared_ptr<ServerPlayer> newPlayer = *itP;
1441
1442 INetworkPlayer *otherPlayer = newPlayer->connection->getNetworkPlayer();
1443
1444 if( !newPlayer->removed &&
1445 newPlayer != player &&
1446 newPlayer->dimension == playerDim &&
1447 otherPlayer != NULL &&
1448 otherPlayer->IsSameSystem( thisPlayer )
1449 )
1450 {
1451 return newPlayer;
1452 }
1453 }
1454 }
1455
1456 return nullptr;
1457}
1458
1459void PlayerList::removePlayerFromReceiving(shared_ptr<ServerPlayer> player, bool usePlayerDimension /*= true*/, int dimension /*= 0*/)
1460{
1461 int dimIndex, playerDim;
1462 dimIndex = playerDim = usePlayerDimension ? player->dimension : dimension;
1463 if( dimIndex == -1 ) dimIndex = 1;
1464 else if( dimIndex == 1) dimIndex = 2;
1465
1466#ifndef _CONTENT_PACKAGE
1467 app.DebugPrintf("Requesting remove player %ls as primary in dimension %d\n", player->name.c_str(), dimIndex);
1468#endif
1469 bool playerRemoved = false;
1470
1471 AUTO_VAR(it, find( receiveAllPlayers[dimIndex].begin(), receiveAllPlayers[dimIndex].end(), player));
1472 if( it != receiveAllPlayers[dimIndex].end() )
1473 {
1474#ifndef _CONTENT_PACKAGE
1475 app.DebugPrintf("Remove: Removing player %ls as primary in dimension %d\n", player->name.c_str(), dimIndex);
1476#endif
1477 receiveAllPlayers[dimIndex].erase(it);
1478 playerRemoved = true;
1479 }
1480
1481 INetworkPlayer *thisPlayer = player->connection->getNetworkPlayer();
1482 if( thisPlayer != NULL && playerRemoved )
1483 {
1484 for(AUTO_VAR(itP, players.begin()); itP != players.end(); ++itP)
1485 {
1486 shared_ptr<ServerPlayer> newPlayer = *itP;
1487
1488 INetworkPlayer *otherPlayer = newPlayer->connection->getNetworkPlayer();
1489
1490 if( newPlayer != player &&
1491 newPlayer->dimension == playerDim &&
1492 otherPlayer != NULL &&
1493 otherPlayer->IsSameSystem( thisPlayer )
1494 )
1495 {
1496#ifndef _CONTENT_PACKAGE
1497 app.DebugPrintf("Remove: Adding player %ls as primary in dimension %d\n", newPlayer->name.c_str(), dimIndex);
1498#endif
1499 receiveAllPlayers[dimIndex].push_back( newPlayer );
1500 break;
1501 }
1502 }
1503 }
1504 else if( thisPlayer == NULL )
1505 {
1506#ifndef _CONTENT_PACKAGE
1507 app.DebugPrintf("Remove: Qnet player for %ls was NULL so re-checking all players\n", player->name.c_str() );
1508#endif
1509 // 4J Stu - Something went wrong, or possibly the QNet player left before we got here.
1510 // Re-check all active players and make sure they have someone on their system to receive all packets
1511 for(AUTO_VAR(itP, players.begin()); itP != players.end(); ++itP)
1512 {
1513 shared_ptr<ServerPlayer> newPlayer = *itP;
1514 INetworkPlayer *checkingPlayer = newPlayer->connection->getNetworkPlayer();
1515
1516 if( checkingPlayer != NULL )
1517 {
1518 int newPlayerDim = 0;
1519 if( newPlayer->dimension == -1 ) newPlayerDim = 1;
1520 else if( newPlayer->dimension == 1) newPlayerDim = 2;
1521 bool foundPrimary = false;
1522 for(AUTO_VAR(it, receiveAllPlayers[newPlayerDim].begin()); it != receiveAllPlayers[newPlayerDim].end(); ++it)
1523 {
1524 shared_ptr<ServerPlayer> primaryPlayer = *it;
1525 INetworkPlayer *primPlayer = primaryPlayer->connection->getNetworkPlayer();
1526 if(primPlayer != NULL && checkingPlayer->IsSameSystem( primPlayer ) )
1527 {
1528 foundPrimary = true;
1529 break;
1530 }
1531 }
1532 if(!foundPrimary)
1533 {
1534#ifndef _CONTENT_PACKAGE
1535 app.DebugPrintf("Remove: Adding player %ls as primary in dimension %d\n", newPlayer->name.c_str(), newPlayerDim);
1536#endif
1537 receiveAllPlayers[newPlayerDim].push_back( newPlayer );
1538 }
1539 }
1540 }
1541 }
1542}
1543
1544void PlayerList::addPlayerToReceiving(shared_ptr<ServerPlayer> player)
1545{
1546 int playerDim = 0;
1547 if( player->dimension == -1 ) playerDim = 1;
1548 else if( player->dimension == 1) playerDim = 2;
1549
1550#ifndef _CONTENT_PACKAGE
1551 app.DebugPrintf("Requesting add player %ls as primary in dimension %d\n", player->name.c_str(), playerDim);
1552#endif
1553
1554 bool shouldAddPlayer = true;
1555
1556 INetworkPlayer *thisPlayer = player->connection->getNetworkPlayer();
1557
1558 if( thisPlayer == NULL )
1559 {
1560#ifndef _CONTENT_PACKAGE
1561 app.DebugPrintf("Add: Qnet player for player %ls is NULL so not adding them\n", player->name.c_str() );
1562#endif
1563 shouldAddPlayer = false;
1564 }
1565 else
1566 {
1567 for(AUTO_VAR(it, receiveAllPlayers[playerDim].begin()); it != receiveAllPlayers[playerDim].end(); ++it)
1568 {
1569 shared_ptr<ServerPlayer> oldPlayer = *it;
1570 INetworkPlayer *checkingPlayer = oldPlayer->connection->getNetworkPlayer();
1571 if(checkingPlayer != NULL && checkingPlayer->IsSameSystem( thisPlayer ) )
1572 {
1573 shouldAddPlayer = false;
1574 break;
1575 }
1576 }
1577 }
1578
1579 if( shouldAddPlayer )
1580 {
1581#ifndef _CONTENT_PACKAGE
1582 app.DebugPrintf("Add: Adding player %ls as primary in dimension %d\n", player->name.c_str(), playerDim);
1583#endif
1584 receiveAllPlayers[playerDim].push_back( player );
1585 }
1586}
1587
1588bool PlayerList::canReceiveAllPackets(shared_ptr<ServerPlayer> player)
1589{
1590 int playerDim = 0;
1591 if( player->dimension == -1 ) playerDim = 1;
1592 else if( player->dimension == 1) playerDim = 2;
1593 for(AUTO_VAR(it, receiveAllPlayers[playerDim].begin()); it != receiveAllPlayers[playerDim].end(); ++it)
1594 {
1595 shared_ptr<ServerPlayer> newPlayer = *it;
1596 if(newPlayer == player)
1597 {
1598 return true;
1599 }
1600 }
1601 return false;
1602}
1603
1604void PlayerList::kickPlayerByShortId(BYTE networkSmallId)
1605{
1606 EnterCriticalSection(&m_kickPlayersCS);
1607 m_smallIdsToKick.push_back(networkSmallId);
1608 LeaveCriticalSection(&m_kickPlayersCS);
1609}
1610
1611void PlayerList::closePlayerConnectionBySmallId(BYTE networkSmallId)
1612{
1613 EnterCriticalSection(&m_closePlayersCS);
1614 m_smallIdsToClose.push_back(networkSmallId);
1615 LeaveCriticalSection(&m_closePlayersCS);
1616}
1617
1618bool PlayerList::isXuidBanned(PlayerUID xuid)
1619{
1620 if( xuid == INVALID_XUID ) return false;
1621
1622 bool banned = false;
1623
1624 for( AUTO_VAR(it, m_bannedXuids.begin()); it != m_bannedXuids.end(); ++it )
1625 {
1626 if( ProfileManager.AreXUIDSEqual( xuid, *it ) )
1627 {
1628 banned = true;
1629 break;
1630 }
1631 }
1632
1633 return banned;
1634}
1635
1636// AP added for Vita so the range can be increased once the level starts
1637void PlayerList::setViewDistance(int newViewDistance)
1638{
1639 viewDistance = newViewDistance;
1640}