aboutsummaryrefslogtreecommitdiff
path: root/Minecraft.Client/PlayerChunkMap.cpp
diff options
context:
space:
mode:
authordaoge_cmd <3523206925@qq.com>2026-03-01 12:16:08 +0800
committerdaoge_cmd <3523206925@qq.com>2026-03-01 12:16:08 +0800
commitb691c43c44ff180d10e7d4a9afc83b98551ff586 (patch)
tree3e9849222cbc6ba49f2f1fc6e5fe7179632c7390 /Minecraft.Client/PlayerChunkMap.cpp
parentdef8cb415354ac390b7e89052a50605285f1aca9 (diff)
Initial commit
Diffstat (limited to 'Minecraft.Client/PlayerChunkMap.cpp')
-rw-r--r--Minecraft.Client/PlayerChunkMap.cpp800
1 files changed, 800 insertions, 0 deletions
diff --git a/Minecraft.Client/PlayerChunkMap.cpp b/Minecraft.Client/PlayerChunkMap.cpp
new file mode 100644
index 00000000..aedd5a9d
--- /dev/null
+++ b/Minecraft.Client/PlayerChunkMap.cpp
@@ -0,0 +1,800 @@
+#include "stdafx.h"
+#include "PlayerChunkMap.h"
+#include "PlayerConnection.h"
+#include "ServerLevel.h"
+#include "ServerChunkCache.h"
+#include "ServerPlayer.h"
+#include "MinecraftServer.h"
+#include "..\Minecraft.World\net.minecraft.network.packet.h"
+#include "..\Minecraft.World\net.minecraft.world.level.h"
+#include "..\Minecraft.World\net.minecraft.world.level.tile.h"
+#include "..\Minecraft.World\ArrayWithLength.h"
+#include "..\Minecraft.World\System.h"
+#include "PlayerList.h"
+
+PlayerChunkMap::PlayerChunk::PlayerChunk(int x, int z, PlayerChunkMap *pcm) : pos(x,z)
+{
+ // 4J - added initialisers
+ changes = 0;
+ changedTiles = shortArray(MAX_CHANGES_BEFORE_RESEND);
+ xChangeMin = xChangeMax = 0;
+ yChangeMin = yChangeMax = 0;
+ zChangeMin = zChangeMax = 0;
+ parent = pcm; // 4J added
+ ticksToNextRegionUpdate = 0; // 4J added
+ prioritised = false; // 4J added
+
+ parent->getLevel()->cache->create(x, z);
+ // 4J - added make sure our lights are up to date as soon as we make it. This is of particular concern for local clients, who have their data
+ // shared as soon as the chunkvisibilitypacket is sent, and so could potentially create render data for this chunk before it has been properly lit.
+ while( parent->getLevel()->updateLights() )
+ ;
+}
+
+PlayerChunkMap::PlayerChunk::~PlayerChunk()
+{
+ delete changedTiles.data;
+}
+
+// 4J added - construct an an array of flags that indicate which entities are still waiting to have network packets sent out to say that they have been removed
+// If there aren't any entities to be flagged, this function does nothing. If there *are* entities to be added, uses the removedFound as an input to
+// determine if the flag array has already been initialised at all - if it has been, then just adds flags to it; if it hasn't, then memsets the output
+// flag array and adds to it for this ServerPlayer.
+void PlayerChunkMap::flagEntitiesToBeRemoved(unsigned int *flags, bool *flagToBeRemoved)
+{
+ for(AUTO_VAR(it,players.begin()); it != players.end(); it++)
+ {
+ shared_ptr<ServerPlayer> serverPlayer = *it;
+ serverPlayer->flagEntitiesToBeRemoved(flags, flagToBeRemoved);
+ }
+}
+
+void PlayerChunkMap::PlayerChunk::add(shared_ptr<ServerPlayer> player, bool sendPacket /*= true*/)
+{
+ //app.DebugPrintf("--- Adding player to chunk x=%d\tz=%d\n",x, z);
+ if (find(players.begin(),players.end(),player) != players.end())
+ {
+ // 4J-PB - At the start of the game, lots of chunks are added, and we can then move into an area that is outside the diameter of our starting area,
+ // but is inside the area loaded at the start.
+ app.DebugPrintf("--- Adding player to chunk x=%d\t z=%d, but they are already in there!\n",pos.x, pos.z);
+ return;
+
+ //assert(false);
+// 4J - was throw new IllegalStateException("Failed to add player. " + player + " already is in chunk " + x + ", " + z);
+ }
+
+ player->seenChunks.insert(pos);
+
+ // 4J Added the sendPacket check. See PlayerChunkMap::add for the usage
+ if( sendPacket ) player->connection->send( shared_ptr<ChunkVisibilityPacket>( new ChunkVisibilityPacket(pos.x, pos.z, true) ) );
+
+ players.push_back(player);
+
+ player->chunksToSend.push_back(pos);
+
+#ifdef _LARGE_WORLDS
+ parent->getLevel()->cache->dontDrop(pos.x, pos.z); // 4J Added;
+#endif
+}
+
+void PlayerChunkMap::PlayerChunk::remove(shared_ptr<ServerPlayer> player)
+{
+ PlayerChunkMap::PlayerChunk *toDelete = NULL;
+
+ //app.DebugPrintf("--- PlayerChunkMap::PlayerChunk::remove x=%d\tz=%d\n",x,z);
+ AUTO_VAR(it, find(players.begin(),players.end(),player));
+ if ( it == players.end())
+ {
+ app.DebugPrintf("--- INFO - Removing player from chunk x=%d\t z=%d, but they are not in that chunk!\n",pos.x, pos.z);
+
+ return;
+ }
+
+ players.erase(it);
+ if (players.size() == 0)
+ {
+ __int64 id = (pos.x + 0x7fffffffLL) | ((pos.z + 0x7fffffffLL) << 32);
+ AUTO_VAR(it, parent->chunks.find(id));
+ if( it != parent->chunks.end() )
+ {
+ toDelete = it->second; // Don't delete until the end of the function, as this might be this instance
+ parent->chunks.erase(it);
+ }
+ if (changes > 0)
+ {
+ AUTO_VAR(it, find(parent->changedChunks.begin(),parent->changedChunks.end(),this));
+ parent->changedChunks.erase(it);
+ }
+ parent->getLevel()->cache->drop(pos.x, pos.z);
+ }
+
+ player->chunksToSend.remove(pos);
+ // 4J - I don't think there's any point sending these anymore, as we don't need to unload chunks with fixed sized maps
+ // 4J - We do need to send these to unload entities in chunks when players are dead. If we do not and the entity is removed
+ // while they are dead, that entity will remain in the clients world
+ if (player->connection != NULL && player->seenChunks.find(pos) != player->seenChunks.end())
+ {
+ INetworkPlayer *thisNetPlayer = player->connection->getNetworkPlayer();
+ bool noOtherPlayersFound = true;
+
+ if( thisNetPlayer != NULL )
+ {
+ for( AUTO_VAR(it, players.begin()); it < players.end(); ++it )
+ {
+ shared_ptr<ServerPlayer> currPlayer = *it;
+ INetworkPlayer *currNetPlayer = currPlayer->connection->getNetworkPlayer();
+ if( currNetPlayer != NULL && currNetPlayer->IsSameSystem( thisNetPlayer ) && currPlayer->seenChunks.find(pos) != currPlayer->seenChunks.end() )
+ {
+ noOtherPlayersFound = false;
+ break;
+ }
+ }
+ if(noOtherPlayersFound)
+ {
+ //wprintf(L"Sending ChunkVisiblity packet false for chunk (%d,%d) to player %ls\n", x, z, player->name.c_str() );
+ player->connection->send( shared_ptr<ChunkVisibilityPacket>( new ChunkVisibilityPacket(pos.x, pos.z, false) ) );
+ }
+ }
+ else
+ {
+ //app.DebugPrintf("PlayerChunkMap::PlayerChunk::remove - QNetPlayer is NULL\n");
+ }
+ }
+
+ delete toDelete;
+}
+
+void PlayerChunkMap::PlayerChunk::tileChanged(int x, int y, int z)
+{
+ if (changes == 0)
+ {
+ parent->changedChunks.push_back(this);
+ xChangeMin = xChangeMax = x;
+ yChangeMin = yChangeMax = y;
+ zChangeMin = zChangeMax = z;
+ }
+ if (xChangeMin > x) xChangeMin = x;
+ if (xChangeMax < x) xChangeMax = x;
+
+ if (yChangeMin > y) yChangeMin = y;
+ if (yChangeMax < y) yChangeMax = y;
+
+ if (zChangeMin > z) zChangeMin = z;
+ if (zChangeMax < z) zChangeMax = z;
+
+ if (changes < MAX_CHANGES_BEFORE_RESEND)
+ {
+ short id = (short) ((x << 12) | (z << 8) | (y));
+
+ for (int i = 0; i < changes; i++)
+ {
+ if (changedTiles[i] == id) return;
+ }
+
+ changedTiles[changes++] = id;
+ }
+}
+
+// 4J added - make sure that any tile updates for the chunk at this location get prioritised for sending
+void PlayerChunkMap::PlayerChunk::prioritiseTileChanges()
+{
+ prioritised = true;
+}
+
+void PlayerChunkMap::PlayerChunk::broadcast(shared_ptr<Packet> packet)
+{
+ vector< shared_ptr<ServerPlayer> > sentTo;
+ for (unsigned int i = 0; i < players.size(); i++)
+ {
+ shared_ptr<ServerPlayer> player = players[i];
+
+ // 4J - don't send to a player we've already sent this data to that shares the same machine. TileUpdatePacket,
+ // ChunkTilesUpdatePacket and SignUpdatePacket all used to limit themselves to sending once to each machine
+ // by only sending to the primary player on each machine. This was causing trouble for split screen
+ // as updates were only coming in for the region round this one player. Now these packets can be sent to any
+ // player, but we try to restrict the network impact this has by not resending to the one machine
+ bool dontSend = false;
+ if( sentTo.size() )
+ {
+ INetworkPlayer *thisPlayer = player->connection->getNetworkPlayer();
+ if( thisPlayer == NULL )
+ {
+ dontSend = true;
+ }
+ else
+ {
+ for(unsigned int j = 0; j < sentTo.size(); j++ )
+ {
+ shared_ptr<ServerPlayer> player2 = sentTo[j];
+ INetworkPlayer *otherPlayer = player2->connection->getNetworkPlayer();
+ if( otherPlayer != NULL && thisPlayer->IsSameSystem(otherPlayer) )
+ {
+ dontSend = true;
+ }
+ }
+ }
+ }
+ if( dontSend )
+ {
+ continue;
+ }
+
+ // 4J Changed to get the flag index for the player before we send a packet. This flag is updated when we queue
+ // for send the first BlockRegionUpdatePacket for this chunk to that player/players system. Therefore there is no need to
+ // send tile updates or other updates until that has been sent
+ int flagIndex = ServerPlayer::getFlagIndexForChunk(pos, parent->dimension);
+ if (player->seenChunks.find(pos) != player->seenChunks.end() && (player->connection->isLocal() || g_NetworkManager.SystemFlagGet(player->connection->getNetworkPlayer(),flagIndex) ))
+ {
+ player->connection->send(packet);
+ sentTo.push_back(player);
+ }
+ }
+ // Now also check round all the players that are involved in this game. We also want to send the packet
+ // to them if their system hasn't received it already, but they have received the first BlockRegionUpdatePacket for this
+ // chunk
+
+ // Make sure we are only doing this for BlockRegionUpdatePacket, ChunkTilesUpdatePacket and TileUpdatePacket.
+ // We'll be potentially sending to players who aren't on the same level as this packet is intended for,
+ // and only these 3 packets have so far been updated to be able to encode the level so they are robust
+ // enough to cope with this
+ if(!( ( packet->getId() == 51 ) || ( packet->getId() == 52 ) || ( packet->getId() == 53 ) ) )
+ {
+ return;
+ }
+
+ for( int i = 0; i < parent->level->getServer()->getPlayers()->players.size(); i++ )
+ {
+ shared_ptr<ServerPlayer> player = parent->level->getServer()->getPlayers()->players[i];
+ // Don't worry about local players, they get all their updates through sharing level with the server anyway
+ if ( player->connection == NULL ) continue;
+ if( player->connection->isLocal() ) continue;
+
+ // Don't worry about this player if they haven't had this chunk yet (this flag will be the
+ // same for all players on the same system)
+ int flagIndex = ServerPlayer::getFlagIndexForChunk(pos,parent->dimension);
+ if(!g_NetworkManager.SystemFlagGet(player->connection->getNetworkPlayer(),flagIndex)) continue;
+
+ // From here on the same rules as in the loop above - don't send it if we've already sent to the same system
+ bool dontSend = false;
+ if( sentTo.size() )
+ {
+ INetworkPlayer *thisPlayer = player->connection->getNetworkPlayer();
+ if( thisPlayer == NULL )
+ {
+ dontSend = true;
+ }
+ else
+ {
+ for(unsigned int j = 0; j < sentTo.size(); j++ )
+ {
+ shared_ptr<ServerPlayer> player2 = sentTo[j];
+ INetworkPlayer *otherPlayer = player2->connection->getNetworkPlayer();
+ if( otherPlayer != NULL && thisPlayer->IsSameSystem(otherPlayer) )
+ {
+ dontSend = true;
+ }
+ }
+ }
+ }
+ if( !dontSend )
+ {
+ player->connection->send(packet);
+ sentTo.push_back(player);
+ }
+ }
+}
+
+bool PlayerChunkMap::PlayerChunk::broadcastChanges(bool allowRegionUpdate)
+{
+ bool didRegionUpdate = false;
+ ServerLevel *level = parent->getLevel();
+ if( ticksToNextRegionUpdate > 0 ) ticksToNextRegionUpdate--;
+ if (changes == 0)
+ {
+ prioritised = false;
+ return false;
+ }
+ if (changes == 1)
+ {
+ int x = pos.x * 16 + xChangeMin;
+ int y = yChangeMin;
+ int z = pos.z * 16 + zChangeMin;
+ broadcast( shared_ptr<TileUpdatePacket>( new TileUpdatePacket(x, y, z, level) ) );
+ if (level->isEntityTile(x, y, z))
+ {
+ broadcast(level->getTileEntity(x, y, z));
+ }
+ }
+ else if (changes == MAX_CHANGES_BEFORE_RESEND)
+ {
+ // 4J added, to allow limiting of region update packets created
+ if( !prioritised )
+ {
+ if( !allowRegionUpdate || ( ticksToNextRegionUpdate > 0 ) )
+ {
+ return false;
+ }
+ }
+
+ yChangeMin = yChangeMin / 2 * 2;
+ yChangeMax = (yChangeMax / 2 + 1) * 2;
+ int xp = xChangeMin + pos.x * 16;
+ int yp = yChangeMin;
+ int zp = zChangeMin + pos.z * 16;
+ int xs = xChangeMax - xChangeMin + 1;
+ int ys = yChangeMax - yChangeMin + 2;
+ int zs = zChangeMax - zChangeMin + 1;
+
+ // Fix for buf #95007 : TCR #001 BAS Game Stability: TU12: Code: Compliance: More than 192 dropped items causes game to freeze or crash.
+ // Block region update packets can only encode ys in a range of 1 - 256
+ if( ys > 256 ) ys = 256;
+
+ broadcast( shared_ptr<BlockRegionUpdatePacket>( new BlockRegionUpdatePacket(xp, yp, zp, xs, ys, zs, level) ) );
+ vector<shared_ptr<TileEntity> > *tes = level->getTileEntitiesInRegion(xp, yp, zp, xp + xs, yp + ys, zp + zs);
+ for (unsigned int i = 0; i < tes->size(); i++)
+ {
+ broadcast(tes->at(i));
+ }
+ delete tes;
+ ticksToNextRegionUpdate = MIN_TICKS_BETWEEN_REGION_UPDATE;
+ didRegionUpdate = true;
+ }
+ else
+ {
+ // 4J As we only get here if changes is less than MAX_CHANGES_BEFORE_RESEND (10) we only need to send a byte value in the packet
+ broadcast( shared_ptr<ChunkTilesUpdatePacket>( new ChunkTilesUpdatePacket(pos.x, pos.z, changedTiles, (byte)changes, level) ) );
+ for (int i = 0; i < changes; i++)
+ {
+ int x = pos.x * 16 + ((changedTiles[i] >> 12) & 15);
+ int y = ((changedTiles[i]) & 255);
+ int z = pos.z * 16 + ((changedTiles[i] >> 8) & 15);
+
+ if (level->isEntityTile(x, y, z))
+ {
+// System.out.println("Sending!");
+ broadcast(level->getTileEntity(x, y, z));
+ }
+ }
+ }
+ changes = 0;
+ prioritised = false;
+ return didRegionUpdate;
+}
+
+void PlayerChunkMap::PlayerChunk::broadcast(shared_ptr<TileEntity> te)
+{
+ if (te != NULL)
+ {
+ shared_ptr<Packet> p = te->getUpdatePacket();
+ if (p != NULL)
+ {
+ broadcast(p);
+ }
+ }
+}
+
+PlayerChunkMap::PlayerChunkMap(ServerLevel *level, int dimension, int radius)
+{
+ assert(radius <= MAX_VIEW_DISTANCE);
+ assert(radius >= MIN_VIEW_DISTANCE);
+ this->radius = radius;
+ this->level = level;
+ this->dimension = dimension;
+}
+
+PlayerChunkMap::~PlayerChunkMap()
+{
+ for( AUTO_VAR(it, chunks.begin()); it != chunks.end(); it++ )
+ {
+ delete it->second;
+ }
+}
+
+ServerLevel *PlayerChunkMap::getLevel()
+{
+ return level;
+}
+
+void PlayerChunkMap::tick()
+{
+ // 4J - some changes here so that we only send one region update per tick. The chunks themselves also
+ // limit their resend rate to once every MIN_TICKS_BETWEEN_REGION_UPDATE ticks
+ bool regionUpdateSent = false;
+ for (unsigned int i = 0; i < changedChunks.size();)
+ {
+ regionUpdateSent |= changedChunks[i]->broadcastChanges(!regionUpdateSent);
+ // Changes will be 0 if the chunk actually sent something, in which case we can delete it from this array
+ if( changedChunks[i]->changes == 0 )
+ {
+ changedChunks[i] = changedChunks.back();
+ changedChunks.pop_back();
+ }
+ else
+ {
+ // Limiting of some kind means we didn't send this chunk so move onto the next
+ i++;
+ }
+ }
+
+ for( unsigned int i = 0; i < players.size(); i++ )
+ {
+ tickAddRequests(players[i]);
+ }
+
+ // 4J Stu - Added 1.1 but not relevant to us as we never no 0 players anyway, and don't think we should be dropping stuff
+ //if (players.isEmpty()) {
+ // ServerLevel level = server.getLevel(this.dimension);
+ // Dimension dimension = level.dimension;
+ // if (!dimension.mayRespawn()) {
+ // level.cache.dropAll();
+ // }
+ //}
+}
+
+bool PlayerChunkMap::hasChunk(int x, int z)
+{
+ __int64 id = (x + 0x7fffffffLL) | ((z + 0x7fffffffLL) << 32);
+ return chunks.find(id) != chunks.end();
+}
+
+PlayerChunkMap::PlayerChunk *PlayerChunkMap::getChunk(int x, int z, bool create)
+{
+ __int64 id = (x + 0x7fffffffLL) | ((z + 0x7fffffffLL) << 32);
+ AUTO_VAR(it, chunks.find(id));
+
+ PlayerChunk *chunk = NULL;
+ if( it != chunks.end() )
+ {
+ chunk = it->second;
+ }
+ else if ( create)
+ {
+ chunk = new PlayerChunk(x, z, this);
+ chunks[id] = chunk;
+ }
+
+ return chunk;
+}
+
+// 4J - added. If a chunk exists, add a player to it straight away. If it doesn't exist,
+// queue a request for it to be created.
+void PlayerChunkMap::getChunkAndAddPlayer(int x, int z, shared_ptr<ServerPlayer> player)
+{
+ __int64 id = (x + 0x7fffffffLL) | ((z + 0x7fffffffLL) << 32);
+ AUTO_VAR(it, chunks.find(id));
+
+ if( it != chunks.end() )
+ {
+ it->second->add(player);
+ }
+ else
+ {
+ addRequests.push_back(PlayerChunkAddRequest(x,z,player));
+ }
+}
+
+// 4J - added. If the chunk and player are in the queue to be added, remove from there. Otherwise
+// attempt to remove from main chunk map.
+void PlayerChunkMap::getChunkAndRemovePlayer(int x, int z, shared_ptr<ServerPlayer> player)
+{
+ for( AUTO_VAR(it, addRequests.begin()); it != addRequests.end(); it++ )
+ {
+ if( ( it->x == x ) &&
+ ( it->z == z ) &&
+ ( it->player == player ) )
+ {
+ addRequests.erase(it);
+ return;
+ }
+ }
+ __int64 id = (x + 0x7fffffffLL) | ((z + 0x7fffffffLL) << 32);
+ AUTO_VAR(it, chunks.find(id));
+
+ if( it != chunks.end() )
+ {
+ it->second->remove(player);
+ }
+}
+
+// 4J - added - actually create & add player to a playerchunk, if there is one queued for this player.
+void PlayerChunkMap::tickAddRequests(shared_ptr<ServerPlayer> player)
+{
+ if( addRequests.size() )
+ {
+ // Find the nearest chunk request to the player
+ int px = (int)player->x;
+ int pz = (int)player->z;
+ int minDistSq = -1;
+
+ AUTO_VAR(itNearest, addRequests.end());
+ for( AUTO_VAR(it, addRequests.begin()); it != addRequests.end(); it++ )
+ {
+ if( it->player == player )
+ {
+ int xm = ( it->x * 16 ) + 8;
+ int zm = ( it->z * 16 ) + 8;
+ int distSq = (xm - px) * (xm - px) +
+ (zm - pz) * (zm - pz);
+ if( ( minDistSq == -1 ) || ( distSq < minDistSq ) )
+ {
+ minDistSq = distSq;
+ itNearest = it;
+ }
+ }
+ }
+
+ // If we found one at all, then do this one
+ if( itNearest != addRequests.end() )
+ {
+ getChunk(itNearest->x, itNearest->z, true)->add(itNearest->player);
+ addRequests.erase(itNearest);
+ return;
+ }
+ }
+}
+
+void PlayerChunkMap::broadcastTileUpdate(shared_ptr<Packet> packet, int x, int y, int z)
+{
+ int xc = x >> 4;
+ int zc = z >> 4;
+ PlayerChunk *chunk = getChunk(xc, zc, false);
+ if (chunk != NULL)
+ {
+ chunk->broadcast(packet);
+ }
+}
+
+void PlayerChunkMap::tileChanged(int x, int y, int z)
+{
+ int xc = x >> 4;
+ int zc = z >> 4;
+ PlayerChunk *chunk = getChunk(xc, zc, false);
+ if (chunk != NULL)
+ {
+ chunk->tileChanged(x & 15, y, z & 15);
+ }
+}
+
+bool PlayerChunkMap::isTrackingTile(int x, int y, int z)
+{
+ int xc = x >> 4;
+ int zc = z >> 4;
+ PlayerChunk *chunk = getChunk(xc, zc, false);
+ if( chunk ) return true;
+ return false;
+}
+
+// 4J added - make sure that any tile updates for the chunk at this location get prioritised for sending
+void PlayerChunkMap::prioritiseTileChanges(int x, int y, int z)
+{
+ int xc = x >> 4;
+ int zc = z >> 4;
+ PlayerChunk *chunk = getChunk(xc, zc, false);
+ if (chunk != NULL)
+ {
+ chunk->prioritiseTileChanges();
+ }
+}
+
+void PlayerChunkMap::add(shared_ptr<ServerPlayer> player)
+{
+ static int direction[4][2] = { { 1, 0 }, { 0, 1 }, { -1, 0 }, {0, -1} };
+
+ int xc = (int) player->x >> 4;
+ int zc = (int) player->z >> 4;
+
+ player->lastMoveX = player->x;
+ player->lastMoveZ = player->z;
+
+// for (int x = xc - radius; x <= xc + radius; x++)
+// for (int z = zc - radius; z <= zc + radius; z++) {
+// getChunk(x, z, true).add(player);
+// }
+
+ // CraftBukkit start
+ int facing = 0;
+ int size = radius;
+ int dx = 0;
+ int dz = 0;
+
+ // Origin
+ getChunk(xc, zc, true)->add(player, false);
+
+ // 4J Added so we send an area packet rather than one visibility packet per chunk
+ int minX, maxX, minZ, maxZ;
+ minX = maxX = xc;
+ minZ = maxZ = zc;
+
+ // All but the last leg
+ for (int legSize = 1; legSize <= size * 2; legSize++)
+ {
+ for (int leg = 0; leg < 2; leg++)
+ {
+ int *dir = direction[facing++ % 4];
+
+ for (int k = 0; k < legSize; k++)
+ {
+ dx += dir[0];
+ dz += dir[1];
+
+ int targetX, targetZ;
+ targetX = xc + dx;
+ targetZ = zc + dz;
+ if( targetX > maxX ) maxX = targetX;
+ if( targetX < minX ) minX = targetX;
+ if( targetZ > maxZ ) maxZ = targetZ;
+ if( targetZ < minZ ) minZ = targetZ;
+
+ getChunk(targetX, targetZ, true)->add(player, false);
+ }
+ }
+ }
+
+ // Final leg
+ facing %= 4;
+ for (int k = 0; k < size * 2; k++)
+ {
+ dx += direction[facing][0];
+ dz += direction[facing][1];
+
+ int targetX, targetZ;
+ targetX = xc + dx;
+ targetZ = zc + dz;
+ if( targetX > maxX ) maxX = targetX;
+ if( targetX < minX ) minX = targetX;
+ if( targetZ > maxZ ) maxZ = targetZ;
+ if( targetZ < minZ ) minZ = targetZ;
+
+ getChunk(targetX, targetZ, true)->add(player, false);
+ }
+ // CraftBukkit end
+
+ player->connection->send( shared_ptr<ChunkVisibilityAreaPacket>( new ChunkVisibilityAreaPacket(minX, maxX, minZ, maxZ) ) );
+
+#ifdef _LARGE_WORLDS
+ getLevel()->cache->dontDrop(xc,zc);
+#endif
+
+ players.push_back(player);
+
+}
+
+void PlayerChunkMap::remove(shared_ptr<ServerPlayer> player)
+{
+ int xc = ((int) player->lastMoveX) >> 4;
+ int zc = ((int) player->lastMoveZ) >> 4;
+
+ for (int x = xc - radius; x <= xc + radius; x++)
+ for (int z = zc - radius; z <= zc + radius; z++)
+ {
+ PlayerChunk *playerChunk = getChunk(x, z, false);
+ if (playerChunk != NULL) playerChunk->remove(player);
+ }
+
+ AUTO_VAR(it, find(players.begin(),players.end(),player));
+ if( players.size() > 0 && it != players.end() )
+ players.erase(find(players.begin(),players.end(),player));
+
+ // 4J - added - also remove any queued requests to be added to playerchunks here
+ for( AUTO_VAR(it, addRequests.begin()); it != addRequests.end(); )
+ {
+ if( it->player == player )
+ {
+ it = addRequests.erase(it);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+
+}
+
+bool PlayerChunkMap::chunkInRange(int x, int z, int xc, int zc)
+{
+ // If the distance between x and xc
+ int xd = x - xc;
+ int zd = z - zc;
+ if (xd < -radius || xd > radius) return false;
+ if (zd < -radius || zd > radius) return false;
+ return true;
+}
+
+// 4J - have changed this so that we queue requests to add the player to chunks if they
+// need to be created, so that we aren't creating potentially 20 chunks per player per tick
+void PlayerChunkMap::move(shared_ptr<ServerPlayer> player)
+{
+ int xc = ((int) player->x) >> 4;
+ int zc = ((int) player->z) >> 4;
+
+ double _xd = player->lastMoveX - player->x;
+ double _zd = player->lastMoveZ - player->z;
+ double dist = _xd * _xd + _zd * _zd;
+ if (dist < 8 * 8) return;
+
+ int last_xc = ((int) player->lastMoveX) >> 4;
+ int last_zc = ((int) player->lastMoveZ) >> 4;
+
+ int xd = xc - last_xc;
+ int zd = zc - last_zc;
+ if (xd == 0 && zd == 0) return;
+
+ for (int x = xc - radius; x <= xc + radius; x++)
+ for (int z = zc - radius; z <= zc + radius; z++)
+ {
+ if (!chunkInRange(x, z, last_xc, last_zc))
+ {
+ // 4J - changed from separate getChunk & add so we can wrap these operations up and queue
+ getChunkAndAddPlayer(x, z, player);
+ }
+
+ if (!chunkInRange(x - xd, z - zd, xc, zc))
+ {
+ // 4J - changed from separate getChunk & remove so we can wrap these operations up and queue
+ getChunkAndRemovePlayer(x - xd, z - zd, player);
+ }
+ }
+
+ player->lastMoveX = player->x;
+ player->lastMoveZ = player->z;
+}
+
+int PlayerChunkMap::getMaxRange()
+{
+ return radius * 16 - 16;
+}
+
+bool PlayerChunkMap::isPlayerIn(shared_ptr<ServerPlayer> player, int xChunk, int zChunk)
+{
+ PlayerChunk *chunk = getChunk(xChunk, zChunk, false);
+
+ if(chunk == NULL)
+ {
+ return false;
+ }
+ else
+ {
+ AUTO_VAR(it1, find(chunk->players.begin(), chunk->players.end(), player));
+ AUTO_VAR(it2, find(player->chunksToSend.begin(), player->chunksToSend.end(), chunk->pos));
+ return it1 != chunk->players.end() && it2 == player->chunksToSend.end();
+ }
+
+ //return chunk == NULL ? false : chunk->players->contains(player) && !player->chunksToSend->contains(chunk->pos);
+}
+
+int PlayerChunkMap::convertChunkRangeToBlock(int radius)
+{
+ return radius * 16 - 16;
+}
+
+// AP added for Vita so the range can be increased once the level starts
+void PlayerChunkMap::setRadius(int newRadius)
+{
+ if( radius != newRadius )
+ {
+ PlayerList* players = level->getServer()->getPlayerList();
+ for( int i = 0;i < players->players.size();i += 1 )
+ {
+ shared_ptr<ServerPlayer> player = players->players[i];
+ if( player->level == level )
+ {
+ int xc = ((int) player->x) >> 4;
+ int zc = ((int) player->z) >> 4;
+
+ for (int x = xc - newRadius; x <= xc + newRadius; x++)
+ for (int z = zc - newRadius; z <= zc + newRadius; z++)
+ {
+ // check if this chunk is outside the old radius area
+ if ( x < xc - radius || x > xc + radius || z < zc - radius || z > zc + radius )
+ {
+ getChunkAndAddPlayer(x, z, player);
+ }
+ }
+ }
+ }
+
+ assert(radius <= MAX_VIEW_DISTANCE);
+ assert(radius >= MIN_VIEW_DISTANCE);
+ this->radius = newRadius;
+ }
+} \ No newline at end of file