the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
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}