Something went wrong. Try again.
the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
Something went wrong. Try again.
26 kB · 861 lines
C++
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861#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.chunk.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 firstInhabitedTime = 0;
parent->getLevel()->cache->create(x, z);}
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) ) );
if (players.empty()) { firstInhabitedTime = parent->level->getGameTime(); }
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) { { LevelChunk *chunk = parent->level->getChunk(pos.x, pos.z); updateInhabitedTime(chunk); AUTO_VAR(it, find(parent->knownChunks.begin(), parent->knownChunks.end(),this)); if(it != parent->knownChunks.end()) parent->knownChunks.erase(it); } __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::updateInhabitedTime(){ updateInhabitedTime(parent->level->getChunk(pos.x, pos.z));}
void PlayerChunkMap::PlayerChunk::updateInhabitedTime(LevelChunk *chunk){ chunk->inhabitedTime += parent->level->getGameTime() - firstInhabitedTime;
firstInhabitedTime = parent->level->getGameTime();}
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 sendingvoid 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; lastInhabitedUpdate = 0;}
PlayerChunkMap::~PlayerChunkMap(){ for( AUTO_VAR(it, chunks.begin()); it != chunks.end(); it++ ) { delete it->second; }}
ServerLevel *PlayerChunkMap::getLevel(){ return level;}
void PlayerChunkMap::tick(){ __int64 time = level->getGameTime();
if (time - lastInhabitedUpdate > Level::TICKS_PER_DAY / 3) { lastInhabitedUpdate = time;
for (int i = 0; i < knownChunks.size(); i++) { PlayerChunk *chunk = knownChunks.at(i);
// 4J Stu - Going to let our changeChunks handler below deal with this //chunk.broadcastChanges();
chunk->updateInhabitedTime(); } }
// 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; knownChunks.push_back(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); } }}
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 sendingvoid 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;
// 4J - added so that we don't fully create/send every chunk at this stage. Particularly since moving on to large worlds, where // we can be adding 1024 chunks here of which a large % might need to be fully created, this can take a long time. Instead use // the getChunkAndAddPlayer for anything but the central region of chunks, which adds them to a queue of chunks which are added // one per tick per player. const int maxLegSizeToAddNow = 14;
// 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( ( legSize < maxLegSizeToAddNow ) || ( ( legSize == maxLegSizeToAddNow ) && ( ( leg == 0 ) || ( k < ( legSize - 1 ) ) ) ) ) { 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); } else { getChunkAndAddPlayer(targetX, targetZ, player); } } } }
// 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( ( size * 2 ) <= maxLegSizeToAddNow ) { 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); } else { getChunkAndAddPlayer(targetX, targetZ, player); } } // 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 tickvoid 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 startsvoid 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; }}