the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 861 lines 26 kB view raw
1#include "stdafx.h" 2#include "PlayerChunkMap.h" 3#include "PlayerConnection.h" 4#include "ServerLevel.h" 5#include "ServerChunkCache.h" 6#include "ServerPlayer.h" 7#include "MinecraftServer.h" 8#include "..\Minecraft.World\net.minecraft.network.packet.h" 9#include "..\Minecraft.World\net.minecraft.world.level.h" 10#include "..\Minecraft.World\net.minecraft.world.level.chunk.h" 11#include "..\Minecraft.World\net.minecraft.world.level.tile.h" 12#include "..\Minecraft.World\ArrayWithLength.h" 13#include "..\Minecraft.World\System.h" 14#include "PlayerList.h" 15 16PlayerChunkMap::PlayerChunk::PlayerChunk(int x, int z, PlayerChunkMap *pcm) : pos(x,z) 17{ 18 // 4J - added initialisers 19 changes = 0; 20 changedTiles = shortArray(MAX_CHANGES_BEFORE_RESEND); 21 xChangeMin = xChangeMax = 0; 22 yChangeMin = yChangeMax = 0; 23 zChangeMin = zChangeMax = 0; 24 parent = pcm; // 4J added 25 ticksToNextRegionUpdate = 0; // 4J added 26 prioritised = false; // 4J added 27 firstInhabitedTime = 0; 28 29 parent->getLevel()->cache->create(x, z); 30} 31 32PlayerChunkMap::PlayerChunk::~PlayerChunk() 33{ 34 delete changedTiles.data; 35} 36 37// 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 38// 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 39// 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 40// flag array and adds to it for this ServerPlayer. 41void PlayerChunkMap::flagEntitiesToBeRemoved(unsigned int *flags, bool *flagToBeRemoved) 42{ 43 for(AUTO_VAR(it,players.begin()); it != players.end(); it++) 44 { 45 shared_ptr<ServerPlayer> serverPlayer = *it; 46 serverPlayer->flagEntitiesToBeRemoved(flags, flagToBeRemoved); 47 } 48} 49 50void PlayerChunkMap::PlayerChunk::add(shared_ptr<ServerPlayer> player, bool sendPacket /*= true*/) 51{ 52 //app.DebugPrintf("--- Adding player to chunk x=%d\tz=%d\n",x, z); 53 if (find(players.begin(),players.end(),player) != players.end()) 54 { 55 // 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, 56 // but is inside the area loaded at the start. 57 app.DebugPrintf("--- Adding player to chunk x=%d\t z=%d, but they are already in there!\n",pos.x, pos.z); 58 return; 59 60 //assert(false); 61// 4J - was throw new IllegalStateException("Failed to add player. " + player + " already is in chunk " + x + ", " + z); 62 } 63 64 player->seenChunks.insert(pos); 65 66 // 4J Added the sendPacket check. See PlayerChunkMap::add for the usage 67 if( sendPacket ) player->connection->send( shared_ptr<ChunkVisibilityPacket>( new ChunkVisibilityPacket(pos.x, pos.z, true) ) ); 68 69 if (players.empty()) 70 { 71 firstInhabitedTime = parent->level->getGameTime(); 72 } 73 74 players.push_back(player); 75 76 player->chunksToSend.push_back(pos); 77 78#ifdef _LARGE_WORLDS 79 parent->getLevel()->cache->dontDrop(pos.x, pos.z); // 4J Added; 80#endif 81} 82 83void PlayerChunkMap::PlayerChunk::remove(shared_ptr<ServerPlayer> player) 84{ 85 PlayerChunkMap::PlayerChunk *toDelete = NULL; 86 87 //app.DebugPrintf("--- PlayerChunkMap::PlayerChunk::remove x=%d\tz=%d\n",x,z); 88 AUTO_VAR(it, find(players.begin(),players.end(),player)); 89 if ( it == players.end()) 90 { 91 app.DebugPrintf("--- INFO - Removing player from chunk x=%d\t z=%d, but they are not in that chunk!\n",pos.x, pos.z); 92 93 return; 94 } 95 96 players.erase(it); 97 if (players.size() == 0) 98 { 99 { 100 LevelChunk *chunk = parent->level->getChunk(pos.x, pos.z); 101 updateInhabitedTime(chunk); 102 AUTO_VAR(it, find(parent->knownChunks.begin(), parent->knownChunks.end(),this)); 103 if(it != parent->knownChunks.end()) parent->knownChunks.erase(it); 104 } 105 __int64 id = (pos.x + 0x7fffffffLL) | ((pos.z + 0x7fffffffLL) << 32); 106 AUTO_VAR(it, parent->chunks.find(id)); 107 if( it != parent->chunks.end() ) 108 { 109 toDelete = it->second; // Don't delete until the end of the function, as this might be this instance 110 parent->chunks.erase(it); 111 } 112 if (changes > 0) 113 { 114 AUTO_VAR(it, find(parent->changedChunks.begin(),parent->changedChunks.end(),this)); 115 parent->changedChunks.erase(it); 116 } 117 parent->getLevel()->cache->drop(pos.x, pos.z); 118 } 119 120 player->chunksToSend.remove(pos); 121 // 4J - I don't think there's any point sending these anymore, as we don't need to unload chunks with fixed sized maps 122 // 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 123 // while they are dead, that entity will remain in the clients world 124 if (player->connection != NULL && player->seenChunks.find(pos) != player->seenChunks.end()) 125 { 126 INetworkPlayer *thisNetPlayer = player->connection->getNetworkPlayer(); 127 bool noOtherPlayersFound = true; 128 129 if( thisNetPlayer != NULL ) 130 { 131 for( AUTO_VAR(it, players.begin()); it < players.end(); ++it ) 132 { 133 shared_ptr<ServerPlayer> currPlayer = *it; 134 INetworkPlayer *currNetPlayer = currPlayer->connection->getNetworkPlayer(); 135 if( currNetPlayer != NULL && currNetPlayer->IsSameSystem( thisNetPlayer ) && currPlayer->seenChunks.find(pos) != currPlayer->seenChunks.end() ) 136 { 137 noOtherPlayersFound = false; 138 break; 139 } 140 } 141 if(noOtherPlayersFound) 142 { 143 //wprintf(L"Sending ChunkVisiblity packet false for chunk (%d,%d) to player %ls\n", x, z, player->name.c_str() ); 144 player->connection->send( shared_ptr<ChunkVisibilityPacket>( new ChunkVisibilityPacket(pos.x, pos.z, false) ) ); 145 } 146 } 147 else 148 { 149 //app.DebugPrintf("PlayerChunkMap::PlayerChunk::remove - QNetPlayer is NULL\n"); 150 } 151 } 152 153 delete toDelete; 154} 155 156void PlayerChunkMap::PlayerChunk::updateInhabitedTime() 157{ 158 updateInhabitedTime(parent->level->getChunk(pos.x, pos.z)); 159} 160 161void PlayerChunkMap::PlayerChunk::updateInhabitedTime(LevelChunk *chunk) 162{ 163 chunk->inhabitedTime += parent->level->getGameTime() - firstInhabitedTime; 164 165 firstInhabitedTime = parent->level->getGameTime(); 166} 167 168void PlayerChunkMap::PlayerChunk::tileChanged(int x, int y, int z) 169{ 170 if (changes == 0) 171 { 172 parent->changedChunks.push_back(this); 173 xChangeMin = xChangeMax = x; 174 yChangeMin = yChangeMax = y; 175 zChangeMin = zChangeMax = z; 176 } 177 if (xChangeMin > x) xChangeMin = x; 178 if (xChangeMax < x) xChangeMax = x; 179 180 if (yChangeMin > y) yChangeMin = y; 181 if (yChangeMax < y) yChangeMax = y; 182 183 if (zChangeMin > z) zChangeMin = z; 184 if (zChangeMax < z) zChangeMax = z; 185 186 if (changes < MAX_CHANGES_BEFORE_RESEND) 187 { 188 short id = (short) ((x << 12) | (z << 8) | (y)); 189 190 for (int i = 0; i < changes; i++) 191 { 192 if (changedTiles[i] == id) return; 193 } 194 195 changedTiles[changes++] = id; 196 } 197} 198 199// 4J added - make sure that any tile updates for the chunk at this location get prioritised for sending 200void PlayerChunkMap::PlayerChunk::prioritiseTileChanges() 201{ 202 prioritised = true; 203} 204 205void PlayerChunkMap::PlayerChunk::broadcast(shared_ptr<Packet> packet) 206{ 207 vector< shared_ptr<ServerPlayer> > sentTo; 208 for (unsigned int i = 0; i < players.size(); i++) 209 { 210 shared_ptr<ServerPlayer> player = players[i]; 211 212 // 4J - don't send to a player we've already sent this data to that shares the same machine. TileUpdatePacket, 213 // ChunkTilesUpdatePacket and SignUpdatePacket all used to limit themselves to sending once to each machine 214 // by only sending to the primary player on each machine. This was causing trouble for split screen 215 // as updates were only coming in for the region round this one player. Now these packets can be sent to any 216 // player, but we try to restrict the network impact this has by not resending to the one machine 217 bool dontSend = false; 218 if( sentTo.size() ) 219 { 220 INetworkPlayer *thisPlayer = player->connection->getNetworkPlayer(); 221 if( thisPlayer == NULL ) 222 { 223 dontSend = true; 224 } 225 else 226 { 227 for(unsigned int j = 0; j < sentTo.size(); j++ ) 228 { 229 shared_ptr<ServerPlayer> player2 = sentTo[j]; 230 INetworkPlayer *otherPlayer = player2->connection->getNetworkPlayer(); 231 if( otherPlayer != NULL && thisPlayer->IsSameSystem(otherPlayer) ) 232 { 233 dontSend = true; 234 } 235 } 236 } 237 } 238 if( dontSend ) 239 { 240 continue; 241 } 242 243 // 4J Changed to get the flag index for the player before we send a packet. This flag is updated when we queue 244 // for send the first BlockRegionUpdatePacket for this chunk to that player/players system. Therefore there is no need to 245 // send tile updates or other updates until that has been sent 246 int flagIndex = ServerPlayer::getFlagIndexForChunk(pos, parent->dimension); 247 if (player->seenChunks.find(pos) != player->seenChunks.end() && (player->connection->isLocal() || g_NetworkManager.SystemFlagGet(player->connection->getNetworkPlayer(),flagIndex) )) 248 { 249 player->connection->send(packet); 250 sentTo.push_back(player); 251 } 252 } 253 // Now also check round all the players that are involved in this game. We also want to send the packet 254 // to them if their system hasn't received it already, but they have received the first BlockRegionUpdatePacket for this 255 // chunk 256 257 // Make sure we are only doing this for BlockRegionUpdatePacket, ChunkTilesUpdatePacket and TileUpdatePacket. 258 // We'll be potentially sending to players who aren't on the same level as this packet is intended for, 259 // and only these 3 packets have so far been updated to be able to encode the level so they are robust 260 // enough to cope with this 261 if(!( ( packet->getId() == 51 ) || ( packet->getId() == 52 ) || ( packet->getId() == 53 ) ) ) 262 { 263 return; 264 } 265 266 for( int i = 0; i < parent->level->getServer()->getPlayers()->players.size(); i++ ) 267 { 268 shared_ptr<ServerPlayer> player = parent->level->getServer()->getPlayers()->players[i]; 269 // Don't worry about local players, they get all their updates through sharing level with the server anyway 270 if ( player->connection == NULL ) continue; 271 if( player->connection->isLocal() ) continue; 272 273 // Don't worry about this player if they haven't had this chunk yet (this flag will be the 274 // same for all players on the same system) 275 int flagIndex = ServerPlayer::getFlagIndexForChunk(pos,parent->dimension); 276 if(!g_NetworkManager.SystemFlagGet(player->connection->getNetworkPlayer(),flagIndex)) continue; 277 278 // From here on the same rules as in the loop above - don't send it if we've already sent to the same system 279 bool dontSend = false; 280 if( sentTo.size() ) 281 { 282 INetworkPlayer *thisPlayer = player->connection->getNetworkPlayer(); 283 if( thisPlayer == NULL ) 284 { 285 dontSend = true; 286 } 287 else 288 { 289 for(unsigned int j = 0; j < sentTo.size(); j++ ) 290 { 291 shared_ptr<ServerPlayer> player2 = sentTo[j]; 292 INetworkPlayer *otherPlayer = player2->connection->getNetworkPlayer(); 293 if( otherPlayer != NULL && thisPlayer->IsSameSystem(otherPlayer) ) 294 { 295 dontSend = true; 296 } 297 } 298 } 299 } 300 if( !dontSend ) 301 { 302 player->connection->send(packet); 303 sentTo.push_back(player); 304 } 305 } 306} 307 308bool PlayerChunkMap::PlayerChunk::broadcastChanges(bool allowRegionUpdate) 309{ 310 bool didRegionUpdate = false; 311 ServerLevel *level = parent->getLevel(); 312 if( ticksToNextRegionUpdate > 0 ) ticksToNextRegionUpdate--; 313 if (changes == 0) 314 { 315 prioritised = false; 316 return false; 317 } 318 if (changes == 1) 319 { 320 int x = pos.x * 16 + xChangeMin; 321 int y = yChangeMin; 322 int z = pos.z * 16 + zChangeMin; 323 broadcast( shared_ptr<TileUpdatePacket>( new TileUpdatePacket(x, y, z, level) ) ); 324 if (level->isEntityTile(x, y, z)) 325 { 326 broadcast(level->getTileEntity(x, y, z)); 327 } 328 } 329 else if (changes == MAX_CHANGES_BEFORE_RESEND) 330 { 331 // 4J added, to allow limiting of region update packets created 332 if( !prioritised ) 333 { 334 if( !allowRegionUpdate || ( ticksToNextRegionUpdate > 0 ) ) 335 { 336 return false; 337 } 338 } 339 340 yChangeMin = yChangeMin / 2 * 2; 341 yChangeMax = (yChangeMax / 2 + 1) * 2; 342 int xp = xChangeMin + pos.x * 16; 343 int yp = yChangeMin; 344 int zp = zChangeMin + pos.z * 16; 345 int xs = xChangeMax - xChangeMin + 1; 346 int ys = yChangeMax - yChangeMin + 2; 347 int zs = zChangeMax - zChangeMin + 1; 348 349 // Fix for buf #95007 : TCR #001 BAS Game Stability: TU12: Code: Compliance: More than 192 dropped items causes game to freeze or crash. 350 // Block region update packets can only encode ys in a range of 1 - 256 351 if( ys > 256 ) ys = 256; 352 353 broadcast( shared_ptr<BlockRegionUpdatePacket>( new BlockRegionUpdatePacket(xp, yp, zp, xs, ys, zs, level) ) ); 354 vector<shared_ptr<TileEntity> > *tes = level->getTileEntitiesInRegion(xp, yp, zp, xp + xs, yp + ys, zp + zs); 355 for (unsigned int i = 0; i < tes->size(); i++) 356 { 357 broadcast(tes->at(i)); 358 } 359 delete tes; 360 ticksToNextRegionUpdate = MIN_TICKS_BETWEEN_REGION_UPDATE; 361 didRegionUpdate = true; 362 } 363 else 364 { 365 // 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 366 broadcast( shared_ptr<ChunkTilesUpdatePacket>( new ChunkTilesUpdatePacket(pos.x, pos.z, changedTiles, (byte)changes, level) ) ); 367 for (int i = 0; i < changes; i++) 368 { 369 int x = pos.x * 16 + ((changedTiles[i] >> 12) & 15); 370 int y = ((changedTiles[i]) & 255); 371 int z = pos.z * 16 + ((changedTiles[i] >> 8) & 15); 372 373 if (level->isEntityTile(x, y, z)) 374 { 375// System.out.println("Sending!"); 376 broadcast(level->getTileEntity(x, y, z)); 377 } 378 } 379 } 380 changes = 0; 381 prioritised = false; 382 return didRegionUpdate; 383} 384 385void PlayerChunkMap::PlayerChunk::broadcast(shared_ptr<TileEntity> te) 386{ 387 if (te != NULL) 388 { 389 shared_ptr<Packet> p = te->getUpdatePacket(); 390 if (p != NULL) 391 { 392 broadcast(p); 393 } 394 } 395} 396 397PlayerChunkMap::PlayerChunkMap(ServerLevel *level, int dimension, int radius) 398{ 399 assert(radius <= MAX_VIEW_DISTANCE); 400 assert(radius >= MIN_VIEW_DISTANCE); 401 this->radius = radius; 402 this->level = level; 403 this->dimension = dimension; 404 lastInhabitedUpdate = 0; 405} 406 407PlayerChunkMap::~PlayerChunkMap() 408{ 409 for( AUTO_VAR(it, chunks.begin()); it != chunks.end(); it++ ) 410 { 411 delete it->second; 412 } 413} 414 415ServerLevel *PlayerChunkMap::getLevel() 416{ 417 return level; 418} 419 420void PlayerChunkMap::tick() 421{ 422 __int64 time = level->getGameTime(); 423 424 if (time - lastInhabitedUpdate > Level::TICKS_PER_DAY / 3) 425 { 426 lastInhabitedUpdate = time; 427 428 for (int i = 0; i < knownChunks.size(); i++) 429 { 430 PlayerChunk *chunk = knownChunks.at(i); 431 432 // 4J Stu - Going to let our changeChunks handler below deal with this 433 //chunk.broadcastChanges(); 434 435 chunk->updateInhabitedTime(); 436 } 437 } 438 439 // 4J - some changes here so that we only send one region update per tick. The chunks themselves also 440 // limit their resend rate to once every MIN_TICKS_BETWEEN_REGION_UPDATE ticks 441 bool regionUpdateSent = false; 442 for (unsigned int i = 0; i < changedChunks.size();) 443 { 444 regionUpdateSent |= changedChunks[i]->broadcastChanges(!regionUpdateSent); 445 // Changes will be 0 if the chunk actually sent something, in which case we can delete it from this array 446 if( changedChunks[i]->changes == 0 ) 447 { 448 changedChunks[i] = changedChunks.back(); 449 changedChunks.pop_back(); 450 } 451 else 452 { 453 // Limiting of some kind means we didn't send this chunk so move onto the next 454 i++; 455 } 456 } 457 458 for( unsigned int i = 0; i < players.size(); i++ ) 459 { 460 tickAddRequests(players[i]); 461 } 462 463 // 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 464 //if (players.isEmpty()) { 465 // ServerLevel level = server.getLevel(this.dimension); 466 // Dimension dimension = level.dimension; 467 // if (!dimension.mayRespawn()) { 468 // level.cache.dropAll(); 469 // } 470 //} 471} 472 473bool PlayerChunkMap::hasChunk(int x, int z) 474{ 475 __int64 id = (x + 0x7fffffffLL) | ((z + 0x7fffffffLL) << 32); 476 return chunks.find(id) != chunks.end(); 477} 478 479PlayerChunkMap::PlayerChunk *PlayerChunkMap::getChunk(int x, int z, bool create) 480{ 481 __int64 id = (x + 0x7fffffffLL) | ((z + 0x7fffffffLL) << 32); 482 AUTO_VAR(it, chunks.find(id)); 483 484 PlayerChunk *chunk = NULL; 485 if( it != chunks.end() ) 486 { 487 chunk = it->second; 488 } 489 else if ( create) 490 { 491 chunk = new PlayerChunk(x, z, this); 492 chunks[id] = chunk; 493 knownChunks.push_back(chunk); 494 } 495 496 return chunk; 497} 498 499// 4J - added. If a chunk exists, add a player to it straight away. If it doesn't exist, 500// queue a request for it to be created. 501void PlayerChunkMap::getChunkAndAddPlayer(int x, int z, shared_ptr<ServerPlayer> player) 502{ 503 __int64 id = (x + 0x7fffffffLL) | ((z + 0x7fffffffLL) << 32); 504 AUTO_VAR(it, chunks.find(id)); 505 506 if( it != chunks.end() ) 507 { 508 it->second->add(player); 509 } 510 else 511 { 512 addRequests.push_back(PlayerChunkAddRequest(x,z,player)); 513 } 514} 515 516// 4J - added. If the chunk and player are in the queue to be added, remove from there. Otherwise 517// attempt to remove from main chunk map. 518void PlayerChunkMap::getChunkAndRemovePlayer(int x, int z, shared_ptr<ServerPlayer> player) 519{ 520 for( AUTO_VAR(it, addRequests.begin()); it != addRequests.end(); it++ ) 521 { 522 if( ( it->x == x ) && 523 ( it->z == z ) && 524 ( it->player == player ) ) 525 { 526 addRequests.erase(it); 527 return; 528 } 529 } 530 __int64 id = (x + 0x7fffffffLL) | ((z + 0x7fffffffLL) << 32); 531 AUTO_VAR(it, chunks.find(id)); 532 533 if( it != chunks.end() ) 534 { 535 it->second->remove(player); 536 } 537} 538 539// 4J - added - actually create & add player to a playerchunk, if there is one queued for this player. 540void PlayerChunkMap::tickAddRequests(shared_ptr<ServerPlayer> player) 541{ 542 if( addRequests.size() ) 543 { 544 // Find the nearest chunk request to the player 545 int px = (int)player->x; 546 int pz = (int)player->z; 547 int minDistSq = -1; 548 549 AUTO_VAR(itNearest, addRequests.end()); 550 for( AUTO_VAR(it, addRequests.begin()); it != addRequests.end(); it++ ) 551 { 552 if( it->player == player ) 553 { 554 int xm = ( it->x * 16 ) + 8; 555 int zm = ( it->z * 16 ) + 8; 556 int distSq = (xm - px) * (xm - px) + 557 (zm - pz) * (zm - pz); 558 if( ( minDistSq == -1 ) || ( distSq < minDistSq ) ) 559 { 560 minDistSq = distSq; 561 itNearest = it; 562 } 563 } 564 } 565 566 // If we found one at all, then do this one 567 if( itNearest != addRequests.end() ) 568 { 569 getChunk(itNearest->x, itNearest->z, true)->add(itNearest->player); 570 addRequests.erase(itNearest); 571 } 572 } 573} 574 575void PlayerChunkMap::broadcastTileUpdate(shared_ptr<Packet> packet, int x, int y, int z) 576{ 577 int xc = x >> 4; 578 int zc = z >> 4; 579 PlayerChunk *chunk = getChunk(xc, zc, false); 580 if (chunk != NULL) 581 { 582 chunk->broadcast(packet); 583 } 584} 585 586void PlayerChunkMap::tileChanged(int x, int y, int z) 587{ 588 int xc = x >> 4; 589 int zc = z >> 4; 590 PlayerChunk *chunk = getChunk(xc, zc, false); 591 if (chunk != NULL) 592 { 593 chunk->tileChanged(x & 15, y, z & 15); 594 } 595} 596 597bool PlayerChunkMap::isTrackingTile(int x, int y, int z) 598{ 599 int xc = x >> 4; 600 int zc = z >> 4; 601 PlayerChunk *chunk = getChunk(xc, zc, false); 602 if( chunk ) return true; 603 return false; 604} 605 606// 4J added - make sure that any tile updates for the chunk at this location get prioritised for sending 607void PlayerChunkMap::prioritiseTileChanges(int x, int y, int z) 608{ 609 int xc = x >> 4; 610 int zc = z >> 4; 611 PlayerChunk *chunk = getChunk(xc, zc, false); 612 if (chunk != NULL) 613 { 614 chunk->prioritiseTileChanges(); 615 } 616} 617 618void PlayerChunkMap::add(shared_ptr<ServerPlayer> player) 619{ 620 static int direction[4][2] = { { 1, 0 }, { 0, 1 }, { -1, 0 }, {0, -1} }; 621 622 int xc = (int) player->x >> 4; 623 int zc = (int) player->z >> 4; 624 625 player->lastMoveX = player->x; 626 player->lastMoveZ = player->z; 627 628// for (int x = xc - radius; x <= xc + radius; x++) 629// for (int z = zc - radius; z <= zc + radius; z++) { 630// getChunk(x, z, true).add(player); 631// } 632 633 // CraftBukkit start 634 int facing = 0; 635 int size = radius; 636 int dx = 0; 637 int dz = 0; 638 639 // Origin 640 getChunk(xc, zc, true)->add(player, false); 641 642 // 4J Added so we send an area packet rather than one visibility packet per chunk 643 int minX, maxX, minZ, maxZ; 644 minX = maxX = xc; 645 minZ = maxZ = zc; 646 647 // 4J - added so that we don't fully create/send every chunk at this stage. Particularly since moving on to large worlds, where 648 // 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 649 // the getChunkAndAddPlayer for anything but the central region of chunks, which adds them to a queue of chunks which are added 650 // one per tick per player. 651 const int maxLegSizeToAddNow = 14; 652 653 // All but the last leg 654 for (int legSize = 1; legSize <= size * 2; legSize++) 655 { 656 for (int leg = 0; leg < 2; leg++) 657 { 658 int *dir = direction[facing++ % 4]; 659 660 for (int k = 0; k < legSize; k++) 661 { 662 dx += dir[0]; 663 dz += dir[1]; 664 665 int targetX, targetZ; 666 targetX = xc + dx; 667 targetZ = zc + dz; 668 669 if( ( legSize < maxLegSizeToAddNow ) || 670 ( ( legSize == maxLegSizeToAddNow ) && ( ( leg == 0 ) || ( k < ( legSize - 1 ) ) ) ) ) 671 { 672 if( targetX > maxX ) maxX = targetX; 673 if( targetX < minX ) minX = targetX; 674 if( targetZ > maxZ ) maxZ = targetZ; 675 if( targetZ < minZ ) minZ = targetZ; 676 677 getChunk(targetX, targetZ, true)->add(player, false); 678 } 679 else 680 { 681 getChunkAndAddPlayer(targetX, targetZ, player); 682 } 683 } 684 } 685 } 686 687 // Final leg 688 facing %= 4; 689 for (int k = 0; k < size * 2; k++) 690 { 691 dx += direction[facing][0]; 692 dz += direction[facing][1]; 693 694 int targetX, targetZ; 695 targetX = xc + dx; 696 targetZ = zc + dz; 697 if( ( size * 2 ) <= maxLegSizeToAddNow ) 698 { 699 if( targetX > maxX ) maxX = targetX; 700 if( targetX < minX ) minX = targetX; 701 if( targetZ > maxZ ) maxZ = targetZ; 702 if( targetZ < minZ ) minZ = targetZ; 703 704 getChunk(targetX, targetZ, true)->add(player, false); 705 } 706 else 707 { 708 getChunkAndAddPlayer(targetX, targetZ, player); 709 } 710 } 711 // CraftBukkit end 712 713 player->connection->send( shared_ptr<ChunkVisibilityAreaPacket>( new ChunkVisibilityAreaPacket(minX, maxX, minZ, maxZ) ) ); 714 715#ifdef _LARGE_WORLDS 716 getLevel()->cache->dontDrop(xc,zc); 717#endif 718 719 players.push_back(player); 720 721} 722 723void PlayerChunkMap::remove(shared_ptr<ServerPlayer> player) 724{ 725 int xc = ((int) player->lastMoveX) >> 4; 726 int zc = ((int) player->lastMoveZ) >> 4; 727 728 for (int x = xc - radius; x <= xc + radius; x++) 729 for (int z = zc - radius; z <= zc + radius; z++) 730 { 731 PlayerChunk *playerChunk = getChunk(x, z, false); 732 if (playerChunk != NULL) playerChunk->remove(player); 733 } 734 735 AUTO_VAR(it, find(players.begin(),players.end(),player)); 736 if( players.size() > 0 && it != players.end() ) 737 players.erase(find(players.begin(),players.end(),player)); 738 739 // 4J - added - also remove any queued requests to be added to playerchunks here 740 for( AUTO_VAR(it, addRequests.begin()); it != addRequests.end(); ) 741 { 742 if( it->player == player ) 743 { 744 it = addRequests.erase(it); 745 } 746 else 747 { 748 ++it; 749 } 750 } 751 752} 753 754bool PlayerChunkMap::chunkInRange(int x, int z, int xc, int zc) 755{ 756 // If the distance between x and xc 757 int xd = x - xc; 758 int zd = z - zc; 759 if (xd < -radius || xd > radius) return false; 760 if (zd < -radius || zd > radius) return false; 761 return true; 762} 763 764// 4J - have changed this so that we queue requests to add the player to chunks if they 765// need to be created, so that we aren't creating potentially 20 chunks per player per tick 766void PlayerChunkMap::move(shared_ptr<ServerPlayer> player) 767{ 768 int xc = ((int) player->x) >> 4; 769 int zc = ((int) player->z) >> 4; 770 771 double _xd = player->lastMoveX - player->x; 772 double _zd = player->lastMoveZ - player->z; 773 double dist = _xd * _xd + _zd * _zd; 774 if (dist < 8 * 8) return; 775 776 int last_xc = ((int) player->lastMoveX) >> 4; 777 int last_zc = ((int) player->lastMoveZ) >> 4; 778 779 int xd = xc - last_xc; 780 int zd = zc - last_zc; 781 if (xd == 0 && zd == 0) return; 782 783 for (int x = xc - radius; x <= xc + radius; x++) 784 for (int z = zc - radius; z <= zc + radius; z++) 785 { 786 if (!chunkInRange(x, z, last_xc, last_zc)) 787 { 788 // 4J - changed from separate getChunk & add so we can wrap these operations up and queue 789 getChunkAndAddPlayer(x, z, player); 790 } 791 792 if (!chunkInRange(x - xd, z - zd, xc, zc)) 793 { 794 // 4J - changed from separate getChunk & remove so we can wrap these operations up and queue 795 getChunkAndRemovePlayer(x - xd, z - zd, player); 796 } 797 } 798 799 player->lastMoveX = player->x; 800 player->lastMoveZ = player->z; 801} 802 803int PlayerChunkMap::getMaxRange() 804{ 805 return radius * 16 - 16; 806} 807 808bool PlayerChunkMap::isPlayerIn(shared_ptr<ServerPlayer> player, int xChunk, int zChunk) 809{ 810 PlayerChunk *chunk = getChunk(xChunk, zChunk, false); 811 812 if(chunk == NULL) 813 { 814 return false; 815 } 816 else 817 { 818 AUTO_VAR(it1, find(chunk->players.begin(), chunk->players.end(), player)); 819 AUTO_VAR(it2, find(player->chunksToSend.begin(), player->chunksToSend.end(), chunk->pos)); 820 return it1 != chunk->players.end() && it2 == player->chunksToSend.end(); 821 } 822 823 //return chunk == NULL ? false : chunk->players->contains(player) && !player->chunksToSend->contains(chunk->pos); 824} 825 826int PlayerChunkMap::convertChunkRangeToBlock(int radius) 827{ 828 return radius * 16 - 16; 829} 830 831// AP added for Vita so the range can be increased once the level starts 832void PlayerChunkMap::setRadius(int newRadius) 833{ 834 if( radius != newRadius ) 835 { 836 PlayerList* players = level->getServer()->getPlayerList(); 837 for( int i = 0;i < players->players.size();i += 1 ) 838 { 839 shared_ptr<ServerPlayer> player = players->players[i]; 840 if( player->level == level ) 841 { 842 int xc = ((int) player->x) >> 4; 843 int zc = ((int) player->z) >> 4; 844 845 for (int x = xc - newRadius; x <= xc + newRadius; x++) 846 for (int z = zc - newRadius; z <= zc + newRadius; z++) 847 { 848 // check if this chunk is outside the old radius area 849 if ( x < xc - radius || x > xc + radius || z < zc - radius || z > zc + radius ) 850 { 851 getChunkAndAddPlayer(x, z, player); 852 } 853 } 854 } 855 } 856 857 assert(radius <= MAX_VIEW_DISTANCE); 858 assert(radius >= MIN_VIEW_DISTANCE); 859 this->radius = newRadius; 860 } 861}