#include "../Platform/stdafx.h" #include #include "PlayerList.h" #include "PlayerChunkMap.h" #include "../MinecraftServer.h" #include "../GameState/Settings.h" #include "../Level/ServerLevel.h" #include "ServerChunkCache.h" #include "../Player/ServerPlayer.h" #include "../Player/ServerPlayerGameMode.h" #include "ServerConnection.h" #include "PendingConnection.h" #include "PlayerConnection.h" #include "../Player/EntityTracker.h" #include "../../Minecraft.World/Headers/net.minecraft.world.level.storage.h" #include "../../Minecraft.World/Headers/net.minecraft.world.level.dimension.h" #include "../../Minecraft.World/Util/ArrayWithLength.h" #include "../../Minecraft.World/Headers/net.minecraft.network.packet.h" #include "../../Minecraft.World/Headers/net.minecraft.network.h" #include "../../Minecraft.World/Util/Pos.h" #include "../../Minecraft.World/Util/ProgressListener.h" #include "../../Minecraft.World/WorldGen/Sources/HellRandomLevelSource.h" #include "../../Minecraft.World/Headers/net.minecraft.world.phys.h" #include "../../Minecraft.World/Headers/net.minecraft.world.item.h" #include "../../Minecraft.World/Headers/net.minecraft.world.level.storage.h" #include "../../Minecraft.World/Headers/net.minecraft.world.level.saveddata.h" #include "../../Minecraft.World/Util/JavaMath.h" #ifdef _XBOX #include "../Platform/Xbox/Network/NetworkPlayerXbox.h" #elif defined(__PS3__) || defined(__ORBIS__) #include "../Platform/Common/Network/Sony/NetworkPlayerSony.h" #endif // 4J - this class is fairly substantially altered as there didn't seem any // point in porting code for banning, whitelisting, ops etc. PlayerList::PlayerList(MinecraftServer* server) { playerIo = NULL; this->server = server; sendAllPlayerInfoIn = 0; overrideGameMode = NULL; allowCheatsForAllPlayers = false; #ifdef __PSVITA__ viewDistance = 3; #elif defined _LARGE_WORLDS viewDistance = 16; #else viewDistance = 10; #endif // int viewDistance = server->settings->getInt(L"view-distance", 10); maxPlayers = server->settings->getInt(L"max-players", 20); doWhiteList = false; InitializeCriticalSection(&m_kickPlayersCS); InitializeCriticalSection(&m_closePlayersCS); } PlayerList::~PlayerList() { for (AUTO_VAR(it, players.begin()); it < players.end(); it++) { (*it)->connection = nullptr; // Must remove reference to connection, or // else there is a circular dependency delete (*it)->gameMode; // Gamemode also needs deleted as it references // back to this player (*it)->gameMode = NULL; } DeleteCriticalSection(&m_kickPlayersCS); DeleteCriticalSection(&m_closePlayersCS); } void PlayerList::placeNewPlayer(Connection* connection, std::shared_ptr player, std::shared_ptr packet) { bool newPlayer = load(player); player->setLevel(server->getLevel(player->dimension)); player->gameMode->setLevel((ServerLevel*)player->level); // Make sure these privileges are always turned off for the host player INetworkPlayer* networkPlayer = connection->getSocket()->getPlayer(); if (networkPlayer != NULL && networkPlayer->IsHost()) { player->enableAllPlayerPrivileges(true); player->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_HOST, 1); } #if defined(__PS3__) || defined(__ORBIS__) // 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 if (!g_NetworkManager.IsLocalGame()) { if (networkPlayer != NULL) { ((NetworkPlayerSony*)networkPlayer)->SetUID(packet->m_onlineXuid); } } #endif // 4J Stu - TU-1 hotfix // 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 validatePlayerSpawnPosition(player); // logger.info(getName() + " logged in with entity id " + // playerEntity.entityId + " at (" + playerEntity.x + ", " + // playerEntity.y + ", " + playerEntity.z + ")"); ServerLevel* level = server->getLevel(player->dimension); std::uint8_t playerIndex = 0; { bool usedIndexes[MINECRAFT_NET_MAX_PLAYERS]; ZeroMemory(&usedIndexes, MINECRAFT_NET_MAX_PLAYERS * sizeof(bool)); for (AUTO_VAR(it, players.begin()); it < players.end(); ++it) { usedIndexes[(int)(*it)->getPlayerIndex()] = true; } for (unsigned int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i) { if (!usedIndexes[i]) { playerIndex = i; break; } } } player->setPlayerIndex(playerIndex); player->setCustomSkin(packet->m_playerSkinId); player->setCustomCape(packet->m_playerCapeId); // 4J-JEV: Moved this here so we can send player-model texture and geometry // data. std::shared_ptr playerConnection = std::shared_ptr( new PlayerConnection(server, connection, player)); // player->connection = playerConnection; // Used to be assigned in // PlayerConnection ctor but moved out so we can use std::shared_ptr if (newPlayer) { int mapScale = 3; #ifdef _LARGE_WORLDS int scale = MapItemSavedData::MAP_SIZE * 2 * (1 << mapScale); int centreXC = (int)(Math::round(player->x / scale) * scale); int centreZC = (int)(Math::round(player->z / scale) * scale); #else // 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 int centreXC = 0; int centreZC = 0; #endif // 4J Added - Give every player a map the first time they join a server player->inventory->setItem( 9, std::shared_ptr(new ItemInstance( Item::map_Id, 1, level->getAuxValueForMap(player->getXuid(), 0, centreXC, centreZC, mapScale)))); if (app.getGameRuleDefinitions() != NULL) { app.getGameRuleDefinitions()->postProcessPlayer(player); } } if (!player->customTextureUrl.empty() && player->customTextureUrl.substr(0, 3).compare(L"def") != 0 && !app.IsFileInMemoryTextures(player->customTextureUrl)) { if (server->getConnection()->addPendingTextureRequest( player->customTextureUrl)) { #ifndef _CONTENT_PACKAGE wprintf( L"Sending texture packet to get custom skin %ls from player " L"%ls\n", player->customTextureUrl.c_str(), player->name.c_str()); #endif playerConnection->send(std::shared_ptr( new TextureAndGeometryPacket(player->customTextureUrl, NULL, 0))); } } else if (!player->customTextureUrl.empty() && app.IsFileInMemoryTextures(player->customTextureUrl)) { // Update the ref count on the memory texture data app.AddMemoryTextureFile(player->customTextureUrl, NULL, 0); } if (!player->customTextureUrl2.empty() && player->customTextureUrl2.substr(0, 3).compare(L"def") != 0 && !app.IsFileInMemoryTextures(player->customTextureUrl2)) { if (server->getConnection()->addPendingTextureRequest( player->customTextureUrl2)) { #ifndef _CONTENT_PACKAGE wprintf( L"Sending texture packet to get custom skin %ls from player " L"%ls\n", player->customTextureUrl2.c_str(), player->name.c_str()); #endif playerConnection->send(std::shared_ptr( new TexturePacket(player->customTextureUrl2, NULL, 0))); } } else if (!player->customTextureUrl2.empty() && app.IsFileInMemoryTextures(player->customTextureUrl2)) { // Update the ref count on the memory texture data app.AddMemoryTextureFile(player->customTextureUrl2, NULL, 0); } player->setIsGuest(packet->m_isGuest); Pos* spawnPos = level->getSharedSpawnPos(); updatePlayerGameMode(player, nullptr, level); // Update the privileges with the correct game mode GameType* gameType = Player::getPlayerGamePrivilege( player->getAllPlayerGamePrivileges(), Player::ePlayerGamePrivilege_CreativeMode) ? GameType::CREATIVE : GameType::SURVIVAL; gameType = LevelSettings::validateGameType(gameType->getId()); if (player->gameMode->getGameModeForPlayer() != gameType) { player->setPlayerGamePrivilege( Player::ePlayerGamePrivilege_CreativeMode, player->gameMode->getGameModeForPlayer()->getId()); } // std::shared_ptr playerConnection = // std::shared_ptr(new PlayerConnection(server, // connection, player)); player->connection = playerConnection; // Used to be assigned in PlayerConnection ctor but // moved out so we can use std::shared_ptr // 4J Added to store UGC settings playerConnection->m_friendsOnlyUGC = packet->m_friendsOnlyUGC; playerConnection->m_offlineXUID = packet->m_offlineXuid; playerConnection->m_onlineXUID = packet->m_onlineXuid; // This player is now added to the list, so incrementing this value // invalidates all previous PreLogin packets if (packet->m_friendsOnlyUGC) ++server->m_ugcPlayersVersion; addPlayerToReceiving(player); playerConnection->send(std::shared_ptr(new LoginPacket( L"", player->entityId, level->getLevelData()->getGenerator(), level->getSeed(), player->gameMode->getGameModeForPlayer()->getId(), (std::uint8_t)level->dimension->id, (std::uint8_t)level->getMaxBuildHeight(), (std::uint8_t)getMaxPlayers(), level->difficulty, TelemetryManager->GetMultiplayerInstanceID(), playerIndex, level->useNewSeaLevel(), player->getAllPlayerGamePrivileges(), level->getLevelData()->getXZSize(), level->getLevelData()->getHellScale()))); playerConnection->send(std::shared_ptr( new SetSpawnPositionPacket(spawnPos->x, spawnPos->y, spawnPos->z))); playerConnection->send(std::shared_ptr( new PlayerAbilitiesPacket(&player->abilities))); delete spawnPos; sendLevelInfo(player, level); // 4J-PB - removed, since it needs to be localised in the language the // client is in // server->players->broadcastAll( std::shared_ptr( new // ChatPacket(L"§e" + playerEntity->name + L" joined the game.") ) ); broadcastAll(std::shared_ptr( new ChatPacket(player->name, ChatPacket::e_ChatPlayerJoinedGame))); MemSect(14); add(player); MemSect(0); 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... playerConnection->teleport(player->x, player->y, player->z, player->yRot, player->xRot); server->getConnection()->addPlayerConnection(playerConnection); playerConnection->send( std::shared_ptr(new SetTimePacket(level->getTime()))); AUTO_VAR(activeEffects, player->getActiveEffects()); for (AUTO_VAR(it, activeEffects->begin()); it != activeEffects->end(); ++it) { MobEffectInstance* effect = *it; playerConnection->send(std::shared_ptr( new UpdateMobEffectPacket(player->entityId, effect))); } player->initMenu(); // If we are joining at the same time as someone in the end on this system // is travelling through the win portal, then we should set our wonGame flag // to true so that respawning works when the EndPoem is closed INetworkPlayer* thisPlayer = player->connection->getNetworkPlayer(); if (thisPlayer != NULL) { for (AUTO_VAR(it, players.begin()); it != players.end(); ++it) { std::shared_ptr servPlayer = *it; INetworkPlayer* checkPlayer = servPlayer->connection->getNetworkPlayer(); if (thisPlayer != checkPlayer && checkPlayer != NULL && thisPlayer->IsSameSystem(checkPlayer) && servPlayer->wonGame) { player->wonGame = true; break; } } } } void PlayerList::setLevel(ServerLevelArray levels) { playerIo = levels[0]->getLevelStorage()->getPlayerIO(); } void PlayerList::changeDimension(std::shared_ptr player, ServerLevel* from) { ServerLevel* to = player->getLevel(); if (from != NULL) from->getChunkMap()->remove(player); to->getChunkMap()->add(player); to->cache->create(((int)player->x) >> 4, ((int)player->z) >> 4); } int PlayerList::getMaxRange() { return PlayerChunkMap::convertChunkRangeToBlock(getViewDistance()); } // 4J Changed return val to bool to check if new player or loaded player bool PlayerList::load(std::shared_ptr player) { return playerIo->load(player); } void PlayerList::save(std::shared_ptr player) { playerIo->save(player); } // 4J Stu - TU-1 hotifx // Add this function to take some of the code from the PlayerList::add function // with the fixes for checking spawn area, especially in the nether. These // needed to be done in a different order from before 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 void PlayerList::validatePlayerSpawnPosition( std::shared_ptr player) { // 4J Stu - Some adjustments to make sure the current players position is // correct Make sure that the player is on the ground, and in the centre x/z // of the current column app.DebugPrintf("Original pos is %f, %f, %f in dimension %d\n", player->x, player->y, player->z, player->dimension); double targetX = 0; if (player->x < 0) targetX = Mth::ceil(player->x) - 0.5; else targetX = Mth::floor(player->x) + 0.5; double targetY = floor(player->y); double targetZ = 0; if (player->z < 0) targetZ = Mth::ceil(player->z) - 0.5; else targetZ = Mth::floor(player->z) + 0.5; player->setPos(targetX, targetY, targetZ); app.DebugPrintf("New pos is %f, %f, %f in dimension %d\n", player->x, player->y, player->z, player->dimension); ServerLevel* level = server->getLevel(player->dimension); while (level->getCubes(player, player->bb)->size() != 0) { player->setPos(player->x, player->y + 1, player->z); } app.DebugPrintf("Final pos is %f, %f, %f in dimension %d\n", player->x, player->y, player->z, player->dimension); // 4J Stu - If we are in the nether and the above while loop has put us // above the nether then we have a problem Finding a valid, safe spawn point // is potentially computationally expensive (may have to hunt through a // large part of the nether) so move the player to their spawn position in // the overworld so that they do not lose their inventory 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 if (level->dimension->id == -1 && player->y > 125) { app.DebugPrintf( "Player in the nether tried to spawn at y = %f, moving to " "overworld\n", player->y); player->setLevel(server->getLevel(0)); player->gameMode->setLevel(server->getLevel(0)); player->dimension = 0; level = server->getLevel(player->dimension); Pos* levelSpawn = level->getSharedSpawnPos(); player->setPos(levelSpawn->x, levelSpawn->y, levelSpawn->z); delete levelSpawn; Pos* bedPosition = player->getRespawnPosition(); if (bedPosition != NULL) { Pos* respawnPosition = Player::checkBedValidRespawnPosition( server->getLevel(player->dimension), bedPosition); if (respawnPosition != NULL) { player->moveTo(respawnPosition->x + 0.5f, respawnPosition->y + 0.1f, respawnPosition->z + 0.5f, 0, 0); player->setRespawnPosition(bedPosition); } delete bedPosition; } while (level->getCubes(player, player->bb)->size() != 0) { player->setPos(player->x, player->y + 1, player->z); } app.DebugPrintf("Updated pos is %f, %f, %f in dimension %d\n", player->x, player->y, player->z, player->dimension); } } void PlayerList::add(std::shared_ptr player) { // broadcastAll(std::shared_ptr( new // PlayerInfoPacket(player->name, true, 1000) ) ); if (player->connection->getNetworkPlayer()) { broadcastAll( std::shared_ptr(new PlayerInfoPacket(player))); } players.push_back(player); // 4J Added addPlayerToReceiving(player); // Ensure the area the player is spawning in is loaded! ServerLevel* level = server->getLevel(player->dimension); // 4J Stu - TU-1 hotfix // 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 Some code from here has been moved to the above // validatePlayerSpawnPosition function // 4J Stu - Swapped these lines about so that we get the chunk visiblity // packet way ahead of all the add tracked entity packets Fix for #9169 - // ART : Sign text is replaced with the words “Awaiting approval”. changeDimension(player, NULL); level->addEntity(player); for (int i = 0; i < players.size(); i++) { std::shared_ptr op = players.at(i); // player->connection->send(std::shared_ptr( new // PlayerInfoPacket(op->name, true, op->latency) ) ); if (op->connection->getNetworkPlayer()) { player->connection->send( std::shared_ptr(new PlayerInfoPacket(op))); } } if (level->isAtLeastOnePlayerSleeping()) { std::shared_ptr firstSleepingPlayer = nullptr; for (unsigned int i = 0; i < players.size(); i++) { std::shared_ptr thisPlayer = players[i]; if (thisPlayer->isSleeping()) { if (firstSleepingPlayer == NULL) firstSleepingPlayer = thisPlayer; thisPlayer->connection->send( std::shared_ptr(new ChatPacket( thisPlayer->name, ChatPacket::e_ChatBedMeSleep))); } } player->connection->send(std::shared_ptr(new ChatPacket( firstSleepingPlayer->name, ChatPacket::e_ChatBedPlayerSleep))); } } void PlayerList::move(std::shared_ptr player) { player->getLevel()->getChunkMap()->move(player); } void PlayerList::remove(std::shared_ptr player) { save(player); // 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 if (player->isGuest()) playerIo->deleteMapFilesForPlayer(player); ServerLevel* level = player->getLevel(); level->removeEntity(player); level->getChunkMap()->remove(player); AUTO_VAR(it, find(players.begin(), players.end(), player)); if (it != players.end()) { players.erase(it); } // broadcastAll(std::shared_ptr( new // PlayerInfoPacket(player->name, false, 9999) ) ); removePlayerFromReceiving(player); player->connection = nullptr; // Must remove reference to connection, or // else there is a circular dependency delete player->gameMode; // Gamemode also needs deleted as it references // back to this player player->gameMode = NULL; // 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 saveAll(NULL, false); } std::shared_ptr PlayerList::getPlayerForLogin( PendingConnection* pendingConnection, const std::wstring& userName, PlayerUID xuid, PlayerUID onlineXuid) { if (players.size() >= maxPlayers) { pendingConnection->disconnect(DisconnectPacket::eDisconnect_ServerFull); return std::shared_ptr(); } std::shared_ptr player = std::shared_ptr( new ServerPlayer(server, server->getLevel(0), userName, new ServerPlayerGameMode(server->getLevel(0)))); player->gameMode->player = player; // 4J added as had to remove this // assignment from ServerPlayer ctor player->setXuid(xuid); // 4J Added player->setOnlineXuid(onlineXuid); // 4J Added // Work out the base server player settings INetworkPlayer* networkPlayer = pendingConnection->connection->getSocket()->getPlayer(); if (networkPlayer != NULL && !networkPlayer->IsHost()) { player->enableAllPlayerPrivileges( app.GetGameHostOption(eGameHostOption_TrustPlayers) > 0); } // 4J Added LevelRuleset* serverRuleDefs = app.getGameRuleDefinitions(); if (serverRuleDefs != NULL) { player->gameMode->setGameRules( GameRuleDefinition::generateNewGameRulesInstance( GameRulesInstance::eGameRulesInstanceType_ServerPlayer, serverRuleDefs, pendingConnection->connection)); } return player; } std::shared_ptr PlayerList::respawn( std::shared_ptr serverPlayer, int targetDimension, bool keepAllPlayerData) { // 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. bool isPrimary = canReceiveAllPackets( serverPlayer); // Is this the primary player in its current dimension? int oldDimension = serverPlayer->dimension; bool isEmptying = (targetDimension != oldDimension); // We're not emptying this dimension on this machine if // this player is going back into the same dimension // Also consider if there is another player on this machine which is in the // same dimension and can take over as primary player if (isEmptying) { INetworkPlayer* thisPlayer = serverPlayer->connection->getNetworkPlayer(); for (unsigned int i = 0; i < players.size(); i++) { std::shared_ptr ep = players[i]; if (ep == serverPlayer) continue; if (ep->dimension != oldDimension) continue; INetworkPlayer* otherPlayer = ep->connection->getNetworkPlayer(); if (otherPlayer != NULL && thisPlayer->IsSameSystem(otherPlayer)) { // There's another player here in the same dimension - we're not // the last one out isEmptying = false; } } } // Now we know where we stand, the actions to take are as follows: // (1) if this isn't the primary player, then we just need to remove it from // the entity tracker (2) if this Is the primary player then: // (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) (b) if isEmptying is false, then we'll be //transferring control of entity tracking to another player if (isPrimary) { if (isEmptying) { app.DebugPrintf("Emptying this dimension\n"); serverPlayer->getLevel()->getTracker()->clear(serverPlayer); } else { app.DebugPrintf("Transferring... storing flags\n"); serverPlayer->getLevel()->getTracker()->removeEntity(serverPlayer); } } else { app.DebugPrintf("Not primary player\n"); serverPlayer->getLevel()->getTracker()->removeEntity(serverPlayer); } serverPlayer->getLevel()->getChunkMap()->remove(serverPlayer); AUTO_VAR(it, find(players.begin(), players.end(), serverPlayer)); if (it != players.end()) { players.erase(it); } server->getLevel(serverPlayer->dimension) ->removeEntityImmediately(serverPlayer); Pos* bedPosition = serverPlayer->getRespawnPosition(); removePlayerFromReceiving(serverPlayer); serverPlayer->dimension = targetDimension; EDefaultSkins skin = serverPlayer->getPlayerDefaultSkin(); std::uint8_t playerIndex = serverPlayer->getPlayerIndex(); PlayerUID playerXuid = serverPlayer->getXuid(); PlayerUID playerOnlineXuid = serverPlayer->getOnlineXuid(); std::shared_ptr player = std::shared_ptr( new ServerPlayer(server, server->getLevel(serverPlayer->dimension), serverPlayer->name, new ServerPlayerGameMode( server->getLevel(serverPlayer->dimension)))); player->restoreFrom(serverPlayer, keepAllPlayerData); if (keepAllPlayerData) { // 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 player->inventory->selected = serverPlayer->inventory->selected; } player->gameMode->player = player; // 4J added as had to remove this // assignment from ServerPlayer ctor player->setXuid(playerXuid); // 4J Added player->setOnlineXuid(playerOnlineXuid); // 4J Added // 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 // player->entityId = serverPlayer->entityId; player->connection = serverPlayer->connection; player->setPlayerDefaultSkin(skin); player->setIsGuest(serverPlayer->isGuest()); player->setPlayerIndex(playerIndex); player->setCustomSkin(serverPlayer->getCustomSkin()); player->setCustomCape(serverPlayer->getCustomCape()); player->setPlayerGamePrivilege(Player::ePlayerGamePrivilege_All, serverPlayer->getAllPlayerGamePrivileges()); player->gameMode->setGameRules(serverPlayer->gameMode->getGameRules()); player->dimension = targetDimension; // 4J Stu - Added this as we need to know earlier if the player is the // player for this connection so that we can work out if they are the // primary for the system and can receive all packets player->connection->setPlayer(player); addPlayerToReceiving(player); ServerLevel* level = server->getLevel(serverPlayer->dimension); // reset the player's game mode (first pick from old, then copy level if // necessary) updatePlayerGameMode(player, serverPlayer, level); if (serverPlayer->wonGame && targetDimension == oldDimension && serverPlayer->getHealth() > 0) { // 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 player->moveTo(serverPlayer->x, serverPlayer->y, serverPlayer->z, serverPlayer->yRot, serverPlayer->xRot); if (bedPosition != NULL) { player->setRespawnPosition(bedPosition); delete bedPosition; } // 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 player->inventory->selected = serverPlayer->inventory->selected; } else if (bedPosition != NULL) { Pos* respawnPosition = Player::checkBedValidRespawnPosition( server->getLevel(serverPlayer->dimension), bedPosition); if (respawnPosition != NULL) { player->moveTo(respawnPosition->x + 0.5f, respawnPosition->y + 0.1f, respawnPosition->z + 0.5f, 0, 0); player->setRespawnPosition(bedPosition); } else { player->connection->send( std::shared_ptr(new GameEventPacket( GameEventPacket::NO_RESPAWN_BED_AVAILABLE, 0))); } delete bedPosition; } // Ensure the area the player is spawning in is loaded! level->cache->create(((int)player->x) >> 4, ((int)player->z) >> 4); while (!level->getCubes(player, player->bb)->empty()) { player->setPos(player->x, player->y + 1, player->z); } player->connection->send(std::shared_ptr(new RespawnPacket( (char)player->dimension, player->level->getSeed(), player->level->getMaxBuildHeight(), player->gameMode->getGameModeForPlayer(), level->difficulty, level->getLevelData()->getGenerator(), player->level->useNewSeaLevel(), player->entityId, level->getLevelData()->getXZSize(), level->getLevelData()->getHellScale()))); player->connection->teleport(player->x, player->y, player->z, player->yRot, player->xRot); if (keepAllPlayerData) { std::vector* activeEffects = player->getActiveEffects(); for (AUTO_VAR(it, activeEffects->begin()); it != activeEffects->end(); ++it) { MobEffectInstance* effect = *it; player->connection->send(std::shared_ptr( new UpdateMobEffectPacket(player->entityId, effect))); } delete activeEffects; player->getEntityData()->markDirty(Mob::DATA_EFFECT_COLOR_ID); } sendLevelInfo(player, level); level->getChunkMap()->add(player); level->addEntity(player); players.push_back(player); player->initMenu(); // 4J-JEV - Dying before this point in the tutorial is pretty annoying, // making sure to remove health/hunger and give you back your meat. if (Minecraft::GetInstance()->isTutorial() && (!Minecraft::GetInstance()->gameMode->getTutorial()->isStateCompleted( e_Tutorial_State_Food_Bar))) { app.getGameRuleDefinitions()->postProcessPlayer(player); } if (oldDimension == 1 && player->dimension != 1) { player->displayClientMessage(IDS_PLAYER_LEFT_END); } return player; } void PlayerList::toggleDimension(std::shared_ptr player, int targetDimension) { int lastDimension = player->dimension; // 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. bool isPrimary = canReceiveAllPackets( player); // Is this the primary player in its current dimension? bool isEmptying = true; // Also consider if there is another player on this machine which is in the // same dimension and can take over as primary player INetworkPlayer* thisPlayer = player->connection->getNetworkPlayer(); for (unsigned int i = 0; i < players.size(); i++) { std::shared_ptr ep = players[i]; if (ep == player) continue; if (ep->dimension != lastDimension) continue; INetworkPlayer* otherPlayer = ep->connection->getNetworkPlayer(); if (otherPlayer != NULL && thisPlayer->IsSameSystem(otherPlayer)) { // There's another player here in the same dimension - we're not the // last one out isEmptying = false; } } // Now we know where we stand, the actions to take are as follows: // (1) if this isn't the primary player, then we just need to remove it from // the entity tracker (2) if this Is the primary player then: // (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) (b) if isEmptying is false, then we'll be //transferring control of entity tracking to another player if (isPrimary) { if (isEmptying) { app.DebugPrintf("Toggle... Emptying this dimension\n"); player->getLevel()->getTracker()->clear(player); } else { app.DebugPrintf("Toggle... transferring\n"); player->getLevel()->getTracker()->removeEntity(player); } } else { app.DebugPrintf("Toggle... Not primary player\n"); player->getLevel()->getTracker()->removeEntity(player); } ServerLevel* oldLevel = server->getLevel(player->dimension); // 4J Stu - Do this much earlier so we don't end up unloading chunks in the // wrong dimension player->getLevel()->getChunkMap()->remove(player); if (player->dimension != 1 && targetDimension == 1) { player->displayClientMessage(IDS_PLAYER_ENTERED_END); } else if (player->dimension == 1) { player->displayClientMessage(IDS_PLAYER_LEFT_END); } player->dimension = targetDimension; ServerLevel* newLevel = server->getLevel(player->dimension); // 4J Stu - Fix for #46423 - TU5: Art: Code: No burning animation visible // after entering The Nether while burning player->clearFire(); // Stop burning if travelling through a portal // 4J Stu Added so that we remove entities from the correct level, after the // respawn packet we will be in the wrong level player->flushEntitiesToRemove(); player->connection->send(std::shared_ptr(new RespawnPacket( (char)player->dimension, newLevel->getSeed(), newLevel->getMaxBuildHeight(), player->gameMode->getGameModeForPlayer(), newLevel->difficulty, newLevel->getLevelData()->getGenerator(), newLevel->useNewSeaLevel(), player->entityId, newLevel->getLevelData()->getXZSize(), newLevel->getLevelData()->getHellScale()))); oldLevel->removeEntityImmediately(player); player->removed = false; double xt = player->x; double zt = player->z; double scale = newLevel->getLevelData() ->getHellScale(); // 4J Scale was 8 but this is all we can fit in if (player->dimension == -1) { xt /= scale; zt /= scale; player->moveTo(xt, player->y, zt, player->yRot, player->xRot); if (player->isAlive()) { oldLevel->tick(player, false); } } else if (player->dimension == 0) { xt *= scale; zt *= scale; player->moveTo(xt, player->y, zt, player->yRot, player->xRot); if (player->isAlive()) { oldLevel->tick(player, false); } } else { Pos* p = newLevel->getDimensionSpecificSpawn(); xt = p->x; player->y = p->y; zt = p->z; delete p; player->moveTo(xt, player->y, zt, 90, 0); if (player->isAlive()) { oldLevel->tick(player, false); } } removePlayerFromReceiving(player, false, lastDimension); addPlayerToReceiving(player); if (lastDimension == 1) { } else { xt = (double)Mth::clamp((int)xt, -Level::MAX_LEVEL_SIZE + 128, Level::MAX_LEVEL_SIZE - 128); zt = (double)Mth::clamp((int)zt, -Level::MAX_LEVEL_SIZE + 128, Level::MAX_LEVEL_SIZE - 128); if (player->isAlive()) { newLevel->addEntity(player); player->moveTo(xt, player->y, zt, player->yRot, player->xRot); newLevel->tick(player, false); newLevel->cache->autoCreate = true; (new PortalForcer())->force(newLevel, player); newLevel->cache->autoCreate = false; } } player->setLevel(newLevel); changeDimension(player, oldLevel); player->gameMode->setLevel(newLevel); // Resend the teleport if we haven't yet sent the chunk they will land on if (!g_NetworkManager.SystemFlagGet( player->connection->getNetworkPlayer(), ServerPlayer::getFlagIndexForChunk( ChunkPos(player->xChunk, player->zChunk), player->level->dimension->id))) { player->connection->teleport(player->x, player->y, player->z, player->yRot, player->xRot, false); // Force sending of the current chunk player->doTick(true, true, true); } player->connection->teleport(player->x, player->y, player->z, player->yRot, player->xRot); // 4J Stu - Fix for #64683 - Customer Encountered: TU7: Content: Gameplay: // Potion effects are removed after using the Nether Portal std::vector* activeEffects = player->getActiveEffects(); for (AUTO_VAR(it, activeEffects->begin()); it != activeEffects->end(); ++it) { MobEffectInstance* effect = *it; player->connection->send(std::shared_ptr( new UpdateMobEffectPacket(player->entityId, effect))); } delete activeEffects; player->getEntityData()->markDirty(Mob::DATA_EFFECT_COLOR_ID); sendLevelInfo(player, newLevel); sendAllPlayerInfo(player); } void PlayerList::tick() { // 4J - brought changes to how often this is sent forward from 1.2.3 if (++sendAllPlayerInfoIn > SEND_PLAYER_INFO_INTERVAL) { sendAllPlayerInfoIn = 0; } if (sendAllPlayerInfoIn < players.size()) { std::shared_ptr op = players[sendAllPlayerInfoIn]; // broadcastAll(std::shared_ptr( new // PlayerInfoPacket(op->name, true, op->latency) ) ); if (op->connection->getNetworkPlayer()) { broadcastAll( std::shared_ptr(new PlayerInfoPacket(op))); } } EnterCriticalSection(&m_closePlayersCS); while (!m_smallIdsToClose.empty()) { std::uint8_t smallId = m_smallIdsToClose.front(); m_smallIdsToClose.pop_front(); std::shared_ptr player = nullptr; for (unsigned int i = 0; i < players.size(); i++) { std::shared_ptr p = players.at(i); // 4J Stu - May be being a bit overprotective with all the NULL // checks, but adding late in TU7 so want to be safe if (p != NULL && p->connection != NULL && p->connection->connection != NULL && p->connection->connection->getSocket() != NULL && p->connection->connection->getSocket()->getSmallId() == smallId) { player = p; break; } } if (player != NULL) { player->connection->disconnect( DisconnectPacket::eDisconnect_Closed); } } LeaveCriticalSection(&m_closePlayersCS); EnterCriticalSection(&m_kickPlayersCS); while (!m_smallIdsToKick.empty()) { std::uint8_t smallId = m_smallIdsToKick.front(); m_smallIdsToKick.pop_front(); INetworkPlayer* selectedPlayer = g_NetworkManager.GetPlayerBySmallId(smallId); if (selectedPlayer != NULL) { if (selectedPlayer->IsLocal() != TRUE) { // #ifdef _XBOX PlayerUID xuid = selectedPlayer->GetUID(); // Kick this player from the game std::shared_ptr player = nullptr; for (unsigned int i = 0; i < players.size(); i++) { std::shared_ptr p = players.at(i); PlayerUID playersXuid = p->getOnlineXuid(); if (p != NULL && ProfileManager.AreXUIDSEqual(playersXuid, xuid)) { player = p; break; } } if (player != NULL) { m_bannedXuids.push_back(player->getOnlineXuid()); // 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 player->enableAllPlayerPrivileges(false); player->connection->setWasKicked(); player->connection->send( std::shared_ptr(new DisconnectPacket( DisconnectPacket::eDisconnect_Kicked))); } // #endif } } } LeaveCriticalSection(&m_kickPlayersCS); // Check our receiving players, and if they are dead see if we can replace // them for (unsigned int dim = 0; dim < 2; ++dim) { for (unsigned int i = 0; i < receiveAllPlayers[dim].size(); ++i) { std::shared_ptr currentPlayer = receiveAllPlayers[dim][i]; if (currentPlayer->removed) { std::shared_ptr newPlayer = findAlivePlayerOnSystem(currentPlayer); if (newPlayer != NULL) { receiveAllPlayers[dim][i] = newPlayer; app.DebugPrintf( "Replacing primary player %ls with %ls in dimension " "%d\n", currentPlayer->name.c_str(), newPlayer->name.c_str(), dim); } } } } } bool PlayerList::isTrackingTile(int x, int y, int z, int dimension) { return server->getLevel(dimension)->getChunkMap()->isTrackingTile(x, y, z); } // 4J added - make sure that any tile updates for the chunk at this location get // prioritised for sending void PlayerList::prioritiseTileChanges(int x, int y, int z, int dimension) { server->getLevel(dimension)->getChunkMap()->prioritiseTileChanges(x, y, z); } void PlayerList::broadcastAll(std::shared_ptr packet) { for (unsigned int i = 0; i < players.size(); i++) { std::shared_ptr player = players[i]; player->connection->send(packet); } } void PlayerList::broadcastAll(std::shared_ptr packet, int dimension) { for (unsigned int i = 0; i < players.size(); i++) { std::shared_ptr player = players[i]; if (player->dimension == dimension) player->connection->send(packet); } } std::wstring PlayerList::getPlayerNames() { std::wstring msg; for (unsigned int i = 0; i < players.size(); i++) { if (i > 0) msg += L", "; msg += players[i]->name; } return msg; } bool PlayerList::isWhiteListed(const std::wstring& name) { return true; } bool PlayerList::isOp(const std::wstring& name) { return false; } bool PlayerList::isOp(std::shared_ptr player) { bool cheatsEnabled = app.GetGameHostOption(eGameHostOption_CheatsEnabled); #ifdef _DEBUG_MENUS_ENABLED cheatsEnabled = cheatsEnabled || app.GetUseDPadForDebug(); #endif INetworkPlayer* networkPlayer = player->connection->getNetworkPlayer(); bool isOp = cheatsEnabled && (player->isModerator() || (networkPlayer != NULL && networkPlayer->IsHost())); return isOp; } std::shared_ptr PlayerList::getPlayer(const std::wstring& name) { for (unsigned int i = 0; i < players.size(); i++) { std::shared_ptr p = players[i]; if (p->name == name) // 4J - used to be case insensitive (using equalsIgnoreCase) // - imagine we'll be shifting to XUIDs anyway { return p; } } return nullptr; } // 4J Added std::shared_ptr PlayerList::getPlayer(PlayerUID uid) { for (unsigned int i = 0; i < players.size(); i++) { std::shared_ptr p = players[i]; if (p->getXuid() == uid || p->getOnlineXuid() == uid) // 4J - used to be case insensitive // (using equalsIgnoreCase) - imagine // we'll be shifting to XUIDs anyway { return p; } } return nullptr; } void PlayerList::sendMessage(const std::wstring& name, const std::wstring& message) { std::shared_ptr player = getPlayer(name); if (player != NULL) { player->connection->send( std::shared_ptr(new ChatPacket(message))); } } void PlayerList::broadcast(double x, double y, double z, double range, int dimension, std::shared_ptr packet) { broadcast(nullptr, x, y, z, range, dimension, packet); } void PlayerList::broadcast(std::shared_ptr except, double x, double y, double z, double range, int dimension, std::shared_ptr packet) { // 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 machine either std::vector > sentTo; if (except != NULL) { sentTo.push_back(std::dynamic_pointer_cast(except)); } for (unsigned int i = 0; i < players.size(); i++) { std::shared_ptr p = players[i]; if (p == except) continue; if (p->dimension != dimension) continue; // 4J - don't send to the same machine more than once bool dontSend = false; if (sentTo.size()) { INetworkPlayer* thisPlayer = p->connection->getNetworkPlayer(); if (thisPlayer == NULL) { dontSend = true; } else { for (unsigned int j = 0; j < sentTo.size(); j++) { std::shared_ptr player2 = sentTo[j]; INetworkPlayer* otherPlayer = player2->connection->getNetworkPlayer(); if (otherPlayer != NULL && thisPlayer->IsSameSystem(otherPlayer)) { dontSend = true; } } } } if (dontSend) { continue; } double xd = x - p->x; double yd = y - p->y; double zd = z - p->z; if (xd * xd + yd * yd + zd * zd < range * range) { #if 0 // _DEBUG std::shared_ptr SoundPacket= std::dynamic_pointer_cast(packet); if(SoundPacket) { app.DebugPrintf("---broadcast - eSoundType_[%d] ",SoundPacket->getSound()); OutputDebugStringW(ConsoleSoundEngine::wchSoundNames[SoundPacket->getSound()]); app.DebugPrintf("\n"); } #endif p->connection->send(packet); sentTo.push_back(p); } } } void PlayerList::broadcastToAllOps(const std::wstring& message) { std::shared_ptr chatPacket = std::shared_ptr(new ChatPacket(message)); for (unsigned int i = 0; i < players.size(); i++) { std::shared_ptr p = players[i]; if (isOp(p->name)) { p->connection->send(chatPacket); } } } bool PlayerList::sendTo(const std::wstring& name, std::shared_ptr packet) { std::shared_ptr player = getPlayer(name); if (player != NULL) { player->connection->send(packet); return true; } return false; } void PlayerList::saveAll(ProgressListener* progressListener, bool bDeleteGuestMaps /*= false*/) { if (progressListener != NULL) progressListener->progressStart(IDS_PROGRESS_SAVING_PLAYERS); // 4J - playerIo can be NULL if we have have to exit a game really early on // due to network failure if (playerIo) { playerIo->saveAllCachedData(); for (unsigned int i = 0; i < players.size(); i++) { playerIo->save(players[i]); // 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 if (bDeleteGuestMaps && players[i]->isGuest()) playerIo->deleteMapFilesForPlayer(players[i]); if (progressListener != NULL) progressListener->progressStagePercentage( (i * 100) / ((int)players.size())); } playerIo->clearOldPlayerFiles(); playerIo->saveMapIdLookup(); } } void PlayerList::whiteList(const std::wstring& playerName) {} void PlayerList::blackList(const std::wstring& playerName) {} void PlayerList::reloadWhitelist() {} void PlayerList::sendLevelInfo(std::shared_ptr player, ServerLevel* level) { player->connection->send( std::shared_ptr(new SetTimePacket(level->getTime()))); if (level->isRaining()) { player->connection->send(std::shared_ptr( new GameEventPacket(GameEventPacket::START_RAINING, 0))); } else { // 4J Stu - Fix for #44836 - Customer Encountered: Out of Sync Weather // [A-10] If it was raining when the player left the level, and is now // not raining we need to make sure that state is updated player->connection->send(std::shared_ptr( new GameEventPacket(GameEventPacket::STOP_RAINING, 0))); } // send the stronghold position if there is one if ((level->dimension->id == 0) && level->getLevelData()->getHasStronghold()) { player->connection->send(std::shared_ptr(new XZPacket( XZPacket::STRONGHOLD, level->getLevelData()->getXStronghold(), level->getLevelData()->getZStronghold()))); } } void PlayerList::sendAllPlayerInfo(std::shared_ptr player) { player->refreshContainer(player->inventoryMenu); player->resetSentInfo(); } int PlayerList::getPlayerCount() { return (int)players.size(); } int PlayerList::getPlayerCount(ServerLevel* level) { int count = 0; for (AUTO_VAR(it, players.begin()); it != players.end(); ++it) { if ((*it)->level == level) ++count; } return count; } int PlayerList::getMaxPlayers() { return maxPlayers; } MinecraftServer* PlayerList::getServer() { return server; } int PlayerList::getViewDistance() { return viewDistance; } void PlayerList::setOverrideGameMode(GameType* gameMode) { this->overrideGameMode = gameMode; } void PlayerList::updatePlayerGameMode(std::shared_ptr newPlayer, std::shared_ptr oldPlayer, Level* level) { // reset the player's game mode (first pick from old, then copy level if // necessary) if (oldPlayer != NULL) { newPlayer->gameMode->setGameModeForPlayer( oldPlayer->gameMode->getGameModeForPlayer()); } else if (overrideGameMode != NULL) { newPlayer->gameMode->setGameModeForPlayer(overrideGameMode); } newPlayer->gameMode->updateGameMode(level->getLevelData()->getGameType()); } void PlayerList::setAllowCheatsForAllPlayers(bool allowCommands) { this->allowCheatsForAllPlayers = allowCommands; } std::shared_ptr PlayerList::findAlivePlayerOnSystem( std::shared_ptr player) { int dimIndex, playerDim; dimIndex = playerDim = player->dimension; if (dimIndex == -1) dimIndex = 1; else if (dimIndex == 1) dimIndex = 2; INetworkPlayer* thisPlayer = player->connection->getNetworkPlayer(); if (thisPlayer != NULL) { for (AUTO_VAR(itP, players.begin()); itP != players.end(); ++itP) { std::shared_ptr newPlayer = *itP; INetworkPlayer* otherPlayer = newPlayer->connection->getNetworkPlayer(); if (!newPlayer->removed && newPlayer != player && newPlayer->dimension == playerDim && otherPlayer != NULL && otherPlayer->IsSameSystem(thisPlayer)) { return newPlayer; } } } return nullptr; } void PlayerList::removePlayerFromReceiving(std::shared_ptr player, bool usePlayerDimension /*= true*/, int dimension /*= 0*/) { int dimIndex, playerDim; dimIndex = playerDim = usePlayerDimension ? player->dimension : dimension; if (dimIndex == -1) dimIndex = 1; else if (dimIndex == 1) dimIndex = 2; #ifndef _CONTENT_PACKAGE app.DebugPrintf("Requesting remove player %ls as primary in dimension %d\n", player->name.c_str(), dimIndex); #endif bool playerRemoved = false; AUTO_VAR(it, find(receiveAllPlayers[dimIndex].begin(), receiveAllPlayers[dimIndex].end(), player)); if (it != receiveAllPlayers[dimIndex].end()) { #ifndef _CONTENT_PACKAGE app.DebugPrintf( "Remove: Removing player %ls as primary in dimension %d\n", player->name.c_str(), dimIndex); #endif receiveAllPlayers[dimIndex].erase(it); playerRemoved = true; } INetworkPlayer* thisPlayer = player->connection->getNetworkPlayer(); if (thisPlayer != NULL && playerRemoved) { for (AUTO_VAR(itP, players.begin()); itP != players.end(); ++itP) { std::shared_ptr newPlayer = *itP; INetworkPlayer* otherPlayer = newPlayer->connection->getNetworkPlayer(); if (newPlayer != player && newPlayer->dimension == playerDim && otherPlayer != NULL && otherPlayer->IsSameSystem(thisPlayer)) { #ifndef _CONTENT_PACKAGE app.DebugPrintf( "Remove: Adding player %ls as primary in dimension %d\n", newPlayer->name.c_str(), dimIndex); #endif receiveAllPlayers[dimIndex].push_back(newPlayer); break; } } } else if (thisPlayer == NULL) { #ifndef _CONTENT_PACKAGE app.DebugPrintf( "Remove: Qnet player for %ls was NULL so re-checking all players\n", player->name.c_str()); #endif // 4J Stu - Something went wrong, or possibly the QNet player left // before we got here. Re-check all active players and make sure they // have someone on their system to receive all packets for (AUTO_VAR(itP, players.begin()); itP != players.end(); ++itP) { std::shared_ptr newPlayer = *itP; INetworkPlayer* checkingPlayer = newPlayer->connection->getNetworkPlayer(); if (checkingPlayer != NULL) { int newPlayerDim = 0; if (newPlayer->dimension == -1) newPlayerDim = 1; else if (newPlayer->dimension == 1) newPlayerDim = 2; bool foundPrimary = false; for (AUTO_VAR(it, receiveAllPlayers[newPlayerDim].begin()); it != receiveAllPlayers[newPlayerDim].end(); ++it) { std::shared_ptr primaryPlayer = *it; INetworkPlayer* primPlayer = primaryPlayer->connection->getNetworkPlayer(); if (primPlayer != NULL && checkingPlayer->IsSameSystem(primPlayer)) { foundPrimary = true; break; } } if (!foundPrimary) { #ifndef _CONTENT_PACKAGE app.DebugPrintf( "Remove: Adding player %ls as primary in dimension " "%d\n", newPlayer->name.c_str(), newPlayerDim); #endif receiveAllPlayers[newPlayerDim].push_back(newPlayer); } } } } } void PlayerList::addPlayerToReceiving(std::shared_ptr player) { int playerDim = 0; if (player->dimension == -1) playerDim = 1; else if (player->dimension == 1) playerDim = 2; #ifndef _CONTENT_PACKAGE app.DebugPrintf("Requesting add player %ls as primary in dimension %d\n", player->name.c_str(), playerDim); #endif bool shouldAddPlayer = true; INetworkPlayer* thisPlayer = player->connection->getNetworkPlayer(); if (thisPlayer == NULL) { #ifndef _CONTENT_PACKAGE app.DebugPrintf( "Add: Qnet player for player %ls is NULL so not adding them\n", player->name.c_str()); #endif shouldAddPlayer = false; } else { for (AUTO_VAR(it, receiveAllPlayers[playerDim].begin()); it != receiveAllPlayers[playerDim].end(); ++it) { std::shared_ptr oldPlayer = *it; INetworkPlayer* checkingPlayer = oldPlayer->connection->getNetworkPlayer(); if (checkingPlayer != NULL && checkingPlayer->IsSameSystem(thisPlayer)) { shouldAddPlayer = false; break; } } } if (shouldAddPlayer) { #ifndef _CONTENT_PACKAGE app.DebugPrintf("Add: Adding player %ls as primary in dimension %d\n", player->name.c_str(), playerDim); #endif receiveAllPlayers[playerDim].push_back(player); } } bool PlayerList::canReceiveAllPackets(std::shared_ptr player) { int playerDim = 0; if (player->dimension == -1) playerDim = 1; else if (player->dimension == 1) playerDim = 2; for (AUTO_VAR(it, receiveAllPlayers[playerDim].begin()); it != receiveAllPlayers[playerDim].end(); ++it) { std::shared_ptr newPlayer = *it; if (newPlayer == player) { return true; } } return false; } void PlayerList::kickPlayerByShortId(std::uint8_t networkSmallId) { EnterCriticalSection(&m_kickPlayersCS); m_smallIdsToKick.push_back(networkSmallId); LeaveCriticalSection(&m_kickPlayersCS); } void PlayerList::closePlayerConnectionBySmallId(std::uint8_t networkSmallId) { EnterCriticalSection(&m_closePlayersCS); m_smallIdsToClose.push_back(networkSmallId); LeaveCriticalSection(&m_closePlayersCS); } bool PlayerList::isXuidBanned(PlayerUID xuid) { if (xuid == INVALID_XUID) return false; bool banned = false; for (AUTO_VAR(it, m_bannedXuids.begin()); it != m_bannedXuids.end(); ++it) { if (ProfileManager.AreXUIDSEqual(xuid, *it)) { banned = true; break; } } return banned; } // AP added for Vita so the range can be increased once the level starts void PlayerList::setViewDistance(int newViewDistance) { viewDistance = newViewDistance; }