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 "Minecraft.h"
3
4#include <ctime>
5
6#include "ConsoleInput.h"
7#include "DerivedServerLevel.h"
8#include "DispenserBootstrap.h"
9#include "EntityTracker.h"
10#include "MinecraftServer.h"
11#include "Options.h"
12#include "PlayerList.h"
13#include "ServerChunkCache.h"
14#include "ServerConnection.h"
15#include "ServerLevel.h"
16#include "ServerLevelListener.h"
17#include "Settings.h"
18#include "..\Minecraft.World\Command.h"
19#include "..\Minecraft.World\AABB.h"
20#include "..\Minecraft.World\Vec3.h"
21#include "..\Minecraft.World\net.minecraft.network.h"
22#include "..\Minecraft.World\net.minecraft.world.level.dimension.h"
23#include "..\Minecraft.World\net.minecraft.world.level.storage.h"
24#include "..\Minecraft.World\net.minecraft.world.h"
25#include "..\Minecraft.World\net.minecraft.world.level.h"
26#include "..\Minecraft.World\net.minecraft.world.level.tile.h"
27#include "..\Minecraft.World\Pos.h"
28#include "..\Minecraft.World\System.h"
29#include "..\Minecraft.World\StringHelpers.h"
30#ifdef SPLIT_SAVES
31#include "..\Minecraft.World\ConsoleSaveFileSplit.h"
32#endif
33#include "..\Minecraft.World\ConsoleSaveFileOriginal.h"
34#include "..\Minecraft.World\Socket.h"
35#include "..\Minecraft.World\net.minecraft.world.entity.h"
36#include "ProgressRenderer.h"
37#include "ServerPlayer.h"
38#include "GameRenderer.h"
39#include "..\Minecraft.World\ThreadName.h"
40#include "..\Minecraft.World\IntCache.h"
41#include "..\Minecraft.World\CompressedTileStorage.h"
42#include "..\Minecraft.World\SparseLightStorage.h"
43#include "..\Minecraft.World\SparseDataStorage.h"
44#include "..\Minecraft.World\compression.h"
45#ifdef _XBOX
46#include "Common\XUI\XUI_DebugSetCamera.h"
47#endif
48#include "PS3\PS3Extras\ShutdownManager.h"
49#include "ServerCommandDispatcher.h"
50#include "..\Minecraft.World\BiomeSource.h"
51#include "PlayerChunkMap.h"
52#include "Common\Telemetry\TelemetryManager.h"
53#include "PlayerConnection.h"
54#ifdef _XBOX_ONE
55#include "Durango\Network\NetworkPlayerDurango.h"
56#endif
57
58#define DEBUG_SERVER_DONT_SPAWN_MOBS 0
59
60//4J Added
61MinecraftServer *MinecraftServer::server = NULL;
62bool MinecraftServer::setTimeAtEndOfTick = false;
63__int64 MinecraftServer::setTime = 0;
64bool MinecraftServer::setTimeOfDayAtEndOfTick = false;
65__int64 MinecraftServer::setTimeOfDay = 0;
66bool MinecraftServer::m_bPrimaryPlayerSignedOut=false;
67bool MinecraftServer::s_bServerHalted=false;
68bool MinecraftServer::s_bSaveOnExitAnswered=false;
69#ifdef _ACK_CHUNK_SEND_THROTTLING
70bool MinecraftServer::s_hasSentEnoughPackets = false;
71__int64 MinecraftServer::s_tickStartTime = 0;
72vector<INetworkPlayer *> MinecraftServer::s_sentTo;
73#else
74int MinecraftServer::s_slowQueuePlayerIndex = 0;
75int MinecraftServer::s_slowQueueLastTime = 0;
76bool MinecraftServer::s_slowQueuePacketSent = false;
77#endif
78
79unordered_map<wstring, int> MinecraftServer::ironTimers;
80
81MinecraftServer::MinecraftServer()
82{
83 // 4J - added initialisers
84 connection = NULL;
85 settings = NULL;
86 players = NULL;
87 commands = NULL;
88 running = true;
89 m_bLoaded = false;
90 stopped = false;
91 tickCount = 0;
92 wstring progressStatus;
93 progress = 0;
94 motd = L"";
95
96 m_isServerPaused = false;
97 m_serverPausedEvent = new C4JThread::Event;
98
99 m_saveOnExit = false;
100 m_suspending = false;
101
102 m_ugcPlayersVersion = 0;
103 m_texturePackId = 0;
104 maxBuildHeight = Level::maxBuildHeight;
105 playerIdleTimeout = 0;
106 m_postUpdateThread = NULL;
107 forceGameType = false;
108
109 commandDispatcher = new ServerCommandDispatcher();
110
111 DispenserBootstrap::bootStrap();
112}
113
114MinecraftServer::~MinecraftServer()
115{
116}
117
118bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DWORD initSettings, bool findSeed)
119{
120 // 4J - removed
121#if 0
122 commands = new ConsoleCommands(this);
123
124 Thread t = new Thread() {
125 public void run() {
126 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
127 String line = null;
128 try {
129 while (!stopped && running && (line = br.readLine()) != null) {
130 handleConsoleInput(line, MinecraftServer.this);
131 }
132 } catch (IOException e) {
133 e.printStackTrace();
134 }
135 }
136 };
137 t.setDaemon(true);
138 t.start();
139
140
141 LogConfigurator.initLogger();
142 logger.info("Starting minecraft server version " + VERSION);
143
144 if (Runtime.getRuntime().maxMemory() / 1024 / 1024 < 512) {
145 logger.warning("**** NOT ENOUGH RAM!");
146 logger.warning("To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\"");
147 }
148
149 logger.info("Loading properties");
150#endif
151 settings = new Settings(new File(L"server.properties"));
152
153 app.DebugPrintf("\n*** SERVER SETTINGS ***\n");
154 app.DebugPrintf("ServerSettings: host-friends-only is %s\n",(app.GetGameHostOption(eGameHostOption_FriendsOfFriends)>0)?"on":"off");
155 app.DebugPrintf("ServerSettings: game-type is %s\n",(app.GetGameHostOption(eGameHostOption_GameType)==0)?"Survival Mode":"Creative Mode");
156 app.DebugPrintf("ServerSettings: pvp is %s\n",(app.GetGameHostOption(eGameHostOption_PvP)>0)?"on":"off");
157 app.DebugPrintf("ServerSettings: fire spreads is %s\n",(app.GetGameHostOption(eGameHostOption_FireSpreads)>0)?"on":"off");
158 app.DebugPrintf("ServerSettings: tnt explodes is %s\n",(app.GetGameHostOption(eGameHostOption_TNT)>0)?"on":"off");
159 app.DebugPrintf("\n");
160
161 // TODO 4J Stu - Init a load of settings based on data passed as params
162 //settings->setBooleanAndSave( L"host-friends-only", (app.GetGameHostOption(eGameHostOption_FriendsOfFriends)>0) );
163
164 // 4J - Unused
165 //localIp = settings->getString(L"server-ip", L"");
166 //onlineMode = settings->getBoolean(L"online-mode", true);
167 //motd = settings->getString(L"motd", L"A Minecraft Server");
168 //motd.replace('�', '$');
169
170 setAnimals(settings->getBoolean(L"spawn-animals", true));
171 setNpcsEnabled(settings->getBoolean(L"spawn-npcs", true));
172 setPvpAllowed(app.GetGameHostOption( eGameHostOption_PvP )>0?true:false); // settings->getBoolean(L"pvp", true);
173
174 // 4J Stu - We should never have hacked clients flying when they shouldn't be like the PC version, so enable flying always
175 // Fix for #46612 - TU5: Code: Multiplayer: A client can be banned for flying when accidentaly being blown by dynamite
176 setFlightAllowed(true); //settings->getBoolean(L"allow-flight", false);
177
178 // 4J Stu - Enabling flight to stop it kicking us when we use it
179#ifdef _DEBUG_MENUS_ENABLED
180 setFlightAllowed(true);
181#endif
182
183#if 1
184 connection = new ServerConnection(this);
185 Socket::Initialise(connection); // 4J - added
186#else
187 // 4J - removed
188 InetAddress localAddress = null;
189 if (localIp.length() > 0) localAddress = InetAddress.getByName(localIp);
190 port = settings.getInt("server-port", DEFAULT_MINECRAFT_PORT);
191
192 logger.info("Starting Minecraft server on " + (localIp.length() == 0 ? "*" : localIp) + ":" + port);
193 try {
194 connection = new ServerConnection(this, localAddress, port);
195 } catch (IOException e) {
196 logger.warning("**** FAILED TO BIND TO PORT!");
197 logger.log(Level.WARNING, "The exception was: " + e.toString());
198 logger.warning("Perhaps a server is already running on that port?");
199 return false;
200 }
201
202 if (!onlineMode) {
203 logger.warning("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!");
204 logger.warning("The server will make no attempt to authenticate usernames. Beware.");
205 logger.warning("While this makes the game possible to play without internet access, it also opens up the ability for hackers to connect with any username they choose.");
206 logger.warning("To change this, set \"online-mode\" to \"true\" in the server.settings file.");
207 }
208#endif
209 setPlayers(new PlayerList(this));
210
211 // 4J-JEV: Need to wait for levelGenerationOptions to load.
212 while ( app.getLevelGenerationOptions() != NULL && !app.getLevelGenerationOptions()->hasLoadedData() )
213 Sleep(1);
214
215 if ( app.getLevelGenerationOptions() != NULL && !app.getLevelGenerationOptions()->ready() )
216 {
217 // TODO: Stop loading, add error message.
218 }
219
220 __int64 levelNanoTime = System::nanoTime();
221
222 wstring levelName = settings->getString(L"level-name", L"world");
223 wstring levelTypeString;
224
225 bool gameRuleUseFlatWorld = false;
226 if(app.getLevelGenerationOptions() != NULL)
227 {
228 gameRuleUseFlatWorld = app.getLevelGenerationOptions()->getuseFlatWorld();
229 }
230 if(gameRuleUseFlatWorld || app.GetGameHostOption(eGameHostOption_LevelType)>0)
231 {
232 levelTypeString = settings->getString(L"level-type", L"flat");
233 }
234 else
235 {
236 levelTypeString = settings->getString(L"level-type",L"default");
237 }
238
239 LevelType *pLevelType = LevelType::getLevelType(levelTypeString);
240 if (pLevelType == NULL)
241 {
242 pLevelType = LevelType::lvl_normal;
243 }
244
245 ProgressRenderer *mcprogress = Minecraft::GetInstance()->progressRenderer;
246 mcprogress->progressStart(IDS_PROGRESS_INITIALISING_SERVER);
247
248 if( findSeed )
249 {
250#ifdef __PSVITA__
251 seed = BiomeSource::findSeed(pLevelType, &running);
252#else
253 seed = BiomeSource::findSeed(pLevelType);
254#endif
255 }
256
257 setMaxBuildHeight(settings->getInt(L"max-build-height", Level::maxBuildHeight));
258 setMaxBuildHeight(((getMaxBuildHeight() + 8) / 16) * 16);
259 setMaxBuildHeight(Mth::clamp(getMaxBuildHeight(), 64, Level::maxBuildHeight));
260 //settings->setProperty(L"max-build-height", maxBuildHeight);
261
262#if 0
263 wstring levelSeedString = settings->getString(L"level-seed", L"");
264 __int64 levelSeed = (new Random())->nextLong();
265 if (levelSeedString.length() > 0)
266 {
267 long newSeed = _fromString<__int64>(levelSeedString);
268 if (newSeed != 0) {
269 levelSeed = newSeed;
270 }
271 }
272#endif
273 // logger.info("Preparing level \"" + levelName + "\"");
274 m_bLoaded = loadLevel(new McRegionLevelStorageSource(File(L".")), levelName, seed, pLevelType, initData);
275 // logger.info("Done (" + (System.nanoTime() - levelNanoTime) + "ns)! For help, type \"help\" or \"?\"");
276
277 // 4J delete passed in save data now - this is only required for the tutorial which is loaded by passing data directly in rather than using the storage manager
278 if( initData->saveData )
279 {
280 delete initData->saveData->data;
281 initData->saveData->data = 0;
282 initData->saveData->fileSize = 0;
283 }
284
285 g_NetworkManager.ServerReady(); // 4J added
286 return m_bLoaded;
287
288}
289
290// 4J - added - extra thread to post processing on separate thread during level creation
291int MinecraftServer::runPostUpdate(void* lpParam)
292{
293 ShutdownManager::HasStarted(ShutdownManager::ePostProcessThread);
294
295 MinecraftServer *server = (MinecraftServer *)lpParam;
296 Entity::useSmallIds(); // This thread can end up spawning entities as resources
297 IntCache::CreateNewThreadStorage();
298 AABB::CreateNewThreadStorage();
299 Vec3::CreateNewThreadStorage();
300 Compression::UseDefaultThreadStorage();
301 Level::enableLightingCache();
302 Tile::CreateNewThreadStorage();
303
304 // Update lights for both levels until we are signalled to terminate
305 do
306 {
307 EnterCriticalSection(&server->m_postProcessCS);
308 if( server->m_postProcessRequests.size() )
309 {
310 MinecraftServer::postProcessRequest request = server->m_postProcessRequests.back();
311 server->m_postProcessRequests.pop_back();
312 LeaveCriticalSection(&server->m_postProcessCS);
313 static int count = 0;
314 PIXBeginNamedEvent(0,"Post processing %d ", (count++)%8);
315 request.chunkSource->postProcess(request.chunkSource, request.x, request.z );
316 PIXEndNamedEvent();
317 }
318 else
319 {
320 LeaveCriticalSection(&server->m_postProcessCS);
321 }
322 Sleep(1);
323 } while (!server->m_postUpdateTerminate && ShutdownManager::ShouldRun(ShutdownManager::ePostProcessThread));
324 //#ifndef __PS3__
325 // One final pass through updates to make sure we're done
326 EnterCriticalSection(&server->m_postProcessCS);
327 int maxRequests = server->m_postProcessRequests.size();
328 while(server->m_postProcessRequests.size() && ShutdownManager::ShouldRun(ShutdownManager::ePostProcessThread) )
329 {
330 MinecraftServer::postProcessRequest request = server->m_postProcessRequests.back();
331 server->m_postProcessRequests.pop_back();
332 LeaveCriticalSection(&server->m_postProcessCS);
333 request.chunkSource->postProcess(request.chunkSource, request.x, request.z );
334#ifdef __PS3__
335#ifndef _CONTENT_PACKAGE
336 if((server->m_postProcessRequests.size() % 10) == 0)
337 printf("processing request %00d\n", server->m_postProcessRequests.size());
338#endif
339 Sleep(1);
340#endif
341 EnterCriticalSection(&server->m_postProcessCS);
342 }
343 LeaveCriticalSection(&server->m_postProcessCS);
344 //#endif //__PS3__
345 Tile::ReleaseThreadStorage();
346 IntCache::ReleaseThreadStorage();
347 AABB::ReleaseThreadStorage();
348 Vec3::ReleaseThreadStorage();
349 Level::destroyLightingCache();
350
351 ShutdownManager::HasFinished(ShutdownManager::ePostProcessThread);
352
353 return 0;
354}
355
356void MinecraftServer::addPostProcessRequest(ChunkSource *chunkSource, int x, int z)
357{
358 EnterCriticalSection(&m_postProcessCS);
359 m_postProcessRequests.push_back(MinecraftServer::postProcessRequest(x,z,chunkSource));
360 LeaveCriticalSection(&m_postProcessCS);
361}
362
363void MinecraftServer::postProcessTerminate(ProgressRenderer *mcprogress)
364{
365 DWORD status = 0;
366
367 EnterCriticalSection(&server->m_postProcessCS);
368 size_t postProcessItemCount = server->m_postProcessRequests.size();
369 LeaveCriticalSection(&server->m_postProcessCS);
370
371 do
372 {
373 status = m_postUpdateThread->WaitForCompletion(50);
374 if( status == WAIT_TIMEOUT )
375 {
376 EnterCriticalSection(&server->m_postProcessCS);
377 size_t postProcessItemRemaining = server->m_postProcessRequests.size();
378 LeaveCriticalSection(&server->m_postProcessCS);
379
380 if( postProcessItemCount )
381 {
382 mcprogress->progressStagePercentage((postProcessItemCount - postProcessItemRemaining) * 100 / postProcessItemCount);
383 }
384 CompressedTileStorage::tick();
385 SparseLightStorage::tick();
386 SparseDataStorage::tick();
387 }
388 } while ( status == WAIT_TIMEOUT );
389 delete m_postUpdateThread;
390 m_postUpdateThread = NULL;
391 DeleteCriticalSection(&m_postProcessCS);
392}
393
394bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring& name, __int64 levelSeed, LevelType *pLevelType, NetworkGameInitData *initData)
395{
396 // 4J - TODO - do with new save stuff
397 // if (storageSource->requiresConversion(name))
398 // {
399 // assert(false);
400 // }
401 ProgressRenderer *mcprogress = Minecraft::GetInstance()->progressRenderer;
402
403 // 4J TODO - free levels here if there are already some?
404 levels = ServerLevelArray(3);
405
406 int gameTypeId = settings->getInt(L"gamemode", app.GetGameHostOption(eGameHostOption_GameType));//LevelSettings::GAMETYPE_SURVIVAL);
407 GameType *gameType = LevelSettings::validateGameType(gameTypeId);
408 app.DebugPrintf("Default game type: %d\n" , gameTypeId);
409
410 LevelSettings *levelSettings = new LevelSettings(levelSeed, gameType, app.GetGameHostOption(eGameHostOption_Structures)>0?true:false, isHardcore(), true, pLevelType, initData->xzSize, initData->hellScale);
411 if( app.GetGameHostOption(eGameHostOption_BonusChest ) ) levelSettings->enableStartingBonusItems();
412
413 // 4J - temp - load existing level
414 shared_ptr<McRegionLevelStorage> storage = nullptr;
415 bool levelChunksNeedConverted = false;
416 if( initData->saveData != NULL )
417 {
418 // We are loading a file from disk with the data passed in
419
420#ifdef SPLIT_SAVES
421 ConsoleSaveFileOriginal oldFormatSave( initData->saveData->saveName, initData->saveData->data, initData->saveData->fileSize, false, initData->savePlatform );
422 ConsoleSaveFile* pSave = new ConsoleSaveFileSplit( &oldFormatSave );
423
424 //ConsoleSaveFile* pSave = new ConsoleSaveFileSplit( initData->saveData->saveName, initData->saveData->data, initData->saveData->fileSize, false, initData->savePlatform );
425#else
426 ConsoleSaveFile* pSave = new ConsoleSaveFileOriginal( initData->saveData->saveName, initData->saveData->data, initData->saveData->fileSize, false, initData->savePlatform );
427#endif
428 if(pSave->isSaveEndianDifferent())
429 levelChunksNeedConverted = true;
430 pSave->ConvertToLocalPlatform(); // check if we need to convert this file from PS3->PS4
431
432 storage = shared_ptr<McRegionLevelStorage>(new McRegionLevelStorage(pSave, File(L"."), name, true));
433 }
434 else
435 {
436 // We are loading a save from the storage manager
437#ifdef SPLIT_SAVES
438 bool bLevelGenBaseSave = false;
439 LevelGenerationOptions *levelGen = app.getLevelGenerationOptions();
440 if( levelGen != NULL && levelGen->requiresBaseSave())
441 {
442 DWORD fileSize = 0;
443 LPVOID pvSaveData = levelGen->getBaseSaveData(fileSize);
444 if(pvSaveData && fileSize != 0) bLevelGenBaseSave = true;
445 }
446 ConsoleSaveFileSplit *newFormatSave = NULL;
447 if(bLevelGenBaseSave)
448 {
449 ConsoleSaveFileOriginal oldFormatSave( L"" );
450 newFormatSave = new ConsoleSaveFileSplit( &oldFormatSave );
451 }
452 else
453 {
454 newFormatSave = new ConsoleSaveFileSplit( L"" );
455 }
456
457 storage = shared_ptr<McRegionLevelStorage>(new McRegionLevelStorage(newFormatSave, File(L"."), name, true));
458#else
459 storage = shared_ptr<McRegionLevelStorage>(new McRegionLevelStorage(new ConsoleSaveFileOriginal( L"" ), File(L"."), name, true));
460#endif
461 }
462
463 // McRegionLevelStorage *storage = new McRegionLevelStorage(new ConsoleSaveFile( L"" ), L"", L"", 0); // original
464 // McRegionLevelStorage *storage = new McRegionLevelStorage(File(L"."), name, true); // TODO
465 for (unsigned int i = 0; i < levels.length; i++)
466 {
467 if( s_bServerHalted || !g_NetworkManager.IsInSession() )
468 {
469 return false;
470 }
471
472 // String levelName = name;
473 // if (i == 1) levelName += "_nether";
474 int dimension = 0;
475 if (i == 1) dimension = -1;
476 if (i == 2) dimension = 1;
477 if (i == 0)
478 {
479 levels[i] = new ServerLevel(this, storage, name, dimension, levelSettings);
480 if(app.getLevelGenerationOptions() != NULL)
481 {
482 LevelGenerationOptions *mapOptions = app.getLevelGenerationOptions();
483 Pos *spawnPos = mapOptions->getSpawnPos();
484 if( spawnPos != NULL )
485 {
486 levels[i]->setSpawnPos( spawnPos );
487 }
488
489 levels[i]->getLevelData()->setHasBeenInCreative(mapOptions->isFromDLC());
490 }
491 }
492 else levels[i] = new DerivedServerLevel(this, storage, name, dimension, levelSettings, levels[0]);
493 // levels[i]->addListener(new ServerLevelListener(this, levels[i])); // 4J - have moved this to the ServerLevel ctor so that it is set up in time for the first chunk to load, which might actually happen there
494
495 // 4J Stu - We set the levels difficulty based on the minecraft options
496 //levels[i]->difficulty = settings->getBoolean(L"spawn-monsters", true) ? Difficulty::EASY : Difficulty::PEACEFUL;
497 Minecraft *pMinecraft = Minecraft::GetInstance();
498 // m_lastSentDifficulty = pMinecraft->options->difficulty;
499 levels[i]->difficulty = app.GetGameHostOption(eGameHostOption_Difficulty); //pMinecraft->options->difficulty;
500 app.DebugPrintf("MinecraftServer::loadLevel - Difficulty = %d\n",levels[i]->difficulty);
501
502#if DEBUG_SERVER_DONT_SPAWN_MOBS
503 levels[i]->setSpawnSettings(false, false);
504#else
505 levels[i]->setSpawnSettings(settings->getBoolean(L"spawn-monsters", true), animals);
506#endif
507 levels[i]->getLevelData()->setGameType(gameType);
508
509 if(app.getLevelGenerationOptions() != NULL)
510 {
511 LevelGenerationOptions *mapOptions = app.getLevelGenerationOptions();
512 levels[i]->getLevelData()->setHasBeenInCreative(mapOptions->getLevelHasBeenInCreative() );
513 }
514
515 players->setLevel(levels);
516 }
517
518 if( levels[0]->isNew )
519 {
520 mcprogress->progressStage(IDS_PROGRESS_GENERATING_SPAWN_AREA);
521 }
522 else
523 {
524 mcprogress->progressStage(IDS_PROGRESS_LOADING_SPAWN_AREA);
525 }
526 app.SetGameHostOption( eGameHostOption_HasBeenInCreative, gameType == GameType::CREATIVE || levels[0]->getHasBeenInCreative() );
527 app.SetGameHostOption( eGameHostOption_Structures, levels[0]->isGenerateMapFeatures() );
528
529 if( s_bServerHalted || !g_NetworkManager.IsInSession() ) return false;
530
531 // 4J - Make a new thread to do post processing
532 InitializeCriticalSection(&m_postProcessCS);
533
534 // 4J-PB - fix for 108310 - TCR #001 BAS Game Stability: TU12: Code: Compliance: Crash after creating world on "journey" seed.
535 // Stack gets very deep with some sand tower falling, so increased the stacj to 256K from 128k on other platforms (was already set to that on PS3 and Orbis)
536
537 m_postUpdateThread = new C4JThread(runPostUpdate, this, "Post processing", 256*1024);
538
539 m_postUpdateTerminate = false;
540 m_postUpdateThread->SetProcessor(CPU_CORE_POST_PROCESSING);
541 m_postUpdateThread->SetPriority(THREAD_PRIORITY_ABOVE_NORMAL);
542 m_postUpdateThread->Run();
543
544 __int64 startTime = System::currentTimeMillis();
545
546 // 4J Stu - Added this to temporarily make starting games on vita faster
547#ifdef __PSVITA__
548 int r = 48;
549#else
550 int r = 196;
551#endif
552
553 // 4J JEV: load gameRules.
554 ConsoleSavePath filepath(GAME_RULE_SAVENAME);
555 ConsoleSaveFile *csf = getLevel(0)->getLevelStorage()->getSaveFile();
556 if( csf->doesFileExist(filepath) )
557 {
558 DWORD numberOfBytesRead;
559 byteArray ba_gameRules;
560
561 FileEntry *fe = csf->createFile(filepath);
562
563 ba_gameRules.length = fe->getFileSize();
564 ba_gameRules.data = new BYTE[ ba_gameRules.length ];
565
566 csf->setFilePointer(fe,0,NULL,FILE_BEGIN);
567 csf->readFile(fe, ba_gameRules.data, ba_gameRules.length, &numberOfBytesRead);
568 assert(numberOfBytesRead == ba_gameRules.length);
569
570 app.m_gameRules.loadGameRules(ba_gameRules.data, ba_gameRules.length);
571 csf->closeHandle(fe);
572 }
573
574 __int64 lastTime = System::currentTimeMillis();
575#ifdef _LARGE_WORLDS
576 if(app.GetGameNewWorldSize() > levels[0]->getLevelData()->getXZSizeOld())
577 {
578 if(!app.GetGameNewWorldSizeUseMoat()) // check the moat settings to see if we should be overwriting the edge tiles
579 {
580 overwriteBordersForNewWorldSize(levels[0]);
581 }
582 // we're always overwriting hell edges
583 int oldHellSize = levels[0]->getLevelData()->getXZHellSizeOld();
584 overwriteHellBordersForNewWorldSize(levels[1], oldHellSize);
585 }
586#endif
587
588 // 4J Stu - This loop is changed in 1.0.1 to only process the first level (ie the overworld), but I think we still want to do them all
589 int i = 0;
590 for (int i = 0; i < levels.length ; i++)
591 {
592 // logger.info("Preparing start region for level " + i);
593 if (i == 0 || settings->getBoolean(L"allow-nether", true))
594 {
595 ServerLevel *level = levels[i];
596 if(levelChunksNeedConverted)
597 {
598 // storage->getSaveFile()->convertLevelChunks(level)
599 }
600
601#if 0
602 __int64 lastStorageTickTime = System::currentTimeMillis();
603
604 // Test code to enable full creation of levels at start up
605 int halfsidelen = ( i == 0 ) ? 27 : 9;
606 for( int x = -halfsidelen; x < halfsidelen; x++ )
607 {
608 for( int z = -halfsidelen; z < halfsidelen; z++ )
609 {
610 int total = halfsidelen * halfsidelen * 4;
611 int pos = z + halfsidelen + ( ( x + halfsidelen ) * 2 * halfsidelen );
612 mcprogress->progressStagePercentage((pos) * 100 / total);
613 level->cache->create(x,z, true); // 4J - added parameter to disable postprocessing here
614
615 if( System::currentTimeMillis() - lastStorageTickTime > 50 )
616 {
617 CompressedTileStorage::tick();
618 SparseLightStorage::tick();
619 SparseDataStorage::tick();
620 lastStorageTickTime = System::currentTimeMillis();
621 }
622 }
623 }
624#else
625 __int64 lastStorageTickTime = System::currentTimeMillis();
626 Pos *spawnPos = level->getSharedSpawnPos();
627
628 int twoRPlusOne = r*2 + 1;
629 int total = twoRPlusOne * twoRPlusOne;
630 for (int x = -r; x <= r && running; x += 16)
631 {
632 for (int z = -r; z <= r && running; z += 16)
633 {
634 if( s_bServerHalted || !g_NetworkManager.IsInSession() )
635 {
636 delete spawnPos;
637 m_postUpdateTerminate = true;
638 postProcessTerminate(mcprogress);
639 return false;
640 }
641 // printf(">>>%d %d %d\n",i,x,z);
642 // __int64 now = System::currentTimeMillis();
643 // if (now < lastTime) lastTime = now;
644 // if (now > lastTime + 1000)
645 {
646 int pos = (x + r) * twoRPlusOne + (z + 1);
647 // setProgress(L"Preparing spawn area", (pos) * 100 / total);
648 mcprogress->progressStagePercentage((pos+r) * 100 / total);
649 // lastTime = now;
650 }
651 static int count = 0;
652 PIXBeginNamedEvent(0,"Creating %d ", (count++)%8);
653 level->cache->create((spawnPos->x + x) >> 4, (spawnPos->z + z) >> 4, true); // 4J - added parameter to disable postprocessing here
654 PIXEndNamedEvent();
655 // while (level->updateLights() && running)
656 // ;
657 if( System::currentTimeMillis() - lastStorageTickTime > 50 )
658 {
659 CompressedTileStorage::tick();
660 SparseLightStorage::tick();
661 SparseDataStorage::tick();
662 lastStorageTickTime = System::currentTimeMillis();
663 }
664 }
665 }
666
667 // 4J - removed this as now doing the recheckGaps call when each chunk is post-processed, so can happen on things outside of the spawn area too
668#if 0
669 // 4J - added this code to propagate lighting properly in the spawn area before we go sharing it with the local client or across the network
670 for (int x = -r; x <= r && running; x += 16)
671 {
672 for (int z = -r; z <= r && running; z += 16)
673 {
674 PIXBeginNamedEvent(0,"Lighting gaps for %d %d",x,z);
675 level->getChunkAt(spawnPos->x + x, spawnPos->z + z)->recheckGaps(true);
676 PIXEndNamedEvent();
677 }
678 }
679#endif
680
681 delete spawnPos;
682#endif
683 }
684 }
685 // printf("Main thread complete at %dms\n",System::currentTimeMillis() - startTime);
686
687 // Wait for post processing, then lighting threads, to end (post-processing may make more lighting changes)
688 m_postUpdateTerminate = true;
689
690 postProcessTerminate(mcprogress);
691
692
693 // stronghold position?
694 if(levels[0]->dimension->id==0)
695 {
696
697 app.DebugPrintf("===================================\n");
698
699 if(!levels[0]->getLevelData()->getHasStronghold())
700 {
701 int x,z;
702 if(app.GetTerrainFeaturePosition(eTerrainFeature_Stronghold,&x,&z))
703 {
704 levels[0]->getLevelData()->setXStronghold(x);
705 levels[0]->getLevelData()->setZStronghold(z);
706 levels[0]->getLevelData()->setHasStronghold();
707
708 app.DebugPrintf("=== FOUND stronghold in terrain features list\n");
709
710 }
711 else
712 {
713 // can't find the stronghold position in the terrain feature list. Do we have to run a post-process?
714 app.DebugPrintf("=== Can't find stronghold in terrain features list\n");
715 }
716 }
717 else
718 {
719 app.DebugPrintf("=== Leveldata has stronghold position\n");
720 }
721 app.DebugPrintf("===================================\n");
722 }
723
724 // printf("Post processing complete at %dms\n",System::currentTimeMillis() - startTime);
725
726 // printf("Lighting complete at %dms\n",System::currentTimeMillis() - startTime);
727
728 if( s_bServerHalted || !g_NetworkManager.IsInSession() ) return false;
729
730 if( levels[1]->isNew )
731 {
732 levels[1]->save(true, mcprogress);
733 }
734
735 if( s_bServerHalted || !g_NetworkManager.IsInSession() ) return false;
736
737 if( levels[2]->isNew )
738 {
739 levels[2]->save(true, mcprogress);
740 }
741
742 if( s_bServerHalted || !g_NetworkManager.IsInSession() ) return false;
743
744 // 4J - added - immediately save newly created level, like single player game
745 // 4J Stu - We also want to immediately save the tutorial
746 if ( levels[0]->isNew )
747 saveGameRules();
748
749 if( levels[0]->isNew )
750 {
751 levels[0]->save(true, mcprogress);
752 }
753
754 if( s_bServerHalted || !g_NetworkManager.IsInSession() ) return false;
755
756 if( levels[0]->isNew || levels[1]->isNew || levels[2]->isNew )
757 {
758 levels[0]->saveToDisc(mcprogress, false);
759 }
760
761 if( s_bServerHalted || !g_NetworkManager.IsInSession() ) return false;
762
763 /*
764 * int r = 24; for (int x = -r; x <= r; x++) {
765 * setProgress("Preparing spawn area", (x + r) * 100 / (r + r + 1)); for (int z
766 * = -r; z <= r; z++) { if (!running) return; level.cache.create((level.xSpawn
767 * >> 4) + x, (level.zSpawn >> 4) + z); while (running && level.updateLights())
768 * ; } }
769 */
770 endProgress();
771
772 return true;
773}
774
775#ifdef _LARGE_WORLDS
776void MinecraftServer::overwriteBordersForNewWorldSize(ServerLevel* level)
777{
778 // recreate the chunks round the border (2 chunks or 32 blocks deep), deleting any player data from them
779 app.DebugPrintf("Expanding level size\n");
780 int oldSize = level->getLevelData()->getXZSizeOld();
781 // top
782 int minVal = -oldSize/2;
783 int maxVal = (oldSize/2)-1;
784 for(int xVal = minVal; xVal <= maxVal; xVal++)
785 {
786 int zVal = minVal;
787 level->cache->overwriteLevelChunkFromSource(xVal, zVal);
788 level->cache->overwriteLevelChunkFromSource(xVal, zVal+1);
789 }
790 // bottom
791 for(int xVal = minVal; xVal <= maxVal; xVal++)
792 {
793 int zVal = maxVal;
794 level->cache->overwriteLevelChunkFromSource(xVal, zVal);
795 level->cache->overwriteLevelChunkFromSource(xVal, zVal-1);
796 }
797 // left
798 for(int zVal = minVal; zVal <= maxVal; zVal++)
799 {
800 int xVal = minVal;
801 level->cache->overwriteLevelChunkFromSource(xVal, zVal);
802 level->cache->overwriteLevelChunkFromSource(xVal+1, zVal);
803 }
804 // right
805 for(int zVal = minVal; zVal <= maxVal; zVal++)
806 {
807 int xVal = maxVal;
808 level->cache->overwriteLevelChunkFromSource(xVal, zVal);
809 level->cache->overwriteLevelChunkFromSource(xVal-1, zVal);
810 }
811}
812
813void MinecraftServer::overwriteHellBordersForNewWorldSize(ServerLevel* level, int oldHellSize)
814{
815 // recreate the chunks round the border (1 chunk or 16 blocks deep), deleting any player data from them
816 app.DebugPrintf("Expanding level size\n");
817 // top
818 int minVal = -oldHellSize/2;
819 int maxVal = (oldHellSize/2)-1;
820 for(int xVal = minVal; xVal <= maxVal; xVal++)
821 {
822 int zVal = minVal;
823 level->cache->overwriteHellLevelChunkFromSource(xVal, zVal, minVal, maxVal);
824 }
825 // bottom
826 for(int xVal = minVal; xVal <= maxVal; xVal++)
827 {
828 int zVal = maxVal;
829 level->cache->overwriteHellLevelChunkFromSource(xVal, zVal, minVal, maxVal);
830 }
831 // left
832 for(int zVal = minVal; zVal <= maxVal; zVal++)
833 {
834 int xVal = minVal;
835 level->cache->overwriteHellLevelChunkFromSource(xVal, zVal, minVal, maxVal);
836 }
837 // right
838 for(int zVal = minVal; zVal <= maxVal; zVal++)
839 {
840 int xVal = maxVal;
841 level->cache->overwriteHellLevelChunkFromSource(xVal, zVal, minVal, maxVal);
842 }
843}
844
845#endif
846
847void MinecraftServer::setProgress(const wstring& status, int progress)
848{
849 progressStatus = status;
850 this->progress = progress;
851 // logger.info(status + ": " + progress + "%");
852}
853
854void MinecraftServer::endProgress()
855{
856 progressStatus = L"";
857 this->progress = 0;
858}
859
860void MinecraftServer::saveAllChunks()
861{
862 // logger.info("Saving chunks");
863 for (unsigned int i = 0; i < levels.length; i++)
864 {
865 // 4J Stu - Due to the way save mounting is handled on XboxOne, we can actually save after the player has signed out.
866#ifndef _XBOX_ONE
867 if( m_bPrimaryPlayerSignedOut ) break;
868#endif
869 // 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat
870 // with the data from the nethers leveldata.
871 // Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting.
872 ServerLevel *level = levels[levels.length - 1 - i];
873 if( level ) // 4J - added check as level can be NULL if we end up in stopServer really early on due to network failure
874 {
875 level->save(true, Minecraft::GetInstance()->progressRenderer);
876
877 // Only close the level storage when we have saved the last level, otherwise we need to recreate the region files
878 // when saving the next levels
879 if( i == (levels.length - 1))
880 {
881 level->closeLevelStorage();
882 }
883 }
884 }
885}
886
887// 4J-JEV: Added
888void MinecraftServer::saveGameRules()
889{
890#ifndef _CONTENT_PACKAGE
891 if(app.DebugSettingsOn() && app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<<eDebugSetting_DistributableSave))
892 {
893 // Do nothing
894 }
895 else
896#endif
897 {
898 byteArray ba;
899 ba.data = NULL;
900 app.m_gameRules.saveGameRules( &ba.data, &ba.length );
901
902 if (ba.data != NULL)
903 {
904 ConsoleSaveFile *csf = getLevel(0)->getLevelStorage()->getSaveFile();
905 FileEntry *fe = csf->createFile(ConsoleSavePath(GAME_RULE_SAVENAME));
906 csf->setFilePointer(fe, 0, NULL, FILE_BEGIN);
907 DWORD length;
908 csf->writeFile(fe, ba.data, ba.length, &length );
909
910 delete [] ba.data;
911
912 csf->closeHandle(fe);
913 }
914 }
915}
916
917void MinecraftServer::Suspend()
918{
919 PIXBeginNamedEvent(0,"Suspending server");
920 m_suspending = true;
921 // Get the frequency of the timer
922 LARGE_INTEGER qwTicksPerSec, qwTime, qwNewTime, qwDeltaTime;
923 float fElapsedTime = 0.0f;
924 QueryPerformanceFrequency( &qwTicksPerSec );
925 float fSecsPerTick = 1.0f / (float)qwTicksPerSec.QuadPart;
926 // Save the start time
927 QueryPerformanceCounter( &qwTime );
928 if(m_bLoaded && ProfileManager.IsFullVersion() && (!StorageManager.GetSaveDisabled()))
929 {
930 if (players != NULL)
931 {
932 players->saveAll(NULL);
933 }
934 for (unsigned int j = 0; j < levels.length; j++)
935 {
936 if( s_bServerHalted ) break;
937 // 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat
938 // with the data from the nethers leveldata.
939 // Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting.
940 ServerLevel *level = levels[levels.length - 1 - j];
941 level->Suspend();
942 }
943 if( !s_bServerHalted )
944 {
945 saveGameRules();
946 levels[0]->saveToDisc(NULL, true);
947 }
948 }
949 QueryPerformanceCounter( &qwNewTime );
950
951 qwDeltaTime.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart;
952 fElapsedTime = fSecsPerTick * ((FLOAT)(qwDeltaTime.QuadPart));
953
954 // 4J-JEV: Flush stats and call PlayerSessionExit.
955 for (int iPad = 0; iPad < XUSER_MAX_COUNT; iPad++)
956 {
957 if (ProfileManager.IsSignedIn(iPad))
958 {
959 TelemetryManager->RecordPlayerSessionExit(iPad, DisconnectPacket::eDisconnect_Quitting);
960 }
961 }
962
963 m_suspending = false;
964 app.DebugPrintf("Suspend server: Elapsed time %f\n", fElapsedTime);
965 PIXEndNamedEvent();
966}
967
968bool MinecraftServer::IsSuspending()
969{
970 return m_suspending;
971}
972
973void MinecraftServer::stopServer(bool didInit)
974{
975
976 // 4J-PB - need to halt the rendering of the data, since we're about to remove it
977#ifdef __PS3__
978 if( ShutdownManager::ShouldRun(ShutdownManager::eServerThread ) ) // This thread will take itself out if we are shutting down
979#endif
980 {
981 Minecraft::GetInstance()->gameRenderer->DisableUpdateThread();
982 }
983
984 connection->stop();
985
986 app.DebugPrintf("Stopping server\n");
987 // logger.info("Stopping server");
988 // 4J-PB - If the primary player has signed out, then don't attempt to save anything
989
990 // also need to check for a profile switch here - primary player signs out, and another player signs in before dismissing the dash
991#ifdef _DURANGO
992 // On Durango check if the primary user is signed in OR mid-sign-out
993 if(ProfileManager.GetUser(0, true) != nullptr)
994#else
995 if((m_bPrimaryPlayerSignedOut==false) && ProfileManager.IsSignedIn(ProfileManager.GetPrimaryPad()))
996#endif
997 {
998#if defined(_XBOX_ONE) || defined(__ORBIS__)
999 // Always save on exit! Except if saves are disabled.
1000 if(!saveOnExitAnswered()) m_saveOnExit = true;
1001#endif
1002 // if trial version or saving is disabled, then don't save anything. Also don't save anything if we didn't actually get through the server initialisation.
1003 if(m_saveOnExit && ProfileManager.IsFullVersion() && (!StorageManager.GetSaveDisabled()) && didInit)
1004 {
1005 if (players != NULL)
1006 {
1007 players->saveAll(Minecraft::GetInstance()->progressRenderer, true);
1008 }
1009 // 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat
1010 // with the data from the nethers leveldata.
1011 // Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting.
1012 //for (unsigned int i = levels.length - 1; i >= 0; i--)
1013 //{
1014 // ServerLevel *level = levels[i];
1015 // if (level != NULL)
1016 // {
1017 saveAllChunks();
1018 // }
1019 //}
1020
1021 saveGameRules();
1022 app.m_gameRules.unloadCurrentGameRules();
1023 if( levels[0] != NULL ) // This can be null if stopServer happens very quickly due to network error
1024 {
1025 levels[0]->saveToDisc(Minecraft::GetInstance()->progressRenderer, false);
1026 }
1027 }
1028 }
1029 // reset the primary player signout flag
1030 m_bPrimaryPlayerSignedOut=false;
1031 s_bServerHalted = false;
1032
1033 // On Durango/Orbis, we need to wait for all the asynchronous saving processes to complete before destroying the levels, as that will ultimately delete
1034 // the directory level storage & therefore the ConsoleSaveSplit instance, which needs to be around until all the sub files have completed saving.
1035#if defined(_DURANGO) || defined(__ORBIS__) || defined(__PSVITA__)
1036 while(StorageManager.GetSaveState() != C4JStorage::ESaveGame_Idle )
1037 {
1038 Sleep(10);
1039 }
1040#endif
1041
1042 // 4J-PB remove the server levels
1043 unsigned int iServerLevelC=levels.length;
1044 for (unsigned int i = 0; i < iServerLevelC; i++)
1045 {
1046 if(levels[i]!=NULL)
1047 {
1048 delete levels[i];
1049 levels[i] = NULL;
1050 }
1051 }
1052
1053#if defined(__PS3__) || defined(__ORBIS__)
1054 // Clear the update flags as it's possible they could be out of sync, causing a crash when starting a new world after the first new level ticks
1055 // Fix for PS3 #1538 - [IN GAME] If the user 'Exit without saving' from inside the Nether or The End, the title can hang when loading back into the save.
1056#endif
1057
1058 delete connection;
1059 connection = NULL;
1060 delete players;
1061 players = NULL;
1062 delete settings;
1063 settings = NULL;
1064
1065 g_NetworkManager.ServerStopped();
1066}
1067
1068void MinecraftServer::halt()
1069{
1070 running = false;
1071}
1072
1073void MinecraftServer::setMaxBuildHeight(int maxBuildHeight)
1074{
1075 this->maxBuildHeight = maxBuildHeight;
1076}
1077
1078int MinecraftServer::getMaxBuildHeight()
1079{
1080 return maxBuildHeight;
1081}
1082
1083PlayerList *MinecraftServer::getPlayers()
1084{
1085 return players;
1086}
1087
1088void MinecraftServer::setPlayers(PlayerList *players)
1089{
1090 this->players = players;
1091}
1092
1093ServerConnection *MinecraftServer::getConnection()
1094{
1095 return connection;
1096}
1097
1098bool MinecraftServer::isAnimals()
1099{
1100 return animals;
1101}
1102
1103void MinecraftServer::setAnimals(bool animals)
1104{
1105 this->animals = animals;
1106}
1107
1108bool MinecraftServer::isNpcsEnabled()
1109{
1110 return npcs;
1111}
1112
1113void MinecraftServer::setNpcsEnabled(bool npcs)
1114{
1115 this->npcs = npcs;
1116}
1117
1118bool MinecraftServer::isPvpAllowed()
1119{
1120 return pvp;
1121}
1122
1123void MinecraftServer::setPvpAllowed(bool pvp)
1124{
1125 this->pvp = pvp;
1126}
1127
1128bool MinecraftServer::isFlightAllowed()
1129{
1130 return allowFlight;
1131}
1132
1133void MinecraftServer::setFlightAllowed(bool allowFlight)
1134{
1135 this->allowFlight = allowFlight;
1136}
1137
1138bool MinecraftServer::isCommandBlockEnabled()
1139{
1140 return false; //settings.getBoolean("enable-command-block", false);
1141}
1142
1143bool MinecraftServer::isNetherEnabled()
1144{
1145 return true; //settings.getBoolean("allow-nether", true);
1146}
1147
1148bool MinecraftServer::isHardcore()
1149{
1150 return false;
1151}
1152
1153int MinecraftServer::getOperatorUserPermissionLevel()
1154{
1155 return Command::LEVEL_OWNERS; //settings.getInt("op-permission-level", Command.LEVEL_OWNERS);
1156}
1157
1158CommandDispatcher *MinecraftServer::getCommandDispatcher()
1159{
1160 return commandDispatcher;
1161}
1162
1163Pos *MinecraftServer::getCommandSenderWorldPosition()
1164{
1165 return new Pos(0, 0, 0);
1166}
1167
1168Level *MinecraftServer::getCommandSenderWorld()
1169{
1170 return levels[0];
1171}
1172
1173int MinecraftServer::getSpawnProtectionRadius()
1174{
1175 return 16;
1176}
1177
1178bool MinecraftServer::isUnderSpawnProtection(Level *level, int x, int y, int z, shared_ptr<Player> player)
1179{
1180 if (level->dimension->id != 0) return false;
1181 //if (getPlayers()->getOps()->empty()) return false;
1182 if (getPlayers()->isOp(player->getName())) return false;
1183 if (getSpawnProtectionRadius() <= 0) return false;
1184
1185 Pos *spawnPos = level->getSharedSpawnPos();
1186 int xd = Mth::abs(x - spawnPos->x);
1187 int zd = Mth::abs(z - spawnPos->z);
1188 int dist = max(xd, zd);
1189
1190 return dist <= getSpawnProtectionRadius();
1191}
1192
1193void MinecraftServer::setForceGameType(bool forceGameType)
1194{
1195 this->forceGameType = forceGameType;
1196}
1197
1198bool MinecraftServer::getForceGameType()
1199{
1200 return forceGameType;
1201}
1202
1203__int64 MinecraftServer::getCurrentTimeMillis()
1204{
1205 return System::currentTimeMillis();
1206}
1207
1208int MinecraftServer::getPlayerIdleTimeout()
1209{
1210 return playerIdleTimeout;
1211}
1212
1213void MinecraftServer::setPlayerIdleTimeout(int playerIdleTimeout)
1214{
1215 this->playerIdleTimeout = playerIdleTimeout;
1216}
1217
1218extern int c0a, c0b, c1a, c1b, c1c, c2a, c2b;
1219void MinecraftServer::run(__int64 seed, void *lpParameter)
1220{
1221 NetworkGameInitData *initData = NULL;
1222 DWORD initSettings = 0;
1223 bool findSeed = false;
1224 if(lpParameter != NULL)
1225 {
1226 initData = (NetworkGameInitData *)lpParameter;
1227 initSettings = app.GetGameHostOption(eGameHostOption_All);
1228 findSeed = initData->findSeed;
1229 m_texturePackId = initData->texturePackId;
1230 }
1231 // try { // 4J - removed try/catch/finally
1232 bool didInit = false;
1233 if (initServer(seed, initData, initSettings,findSeed))
1234 {
1235 didInit = true;
1236 ServerLevel *levelNormalDimension = levels[0];
1237 // 4J-PB - Set the Stronghold position in the leveldata if there isn't one in there
1238 Minecraft *pMinecraft = Minecraft::GetInstance();
1239 LevelData *pLevelData=levelNormalDimension->getLevelData();
1240
1241 if(pLevelData && pLevelData->getHasStronghold()==false)
1242 {
1243 int x,z;
1244 if(app.GetTerrainFeaturePosition(eTerrainFeature_Stronghold,&x,&z))
1245 {
1246 pLevelData->setXStronghold(x);
1247 pLevelData->setZStronghold(z);
1248 pLevelData->setHasStronghold();
1249 }
1250 }
1251
1252 __int64 lastTime = getCurrentTimeMillis();
1253 __int64 unprocessedTime = 0;
1254 while (running && !s_bServerHalted)
1255 {
1256 __int64 now = getCurrentTimeMillis();
1257
1258 // 4J Stu - When we pause the server, we don't want to count that as time passed
1259 // 4J Stu - TU-1 hotifx - Remove this line. We want to make sure that we tick connections at the proper rate when paused
1260 //Fix for #13191 - The host of a game can get a message informing them that the connection to the server has been lost
1261 //if(m_isServerPaused) lastTime = now;
1262
1263 __int64 passedTime = now - lastTime;
1264 if (passedTime > MS_PER_TICK * 40)
1265 {
1266 // logger.warning("Can't keep up! Did the system time change, or is the server overloaded?");
1267 passedTime = MS_PER_TICK * 40;
1268 }
1269 if (passedTime < 0)
1270 {
1271 // logger.warning("Time ran backwards! Did the system time change?");
1272 passedTime = 0;
1273 }
1274 unprocessedTime += passedTime;
1275 lastTime = now;
1276
1277 // 4J Added ability to pause the server
1278 if( !m_isServerPaused )
1279 {
1280 bool didTick = false;
1281 if (levels[0]->allPlayersAreSleeping())
1282 {
1283 tick();
1284 unprocessedTime = 0;
1285 }
1286 else
1287 {
1288 // int tickcount = 0;
1289 // __int64 beforeall = System::currentTimeMillis();
1290 while (unprocessedTime > MS_PER_TICK)
1291 {
1292 unprocessedTime -= MS_PER_TICK;
1293 chunkPacketManagement_PreTick();
1294// __int64 before = System::currentTimeMillis();
1295 tick();
1296// __int64 after = System::currentTimeMillis();
1297// PIXReportCounter(L"Server time",(float)(after-before));
1298
1299 chunkPacketManagement_PostTick();
1300 }
1301// __int64 afterall = System::currentTimeMillis();
1302// PIXReportCounter(L"Server time all",(float)(afterall-beforeall));
1303// PIXReportCounter(L"Server ticks",(float)tickcount);
1304 }
1305 }
1306 else
1307 {
1308 // 4J Stu - TU1-hotfix
1309 //Fix for #13191 - The host of a game can get a message informing them that the connection to the server has been lost
1310 // The connections should tick at the same frequency even when paused
1311 while (unprocessedTime > MS_PER_TICK)
1312 {
1313 unprocessedTime -= MS_PER_TICK;
1314 // Keep ticking the connections to stop them timing out
1315 connection->tick();
1316 }
1317 }
1318 if(MinecraftServer::setTimeAtEndOfTick)
1319 {
1320 MinecraftServer::setTimeAtEndOfTick = false;
1321 for (unsigned int i = 0; i < levels.length; i++)
1322 {
1323 // if (i == 0 || settings->getBoolean(L"allow-nether", true)) // 4J removed - we always have nether
1324 {
1325 ServerLevel *level = levels[i];
1326 level->setGameTime( MinecraftServer::setTime );
1327 }
1328 }
1329 }
1330 if(MinecraftServer::setTimeOfDayAtEndOfTick)
1331 {
1332 MinecraftServer::setTimeOfDayAtEndOfTick = false;
1333 for (unsigned int i = 0; i < levels.length; i++)
1334 {
1335 if (i == 0 || settings->getBoolean(L"allow-nether", true))
1336 {
1337 ServerLevel *level = levels[i];
1338 level->setDayTime( MinecraftServer::setTimeOfDay );
1339 }
1340 }
1341 }
1342
1343 // Process delayed actions
1344 eXuiServerAction eAction;
1345 LPVOID param;
1346 for(int i=0;i<XUSER_MAX_COUNT;i++)
1347 {
1348 eAction = app.GetXuiServerAction(i);
1349 param = app.GetXuiServerActionParam(i);
1350
1351 switch(eAction)
1352 {
1353 case eXuiServerAction_AutoSaveGame:
1354#if defined(_XBOX_ONE) || defined(__ORBIS__)
1355 {
1356 PIXBeginNamedEvent(0,"Autosave");
1357
1358 // Get the frequency of the timer
1359 LARGE_INTEGER qwTicksPerSec, qwTime, qwNewTime, qwDeltaTime;
1360 float fElapsedTime = 0.0f;
1361 QueryPerformanceFrequency( &qwTicksPerSec );
1362 float fSecsPerTick = 1.0f / (float)qwTicksPerSec.QuadPart;
1363
1364 // Save the start time
1365 QueryPerformanceCounter( &qwTime );
1366
1367 if (players != NULL)
1368 {
1369 players->saveAll(NULL);
1370 }
1371
1372 for (unsigned int j = 0; j < levels.length; j++)
1373 {
1374 if( s_bServerHalted ) break;
1375 // 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat
1376 // with the data from the nethers leveldata.
1377 // Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting.
1378 ServerLevel *level = levels[levels.length - 1 - j];
1379 PIXBeginNamedEvent(0, "Saving level %d",levels.length - 1 - j);
1380 level->save(false, NULL, true);
1381 PIXEndNamedEvent();
1382 }
1383 if( !s_bServerHalted )
1384 {
1385 PIXBeginNamedEvent(0,"Saving game rules");
1386 saveGameRules();
1387 PIXEndNamedEvent();
1388
1389 PIXBeginNamedEvent(0,"Save to disc");
1390 levels[0]->saveToDisc(Minecraft::GetInstance()->progressRenderer, true);
1391 PIXEndNamedEvent();
1392 }
1393 PIXEndNamedEvent();
1394
1395 QueryPerformanceCounter( &qwNewTime );
1396 qwDeltaTime.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart;
1397 fElapsedTime = fSecsPerTick * ((FLOAT)(qwDeltaTime.QuadPart));
1398 app.DebugPrintf("Autosave: Elapsed time %f\n", fElapsedTime);
1399 }
1400 break;
1401#endif
1402 case eXuiServerAction_SaveGame:
1403 app.EnterSaveNotificationSection();
1404 if (players != NULL)
1405 {
1406 players->saveAll(Minecraft::GetInstance()->progressRenderer);
1407 }
1408
1409 players->broadcastAll( shared_ptr<UpdateProgressPacket>( new UpdateProgressPacket(20) ) );
1410
1411 for (unsigned int j = 0; j < levels.length; j++)
1412 {
1413 if( s_bServerHalted ) break;
1414 // 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat
1415 // with the data from the nethers leveldata.
1416 // Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting.
1417 ServerLevel *level = levels[levels.length - 1 - j];
1418 level->save(true, Minecraft::GetInstance()->progressRenderer, (eAction==eXuiServerAction_AutoSaveGame));
1419
1420 players->broadcastAll( shared_ptr<UpdateProgressPacket>( new UpdateProgressPacket(33 + (j*33) ) ) );
1421 }
1422 if( !s_bServerHalted )
1423 {
1424 saveGameRules();
1425
1426 levels[0]->saveToDisc(Minecraft::GetInstance()->progressRenderer, (eAction==eXuiServerAction_AutoSaveGame));
1427 }
1428 app.LeaveSaveNotificationSection();
1429 break;
1430 case eXuiServerAction_DropItem:
1431 // Find the player, and drop the id at their feet
1432 {
1433 shared_ptr<ServerPlayer> player = players->players.at(0);
1434 size_t id = (size_t) param;
1435 player->drop( shared_ptr<ItemInstance>( new ItemInstance(id, 1, 0 ) ) );
1436 }
1437 break;
1438 case eXuiServerAction_SpawnMob:
1439 {
1440 shared_ptr<ServerPlayer> player = players->players.at(0);
1441 eINSTANCEOF factory = (eINSTANCEOF)((size_t)param);
1442 shared_ptr<Mob> mob = dynamic_pointer_cast<Mob>(EntityIO::newByEnumType(factory,player->level ));
1443 mob->moveTo(player->x+1, player->y, player->z+1, player->level->random->nextFloat() * 360, 0);
1444 mob->setDespawnProtected(); // 4J added, default to being protected against despawning (has to be done after initial position is set)
1445 player->level->addEntity(mob);
1446 }
1447 break;
1448 case eXuiServerAction_PauseServer:
1449 m_isServerPaused = ( (size_t) param == TRUE );
1450 if( m_isServerPaused )
1451 {
1452 m_serverPausedEvent->Set();
1453 }
1454 break;
1455 case eXuiServerAction_ToggleRain:
1456 {
1457 bool isRaining = levels[0]->getLevelData()->isRaining();
1458 levels[0]->getLevelData()->setRaining(!isRaining);
1459 levels[0]->getLevelData()->setRainTime(levels[0]->random->nextInt(Level::TICKS_PER_DAY * 7) + Level::TICKS_PER_DAY / 2);
1460 }
1461 break;
1462 case eXuiServerAction_ToggleThunder:
1463 {
1464 bool isThundering = levels[0]->getLevelData()->isThundering();
1465 levels[0]->getLevelData()->setThundering(!isThundering);
1466 levels[0]->getLevelData()->setThunderTime(levels[0]->random->nextInt(Level::TICKS_PER_DAY * 7) + Level::TICKS_PER_DAY / 2);
1467 }
1468 break;
1469 case eXuiServerAction_ServerSettingChanged_Gamertags:
1470 players->broadcastAll( shared_ptr<ServerSettingsChangedPacket>( new ServerSettingsChangedPacket( ServerSettingsChangedPacket::HOST_OPTIONS, app.GetGameHostOption(eGameHostOption_Gamertags)) ) );
1471 break;
1472 case eXuiServerAction_ServerSettingChanged_BedrockFog:
1473 players->broadcastAll( shared_ptr<ServerSettingsChangedPacket>( new ServerSettingsChangedPacket( ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS, app.GetGameHostOption(eGameHostOption_All)) ) );
1474 break;
1475
1476 case eXuiServerAction_ServerSettingChanged_Difficulty:
1477 players->broadcastAll( shared_ptr<ServerSettingsChangedPacket>( new ServerSettingsChangedPacket( ServerSettingsChangedPacket::HOST_DIFFICULTY, Minecraft::GetInstance()->options->difficulty) ) );
1478 break;
1479 case eXuiServerAction_ExportSchematic:
1480#ifndef _CONTENT_PACKAGE
1481 app.EnterSaveNotificationSection();
1482
1483 //players->broadcastAll( shared_ptr<UpdateProgressPacket>( new UpdateProgressPacket(20) ) );
1484
1485 if( !s_bServerHalted )
1486 {
1487 ConsoleSchematicFile::XboxSchematicInitParam *initData = (ConsoleSchematicFile::XboxSchematicInitParam *)param;
1488#ifdef _XBOX
1489 File targetFileDir(File::pathRoot + File::pathSeparator + L"Schematics");
1490#else
1491 File targetFileDir(L"Schematics");
1492#endif
1493 if(!targetFileDir.exists()) targetFileDir.mkdir();
1494
1495 wchar_t filename[128];
1496 swprintf(filename,128,L"%ls%dx%dx%d.sch",initData->name,(initData->endX - initData->startX + 1), (initData->endY - initData->startY + 1), (initData->endZ - initData->startZ + 1));
1497
1498 File dataFile = File( targetFileDir, wstring(filename) );
1499 if(dataFile.exists()) dataFile._delete();
1500 FileOutputStream fos = FileOutputStream(dataFile);
1501 DataOutputStream dos = DataOutputStream(&fos);
1502 ConsoleSchematicFile::generateSchematicFile(&dos, levels[0], initData->startX, initData->startY, initData->startZ, initData->endX, initData->endY, initData->endZ, initData->bSaveMobs, initData->compressionType);
1503 dos.close();
1504
1505 delete initData;
1506 }
1507 app.LeaveSaveNotificationSection();
1508#endif
1509 break;
1510 case eXuiServerAction_SetCameraLocation:
1511#ifndef _CONTENT_PACKAGE
1512 {
1513 DebugSetCameraPosition *pos = (DebugSetCameraPosition *)param;
1514
1515 app.DebugPrintf( "DEBUG: Player=%i\n", pos->player );
1516 app.DebugPrintf( "DEBUG: Teleporting to pos=(%f.2, %f.2, %f.2), looking at=(%f.2,%f.2)\n",
1517 pos->m_camX, pos->m_camY, pos->m_camZ,
1518 pos->m_yRot, pos->m_elev
1519 );
1520
1521 shared_ptr<ServerPlayer> player = players->players.at(pos->player);
1522 player->debug_setPosition( pos->m_camX, pos->m_camY, pos->m_camZ,
1523 pos->m_yRot, pos->m_elev );
1524
1525 // Doesn't work
1526 //player->setYHeadRot(pos->m_yRot);
1527 //player->absMoveTo(pos->m_camX, pos->m_camY, pos->m_camZ, pos->m_yRot, pos->m_elev);
1528 }
1529#endif
1530 break;
1531 }
1532
1533 app.SetXuiServerAction(i,eXuiServerAction_Idle);
1534 }
1535
1536 Sleep(1);
1537 }
1538 }
1539 //else
1540 //{
1541 // while (running)
1542 // {
1543 // handleConsoleInputs();
1544 // Sleep(10);
1545 // }
1546 //}
1547#if 0
1548} catch (Throwable t) {
1549 t.printStackTrace();
1550 logger.log(Level.SEVERE, "Unexpected exception", t);
1551 while (running) {
1552 handleConsoleInputs();
1553 try {
1554 Thread.sleep(10);
1555 } catch (InterruptedException e1) {
1556 e1.printStackTrace();
1557 }
1558 }
1559} finally {
1560 try {
1561 stopServer();
1562 stopped = true;
1563 } catch (Throwable t) {
1564 t.printStackTrace();
1565 } finally {
1566 System::exit(0);
1567 }
1568}
1569#endif
1570
1571 // 4J Stu - Stop the server when the loops complete, as the finally would do
1572 stopServer(didInit);
1573 stopped = true;
1574}
1575
1576void MinecraftServer::broadcastStartSavingPacket()
1577{
1578 players->broadcastAll( shared_ptr<GameEventPacket>( new GameEventPacket(GameEventPacket::START_SAVING, 0) ) );;
1579}
1580
1581void MinecraftServer::broadcastStopSavingPacket()
1582{
1583 if( !s_bServerHalted )
1584 {
1585 players->broadcastAll( shared_ptr<GameEventPacket>( new GameEventPacket(GameEventPacket::STOP_SAVING, 0) ) );;
1586 }
1587}
1588
1589void MinecraftServer::tick()
1590{
1591 vector<wstring> toRemove;
1592 for (AUTO_VAR(it, ironTimers.begin()); it != ironTimers.end(); it++ )
1593 {
1594 int t = it->second;
1595 if (t > 0)
1596 {
1597 ironTimers[it->first] = t - 1;
1598 }
1599 else
1600 {
1601 toRemove.push_back(it->first);
1602 }
1603 }
1604 for (unsigned int i = 0; i < toRemove.size(); i++)
1605 {
1606 ironTimers.erase(toRemove[i]);
1607 }
1608
1609 AABB::resetPool();
1610 Vec3::resetPool();
1611
1612 tickCount++;
1613
1614 // 4J We need to update client difficulty levels based on the servers
1615 Minecraft *pMinecraft = Minecraft::GetInstance();
1616 // 4J-PB - sending this on the host changing the difficulty in the menus
1617 /* if(m_lastSentDifficulty != pMinecraft->options->difficulty)
1618 {
1619 m_lastSentDifficulty = pMinecraft->options->difficulty;
1620 players->broadcastAll( shared_ptr<ServerSettingsChangedPacket>( new ServerSettingsChangedPacket( ServerSettingsChangedPacket::HOST_DIFFICULTY, pMinecraft->options->difficulty) ) );
1621 }*/
1622
1623 for (unsigned int i = 0; i < levels.length; i++)
1624 {
1625 // if (i == 0 || settings->getBoolean(L"allow-nether", true)) // 4J removed - we always have nether
1626 {
1627 ServerLevel *level = levels[i];
1628
1629 // 4J Stu - We set the levels difficulty based on the minecraft options
1630 level->difficulty = app.GetGameHostOption(eGameHostOption_Difficulty); //pMinecraft->options->difficulty;
1631
1632#if DEBUG_SERVER_DONT_SPAWN_MOBS
1633 level->setSpawnSettings(false, false);
1634#else
1635 level->setSpawnSettings(level->difficulty > 0 && !Minecraft::GetInstance()->isTutorial(), animals);
1636#endif
1637
1638 if (tickCount % 20 == 0)
1639 {
1640 players->broadcastAll( shared_ptr<SetTimePacket>( new SetTimePacket(level->getGameTime(), level->getDayTime(), level->getGameRules()->getBoolean(GameRules::RULE_DAYLIGHT) ) ), level->dimension->id);
1641 }
1642 // #ifndef __PS3__
1643 static __int64 stc = 0;
1644 __int64 st0 = System::currentTimeMillis();
1645 PIXBeginNamedEvent(0,"Level tick %d",i);
1646 ((Level *)level)->tick();
1647 __int64 st1 = System::currentTimeMillis();
1648 PIXEndNamedEvent();
1649 PIXBeginNamedEvent(0,"Update lights %d",i);
1650
1651 __int64 st2 = System::currentTimeMillis();
1652 PIXEndNamedEvent();
1653 PIXBeginNamedEvent(0,"Entity tick %d",i);
1654 // 4J added to stop ticking entities in levels when players are not in those levels.
1655 // Note: now changed so that we also tick if there are entities to be removed, as this also happens as a result of calling tickEntities. If we don't do this, then the
1656 // entities get removed at the first point that there is a player count in the level - this has been causing a problem when going from normal dimension -> nether -> normal,
1657 // as the player is getting flagged as to be removed (from the normal dimension) when going to the nether, but Actually gets removed only when it returns
1658 if( ( players->getPlayerCount(level) > 0) || ( level->hasEntitiesToRemove() ) )
1659 {
1660#ifdef __PSVITA__
1661 // AP - the PlayerList->viewDistance initially starts out at 3 to make starting a level speedy
1662 // the problem with this is that spawned monsters are always generated on the edge of the known map
1663 // which means they wont process (unless they are surrounded by 2 visible chunks). This means
1664 // they wont checkDespawn so they are NEVER removed which results in monsters not spawning.
1665 // This bit of hack will modify the view distance once the level is up and running.
1666 int newViewDistance = 5;
1667 level->getServer()->getPlayers()->setViewDistance(newViewDistance);
1668 level->getTracker()->updateMaxRange();
1669 level->getChunkMap()->setRadius(level->getServer()->getPlayers()->getViewDistance());
1670#endif
1671 level->tickEntities();
1672 }
1673 PIXEndNamedEvent();
1674
1675 PIXBeginNamedEvent(0,"Entity tracker tick");
1676 level->getTracker()->tick();
1677 PIXEndNamedEvent();
1678
1679 __int64 st3 = System::currentTimeMillis();
1680 // printf(">>>>>>>>>>>>>>>>>>>>>> Tick %d %d %d : %d\n", st1 - st0, st2 - st1, st3 - st2, st0 - stc );
1681 stc = st0;
1682 // #endif// __PS3__
1683 }
1684 }
1685 Entity::tickExtraWandering(); // 4J added
1686
1687 PIXBeginNamedEvent(0,"Connection tick");
1688 connection->tick();
1689 PIXEndNamedEvent();
1690 PIXBeginNamedEvent(0,"Players tick");
1691 players->tick();
1692 PIXEndNamedEvent();
1693
1694 // 4J - removed
1695#if 0
1696 for (int i = 0; i < tickables.size(); i++) {
1697 tickables.get(i)-tick();
1698 }
1699#endif
1700
1701 // try { // 4J - removed try/catch
1702 handleConsoleInputs();
1703 // } catch (Exception e) {
1704 // logger.log(Level.WARNING, "Unexpected exception while parsing console command", e);
1705 // }
1706}
1707
1708void MinecraftServer::handleConsoleInput(const wstring& msg, ConsoleInputSource *source)
1709{
1710 consoleInput.push_back(new ConsoleInput(msg, source));
1711}
1712
1713void MinecraftServer::handleConsoleInputs()
1714{
1715 while (consoleInput.size() > 0)
1716 {
1717 AUTO_VAR(it, consoleInput.begin());
1718 ConsoleInput *input = *it;
1719 consoleInput.erase(it);
1720 // commands->handleCommand(input); // 4J - removed - TODO - do we want equivalent of console commands?
1721 }
1722}
1723
1724void MinecraftServer::main(__int64 seed, void *lpParameter)
1725{
1726#if __PS3__
1727 ShutdownManager::HasStarted(ShutdownManager::eServerThread );
1728#endif
1729 server = new MinecraftServer();
1730 server->run(seed, lpParameter);
1731 delete server;
1732 server = NULL;
1733 ShutdownManager::HasFinished(ShutdownManager::eServerThread );
1734}
1735
1736void MinecraftServer::HaltServer(bool bPrimaryPlayerSignedOut)
1737{
1738 s_bServerHalted = true;
1739 if( server != NULL )
1740 {
1741 m_bPrimaryPlayerSignedOut=bPrimaryPlayerSignedOut;
1742 server->halt();
1743 }
1744}
1745
1746File *MinecraftServer::getFile(const wstring& name)
1747{
1748 return new File(name);
1749}
1750
1751void MinecraftServer::info(const wstring& string)
1752{
1753}
1754
1755void MinecraftServer::warn(const wstring& string)
1756{
1757}
1758
1759wstring MinecraftServer::getConsoleName()
1760{
1761 return L"CONSOLE";
1762}
1763
1764ServerLevel *MinecraftServer::getLevel(int dimension)
1765{
1766 if (dimension == -1) return levels[1];
1767 else if (dimension == 1) return levels[2];
1768 else return levels[0];
1769}
1770
1771// 4J added
1772void MinecraftServer::setLevel(int dimension, ServerLevel *level)
1773{
1774 if (dimension == -1) levels[1] = level;
1775 else if (dimension == 1) levels[2] = level;
1776 else levels[0] = level;
1777}
1778
1779#if defined _ACK_CHUNK_SEND_THROTTLING
1780bool MinecraftServer::chunkPacketManagement_CanSendTo(INetworkPlayer *player)
1781{
1782 if( s_hasSentEnoughPackets ) return false;
1783 if( player == NULL ) return false;
1784
1785 for( int i = 0; i < s_sentTo.size(); i++ )
1786 {
1787 if( s_sentTo[i]->IsSameSystem(player) )
1788 {
1789 return false;
1790 }
1791 }
1792
1793#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__)
1794 return ( player->GetOutstandingAckCount() < 3 );
1795#else
1796 return ( player->GetOutstandingAckCount() < 2 );
1797#endif
1798}
1799
1800void MinecraftServer::chunkPacketManagement_DidSendTo(INetworkPlayer *player)
1801{
1802 __int64 currentTime = System::currentTimeMillis();
1803
1804 if( ( currentTime - s_tickStartTime ) >= MAX_TICK_TIME_FOR_PACKET_SENDS )
1805 {
1806 s_hasSentEnoughPackets = true;
1807// app.DebugPrintf("Sending, setting enough packet flag: %dms\n",currentTime - s_tickStartTime);
1808 }
1809 else
1810 {
1811// app.DebugPrintf("Sending, more time: %dms\n",currentTime - s_tickStartTime);
1812 }
1813
1814 player->SentChunkPacket();
1815
1816 s_sentTo.push_back(player);
1817}
1818
1819void MinecraftServer::chunkPacketManagement_PreTick()
1820{
1821// app.DebugPrintf("*************************************************************************************************************************************************************************\n");
1822 s_hasSentEnoughPackets = false;
1823 s_tickStartTime = System::currentTimeMillis();
1824 s_sentTo.clear();
1825
1826 vector< shared_ptr<PlayerConnection> > *players = connection->getPlayers();
1827
1828 if( players->size() )
1829 {
1830 vector< shared_ptr<PlayerConnection> > playersOrig = *players;
1831 players->clear();
1832
1833 do
1834 {
1835 int longestTime = 0;
1836 AUTO_VAR(playerConnectionBest,playersOrig.begin());
1837 for( AUTO_VAR(it, playersOrig.begin()); it != playersOrig.end(); it++)
1838 {
1839 int thisTime = 0;
1840 INetworkPlayer *np = (*it)->getNetworkPlayer();
1841 if( np )
1842 {
1843 thisTime = np->GetTimeSinceLastChunkPacket_ms();
1844 }
1845
1846 if( thisTime > longestTime )
1847 {
1848 playerConnectionBest = it;
1849 longestTime = thisTime;
1850 }
1851 }
1852 players->push_back(*playerConnectionBest);
1853 playersOrig.erase(playerConnectionBest);
1854 } while ( playersOrig.size() > 0 );
1855 }
1856}
1857
1858void MinecraftServer::chunkPacketManagement_PostTick()
1859{
1860}
1861
1862#else
1863// 4J Added
1864bool MinecraftServer::chunkPacketManagement_CanSendTo(INetworkPlayer *player)
1865{
1866 if( player == NULL ) return false;
1867
1868 int time = GetTickCount();
1869 if( player->GetSessionIndex() == s_slowQueuePlayerIndex && (time - s_slowQueueLastTime) > MINECRAFT_SERVER_SLOW_QUEUE_DELAY )
1870 {
1871// app.DebugPrintf("Slow queue OK for player #%d\n", player->GetSessionIndex());
1872 return true;
1873 }
1874
1875 return false;
1876}
1877
1878void MinecraftServer::chunkPacketManagement_DidSendTo(INetworkPlayer *player)
1879{
1880 s_slowQueuePacketSent = true;
1881}
1882
1883void MinecraftServer::chunkPacketManagement_PreTick()
1884{
1885}
1886
1887void MinecraftServer::chunkPacketManagement_PostTick()
1888{
1889 // 4J Ensure that the slow queue owner keeps cycling if it's not been used in a while
1890 int time = GetTickCount();
1891 if( ( s_slowQueuePacketSent ) || ( (time - s_slowQueueLastTime) > ( 2 * MINECRAFT_SERVER_SLOW_QUEUE_DELAY ) ) )
1892 {
1893// app.DebugPrintf("Considering cycling: (%d) %d - %d -> %d > %d\n",s_slowQueuePacketSent, time, s_slowQueueLastTime, (time - s_slowQueueLastTime), (2*MINECRAFT_SERVER_SLOW_QUEUE_DELAY));
1894 MinecraftServer::cycleSlowQueueIndex();
1895 s_slowQueuePacketSent = false;
1896 s_slowQueueLastTime = time;
1897 }
1898// else
1899// {
1900// app.DebugPrintf("Not considering cycling: %d - %d -> %d > %d\n",time, s_slowQueueLastTime, (time - s_slowQueueLastTime), (2*MINECRAFT_SERVER_SLOW_QUEUE_DELAY));
1901// }
1902}
1903
1904void MinecraftServer::cycleSlowQueueIndex()
1905{
1906 if( !g_NetworkManager.IsInSession() ) return;
1907
1908 int startingIndex = s_slowQueuePlayerIndex;
1909 INetworkPlayer *currentPlayer = NULL;
1910 DWORD currentPlayerCount = 0;
1911 do
1912 {
1913 currentPlayerCount = g_NetworkManager.GetPlayerCount();
1914 if( startingIndex >= currentPlayerCount ) startingIndex = 0;
1915 ++s_slowQueuePlayerIndex;
1916
1917 if( currentPlayerCount > 0 )
1918 {
1919 s_slowQueuePlayerIndex %= currentPlayerCount;
1920 // Fix for #9530 - NETWORKING: Attempting to fill a multiplayer game beyond capacity results in a softlock for the last players to join.
1921 // The QNet session might be ending while we do this, so do a few more checks that the player is real
1922 currentPlayer = g_NetworkManager.GetPlayerByIndex( s_slowQueuePlayerIndex );
1923 }
1924 else
1925 {
1926 s_slowQueuePlayerIndex = 0;
1927 }
1928 } while ( g_NetworkManager.IsInSession() &&
1929 currentPlayerCount > 0 &&
1930 s_slowQueuePlayerIndex != startingIndex &&
1931 currentPlayer != NULL &&
1932 currentPlayer->IsLocal()
1933 );
1934// app.DebugPrintf("Cycled slow queue index to %d\n", s_slowQueuePlayerIndex);
1935}
1936#endif
1937
1938// 4J added - sets up a vector of flags to indicate which entities (with small Ids) have been removed from the level, but are still haven't constructed a network packet
1939// to tell a remote client about it. These small Ids shouldn't be re-used. Most of the time this method shouldn't actually do anything, in which case it will return false
1940// and nothing is set up.
1941bool MinecraftServer::flagEntitiesToBeRemoved(unsigned int *flags)
1942{
1943 bool removedFound = false;
1944 for( unsigned int i = 0; i < levels.length; i++ )
1945 {
1946 ServerLevel *level = levels[i];
1947 if( level )
1948 {
1949 level->flagEntitiesToBeRemoved( flags, &removedFound );
1950 }
1951 }
1952 return removedFound;
1953}