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