the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 1624 lines 50 kB view raw
1#include "stdafx.h" 2#include "ServerLevel.h" 3#include "MinecraftServer.h" 4#include "ServerChunkCache.h" 5#include "PlayerList.h" 6#include "ServerPlayer.h" 7#include "PlayerConnection.h" 8#include "EntityTracker.h" 9#include "ServerScoreboard.h" 10#include "..\Minecraft.World\ScoreboardSaveData.h" 11#include "..\Minecraft.World\net.minecraft.world.h" 12#include "..\Minecraft.World\net.minecraft.world.item.h" 13#include "..\Minecraft.World\net.minecraft.world.entity.h" 14#include "..\Minecraft.World\net.minecraft.world.entity.ai.village.h" 15#include "..\Minecraft.World\net.minecraft.world.entity.global.h" 16#include "..\Minecraft.World\net.minecraft.world.entity.npc.h" 17#include "..\Minecraft.World\net.minecraft.world.entity.player.h" 18#include "..\Minecraft.World\net.minecraft.world.level.h" 19#include "..\Minecraft.World\net.minecraft.world.level.biome.h" 20#include "..\Minecraft.World\net.minecraft.world.level.chunk.h" 21#include "..\Minecraft.World\net.minecraft.world.level.dimension.h" 22#include "..\Minecraft.World\net.minecraft.world.level.levelgen.feature.h" 23#include "..\Minecraft.World\net.minecraft.world.level.storage.h" 24#include "..\Minecraft.World\net.minecraft.world.level.tile.entity.h" 25#include "..\Minecraft.World\net.minecraft.world.scores.h" 26#include "..\Minecraft.World\ItemEntity.h" 27#include "..\Minecraft.World\Arrow.h" 28#include "..\Minecraft.World\PrimedTnt.h" 29#include "..\Minecraft.World\FallingTile.h" 30#include "..\Minecraft.World\net.minecraft.network.packet.h" 31#include "..\Minecraft.World\Mth.h" 32#include "..\Minecraft.World\StructurePiece.h" 33#include "..\Minecraft.Client\ServerLevelListener.h" 34#include "..\Minecraft.World\WeighedTreasure.h" 35#include "TexturePackRepository.h" 36#include "DLCTexturePack.h" 37#include "..\Minecraft.World\ProgressListener.h" 38#include "PS3\PS3Extras\ShutdownManager.h" 39#include "PlayerChunkMap.h" 40 41WeighedTreasureArray ServerLevel::RANDOM_BONUS_ITEMS; 42 43C4JThread* ServerLevel::m_updateThread = NULL; 44C4JThread::EventArray* ServerLevel::m_updateTrigger; 45CRITICAL_SECTION ServerLevel::m_updateCS[3]; 46 47Level *ServerLevel::m_level[3]; 48int ServerLevel::m_updateChunkX[3][LEVEL_CHUNKS_TO_UPDATE_MAX]; 49int ServerLevel::m_updateChunkZ[3][LEVEL_CHUNKS_TO_UPDATE_MAX]; 50int ServerLevel::m_updateChunkCount[3]; 51int ServerLevel::m_updateTileX[3][MAX_UPDATES]; 52int ServerLevel::m_updateTileY[3][MAX_UPDATES]; 53int ServerLevel::m_updateTileZ[3][MAX_UPDATES]; 54int ServerLevel::m_updateTileCount[3]; 55int ServerLevel::m_randValue[3]; 56 57void ServerLevel::staticCtor() 58{ 59 m_updateTrigger = new C4JThread::EventArray(3); 60 InitializeCriticalSection(&m_updateCS[0]); 61 InitializeCriticalSection(&m_updateCS[1]); 62 InitializeCriticalSection(&m_updateCS[2]); 63 64 m_updateThread = new C4JThread(runUpdate, NULL, "Tile update"); 65 m_updateThread->SetProcessor(CPU_CORE_TILE_UPDATE); 66#ifdef __ORBIS__ 67 m_updateThread->SetPriority(THREAD_PRIORITY_BELOW_NORMAL); // On Orbis, this core is also used for Matching 2, and that priority of that seems to be always at default no matter what we set it to. Prioritise this below Matching 2. 68#endif 69 m_updateThread->Run(); 70 71 RANDOM_BONUS_ITEMS = WeighedTreasureArray(20); 72 73 RANDOM_BONUS_ITEMS[0] = new WeighedTreasure(Item::stick_Id, 0, 1, 3, 10); 74 RANDOM_BONUS_ITEMS[1] = new WeighedTreasure(Tile::wood_Id, 0, 1, 3, 10); 75 RANDOM_BONUS_ITEMS[2] = new WeighedTreasure(Tile::treeTrunk_Id, 0, 1, 3, 10); 76 RANDOM_BONUS_ITEMS[3] = new WeighedTreasure(Item::hatchet_stone_Id, 0, 1, 1, 3); 77 RANDOM_BONUS_ITEMS[4] = new WeighedTreasure(Item::hatchet_wood_Id, 0, 1, 1, 5); 78 RANDOM_BONUS_ITEMS[5] = new WeighedTreasure(Item::pickAxe_stone_Id, 0, 1, 1, 3); 79 RANDOM_BONUS_ITEMS[6] = new WeighedTreasure(Item::pickAxe_wood_Id, 0, 1, 1, 5); 80 RANDOM_BONUS_ITEMS[7] = new WeighedTreasure(Item::apple_Id, 0, 2, 3, 5); 81 RANDOM_BONUS_ITEMS[8] = new WeighedTreasure(Item::bread_Id, 0, 2, 3, 3); 82 // 4J-PB - new items 83 RANDOM_BONUS_ITEMS[9] = new WeighedTreasure(Tile::sapling_Id, 0, 4, 4, 2); 84 RANDOM_BONUS_ITEMS[10] = new WeighedTreasure(Tile::sapling_Id, 1, 4, 4, 2); 85 RANDOM_BONUS_ITEMS[11] = new WeighedTreasure(Tile::sapling_Id, 2, 4, 4, 2); 86 RANDOM_BONUS_ITEMS[12] = new WeighedTreasure(Tile::sapling_Id, 3, 4, 4, 4); 87 RANDOM_BONUS_ITEMS[13] = new WeighedTreasure(Item::seeds_melon_Id, 0, 1, 2, 3); 88 RANDOM_BONUS_ITEMS[14] = new WeighedTreasure(Item::seeds_pumpkin_Id, 0, 1, 2, 3); 89 RANDOM_BONUS_ITEMS[15] = new WeighedTreasure(Tile::cactus_Id, 0, 1, 2, 3); 90 RANDOM_BONUS_ITEMS[16] = new WeighedTreasure(Item::dye_powder_Id, DyePowderItem::BROWN, 1, 2, 2); 91 RANDOM_BONUS_ITEMS[17] = new WeighedTreasure(Item::potato_Id, 0, 1, 2, 3); 92 RANDOM_BONUS_ITEMS[18] = new WeighedTreasure(Item::carrots_Id, 0, 1, 2, 3); 93 RANDOM_BONUS_ITEMS[19] = new WeighedTreasure(Tile::mushroom_brown_Id, 0, 1, 2, 2); 94 95}; 96 97ServerLevel::ServerLevel(MinecraftServer *server, shared_ptr<LevelStorage>levelStorage, const wstring& levelName, int dimension, LevelSettings *levelSettings) : Level(levelStorage, levelName, levelSettings, Dimension::getNew(dimension), false) 98{ 99 InitializeCriticalSection(&m_limiterCS); 100 InitializeCriticalSection(&m_tickNextTickCS); 101 InitializeCriticalSection(&m_csQueueSendTileUpdates); 102 m_fallingTileCount = 0; 103 m_primedTntCount = 0; 104 105 // 4J - this this used to be called in parent ctor via a virtual fn 106 chunkSource = createChunkSource(); 107 // 4J - optimisation - keep direct reference of underlying cache here 108 chunkSourceCache = chunkSource->getCache(); 109 chunkSourceXZSize = chunkSource->m_XZSize; 110 111 // 4J - The listener used to be added in MinecraftServer::loadLevel but we need it to be set up before we do the next couple of things, or else chunks get loaded before we have the entity tracker set up to listen to them 112 this->server = server; 113 server->setLevel(dimension, this); // The listener needs the server to have the level set up... this will be set up anyway on return of this ctor but setting up early here 114 addListener(new ServerLevelListener(server, this)); 115 116 tracker = new EntityTracker(this); 117 chunkMap = new PlayerChunkMap(this, dimension, server->getPlayers()->getViewDistance()); 118 119 mobSpawner = new MobSpawner(); 120 portalForcer = new PortalForcer(this); 121 scoreboard = new ServerScoreboard(server); 122 123 //shared_ptr<ScoreboardSaveData> scoreboardSaveData = dynamic_pointer_cast<ScoreboardSaveData>( savedDataStorage->get(typeid(ScoreboardSaveData), ScoreboardSaveData::FILE_ID) ); 124 //if (scoreboardSaveData == NULL) 125 //{ 126 // scoreboardSaveData = shared_ptr<ScoreboardSaveData>( new ScoreboardSaveData() ); 127 // savedDataStorage->set(ScoreboardSaveData::FILE_ID, scoreboardSaveData); 128 //} 129 //scoreboardSaveData->setScoreboard(scoreboard); 130 //((ServerScoreboard *) scoreboard)->setSaveData(scoreboardSaveData); 131 132 // This also used to be called in parent ctor, but can't be called until chunkSource is created. Call now if required. 133 if (!levelData->isInitialized()) 134 { 135 initializeLevel(levelSettings); 136 levelData->setInitialized(true); 137 } 138 else if ( (dimension==0) && levelData->getSpawnBonusChest() ) // 4J-JEV, still would like bonus chests to respawn. 139 { 140 // 4J - added isFindingSpawn as we want any chunks we are looking in here for suitable locations for the bonus chest to actually create those chunks rather than just get emptychunks if they aren't loaded 141 isFindingSpawn = true; 142 generateBonusItemsNearSpawn(); 143 isFindingSpawn = false; 144 } 145 146 // 4J - added initialisers 147 // 4J Stu - Allowing spawn edit for our game, and consider a better solution for the possible griefing 148 canEditSpawn = true; //false; 149 noSave = false; 150 allPlayersSleeping = false; 151 m_bAtLeastOnePlayerSleeping = false; 152 emptyTime = 0; 153 activeTileEventsList = 0; 154 155#ifdef _LARGE_WORLDS 156 saveInterval = 3; 157#else 158 saveInterval = 20 * 2; 159#endif 160} 161 162ServerLevel::~ServerLevel() 163{ 164 delete portalForcer; 165 delete mobSpawner; 166 167 EnterCriticalSection(&m_csQueueSendTileUpdates); 168 for(AUTO_VAR(it, m_queuedSendTileUpdates.begin()); it != m_queuedSendTileUpdates.end(); ++it) 169 { 170 Pos *p = *it; 171 delete p; 172 } 173 m_queuedSendTileUpdates.clear(); 174 175 delete this->tracker; // MGH - added, we were losing about 500K going in and out the menus 176 delete this->chunkMap; 177 178 LeaveCriticalSection(&m_csQueueSendTileUpdates); 179 DeleteCriticalSection(&m_csQueueSendTileUpdates); 180 DeleteCriticalSection(&m_limiterCS); 181 DeleteCriticalSection(&m_tickNextTickCS); 182 183 // Make sure that the update thread isn't actually doing any updating 184 EnterCriticalSection(&m_updateCS[0]); 185 LeaveCriticalSection(&m_updateCS[0]); 186 EnterCriticalSection(&m_updateCS[1]); 187 LeaveCriticalSection(&m_updateCS[1]); 188 EnterCriticalSection(&m_updateCS[2]); 189 LeaveCriticalSection(&m_updateCS[2]); 190 m_updateTrigger->ClearAll(); 191} 192 193 194void ServerLevel::tick() 195{ 196 Level::tick(); 197 if (getLevelData()->isHardcore() && difficulty < 3) 198 { 199 difficulty = 3; 200 } 201 202 dimension->biomeSource->update(); 203 204 if (allPlayersAreSleeping()) 205 { 206 if (getGameRules()->getBoolean(GameRules::RULE_DAYLIGHT)) 207 { 208 // skip time until new day 209 __int64 newTime = levelData->getDayTime() + TICKS_PER_DAY; 210 211 // 4J : WESTY : Changed so that time update goes through stats tracking update code. 212 //levelData->setTime(newTime - (newTime % TICKS_PER_DAY)); 213 setDayTime(newTime - (newTime % TICKS_PER_DAY)); 214 } 215 awakenAllPlayers(); 216 } 217 218 PIXBeginNamedEvent(0,"Mob spawner tick"); 219 // for Minecraft 1.8, spawn friendlies really rarely - 4J - altered from once every 400 ticks to 40 ticks as we depend on this a more than the original since we don't have chunk post-process spawning 220 if (getGameRules()->getBoolean(GameRules::RULE_DOMOBSPAWNING)) 221 { 222 // Note - these flags are used logically in an inverted way. Mob spawning is not performed if: 223 // (1) finalSpawnEnemies isn't set, and mob category isn't friendly 224 // (2) finalSpawnFriendlies isn't set, and mob category is friendly 225 // (3) finalSpawnPersistent isn't set, and mob category is persistent 226 bool finalSpawnEnemies = spawnEnemies && ((levelData->getGameTime() % 2) == 0); // Spawn enemies every other tick 227 bool finalSpawnFriendlies = spawnFriendlies && ((levelData->getGameTime() % 40) == 0); // Spawn friendlies once per 40 ticks 228 bool finalSpawnPersistent = finalSpawnFriendlies && ((levelData->getGameTime() % 80) == 0); // All persistents are also friendly - do them once every other friendly spawning, ie once per 80 ticks 229 mobSpawner->tick(this, finalSpawnEnemies, finalSpawnFriendlies, finalSpawnPersistent); 230 } 231 PIXEndNamedEvent(); 232 PIXBeginNamedEvent(0,"Chunk source tick"); 233 chunkSource->tick(); 234 PIXEndNamedEvent(); 235 int newDark = getOldSkyDarken(1); 236 if (newDark != skyDarken) 237 { 238 skyDarken = newDark; 239 if (!SharedConstants::TEXTURE_LIGHTING) // 4J - change brought forward from 1.8.2 240 { 241 AUTO_VAR(itEnd, listeners.end()); 242 for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) 243 { 244 (*it)->skyColorChanged(); 245 } 246 } 247 } 248 249 //4J - temporarily disabling saves as they are causing gameplay to generally stutter quite a lot 250 251 __int64 time = levelData->getGameTime() + 1; 252 // 4J Stu - Putting this back in, but I have reduced the number of chunks that save when not forced 253#ifdef _LARGE_WORLDS 254 if (time % (saveInterval) == (dimension->id + 1)) 255#else 256 if (time % (saveInterval) == (dimension->id * dimension->id * (saveInterval/2))) 257#endif 258 { 259 //app.DebugPrintf("Incremental save\n"); 260 PIXBeginNamedEvent(0,"Incremental save"); 261 save(false, NULL); 262 PIXEndNamedEvent(); 263 } 264 265 // 4J : WESTY : Changed so that time update goes through stats tracking update code. 266 //levelData->setTime(time); 267 setGameTime(levelData->getGameTime() + 1); 268 if (getGameRules()->getBoolean(GameRules::RULE_DAYLIGHT)) 269 { 270 // 4J: Debug setting added to keep it at day time 271#ifndef _FINAL_BUILD 272 bool freezeTime = app.DebugSettingsOn() && app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<<eDebugSetting_FreezeTime); 273 if (!freezeTime) 274#endif 275 { 276 setDayTime(levelData->getDayTime() + 1); 277 } 278 } 279 280 PIXBeginNamedEvent(0,"Tick pending ticks"); 281 // if (tickCount % 5 == 0) { 282 tickPendingTicks(false); 283 PIXEndNamedEvent(); 284 285 PIXBeginNamedEvent(0,"Tick tiles"); 286 MemSect(18); 287 tickTiles(); 288 MemSect(0); 289 PIXEndNamedEvent(); 290 291 chunkMap->tick(); 292 293 PIXBeginNamedEvent(0,"Tick villages"); 294 //MemSect(18); 295 villages->tick(); 296 villageSiege->tick(); 297 //MemSect(0); 298 PIXEndNamedEvent(); 299 300 PIXBeginNamedEvent(0,"Tick portal forcer"); 301 portalForcer->tick(getGameTime()); 302 PIXEndNamedEvent(); 303 304 // repeat after tile ticks 305 PIXBeginNamedEvent(0,"runTileEvents"); 306 runTileEvents(); 307 PIXEndNamedEvent(); 308 309 // 4J Added 310 runQueuedSendTileUpdates(); 311} 312 313Biome::MobSpawnerData *ServerLevel::getRandomMobSpawnAt(MobCategory *mobCategory, int x, int y, int z) 314{ 315 vector<Biome::MobSpawnerData *> *mobList = getChunkSource()->getMobsAt(mobCategory, x, y, z); 316 if (mobList == NULL || mobList->empty()) return NULL; 317 318 return (Biome::MobSpawnerData *) WeighedRandom::getRandomItem(random, (vector<WeighedRandomItem *> *)mobList); 319} 320 321void ServerLevel::updateSleepingPlayerList() 322{ 323 allPlayersSleeping = !players.empty(); 324 m_bAtLeastOnePlayerSleeping = false; 325 326 AUTO_VAR(itEnd, players.end()); 327 for (AUTO_VAR(it, players.begin()); it != itEnd; it++) 328 { 329 if (!(*it)->isSleeping()) 330 { 331 allPlayersSleeping = false; 332 //break; 333 } 334 else 335 { 336 m_bAtLeastOnePlayerSleeping = true; 337 } 338 if(m_bAtLeastOnePlayerSleeping && !allPlayersSleeping) break; 339 } 340} 341 342void ServerLevel::awakenAllPlayers() 343{ 344 allPlayersSleeping = false; 345 m_bAtLeastOnePlayerSleeping = false; 346 347 AUTO_VAR(itEnd, players.end()); 348 for (vector<shared_ptr<Player> >::iterator it = players.begin(); it != itEnd; it++) 349 { 350 if ((*it)->isSleeping()) 351 { 352 (*it)->stopSleepInBed(false, false, true); 353 } 354 } 355 356 stopWeather(); 357} 358 359void ServerLevel::stopWeather() 360{ 361 levelData->setRainTime(0); 362 levelData->setRaining(false); 363 levelData->setThunderTime(0); 364 levelData->setThundering(false); 365} 366 367bool ServerLevel::allPlayersAreSleeping() 368{ 369 if (allPlayersSleeping && !isClientSide) 370 { 371 // all players are sleeping, but have they slept long enough? 372 AUTO_VAR(itEnd, players.end()); 373 for (vector<shared_ptr<Player> >::iterator it = players.begin(); it != itEnd; it++ ) 374 { 375 // System.out.println(player->entityId + ": " + player->getSleepTimer()); 376 if (! (*it)->isSleepingLongEnough()) 377 { 378 return false; 379 } 380 } 381 // yep 382 return true; 383 } 384 return false; 385} 386 387void ServerLevel::validateSpawn() 388{ 389 if (levelData->getYSpawn() <= 0) 390 { 391 levelData->setYSpawn(genDepth / 2); 392 } 393 int xSpawn = levelData->getXSpawn(); 394 int zSpawn = levelData->getZSpawn(); 395 int tries = 0; 396 while (getTopTile(xSpawn, zSpawn) == 0) 397 { 398 xSpawn += random->nextInt(8) - random->nextInt(8); 399 zSpawn += random->nextInt(8) - random->nextInt(8); 400 if (++tries == 10000) break; 401 } 402 levelData->setXSpawn(xSpawn); 403 levelData->setZSpawn(zSpawn); 404} 405 406// 4J - Changes made here to move a section of code (which randomly determines which tiles in the current chunks to tick, and is very cache unfriendly by nature) 407// This code now has a thread of its own so it can wait all it wants on the cache without holding the main game thread up. This slightly changes how things are 408// processed, as we now tick the tiles that were determined in the previous tick. Have also limited the amount of tiles to be ticked to 256 (it never seemed to creep 409// up much beyond this in normal play anyway, and we need some finite limit). 410void ServerLevel::tickTiles() 411{ 412 // Index into the arrays used by the update thread 413 int iLev = 0; 414 if( dimension->id == -1 ) 415 { 416 iLev = 1; 417 } 418 else if( dimension->id == 1 ) 419 { 420 iLev = 2; 421 } 422 chunksToPoll.clear(); 423 424 unsigned int tickCount = 0; 425 426 EnterCriticalSection(&m_updateCS[iLev]); 427 // This section processes the tiles that need to be ticked, which we worked out in the previous tick (or haven't yet, if this is the first frame) 428 /*int grassTicks = 0; 429 int lavaTicks = 0; 430 int otherTicks = 0;*/ 431 for( int i = 0; i < m_updateTileCount[iLev]; i++ ) 432 { 433 int x = m_updateTileX[iLev][i]; 434 int y = m_updateTileY[iLev][i]; 435 int z = m_updateTileZ[iLev][i]; 436 if( hasChunkAt(x,y,z) ) 437 { 438 int id = getTile(x,y,z); 439 if (Tile::tiles[id] != NULL && Tile::tiles[id]->isTicking()) 440 { 441 /*if(id == 2) ++grassTicks; 442 else if(id == 11) ++lavaTicks; 443 else ++otherTicks;*/ 444 Tile::tiles[id]->tick(this, x, y, z, random); 445 } 446 } 447 } 448 //printf("Total ticks - Grass: %d, Lava: %d, Other: %d, Total: %d\n", grassTicks, lavaTicks, otherTicks, grassTicks + lavaTicks + otherTicks); 449 m_updateTileCount[iLev] = 0; 450 m_updateChunkCount[iLev] = 0; 451 LeaveCriticalSection(&m_updateCS[iLev]); 452 453 Level::tickTiles(); 454 455 // AP moved this outside of the loop 456 int prob = 100000; 457 if(app.GetGameSettingsDebugMask()&(1L<<eDebugSetting_RegularLightning)) prob = 100; 458 459#ifdef __PSVITA__ 460 // AP - see CustomSet.h for and explanation 461 for( int i = 0;i < chunksToPoll.end();i += 1 ) 462 { 463 ChunkPos cp = chunksToPoll.get(i); 464#else 465 AUTO_VAR(itEndCtp, chunksToPoll.end()); 466 for (AUTO_VAR(it, chunksToPoll.begin()); it != itEndCtp; it++) 467 { 468 ChunkPos cp = *it; 469#endif 470 int xo = cp.x * 16; 471 int zo = cp.z * 16; 472 473 // 4J added - don't let this actually load/create any chunks, we'll let the normal updateDirtyChunks etc. processes do that, so it can happen on another thread 474 if( !this->hasChunk(cp.x, cp.z) ) continue; 475 476 // 4J Stu - When adding a 5th player to the game, the number of chunksToPoll is greater than the size of 477 // the m_updateChunkX & m_updateChunkZ arrays (19*19*4 at time of writing). It doesn't seem like there should 478 // ever be that many chunks needing polled, so this needs looked at in more detail. For now I have enlarged 479 // the size of the array to 19*19*8 but this seems way to big for our needs. 480 481 // The cause of this is largely because the chunksToPoll vector does not enforce unique elements 482 // The java version used a HashSet which would, although if our world size gets a lot larger 483 // then we may have no overlaps of players surrounding chunks 484 //assert(false); 485 486 // If you hit this assert, then a memory overwrite will occur when you continue 487 assert(m_updateChunkCount[iLev] < LEVEL_CHUNKS_TO_UPDATE_MAX); 488 489 m_updateChunkX[iLev][m_updateChunkCount[iLev]] = cp.x; 490 m_updateChunkZ[iLev][m_updateChunkCount[iLev]++] = cp.z; 491 492 LevelChunk *lc = getChunk(cp.x, cp.z); 493 tickClientSideTiles(xo, zo, lc); 494 495 if (random->nextInt(prob) == 0 && isRaining() && isThundering()) 496 { 497 randValue = randValue * 3 + addend; 498 int val = (randValue >> 2); 499 int x = xo + (val & 15); 500 int z = zo + ((val >> 8) & 15); 501 int y = getTopRainBlock(x, z); 502 503 if (isRainingAt(x, y, z)) 504 { 505 addGlobalEntity( shared_ptr<LightningBolt>( new LightningBolt(this, x, y, z) ) ); 506 } 507 } 508 509 // 4J - changes here brought forrward from 1.2.3 510 if (random->nextInt(16) == 0) 511 { 512 randValue = randValue * 3 + addend; 513 int val = (randValue >> 2); 514 int x = (val & 15); 515 int z = ((val >> 8) & 15); 516 int yy = getTopRainBlock(x + xo, z + zo); 517 if (shouldFreeze(x + xo, yy - 1, z + zo)) 518 { 519 setTileAndUpdate(x + xo, yy - 1, z + zo, Tile::ice_Id); 520 } 521 if (isRaining() && shouldSnow(x + xo, yy, z + zo)) 522 { 523 setTileAndUpdate(x + xo, yy, z + zo, Tile::topSnow_Id); 524 } 525 if (isRaining()) 526 { 527 Biome *b = getBiome(x + xo, z + zo); 528 if (b->hasRain()) 529 { 530 int tile = getTile(x + xo, yy - 1, z + zo); 531 if (tile != 0) 532 { 533 Tile::tiles[tile]->handleRain(this, x + xo, yy - 1, z + zo); 534 } 535 } 536 } 537 } 538 539 // 4J - lighting change brought forward from 1.8.2 540 checkLight(xo + random->nextInt(16), random->nextInt(128), zo + random->nextInt(16)); 541 } 542 543 m_level[iLev] = this; 544 m_randValue[iLev] = randValue; 545 // We've set up everything that the udpate thread needs, so kick it off 546 m_updateTrigger->Set(iLev); 547} 548 549bool ServerLevel::isTileToBeTickedAt(int x, int y, int z, int tileId) 550{ 551 TickNextTickData td = TickNextTickData(x, y, z, tileId); 552 return std::find(toBeTicked.begin(), toBeTicked.end(), td) != toBeTicked.end(); 553} 554 555void ServerLevel::addToTickNextTick(int x, int y, int z, int tileId, int tickDelay) 556{ 557 addToTickNextTick(x, y, z, tileId, tickDelay, 0); 558} 559 560void ServerLevel::addToTickNextTick(int x, int y, int z, int tileId, int tickDelay, int priorityTilt) 561{ 562 MemSect(27); 563 TickNextTickData td = TickNextTickData(x, y, z, tileId); 564 int r = 0; 565 if (getInstaTick() && tileId > 0) 566 { 567 if(Tile::tiles[tileId]->canInstantlyTick()) 568 { 569 r = 8; 570 if (hasChunksAt(td.x - r, td.y - r, td.z - r, td.x + r, td.y + r, td.z + r)) 571 { 572 int id = getTile(td.x, td.y, td.z); 573 if (id == td.tileId && id > 0) 574 { 575 Tile::tiles[id]->tick(this, td.x, td.y, td.z, random); 576 } 577 } 578 MemSect(0); 579 return; 580 } 581 else 582 { 583 tickDelay = 1; 584 } 585 } 586 587 if (hasChunksAt(x - r, y - r, z - r, x + r, y + r, z + r)) 588 { 589 if (tileId > 0) 590 { 591 td.delay(tickDelay + levelData->getGameTime()); 592 td.setPriorityTilt(priorityTilt); 593 } 594 EnterCriticalSection(&m_tickNextTickCS); 595 if ( tickNextTickSet.find(td) == tickNextTickSet.end() ) 596 { 597 tickNextTickSet.insert(td); 598 tickNextTickList.insert(td); 599 } 600 LeaveCriticalSection(&m_tickNextTickCS); 601 } 602 MemSect(0); 603} 604 605void ServerLevel::forceAddTileTick(int x, int y, int z, int tileId, int tickDelay, int prioTilt) 606{ 607 TickNextTickData td = TickNextTickData(x, y, z, tileId); 608 td.setPriorityTilt(prioTilt); 609 610 if (tileId > 0) 611 { 612 td.delay(tickDelay + levelData->getGameTime()); 613 } 614 EnterCriticalSection(&m_tickNextTickCS); 615 if ( tickNextTickSet.find(td) == tickNextTickSet.end() ) 616 { 617 tickNextTickSet.insert(td); 618 tickNextTickList.insert(td); 619 } 620 LeaveCriticalSection(&m_tickNextTickCS); 621} 622 623void ServerLevel::tickEntities() 624{ 625 if (players.empty()) 626 { 627 if (emptyTime++ >= EMPTY_TIME_NO_TICK) 628 { 629 return; 630 } 631 } 632 else 633 { 634 resetEmptyTime(); 635 } 636 637 Level::tickEntities(); 638} 639 640void ServerLevel::resetEmptyTime() 641{ 642 emptyTime = 0; 643} 644 645bool ServerLevel::tickPendingTicks(bool force) 646{ 647 EnterCriticalSection(&m_tickNextTickCS); 648 int count = (int)tickNextTickList.size(); 649 int count2 = (int)tickNextTickSet.size(); 650 if (count != tickNextTickSet.size()) 651 { 652 // TODO 4J Stu - Add new exception types 653 //throw new IllegalStateException("TickNextTick list out of synch"); 654 } 655 if (count > MAX_TICK_TILES_PER_TICK) count = MAX_TICK_TILES_PER_TICK; 656 657 AUTO_VAR(itTickList, tickNextTickList.begin()); 658 for (int i = 0; i < count; i++) 659 { 660 TickNextTickData td = *(itTickList); 661 if (!force && td.m_delay > levelData->getGameTime()) 662 { 663 break; 664 } 665 666 itTickList = tickNextTickList.erase(itTickList); 667 tickNextTickSet.erase(td); 668 toBeTicked.push_back(td); 669 } 670 671 for(AUTO_VAR(it,toBeTicked.begin()); it != toBeTicked.end();) 672 { 673 TickNextTickData td = *it; 674 it = toBeTicked.erase(it); 675 676 int r = 0; 677 if (hasChunksAt(td.x - r, td.y - r, td.z - r, td.x + r, td.y + r, td.z + r)) 678 { 679 int id = getTile(td.x, td.y, td.z); 680 if (id > 0 && Tile::isMatching(id, td.tileId)) 681 { 682 Tile::tiles[id]->tick(this, td.x, td.y, td.z, random); 683 } 684 } 685 else 686 { 687 addToTickNextTick(td.x, td.y, td.z, td.tileId, 0); 688 } 689 } 690 691 toBeTicked.clear(); 692 693 int count3 = (int)tickNextTickList.size(); 694 int count4 = (int)tickNextTickSet.size(); 695 696 bool retval = tickNextTickList.size() != 0; 697 LeaveCriticalSection(&m_tickNextTickCS); 698 699 return retval; 700} 701 702vector<TickNextTickData> *ServerLevel::fetchTicksInChunk(LevelChunk *chunk, bool remove) 703{ 704 EnterCriticalSection(&m_tickNextTickCS); 705 vector<TickNextTickData> *results = new vector<TickNextTickData>; 706 707 ChunkPos *pos = chunk->getPos(); 708 int xMin = (pos->x << 4) - 2; 709 int xMax = (xMin + 16) + 2; 710 int zMin = (pos->z << 4) - 2; 711 int zMax = (zMin + 16) + 2; 712 delete pos; 713 714 for (int i = 0; i < 2; i++) 715 { 716 if (i == 0) 717 { 718 for( AUTO_VAR(it, tickNextTickList.begin()); it != tickNextTickList.end(); ) 719 { 720 TickNextTickData td = *it; 721 722 if (td.x >= xMin && td.x < xMax && td.z >= zMin && td.z < zMax) 723 { 724 if (remove) 725 { 726 tickNextTickSet.erase(td); 727 it = tickNextTickList.erase(it); 728 } 729 else 730 { 731 it++; 732 } 733 734 results->push_back(td); 735 } 736 else 737 { 738 it++; 739 } 740 } 741 } 742 else 743 { 744 if (!toBeTicked.empty()) 745 { 746 app.DebugPrintf("To be ticked size: %d\n",toBeTicked.size()); 747 } 748 for( AUTO_VAR(it, toBeTicked.begin()); it != toBeTicked.end();) 749 { 750 TickNextTickData td = *it; 751 752 if (td.x >= xMin && td.x < xMax && td.z >= zMin && td.z < zMax) 753 { 754 if (remove) 755 { 756 tickNextTickList.erase(td); 757 it = toBeTicked.erase(it); 758 } 759 else 760 { 761 it++; 762 } 763 764 results->push_back(td); 765 } 766 else 767 { 768 it++; 769 } 770 } 771 } 772 } 773 774 LeaveCriticalSection(&m_tickNextTickCS); 775 return results; 776} 777 778void ServerLevel::tick(shared_ptr<Entity> e, bool actual) 779{ 780 if ( !server->isAnimals() && (e->instanceof(eTYPE_ANIMAL) || e->instanceof(eTYPE_WATERANIMAL)) ) 781 { 782 e->remove(); 783 } 784 if (!server->isNpcsEnabled() && (dynamic_pointer_cast<Npc>(e) != NULL)) 785 { 786 e->remove(); 787 } 788 Level::tick(e, actual); 789} 790 791void ServerLevel::forceTick(shared_ptr<Entity> e, bool actual) 792{ 793 Level::tick(e, actual); 794} 795 796ChunkSource *ServerLevel::createChunkSource() 797{ 798 ChunkStorage *storage = levelStorage->createChunkStorage(dimension); 799 cache = new ServerChunkCache(this, storage, dimension->createRandomLevelSource()); 800 return cache; 801} 802 803vector<shared_ptr<TileEntity> > *ServerLevel::getTileEntitiesInRegion(int x0, int y0, int z0, int x1, int y1, int z1) 804{ 805 vector<shared_ptr<TileEntity> > *result = new vector<shared_ptr<TileEntity> >; 806 for (unsigned int i = 0; i < tileEntityList.size(); i++) 807 { 808 shared_ptr<TileEntity> te = tileEntityList[i]; 809 if (te->x >= x0 && te->y >= y0 && te->z >= z0 && te->x < x1 && te->y < y1 && te->z < z1) 810 { 811 result->push_back(te); 812 } 813 } 814 return result; 815} 816 817bool ServerLevel::mayInteract(shared_ptr<Player> player, int xt, int yt, int zt, int content) 818{ 819 // 4J-PB - This will look like a bug to players, and we really should have a message to explain why we're not allowing lava to be placed at or near a spawn point 820 // We'll need to do this in a future update 821 822 // 4J-PB - Let's allow water near the spawn point, but not lava 823 if(content!=Tile::lava_Id) 824 { 825 // allow this to be used 826 return true; 827 } 828 else if(dimension->id == 0) // 4J Stu - Only limit this in the overworld 829 { 830 return !server->isUnderSpawnProtection(this, xt, yt, zt, player); 831 } 832 return true; 833} 834 835void ServerLevel::initializeLevel(LevelSettings *settings) 836{ 837 setInitialSpawn(settings); 838 839 Level::initializeLevel(settings); 840} 841 842/** 843* Sets the initial spawn, created this method so we could do a special 844* location for the demo version. 845*/ 846void ServerLevel::setInitialSpawn(LevelSettings *levelSettings) 847{ 848 if (!dimension->mayRespawn()) 849 { 850 levelData->setSpawn(0, dimension->getSpawnYPosition(), 0); 851 return; 852 } 853 854 isFindingSpawn = true; 855 856 BiomeSource *biomeSource = dimension->biomeSource; 857 vector<Biome *> playerSpawnBiomes = biomeSource->getPlayerSpawnBiomes(); 858 Random random(getSeed()); 859 860 TilePos *findBiome = biomeSource->findBiome(0, 0, 16 * 16, playerSpawnBiomes, &random); 861 862 int xSpawn = 0; // (Level.MAX_LEVEL_SIZE - 100) * 0; 863 int ySpawn = dimension->getSpawnYPosition(); 864 int zSpawn = 0; // (Level.MAX_LEVEL_SIZE - 100) * 0; 865 int minXZ = - (dimension->getXZSize() * 16 ) / 2; 866 int maxXZ = (dimension->getXZSize() * 16 ) / 2 - 1; 867 868 if (findBiome != NULL) 869 { 870 xSpawn = findBiome->x; 871 zSpawn = findBiome->z; 872 delete findBiome; 873 } 874 else 875 { 876 app.DebugPrintf("Level::setInitialSpawn - Unable to find spawn biome\n"); 877 } 878 879 int tries = 0; 880 881 while (!dimension->isValidSpawn(xSpawn, zSpawn)) 882 { 883 // 4J-PB changed to stay within our level limits 884 xSpawn += random.nextInt(64) - random.nextInt(64); 885 if(xSpawn>maxXZ) xSpawn=0; 886 if(xSpawn<minXZ) xSpawn=0; 887 zSpawn += random.nextInt(64) - random.nextInt(64); 888 if(zSpawn>maxXZ) zSpawn=0; 889 if(zSpawn<minXZ) zSpawn=0; 890 891 if (++tries == 1000) break; 892 } 893 894 levelData->setSpawn(xSpawn, ySpawn, zSpawn); 895 if (levelSettings->hasStartingBonusItems()) 896 { 897 generateBonusItemsNearSpawn(); 898 } 899 isFindingSpawn = false; 900} 901 902// 4J - brought forward from 1.3.2 903void ServerLevel::generateBonusItemsNearSpawn() 904{ 905 // once we've found the initial spawn, try to find a location for the 906 // starting bonus chest 907 // 4J - added - scan the spawn area first to see if there's already a chest near here 908 909 static const int r = 20; 910 int xs = levelData->getXSpawn(); 911 int zs = levelData->getZSpawn(); 912 for( int xx = -r; xx <= r; xx++ ) 913 for( int zz = -r; zz <= r; zz++ ) 914 { 915 int x = xx + xs; 916 int z = zz + zs; 917 int y = getTopSolidBlock( x, z ) - 1; 918 919 if( getTile( x, y, z ) == Tile::chest_Id ) 920 { 921 shared_ptr<ChestTileEntity> chest = dynamic_pointer_cast<ChestTileEntity>(getTileEntity(x, y, z)); 922 if (chest != NULL) 923 { 924 if( chest->isBonusChest ) 925 { 926 return; 927 } 928 } 929 } 930 } 931 932 BonusChestFeature *feature = new BonusChestFeature(RANDOM_BONUS_ITEMS, 16); 933 for (int attempt = 0; attempt < 16; attempt++) 934 { 935 int x = levelData->getXSpawn() + random->nextInt(6) - random->nextInt(6); 936 int z = levelData->getZSpawn() + random->nextInt(6) - random->nextInt(6); 937 int y = getTopSolidBlock(x, z) + 1; 938 939 if (feature->place(this, random, x, y, z, (attempt == 15) )) 940 { 941 break; 942 } 943 } 944 delete feature; 945} 946 947Pos *ServerLevel::getDimensionSpecificSpawn() 948{ 949 return dimension->getSpawnPos(); 950} 951 952// 4j Added for XboxOne PLM 953void ServerLevel::Suspend() 954{ 955 if(StorageManager.GetSaveDisabled()) return; 956 saveLevelData(); 957 chunkSource->saveAllEntities(); 958} 959 960void ServerLevel::save(bool force, ProgressListener *progressListener, bool bAutosave) 961{ 962 if (!chunkSource->shouldSave()) return; 963 964 // 4J-PB - check that saves are enabled 965 if(StorageManager.GetSaveDisabled()) return; 966 967 968 if (progressListener != NULL) 969 { 970 if(bAutosave) 971 { 972 progressListener->progressStartNoAbort(IDS_PROGRESS_AUTOSAVING_LEVEL); 973 } 974 else 975 { 976 progressListener->progressStartNoAbort(IDS_PROGRESS_SAVING_LEVEL); 977 } 978 979 } 980 PIXBeginNamedEvent(0,"Saving level data"); 981 saveLevelData(); 982 PIXEndNamedEvent(); 983 984 if (progressListener != NULL) progressListener->progressStage(IDS_PROGRESS_SAVING_CHUNKS); 985 986#if defined(_XBOX_ONE) || defined(__ORBIS__) 987 // Our autosave is a minimal save. All the chunks are saves by the constant save process 988 if(bAutosave) 989 { 990 chunkSource->saveAllEntities(); 991 } 992 else 993#endif 994 { 995 chunkSource->save(force, progressListener); 996 997#ifdef _LARGE_WORLDS 998 // 4J Stu - Only do this if there are players in the level 999 if(chunkMap->players.size() > 0) 1000 { 1001 // 4J Stu - This will come in a later change anyway 1002 // clean cache 1003 vector<LevelChunk *> *loadedChunkList = cache->getLoadedChunkList(); 1004 for (AUTO_VAR(it, loadedChunkList->begin()); it != loadedChunkList->end(); ++it) 1005 { 1006 LevelChunk *lc = *it; 1007 if (!chunkMap->hasChunk(lc->x, lc->z) ) 1008 { 1009 cache->drop(lc->x, lc->z); 1010 } 1011 } 1012 } 1013#endif 1014 } 1015 1016 //if( force && !isClientSide ) 1017 //{ 1018 // if (progressListener != NULL) progressListener->progressStage(IDS_PROGRESS_SAVING_TO_DISC); 1019 // levelStorage->flushSaveFile(); 1020 //} 1021} 1022 1023// 4J Added 1024void ServerLevel::saveToDisc(ProgressListener *progressListener, bool autosave) 1025{ 1026 // 4J-PB - check that saves are enabled 1027 if(StorageManager.GetSaveDisabled()) return; 1028 1029 // Check if we are using a trial version of a texture pack (which will be the case for going into the mash-up pack world with a trial version) 1030 if(!Minecraft::GetInstance()->skins->isUsingDefaultSkin()) 1031 { 1032 TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); 1033 DLCTexturePack *pDLCTexPack=(DLCTexturePack *)tPack; 1034 1035 DLCPack * pDLCPack=pDLCTexPack->getDLCInfoParentPack(); 1036 1037 if(!pDLCPack->hasPurchasedFile( DLCManager::e_DLCType_Texture, L"" )) 1038 { 1039 return; 1040 } 1041 } 1042 1043 if (progressListener != NULL) progressListener->progressStage(IDS_PROGRESS_SAVING_TO_DISC); 1044 levelStorage->flushSaveFile(autosave); 1045} 1046 1047void ServerLevel::saveLevelData() 1048{ 1049 checkSession(); 1050 1051 levelStorage->saveLevelData(levelData, &players); 1052 savedDataStorage->save(); 1053} 1054 1055void ServerLevel::entityAdded(shared_ptr<Entity> e) 1056{ 1057 Level::entityAdded(e); 1058 entitiesById[e->entityId] = e; 1059 vector<shared_ptr<Entity> > *es = e->getSubEntities(); 1060 if (es != NULL) 1061 { 1062 //for (int i = 0; i < es.length; i++) 1063 for(AUTO_VAR(it, es->begin()); it != es->end(); ++it) 1064 { 1065 entitiesById.insert( intEntityMap::value_type( (*it)->entityId, (*it) )); 1066 } 1067 } 1068 entityAddedExtra(e); // 4J added 1069} 1070 1071void ServerLevel::entityRemoved(shared_ptr<Entity> e) 1072{ 1073 Level::entityRemoved(e); 1074 entitiesById.erase(e->entityId); 1075 vector<shared_ptr<Entity> > *es = e->getSubEntities(); 1076 if (es != NULL) 1077 { 1078 //for (int i = 0; i < es.length; i++) 1079 for(AUTO_VAR(it, es->begin()); it != es->end(); ++it) 1080 { 1081 entitiesById.erase((*it)->entityId); 1082 } 1083 } 1084 entityRemovedExtra(e); // 4J added 1085} 1086 1087shared_ptr<Entity> ServerLevel::getEntity(int id) 1088{ 1089 return entitiesById[id]; 1090} 1091 1092bool ServerLevel::addGlobalEntity(shared_ptr<Entity> e) 1093{ 1094 if (Level::addGlobalEntity(e)) 1095 { 1096 server->getPlayers()->broadcast(e->x, e->y, e->z, 512, dimension->id, shared_ptr<AddGlobalEntityPacket>( new AddGlobalEntityPacket(e) ) ); 1097 return true; 1098 } 1099 return false; 1100} 1101 1102void ServerLevel::broadcastEntityEvent(shared_ptr<Entity> e, byte event) 1103{ 1104 shared_ptr<Packet> p = shared_ptr<EntityEventPacket>( new EntityEventPacket(e->entityId, event) ); 1105 server->getLevel(dimension->id)->getTracker()->broadcastAndSend(e, p); 1106} 1107 1108shared_ptr<Explosion> ServerLevel::explode(shared_ptr<Entity> source, double x, double y, double z, float r, bool fire, bool destroyBlocks) 1109{ 1110 // instead of calling super, we run the same explosion code here except 1111 // we don't generate any particles 1112 shared_ptr<Explosion> explosion = shared_ptr<Explosion>( new Explosion(this, source, x, y, z, r) ); 1113 explosion->fire = fire; 1114 explosion->destroyBlocks = destroyBlocks; 1115 explosion->explode(); 1116 explosion->finalizeExplosion(false); 1117 1118 if (!destroyBlocks) 1119 { 1120 explosion->toBlow.clear(); 1121 } 1122 1123 vector<shared_ptr<ServerPlayer> > sentTo; 1124 for(AUTO_VAR(it, players.begin()); it != players.end(); ++it) 1125 { 1126 shared_ptr<ServerPlayer> player = dynamic_pointer_cast<ServerPlayer>(*it); 1127 if (player->dimension != dimension->id) continue; 1128 1129 bool knockbackOnly = false; 1130 if( sentTo.size() ) 1131 { 1132 INetworkPlayer *thisPlayer = player->connection->getNetworkPlayer(); 1133 if( thisPlayer == NULL ) 1134 { 1135 continue; 1136 } 1137 else 1138 { 1139 for(unsigned int j = 0; j < sentTo.size(); j++ ) 1140 { 1141 shared_ptr<ServerPlayer> player2 = sentTo[j]; 1142 INetworkPlayer *otherPlayer = player2->connection->getNetworkPlayer(); 1143 if( otherPlayer != NULL && thisPlayer->IsSameSystem(otherPlayer) ) 1144 { 1145 knockbackOnly = true; 1146 } 1147 } 1148 } 1149 } 1150 1151 if (player->distanceToSqr(x, y, z) < 64 * 64) 1152 { 1153 Vec3 *knockbackVec = explosion->getHitPlayerKnockback(player); 1154 //app.DebugPrintf("Sending %s with knockback (%f,%f,%f)\n", knockbackOnly?"knockbackOnly":"allExplosion",knockbackVec->x,knockbackVec->y,knockbackVec->z); 1155 // If the player is not the primary on the system, then we only want to send info for the knockback 1156 player->connection->send( shared_ptr<ExplodePacket>( new ExplodePacket(x, y, z, r, &explosion->toBlow, knockbackVec, knockbackOnly))); 1157 sentTo.push_back( player ); 1158 } 1159 } 1160 1161 return explosion; 1162} 1163 1164void ServerLevel::tileEvent(int x, int y, int z, int tile, int b0, int b1) 1165{ 1166 // super.tileEvent(x, y, z, b0, b1); 1167 // server.getPlayers().broadcast(x, y, z, 64, dimension.id, new TileEventPacket(x, y, z, b0, b1)); 1168 TileEventData newEvent(x, y, z, tile, b0, b1); 1169 //for (TileEventData te : tileEvents[activeTileEventsList]) 1170 for(AUTO_VAR(it, tileEvents[activeTileEventsList].begin()); it != tileEvents[activeTileEventsList].end(); ++it) 1171 { 1172 if ((*it).equals(newEvent)) 1173 { 1174 return; 1175 } 1176 } 1177 tileEvents[activeTileEventsList].push_back(newEvent); 1178} 1179 1180void ServerLevel::runTileEvents() 1181{ 1182 // use two lists until both are empty, intended to avoid concurrent 1183 // modifications 1184 while (!tileEvents[activeTileEventsList].empty()) 1185 { 1186 int runList = activeTileEventsList; 1187 activeTileEventsList ^= 1; 1188 1189 //for (TileEventData te : tileEvents[runList]) 1190 for(AUTO_VAR(it, tileEvents[runList].begin()); it != tileEvents[runList].end(); ++it) 1191 { 1192 if (doTileEvent(&(*it))) 1193 { 1194 TileEventData te = *it; 1195 server->getPlayers()->broadcast(te.getX(), te.getY(), te.getZ(), 64, dimension->id, shared_ptr<TileEventPacket>( new TileEventPacket(te.getX(), te.getY(), te.getZ(), te.getTile(), te.getParamA(), te.getParamB()))); 1196 } 1197 } 1198 tileEvents[runList].clear(); 1199 } 1200} 1201 1202bool ServerLevel::doTileEvent(TileEventData *te) 1203{ 1204 int t = getTile(te->getX(), te->getY(), te->getZ()); 1205 if (t == te->getTile()) { 1206 return Tile::tiles[t]->triggerEvent(this, te->getX(), te->getY(), te->getZ(), te->getParamA(), te->getParamB()); 1207 } 1208 return false; 1209} 1210 1211void ServerLevel::closeLevelStorage() 1212{ 1213 levelStorage->closeAll(); 1214} 1215 1216void ServerLevel::tickWeather() 1217{ 1218 bool wasRaining = isRaining(); 1219 Level::tickWeather(); 1220 1221 if (wasRaining != isRaining()) 1222 { 1223 if (wasRaining) 1224 { 1225 server->getPlayers()->broadcastAll( shared_ptr<GameEventPacket>( new GameEventPacket(GameEventPacket::STOP_RAINING, 0) ) ); 1226 } 1227 else 1228 { 1229 server->getPlayers()->broadcastAll( shared_ptr<GameEventPacket>( new GameEventPacket(GameEventPacket::START_RAINING, 0) ) ); 1230 } 1231 } 1232 1233} 1234 1235MinecraftServer *ServerLevel::getServer() 1236{ 1237 return server; 1238} 1239 1240EntityTracker *ServerLevel::getTracker() 1241{ 1242 return tracker; 1243} 1244 1245void ServerLevel::setTimeAndAdjustTileTicks(__int64 newTime) 1246{ 1247 __int64 delta = newTime - levelData->getGameTime(); 1248 // 4J - can't directly adjust m_delay in a set as it has a const interator, since changing values in here might change the ordering of the elements in the set. 1249 // Instead move to a vector, do the adjustment, put back in the set. 1250 vector<TickNextTickData> temp; 1251 for(AUTO_VAR(it, tickNextTickList.begin()); it != tickNextTickList.end(); ++it) 1252 { 1253 temp.push_back(*it); 1254 temp.back().m_delay += delta; 1255 } 1256 tickNextTickList.clear(); 1257 for(unsigned int i = 0; i < temp.size(); i++ ) 1258 { 1259 tickNextTickList.insert(temp[i]); 1260 } 1261 setGameTime(newTime); 1262} 1263 1264PlayerChunkMap *ServerLevel::getChunkMap() 1265{ 1266 return chunkMap; 1267} 1268 1269PortalForcer *ServerLevel::getPortalForcer() 1270{ 1271 return portalForcer; 1272} 1273 1274void ServerLevel::sendParticles(const wstring &name, double x, double y, double z, int count) 1275{ 1276 sendParticles(name, x + 0.5f, y + 0.5f, z + 0.5f, count, 0.5f, 0.5f, 0.5f, 0.02f); 1277} 1278 1279void ServerLevel::sendParticles(const wstring &name, double x, double y, double z, int count, double xDist, double yDist, double zDist, double speed) 1280{ 1281 shared_ptr<Packet> packet = shared_ptr<LevelParticlesPacket>( new LevelParticlesPacket(name, (float) x, (float) y, (float) z, (float) xDist, (float) yDist, (float) zDist, (float) speed, count) ); 1282 1283 1284 for(AUTO_VAR(it, players.begin()); it != players.end(); ++it) 1285 { 1286 shared_ptr<ServerPlayer> player = dynamic_pointer_cast<ServerPlayer>(*it); 1287 player->connection->send(packet); 1288 } 1289} 1290 1291// 4J Stu - Sometimes we want to update tiles on the server from the main thread (eg SignTileEntity when string verify returns) 1292void ServerLevel::queueSendTileUpdate(int x, int y, int z) 1293{ 1294 EnterCriticalSection(&m_csQueueSendTileUpdates); 1295 m_queuedSendTileUpdates.push_back( new Pos(x,y,z) ); 1296 LeaveCriticalSection(&m_csQueueSendTileUpdates); 1297} 1298 1299void ServerLevel::runQueuedSendTileUpdates() 1300{ 1301 EnterCriticalSection(&m_csQueueSendTileUpdates); 1302 for(AUTO_VAR(it, m_queuedSendTileUpdates.begin()); it != m_queuedSendTileUpdates.end(); ++it) 1303 { 1304 Pos *p = *it; 1305 sendTileUpdated(p->x, p->y, p->z); 1306 delete p; 1307 } 1308 m_queuedSendTileUpdates.clear(); 1309 LeaveCriticalSection(&m_csQueueSendTileUpdates); 1310} 1311 1312// 4J - added special versions of addEntity and extra processing on entity removed and added so we can limit the number of itementities created 1313bool ServerLevel::addEntity(shared_ptr<Entity> e) 1314{ 1315 // If its an item entity, and we've got to our capacity, delete the oldest 1316 if( e->instanceof(eTYPE_ITEMENTITY) ) 1317 { 1318 // printf("Adding item entity count %d\n",m_itemEntities.size()); 1319 EnterCriticalSection(&m_limiterCS); 1320 if( m_itemEntities.size() >= MAX_ITEM_ENTITIES ) 1321 { 1322 // printf("Adding - doing remove\n"); 1323 removeEntityImmediately(m_itemEntities.front()); 1324 } 1325 LeaveCriticalSection(&m_limiterCS); 1326 } 1327 // If its an hanging entity, and we've got to our capacity, delete the oldest 1328 else if( e->instanceof(eTYPE_HANGING_ENTITY) ) 1329 { 1330 // printf("Adding item entity count %d\n",m_itemEntities.size()); 1331 EnterCriticalSection(&m_limiterCS); 1332 if( m_hangingEntities.size() >= MAX_HANGING_ENTITIES ) 1333 { 1334 // printf("Adding - doing remove\n"); 1335 1336 // 4J-PB - refuse to add the entity, since we'll be removing one already there, and it may be an item frame with something in it. 1337 LeaveCriticalSection(&m_limiterCS); 1338 return FALSE; 1339 1340 //removeEntityImmediately(m_hangingEntities.front()); 1341 } 1342 LeaveCriticalSection(&m_limiterCS); 1343 } 1344 // If its an arrow entity, and we've got to our capacity, delete the oldest 1345 else if( e->instanceof(eTYPE_ARROW) ) 1346 { 1347 // printf("Adding arrow entity count %d\n",m_arrowEntities.size()); 1348 EnterCriticalSection(&m_limiterCS); 1349 if( m_arrowEntities.size() >= MAX_ARROW_ENTITIES ) 1350 { 1351 // printf("Adding - doing remove\n"); 1352 removeEntityImmediately(m_arrowEntities.front()); 1353 } 1354 LeaveCriticalSection(&m_limiterCS); 1355 } 1356 // If its an experience orb entity, and we've got to our capacity, delete the oldest 1357 else if( e->instanceof(eTYPE_EXPERIENCEORB) ) 1358 { 1359 // printf("Adding arrow entity count %d\n",m_arrowEntities.size()); 1360 EnterCriticalSection(&m_limiterCS); 1361 if( m_experienceOrbEntities.size() >= MAX_EXPERIENCEORB_ENTITIES ) 1362 { 1363 // printf("Adding - doing remove\n"); 1364 removeEntityImmediately(m_experienceOrbEntities.front()); 1365 } 1366 LeaveCriticalSection(&m_limiterCS); 1367 } 1368 return Level::addEntity(e); 1369} 1370 1371// 4J: Returns true if the level is at its limit for this type of entity (only checks arrows, hanging, item and experience orbs) 1372bool ServerLevel::atEntityLimit(shared_ptr<Entity> e) 1373{ 1374 // TODO: This duplicates code from addEntity above, fix 1375 1376 bool atLimit = false; 1377 1378 if( e->instanceof(eTYPE_ITEMENTITY) ) 1379 { 1380 EnterCriticalSection(&m_limiterCS); 1381 atLimit = m_itemEntities.size() >= MAX_ITEM_ENTITIES; 1382 LeaveCriticalSection(&m_limiterCS); 1383 } 1384 else if( e->instanceof(eTYPE_HANGING_ENTITY) ) 1385 { 1386 EnterCriticalSection(&m_limiterCS); 1387 atLimit = m_hangingEntities.size() >= MAX_HANGING_ENTITIES; 1388 LeaveCriticalSection(&m_limiterCS); 1389 } 1390 else if( e->instanceof(eTYPE_ARROW) ) 1391 { 1392 EnterCriticalSection(&m_limiterCS); 1393 atLimit = m_arrowEntities.size() >= MAX_ARROW_ENTITIES; 1394 LeaveCriticalSection(&m_limiterCS); 1395 } 1396 else if( e->instanceof(eTYPE_EXPERIENCEORB) ) 1397 { 1398 EnterCriticalSection(&m_limiterCS); 1399 atLimit = m_experienceOrbEntities.size() >= MAX_EXPERIENCEORB_ENTITIES; 1400 LeaveCriticalSection(&m_limiterCS); 1401 } 1402 1403 return atLimit; 1404} 1405 1406// Maintain a cound of primed tnt & falling tiles in this level 1407void ServerLevel::entityAddedExtra(shared_ptr<Entity> e) 1408{ 1409 if( e->instanceof(eTYPE_ITEMENTITY) ) 1410 { 1411 EnterCriticalSection(&m_limiterCS); 1412 m_itemEntities.push_back(e); 1413 // printf("entity added: item entity count now %d\n",m_itemEntities.size()); 1414 LeaveCriticalSection(&m_limiterCS); 1415 } 1416 else if( e->instanceof(eTYPE_HANGING_ENTITY) ) 1417 { 1418 EnterCriticalSection(&m_limiterCS); 1419 m_hangingEntities.push_back(e); 1420 // printf("entity added: item entity count now %d\n",m_itemEntities.size()); 1421 LeaveCriticalSection(&m_limiterCS); 1422 } 1423 else if( e->instanceof(eTYPE_ARROW) ) 1424 { 1425 EnterCriticalSection(&m_limiterCS); 1426 m_arrowEntities.push_back(e); 1427 // printf("entity added: arrow entity count now %d\n",m_arrowEntities.size()); 1428 LeaveCriticalSection(&m_limiterCS); 1429 } 1430 else if( e->instanceof(eTYPE_EXPERIENCEORB) ) 1431 { 1432 EnterCriticalSection(&m_limiterCS); 1433 m_experienceOrbEntities.push_back(e); 1434 // printf("entity added: experience orb entity count now %d\n",m_arrowEntities.size()); 1435 LeaveCriticalSection(&m_limiterCS); 1436 } 1437 else if( e->instanceof(eTYPE_PRIMEDTNT) ) 1438 { 1439 EnterCriticalSection(&m_limiterCS); 1440 m_primedTntCount++; 1441 LeaveCriticalSection(&m_limiterCS); 1442 } 1443 else if( e->instanceof(eTYPE_FALLINGTILE) ) 1444 { 1445 EnterCriticalSection(&m_limiterCS); 1446 m_fallingTileCount++; 1447 LeaveCriticalSection(&m_limiterCS); 1448 } 1449} 1450 1451// Maintain a cound of primed tnt & falling tiles in this level, and remove any item entities from our list 1452void ServerLevel::entityRemovedExtra(shared_ptr<Entity> e) 1453{ 1454 if( e->instanceof(eTYPE_ITEMENTITY) ) 1455 { 1456 EnterCriticalSection(&m_limiterCS); 1457 // printf("entity removed: item entity count %d\n",m_itemEntities.size()); 1458 AUTO_VAR(it, find(m_itemEntities.begin(),m_itemEntities.end(),e)); 1459 if( it != m_itemEntities.end() ) 1460 { 1461 // printf("Item to remove found\n"); 1462 m_itemEntities.erase(it); 1463 } 1464 // printf("entity removed: item entity count now %d\n",m_itemEntities.size()); 1465 LeaveCriticalSection(&m_limiterCS); 1466 } 1467 else if( e->instanceof(eTYPE_HANGING_ENTITY) ) 1468 { 1469 EnterCriticalSection(&m_limiterCS); 1470 // printf("entity removed: item entity count %d\n",m_itemEntities.size()); 1471 AUTO_VAR(it, find(m_hangingEntities.begin(),m_hangingEntities.end(),e)); 1472 if( it != m_hangingEntities.end() ) 1473 { 1474 // printf("Item to remove found\n"); 1475 m_hangingEntities.erase(it); 1476 } 1477 // printf("entity removed: item entity count now %d\n",m_itemEntities.size()); 1478 LeaveCriticalSection(&m_limiterCS); 1479 } 1480 else if( e->instanceof(eTYPE_ARROW) ) 1481 { 1482 EnterCriticalSection(&m_limiterCS); 1483 // printf("entity removed: arrow entity count %d\n",m_arrowEntities.size()); 1484 AUTO_VAR(it, find(m_arrowEntities.begin(),m_arrowEntities.end(),e)); 1485 if( it != m_arrowEntities.end() ) 1486 { 1487 // printf("Item to remove found\n"); 1488 m_arrowEntities.erase(it); 1489 } 1490 // printf("entity removed: arrow entity count now %d\n",m_arrowEntities.size()); 1491 LeaveCriticalSection(&m_limiterCS); 1492 } 1493 else if( e->instanceof(eTYPE_EXPERIENCEORB) ) 1494 { 1495 EnterCriticalSection(&m_limiterCS); 1496 // printf("entity removed: experience orb entity count %d\n",m_arrowEntities.size()); 1497 AUTO_VAR(it, find(m_experienceOrbEntities.begin(),m_experienceOrbEntities.end(),e)); 1498 if( it != m_experienceOrbEntities.end() ) 1499 { 1500 // printf("Item to remove found\n"); 1501 m_experienceOrbEntities.erase(it); 1502 } 1503 // printf("entity removed: experience orb entity count now %d\n",m_arrowEntities.size()); 1504 LeaveCriticalSection(&m_limiterCS); 1505 } 1506 else if( e->instanceof(eTYPE_PRIMEDTNT) ) 1507 { 1508 EnterCriticalSection(&m_limiterCS); 1509 m_primedTntCount--; 1510 LeaveCriticalSection(&m_limiterCS); 1511 } 1512 else if( e->instanceof(eTYPE_FALLINGTILE) ) 1513 { 1514 EnterCriticalSection(&m_limiterCS); 1515 m_fallingTileCount--; 1516 LeaveCriticalSection(&m_limiterCS); 1517 } 1518} 1519 1520bool ServerLevel::newPrimedTntAllowed() 1521{ 1522 EnterCriticalSection(&m_limiterCS); 1523 bool retval = m_primedTntCount < MAX_PRIMED_TNT; 1524 LeaveCriticalSection(&m_limiterCS); 1525 return retval; 1526} 1527 1528bool ServerLevel::newFallingTileAllowed() 1529{ 1530 EnterCriticalSection(&m_limiterCS); 1531 bool retval = m_fallingTileCount < MAX_FALLING_TILE; 1532 LeaveCriticalSection(&m_limiterCS); 1533 return retval; 1534} 1535 1536int ServerLevel::runUpdate(void* lpParam) 1537{ 1538 ShutdownManager::HasStarted(ShutdownManager::eRunUpdateThread,m_updateTrigger); 1539 while(ShutdownManager::ShouldRun(ShutdownManager::eRunUpdateThread)) 1540 { 1541 m_updateTrigger->WaitForAll(INFINITE); 1542 1543 if(!ShutdownManager::ShouldRun(ShutdownManager::eRunUpdateThread)) break; 1544 1545 PIXBeginNamedEvent(0,"Updating tiles to be ticked"); 1546 // 4J Stu - Grass and Lava ticks currently take up the majority of all tile updates, so I am limiting them 1547 int grassTicks = 0; 1548 int lavaTicks = 0; 1549 for( unsigned int iLev = 0; iLev < 3; ++iLev ) 1550 { 1551 EnterCriticalSection(&m_updateCS[iLev]); 1552 for( int i = 0; i < m_updateChunkCount[iLev]; i++ ) 1553 { 1554 // 4J - some of these tile ticks will check things in neighbouring tiles, causing chunks to load/create that aren't yet in memory. 1555 // Try and avoid this by limiting the min/max x & z values that we will try and inspect in this chunk according to what surround chunks are loaded 1556 int cx = m_updateChunkX[iLev][i]; 1557 int cz = m_updateChunkZ[iLev][i]; 1558 int minx = 0; 1559 int maxx = 15; 1560 int minz = 0; 1561 int maxz = 15; 1562 if( !m_level[iLev]->hasChunk(cx, cz) ) continue; 1563 1564 if( !m_level[iLev]->hasChunk(cx + 1, cz + 0) ) { maxx = 11; } 1565 if( !m_level[iLev]->hasChunk(cx + 0, cz + 1) ) { maxz = 11; } 1566 if( !m_level[iLev]->hasChunk(cx - 1, cz + 0) ) { minx = 4; } 1567 if( !m_level[iLev]->hasChunk(cx + 0, cz - 1) ) { minz = 4; } 1568 if( !m_level[iLev]->hasChunk(cx + 1, cz + 1) ) { maxx = 11; maxz = 11; } 1569 if( !m_level[iLev]->hasChunk(cx + 1, cz - 1) ) { maxx = 11; minz = 4; } 1570 if( !m_level[iLev]->hasChunk(cx - 1, cz - 1) ) { minx = 4; minz = 4; } 1571 if( !m_level[iLev]->hasChunk(cx - 1, cz + 1) ) { minx = 4; maxz = 11; } 1572 1573 LevelChunk *lc = m_level[iLev]->getChunk(cx, cz); 1574 1575 for (int j = 0; j < 80; j++) 1576 { 1577 m_randValue[iLev] = m_randValue[iLev] * 3 + m_level[iLev]->addend; 1578 int val = (m_randValue[iLev] >> 2); 1579 int x = (val & 15); 1580 if( ( x < minx ) || ( x > maxx ) ) continue; 1581 int z = ((val >> 8) & 15); 1582 if( ( z < minz ) || ( z > maxz ) ) continue; 1583 int y = ((val >> 16) & (Level::maxBuildHeight - 1)); 1584 1585 // This array access is a cache miss pretty much every time 1586 int id = lc->getTile(x,y,z); 1587 if( m_updateTileCount[iLev] >= MAX_UPDATES ) break; 1588 1589 // 4J Stu - Grass and Lava ticks currently take up the majority of all tile updates, so I am limiting them 1590 if( (id == Tile::grass_Id && grassTicks >= MAX_GRASS_TICKS) || (id == Tile::calmLava_Id && lavaTicks >= MAX_LAVA_TICKS) ) continue; 1591 1592 // 4J Stu - Added shouldTileTick as some tiles won't even do anything if they are set to tick and use up one of our updates 1593 if (Tile::tiles[id] != NULL && Tile::tiles[id]->isTicking() && Tile::tiles[id]->shouldTileTick(m_level[iLev],x + (cx * 16), y, z + (cz * 16) ) ) 1594 { 1595 if(id == Tile::grass_Id) ++grassTicks; 1596 else if(id == Tile::calmLava_Id) ++lavaTicks; 1597 m_updateTileX[iLev][m_updateTileCount[iLev]] = x + (cx * 16); 1598 m_updateTileY[iLev][m_updateTileCount[iLev]] = y; 1599 m_updateTileZ[iLev][m_updateTileCount[iLev]] = z + (cz * 16); 1600 1601 m_updateTileCount[iLev]++; 1602 } 1603 } 1604 } 1605 LeaveCriticalSection(&m_updateCS[iLev]); 1606 } 1607 PIXEndNamedEvent(); 1608#ifdef __PS3__ 1609 Sleep(10); 1610#endif //__PS3__ 1611 } 1612 1613 ShutdownManager::HasFinished(ShutdownManager::eRunUpdateThread); 1614 1615 return 0; 1616} 1617 1618void ServerLevel::flagEntitiesToBeRemoved(unsigned int *flags, bool *removedFound) 1619{ 1620 if( chunkMap ) 1621 { 1622 chunkMap->flagEntitiesToBeRemoved(flags, removedFound); 1623 } 1624}