2026-03-01 12:16:08 +08:00
# include "stdafx.h"
//#include "Minecraft.h"
# include <ctime>
# include "Options.h"
# include "MinecraftServer.h"
# include "ConsoleInput.h"
# include "PlayerList.h"
# include "ServerLevel.h"
# include "DerivedServerLevel.h"
# include "EntityTracker.h"
# include "ServerConnection.h"
# include "Settings.h"
# include "ServerChunkCache.h"
# include "ServerLevelListener.h"
# include "..\Minecraft.World\AABB.h"
# include "..\Minecraft.World\Vec3.h"
# include "..\Minecraft.World\net.minecraft.network.h"
# include "..\Minecraft.World\net.minecraft.world.level.dimension.h"
# include "..\Minecraft.World\net.minecraft.world.level.storage.h"
# include "..\Minecraft.World\net.minecraft.world.h"
# include "..\Minecraft.World\net.minecraft.world.level.h"
# include "..\Minecraft.World\net.minecraft.world.level.tile.h"
# include "..\Minecraft.World\Pos.h"
# include "..\Minecraft.World\System.h"
# include "..\Minecraft.World\StringHelpers.h"
# ifdef SPLIT_SAVES
# include "..\Minecraft.World\ConsoleSaveFileSplit.h"
# endif
# include "..\Minecraft.World\ConsoleSaveFileOriginal.h"
# include "..\Minecraft.World\Socket.h"
# include "..\Minecraft.World\net.minecraft.world.entity.h"
# include "ProgressRenderer.h"
# include "ServerPlayer.h"
# include "GameRenderer.h"
# include "..\Minecraft.World\ThreadName.h"
# include "..\Minecraft.World\IntCache.h"
# include "..\Minecraft.World\CompressedTileStorage.h"
# include "..\Minecraft.World\SparseLightStorage.h"
# include "..\Minecraft.World\SparseDataStorage.h"
# include "..\Minecraft.World\compression.h"
# ifdef _XBOX
# include "Common\XUI\XUI_DebugSetCamera.h"
# endif
# include "PS3\PS3Extras\ShutdownManager.h"
# include "ServerCommandDispatcher.h"
# include "..\Minecraft.World\BiomeSource.h"
# include "PlayerChunkMap.h"
# include "Common\Telemetry\TelemetryManager.h"
# define DEBUG_SERVER_DONT_SPAWN_MOBS 0
//4J Added
MinecraftServer * MinecraftServer : : server = NULL ;
bool MinecraftServer : : setTimeAtEndOfTick = false ;
2026-03-02 15:53:32 +07:00
int64_t MinecraftServer : : setTime = 0 ;
2026-03-01 12:16:08 +08:00
bool MinecraftServer : : setTimeOfDayAtEndOfTick = false ;
2026-03-02 15:53:32 +07:00
int64_t MinecraftServer : : setTimeOfDay = 0 ;
2026-03-01 12:16:08 +08:00
bool MinecraftServer : : m_bPrimaryPlayerSignedOut = false ;
bool MinecraftServer : : s_bServerHalted = false ;
bool MinecraftServer : : s_bSaveOnExitAnswered = false ;
int MinecraftServer : : s_slowQueuePlayerIndex = 0 ;
int MinecraftServer : : s_slowQueueLastTime = 0 ;
bool MinecraftServer : : s_slowQueuePacketSent = false ;
unordered_map < wstring , int > MinecraftServer : : ironTimers ;
MinecraftServer : : MinecraftServer ( )
{
// 4J - added initialisers
connection = NULL ;
settings = NULL ;
players = NULL ;
commands = NULL ;
running = true ;
m_bLoaded = false ;
stopped = false ;
tickCount = 0 ;
wstring progressStatus ;
progress = 0 ;
motd = L " " ;
m_isServerPaused = false ;
m_serverPausedEvent = new C4JThread : : Event ;
m_saveOnExit = false ;
m_suspending = false ;
m_ugcPlayersVersion = 0 ;
m_texturePackId = 0 ;
maxBuildHeight = Level : : maxBuildHeight ;
m_postUpdateThread = NULL ;
commandDispatcher = new ServerCommandDispatcher ( ) ;
}
MinecraftServer : : ~ MinecraftServer ( )
{
}
2026-03-02 15:53:32 +07:00
bool MinecraftServer : : initServer ( int64_t seed , NetworkGameInitData * initData , DWORD initSettings , bool findSeed )
2026-03-01 12:16:08 +08:00
{
// 4J - removed
#if 0
commands = new ConsoleCommands ( this ) ;
Thread t = new Thread ( ) {
public void run ( ) {
BufferedReader br = new BufferedReader ( new InputStreamReader ( System . in ) ) ;
String line = null ;
try {
while ( ! stopped & & running & & ( line = br . readLine ( ) ) ! = null ) {
handleConsoleInput ( line , MinecraftServer . this ) ;
}
} catch ( IOException e ) {
e . printStackTrace ( ) ;
}
}
} ;
t . setDaemon ( true ) ;
t . start ( ) ;
LogConfigurator . initLogger ( ) ;
logger . info ( " Starting minecraft server version " + VERSION ) ;
if ( Runtime . getRuntime ( ) . maxMemory ( ) / 1024 / 1024 < 512 ) {
logger . warning ( " **** NOT ENOUGH RAM! " ) ;
logger . warning ( " To start the server with more ram, launch it as \" java -Xmx1024M -Xms1024M -jar minecraft_server.jar \" " ) ;
}
logger . info ( " Loading properties " ) ;
# endif
settings = new Settings ( new File ( L " server.properties " ) ) ;
app . DebugPrintf ( " \n *** SERVER SETTINGS *** \n " ) ;
app . DebugPrintf ( " ServerSettings: host-friends-only is %s \n " , ( app . GetGameHostOption ( eGameHostOption_FriendsOfFriends ) > 0 ) ? " on " : " off " ) ;
app . DebugPrintf ( " ServerSettings: game-type is %s \n " , ( app . GetGameHostOption ( eGameHostOption_GameType ) = = 0 ) ? " Survival Mode " : " Creative Mode " ) ;
app . DebugPrintf ( " ServerSettings: pvp is %s \n " , ( app . GetGameHostOption ( eGameHostOption_PvP ) > 0 ) ? " on " : " off " ) ;
app . DebugPrintf ( " ServerSettings: fire spreads is %s \n " , ( app . GetGameHostOption ( eGameHostOption_FireSpreads ) > 0 ) ? " on " : " off " ) ;
app . DebugPrintf ( " ServerSettings: tnt explodes is %s \n " , ( app . GetGameHostOption ( eGameHostOption_TNT ) > 0 ) ? " on " : " off " ) ;
app . DebugPrintf ( " \n " ) ;
// TODO 4J Stu - Init a load of settings based on data passed as params
//settings->setBooleanAndSave( L"host-friends-only", (app.GetGameHostOption(eGameHostOption_FriendsOfFriends)>0) );
// 4J - Unused
//localIp = settings->getString(L"server-ip", L"");
//onlineMode = settings->getBoolean(L"online-mode", true);
//motd = settings->getString(L"motd", L"A Minecraft Server");
2026-03-02 15:53:32 +07:00
//motd.replace('<27> ', '$');
2026-03-01 12:16:08 +08:00
setAnimals ( settings - > getBoolean ( L " spawn-animals " , true ) ) ;
setNpcsEnabled ( settings - > getBoolean ( L " spawn-npcs " , true ) ) ;
setPvpAllowed ( app . GetGameHostOption ( eGameHostOption_PvP ) > 0 ? true : false ) ; // settings->getBoolean(L"pvp", true);
// 4J Stu - We should never have hacked clients flying when they shouldn't be like the PC version, so enable flying always
// Fix for #46612 - TU5: Code: Multiplayer: A client can be banned for flying when accidentaly being blown by dynamite
setFlightAllowed ( true ) ; //settings->getBoolean(L"allow-flight", false);
// 4J Stu - Enabling flight to stop it kicking us when we use it
# ifdef _DEBUG_MENUS_ENABLED
setFlightAllowed ( true ) ;
# endif
# if 1
connection = new ServerConnection ( this ) ;
Socket : : Initialise ( connection ) ; // 4J - added
# else
// 4J - removed
InetAddress localAddress = null ;
if ( localIp . length ( ) > 0 ) localAddress = InetAddress . getByName ( localIp ) ;
port = settings . getInt ( " server-port " , DEFAULT_MINECRAFT_PORT ) ;
logger . info ( " Starting Minecraft server on " + ( localIp . length ( ) = = 0 ? " * " : localIp ) + " : " + port ) ;
try {
connection = new ServerConnection ( this , localAddress , port ) ;
} catch ( IOException e ) {
logger . warning ( " **** FAILED TO BIND TO PORT! " ) ;
logger . log ( Level . WARNING , " The exception was: " + e . toString ( ) ) ;
logger . warning ( " Perhaps a server is already running on that port? " ) ;
return false ;
}
if ( ! onlineMode ) {
logger . warning ( " **** SERVER IS RUNNING IN OFFLINE/INSECURE MODE! " ) ;
logger . warning ( " The server will make no attempt to authenticate usernames. Beware. " ) ;
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. " ) ;
logger . warning ( " To change this, set \" online-mode \" to \" true \" in the server.settings file. " ) ;
}
# endif
setPlayers ( new PlayerList ( this ) ) ;
// 4J-JEV: Need to wait for levelGenerationOptions to load.
while ( app . getLevelGenerationOptions ( ) ! = NULL & & ! app . getLevelGenerationOptions ( ) - > hasLoadedData ( ) )
Sleep ( 1 ) ;
if ( app . getLevelGenerationOptions ( ) ! = NULL & & ! app . getLevelGenerationOptions ( ) - > ready ( ) )
{
// TODO: Stop loading, add error message.
}
2026-03-02 15:53:32 +07:00
int64_t levelNanoTime = System : : nanoTime ( ) ;
2026-03-01 12:16:08 +08:00
wstring levelName = settings - > getString ( L " level-name " , L " world " ) ;
wstring levelTypeString ;
bool gameRuleUseFlatWorld = false ;
if ( app . getLevelGenerationOptions ( ) ! = NULL )
{
gameRuleUseFlatWorld = app . getLevelGenerationOptions ( ) - > getuseFlatWorld ( ) ;
}
if ( gameRuleUseFlatWorld | | app . GetGameHostOption ( eGameHostOption_LevelType ) > 0 )
{
levelTypeString = settings - > getString ( L " level-type " , L " flat " ) ;
}
else
{
levelTypeString = settings - > getString ( L " level-type " , L " default " ) ;
}
LevelType * pLevelType = LevelType : : getLevelType ( levelTypeString ) ;
2026-03-02 15:53:32 +07:00
if ( pLevelType = = NULL )
2026-03-01 12:16:08 +08:00
{
pLevelType = LevelType : : lvl_normal ;
}
ProgressRenderer * mcprogress = Minecraft : : GetInstance ( ) - > progressRenderer ;
mcprogress - > progressStart ( IDS_PROGRESS_INITIALISING_SERVER ) ;
if ( findSeed )
{
# ifdef __PSVITA__
seed = BiomeSource : : findSeed ( pLevelType , & running ) ;
# else
seed = BiomeSource : : findSeed ( pLevelType ) ;
# endif
}
setMaxBuildHeight ( settings - > getInt ( L " max-build-height " , Level : : maxBuildHeight ) ) ;
setMaxBuildHeight ( ( ( getMaxBuildHeight ( ) + 8 ) / 16 ) * 16 ) ;
setMaxBuildHeight ( Mth : : clamp ( getMaxBuildHeight ( ) , 64 , Level : : maxBuildHeight ) ) ;
//settings->setProperty(L"max-build-height", maxBuildHeight);
#if 0
wstring levelSeedString = settings - > getString ( L " level-seed " , L " " ) ;
2026-03-02 15:53:32 +07:00
int64_t levelSeed = ( new Random ( ) ) - > nextLong ( ) ;
2026-03-01 12:16:08 +08:00
if ( levelSeedString . length ( ) > 0 )
{
2026-03-02 15:53:32 +07:00
long newSeed = _fromString < int64_t > ( levelSeedString ) ;
2026-03-01 12:16:08 +08:00
if ( newSeed ! = 0 ) {
levelSeed = newSeed ;
}
}
# endif
// logger.info("Preparing level \"" + levelName + "\"");
m_bLoaded = loadLevel ( new McRegionLevelStorageSource ( File ( L " . " ) ) , levelName , seed , pLevelType , initData ) ;
// logger.info("Done (" + (System.nanoTime() - levelNanoTime) + "ns)! For help, type \"help\" or \"?\"");
// 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
if ( initData - > saveData )
{
delete initData - > saveData - > data ;
initData - > saveData - > data = 0 ;
initData - > saveData - > fileSize = 0 ;
}
g_NetworkManager . ServerReady ( ) ; // 4J added
return m_bLoaded ;
}
// 4J - added - extra thread to post processing on separate thread during level creation
int MinecraftServer : : runPostUpdate ( void * lpParam )
{
ShutdownManager : : HasStarted ( ShutdownManager : : ePostProcessThread ) ;
MinecraftServer * server = ( MinecraftServer * ) lpParam ;
Entity : : useSmallIds ( ) ; // This thread can end up spawning entities as resources
IntCache : : CreateNewThreadStorage ( ) ;
AABB : : CreateNewThreadStorage ( ) ;
2026-03-02 15:53:32 +07:00
Vec3 : : CreateNewThreadStorage ( ) ;
2026-03-01 12:16:08 +08:00
Compression : : UseDefaultThreadStorage ( ) ;
Level : : enableLightingCache ( ) ;
Tile : : CreateNewThreadStorage ( ) ;
// Update lights for both levels until we are signalled to terminate
do
{
EnterCriticalSection ( & server - > m_postProcessCS ) ;
if ( server - > m_postProcessRequests . size ( ) )
{
MinecraftServer : : postProcessRequest request = server - > m_postProcessRequests . back ( ) ;
server - > m_postProcessRequests . pop_back ( ) ;
LeaveCriticalSection ( & server - > m_postProcessCS ) ;
static int count = 0 ;
PIXBeginNamedEvent ( 0 , " Post processing %d " , ( count + + ) % 8 ) ;
request . chunkSource - > postProcess ( request . chunkSource , request . x , request . z ) ;
PIXEndNamedEvent ( ) ;
}
else
{
LeaveCriticalSection ( & server - > m_postProcessCS ) ;
}
Sleep ( 1 ) ;
} while ( ! server - > m_postUpdateTerminate & & ShutdownManager : : ShouldRun ( ShutdownManager : : ePostProcessThread ) ) ;
//#ifndef __PS3__
// One final pass through updates to make sure we're done
EnterCriticalSection ( & server - > m_postProcessCS ) ;
int maxRequests = server - > m_postProcessRequests . size ( ) ;
while ( server - > m_postProcessRequests . size ( ) & & ShutdownManager : : ShouldRun ( ShutdownManager : : ePostProcessThread ) )
{
MinecraftServer : : postProcessRequest request = server - > m_postProcessRequests . back ( ) ;
server - > m_postProcessRequests . pop_back ( ) ;
LeaveCriticalSection ( & server - > m_postProcessCS ) ;
request . chunkSource - > postProcess ( request . chunkSource , request . x , request . z ) ;
# ifdef __PS3__
# ifndef _CONTENT_PACKAGE
if ( ( server - > m_postProcessRequests . size ( ) % 10 ) = = 0 )
printf ( " processing request %00d \n " , server - > m_postProcessRequests . size ( ) ) ;
# endif
Sleep ( 1 ) ;
# endif
EnterCriticalSection ( & server - > m_postProcessCS ) ;
}
LeaveCriticalSection ( & server - > m_postProcessCS ) ;
//#endif //__PS3__
Tile : : ReleaseThreadStorage ( ) ;
IntCache : : ReleaseThreadStorage ( ) ;
AABB : : ReleaseThreadStorage ( ) ;
Vec3 : : ReleaseThreadStorage ( ) ;
Level : : destroyLightingCache ( ) ;
ShutdownManager : : HasFinished ( ShutdownManager : : ePostProcessThread ) ;
return 0 ;
}
void MinecraftServer : : addPostProcessRequest ( ChunkSource * chunkSource , int x , int z )
{
EnterCriticalSection ( & m_postProcessCS ) ;
m_postProcessRequests . push_back ( MinecraftServer : : postProcessRequest ( x , z , chunkSource ) ) ;
LeaveCriticalSection ( & m_postProcessCS ) ;
}
void MinecraftServer : : postProcessTerminate ( ProgressRenderer * mcprogress )
{
DWORD status = 0 ;
EnterCriticalSection ( & server - > m_postProcessCS ) ;
size_t postProcessItemCount = server - > m_postProcessRequests . size ( ) ;
LeaveCriticalSection ( & server - > m_postProcessCS ) ;
do
{
status = m_postUpdateThread - > WaitForCompletion ( 50 ) ;
if ( status = = WAIT_TIMEOUT )
{
EnterCriticalSection ( & server - > m_postProcessCS ) ;
size_t postProcessItemRemaining = server - > m_postProcessRequests . size ( ) ;
LeaveCriticalSection ( & server - > m_postProcessCS ) ;
if ( postProcessItemCount )
{
mcprogress - > progressStagePercentage ( ( postProcessItemCount - postProcessItemRemaining ) * 100 / postProcessItemCount ) ;
}
CompressedTileStorage : : tick ( ) ;
SparseLightStorage : : tick ( ) ;
SparseDataStorage : : tick ( ) ;
}
} while ( status = = WAIT_TIMEOUT ) ;
delete m_postUpdateThread ;
m_postUpdateThread = NULL ;
DeleteCriticalSection ( & m_postProcessCS ) ;
}
2026-03-02 15:53:32 +07:00
bool MinecraftServer : : loadLevel ( LevelStorageSource * storageSource , const wstring & name , int64_t levelSeed , LevelType * pLevelType , NetworkGameInitData * initData )
2026-03-01 12:16:08 +08:00
{
// 4J - TODO - do with new save stuff
// if (storageSource->requiresConversion(name))
// {
// assert(false);
// }
ProgressRenderer * mcprogress = Minecraft : : GetInstance ( ) - > progressRenderer ;
// 4J TODO - free levels here if there are already some?
levels = ServerLevelArray ( 3 ) ;
int gameTypeId = settings - > getInt ( L " gamemode " , app . GetGameHostOption ( eGameHostOption_GameType ) ) ; //LevelSettings::GAMETYPE_SURVIVAL);
GameType * gameType = LevelSettings : : validateGameType ( gameTypeId ) ;
app . DebugPrintf ( " Default game type: %d \n " , gameTypeId ) ;
LevelSettings * levelSettings = new LevelSettings ( levelSeed , gameType , app . GetGameHostOption ( eGameHostOption_Structures ) > 0 ? true : false , isHardcore ( ) , true , pLevelType , initData - > xzSize , initData - > hellScale ) ;
if ( app . GetGameHostOption ( eGameHostOption_BonusChest ) ) levelSettings - > enableStartingBonusItems ( ) ;
// 4J - temp - load existing level
2026-03-02 15:58:20 +07:00
std : : shared_ptr < McRegionLevelStorage > storage = nullptr ;
2026-03-01 12:16:08 +08:00
bool levelChunksNeedConverted = false ;
if ( initData - > saveData ! = NULL )
{
// We are loading a file from disk with the data passed in
2026-03-02 15:53:32 +07:00
# ifdef SPLIT_SAVES
2026-03-01 12:16:08 +08:00
ConsoleSaveFileOriginal oldFormatSave ( initData - > saveData - > saveName , initData - > saveData - > data , initData - > saveData - > fileSize , false , initData - > savePlatform ) ;
ConsoleSaveFile * pSave = new ConsoleSaveFileSplit ( & oldFormatSave ) ;
2026-03-02 15:53:32 +07:00
2026-03-01 12:16:08 +08:00
//ConsoleSaveFile* pSave = new ConsoleSaveFileSplit( initData->saveData->saveName, initData->saveData->data, initData->saveData->fileSize, false, initData->savePlatform );
# else
ConsoleSaveFile * pSave = new ConsoleSaveFileOriginal ( initData - > saveData - > saveName , initData - > saveData - > data , initData - > saveData - > fileSize , false , initData - > savePlatform ) ;
# endif
if ( pSave - > isSaveEndianDifferent ( ) )
levelChunksNeedConverted = true ;
pSave - > ConvertToLocalPlatform ( ) ; // check if we need to convert this file from PS3->PS4
2026-03-02 15:58:20 +07:00
storage = std : : shared_ptr < McRegionLevelStorage > ( new McRegionLevelStorage ( pSave , File ( L " . " ) , name , true ) ) ;
2026-03-01 12:16:08 +08:00
}
else
{
// We are loading a save from the storage manager
# ifdef SPLIT_SAVES
bool bLevelGenBaseSave = false ;
LevelGenerationOptions * levelGen = app . getLevelGenerationOptions ( ) ;
if ( levelGen ! = NULL & & levelGen - > requiresBaseSave ( ) )
{
DWORD fileSize = 0 ;
LPVOID pvSaveData = levelGen - > getBaseSaveData ( fileSize ) ;
if ( pvSaveData & & fileSize ! = 0 ) bLevelGenBaseSave = true ;
}
ConsoleSaveFileSplit * newFormatSave = NULL ;
if ( bLevelGenBaseSave )
{
ConsoleSaveFileOriginal oldFormatSave ( L " " ) ;
newFormatSave = new ConsoleSaveFileSplit ( & oldFormatSave ) ;
}
else
{
newFormatSave = new ConsoleSaveFileSplit ( L " " ) ;
}
2026-03-02 15:58:20 +07:00
storage = std : : shared_ptr < McRegionLevelStorage > ( new McRegionLevelStorage ( newFormatSave , File ( L " . " ) , name , true ) ) ;
2026-03-01 12:16:08 +08:00
# else
2026-03-02 15:58:20 +07:00
storage = std : : shared_ptr < McRegionLevelStorage > ( new McRegionLevelStorage ( new ConsoleSaveFileOriginal ( L " " ) , File ( L " . " ) , name , true ) ) ;
2026-03-01 12:16:08 +08:00
# endif
}
// McRegionLevelStorage *storage = new McRegionLevelStorage(new ConsoleSaveFile( L"" ), L"", L"", 0); // original
2026-03-02 15:53:32 +07:00
// McRegionLevelStorage *storage = new McRegionLevelStorage(File(L"."), name, true); // TODO
2026-03-01 12:16:08 +08:00
for ( unsigned int i = 0 ; i < levels . length ; i + + )
{
if ( s_bServerHalted | | ! g_NetworkManager . IsInSession ( ) )
{
return false ;
}
// String levelName = name;
// if (i == 1) levelName += "_nether";
int dimension = 0 ;
if ( i = = 1 ) dimension = - 1 ;
if ( i = = 2 ) dimension = 1 ;
if ( i = = 0 )
{
levels [ i ] = new ServerLevel ( this , storage , name , dimension , levelSettings ) ;
if ( app . getLevelGenerationOptions ( ) ! = NULL )
{
LevelGenerationOptions * mapOptions = app . getLevelGenerationOptions ( ) ;
Pos * spawnPos = mapOptions - > getSpawnPos ( ) ;
if ( spawnPos ! = NULL )
{
levels [ i ] - > setSpawnPos ( spawnPos ) ;
}
levels [ i ] - > getLevelData ( ) - > setHasBeenInCreative ( mapOptions - > isFromDLC ( ) ) ;
}
}
else levels [ i ] = new DerivedServerLevel ( this , storage , name , dimension , levelSettings , levels [ 0 ] ) ;
// 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
// 4J Stu - We set the levels difficulty based on the minecraft options
//levels[i]->difficulty = settings->getBoolean(L"spawn-monsters", true) ? Difficulty::EASY : Difficulty::PEACEFUL;
Minecraft * pMinecraft = Minecraft : : GetInstance ( ) ;
// m_lastSentDifficulty = pMinecraft->options->difficulty;
levels [ i ] - > difficulty = app . GetGameHostOption ( eGameHostOption_Difficulty ) ; //pMinecraft->options->difficulty;
app . DebugPrintf ( " MinecraftServer::loadLevel - Difficulty = %d \n " , levels [ i ] - > difficulty ) ;
# if DEBUG_SERVER_DONT_SPAWN_MOBS
levels [ i ] - > setSpawnSettings ( false , false ) ;
# else
levels [ i ] - > setSpawnSettings ( settings - > getBoolean ( L " spawn-monsters " , true ) , animals ) ;
# endif
levels [ i ] - > getLevelData ( ) - > setGameType ( gameType ) ;
players - > setLevel ( levels ) ;
}
if ( levels [ 0 ] - > isNew )
{
mcprogress - > progressStage ( IDS_PROGRESS_GENERATING_SPAWN_AREA ) ;
}
else
{
mcprogress - > progressStage ( IDS_PROGRESS_LOADING_SPAWN_AREA ) ;
}
app . SetGameHostOption ( eGameHostOption_HasBeenInCreative , gameType = = GameType : : CREATIVE | | levels [ 0 ] - > getHasBeenInCreative ( ) ) ;
app . SetGameHostOption ( eGameHostOption_Structures , levels [ 0 ] - > isGenerateMapFeatures ( ) ) ;
if ( s_bServerHalted | | ! g_NetworkManager . IsInSession ( ) ) return false ;
// 4J - Make a new thread to do post processing
InitializeCriticalSection ( & m_postProcessCS ) ;
// 4J-PB - fix for 108310 - TCR #001 BAS Game Stability: TU12: Code: Compliance: Crash after creating world on "journey" seed.
// 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)
m_postUpdateThread = new C4JThread ( runPostUpdate , this , " Post processing " , 256 * 1024 ) ;
m_postUpdateTerminate = false ;
m_postUpdateThread - > SetProcessor ( CPU_CORE_POST_PROCESSING ) ;
m_postUpdateThread - > SetPriority ( THREAD_PRIORITY_ABOVE_NORMAL ) ;
m_postUpdateThread - > Run ( ) ;
2026-03-02 15:53:32 +07:00
int64_t startTime = System : : currentTimeMillis ( ) ;
2026-03-01 12:16:08 +08:00
// 4J Stu - Added this to temporarily make starting games on vita faster
# ifdef __PSVITA__
int r = 48 ;
# else
2026-03-02 15:53:32 +07:00
int r = 196 ;
2026-03-01 12:16:08 +08:00
# endif
// 4J JEV: load gameRules.
ConsoleSavePath filepath ( GAME_RULE_SAVENAME ) ;
ConsoleSaveFile * csf = getLevel ( 0 ) - > getLevelStorage ( ) - > getSaveFile ( ) ;
if ( csf - > doesFileExist ( filepath ) )
{
DWORD numberOfBytesRead ;
byteArray ba_gameRules ;
FileEntry * fe = csf - > createFile ( filepath ) ;
ba_gameRules . length = fe - > getFileSize ( ) ;
ba_gameRules . data = new BYTE [ ba_gameRules . length ] ;
csf - > setFilePointer ( fe , 0 , NULL , FILE_BEGIN ) ;
csf - > readFile ( fe , ba_gameRules . data , ba_gameRules . length , & numberOfBytesRead ) ;
assert ( numberOfBytesRead = = ba_gameRules . length ) ;
app . m_gameRules . loadGameRules ( ba_gameRules . data , ba_gameRules . length ) ;
csf - > closeHandle ( fe ) ;
}
2026-03-02 15:53:32 +07:00
int64_t lastTime = System : : currentTimeMillis ( ) ;
2026-03-01 12:16:08 +08:00
// 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
int i = 0 ;
for ( int i = 0 ; i < levels . length ; i + + )
{
// logger.info("Preparing start region for level " + i);
if ( i = = 0 | | settings - > getBoolean ( L " allow-nether " , true ) )
{
ServerLevel * level = levels [ i ] ;
if ( levelChunksNeedConverted )
{
// storage->getSaveFile()->convertLevelChunks(level)
}
#if 0
2026-03-02 15:53:32 +07:00
int64_t lastStorageTickTime = System : : currentTimeMillis ( ) ;
2026-03-01 12:16:08 +08:00
// Test code to enable full creation of levels at start up
int halfsidelen = ( i = = 0 ) ? 27 : 9 ;
for ( int x = - halfsidelen ; x < halfsidelen ; x + + )
{
for ( int z = - halfsidelen ; z < halfsidelen ; z + + )
{
int total = halfsidelen * halfsidelen * 4 ;
int pos = z + halfsidelen + ( ( x + halfsidelen ) * 2 * halfsidelen ) ;
mcprogress - > progressStagePercentage ( ( pos ) * 100 / total ) ;
level - > cache - > create ( x , z , true ) ; // 4J - added parameter to disable postprocessing here
if ( System : : currentTimeMillis ( ) - lastStorageTickTime > 50 )
{
CompressedTileStorage : : tick ( ) ;
SparseLightStorage : : tick ( ) ;
SparseDataStorage : : tick ( ) ;
lastStorageTickTime = System : : currentTimeMillis ( ) ;
}
}
}
# else
2026-03-02 15:53:32 +07:00
int64_t lastStorageTickTime = System : : currentTimeMillis ( ) ;
2026-03-01 12:16:08 +08:00
Pos * spawnPos = level - > getSharedSpawnPos ( ) ;
int twoRPlusOne = r * 2 + 1 ;
int total = twoRPlusOne * twoRPlusOne ;
for ( int x = - r ; x < = r & & running ; x + = 16 )
{
for ( int z = - r ; z < = r & & running ; z + = 16 )
{
if ( s_bServerHalted | | ! g_NetworkManager . IsInSession ( ) )
{
delete spawnPos ;
m_postUpdateTerminate = true ;
postProcessTerminate ( mcprogress ) ;
return false ;
}
// printf(">>>%d %d %d\n",i,x,z);
2026-03-02 15:53:32 +07:00
// int64_t now = System::currentTimeMillis();
2026-03-01 12:16:08 +08:00
// if (now < lastTime) lastTime = now;
// if (now > lastTime + 1000)
{
int pos = ( x + r ) * twoRPlusOne + ( z + 1 ) ;
// setProgress(L"Preparing spawn area", (pos) * 100 / total);
mcprogress - > progressStagePercentage ( ( pos + r ) * 100 / total ) ;
// lastTime = now;
}
static int count = 0 ;
PIXBeginNamedEvent ( 0 , " Creating %d " , ( count + + ) % 8 ) ;
level - > cache - > create ( ( spawnPos - > x + x ) > > 4 , ( spawnPos - > z + z ) > > 4 , true ) ; // 4J - added parameter to disable postprocessing here
PIXEndNamedEvent ( ) ;
// while (level->updateLights() && running)
// ;
if ( System : : currentTimeMillis ( ) - lastStorageTickTime > 50 )
{
CompressedTileStorage : : tick ( ) ;
SparseLightStorage : : tick ( ) ;
SparseDataStorage : : tick ( ) ;
lastStorageTickTime = System : : currentTimeMillis ( ) ;
}
}
}
// 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
#if 0
// 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
for ( int x = - r ; x < = r & & running ; x + = 16 )
{
for ( int z = - r ; z < = r & & running ; z + = 16 )
{
PIXBeginNamedEvent ( 0 , " Lighting gaps for %d %d " , x , z ) ;
level - > getChunkAt ( spawnPos - > x + x , spawnPos - > z + z ) - > recheckGaps ( true ) ;
PIXEndNamedEvent ( ) ;
}
}
# endif
delete spawnPos ;
# endif
}
}
// printf("Main thread complete at %dms\n",System::currentTimeMillis() - startTime);
// Wait for post processing, then lighting threads, to end (post-processing may make more lighting changes)
m_postUpdateTerminate = true ;
postProcessTerminate ( mcprogress ) ;
// stronghold position?
if ( levels [ 0 ] - > dimension - > id = = 0 )
{
app . DebugPrintf ( " =================================== \n " ) ;
if ( ! levels [ 0 ] - > getLevelData ( ) - > getHasStronghold ( ) )
{
2026-03-02 15:53:32 +07:00
int x , z ;
2026-03-01 12:16:08 +08:00
if ( app . GetTerrainFeaturePosition ( eTerrainFeature_Stronghold , & x , & z ) )
{
levels [ 0 ] - > getLevelData ( ) - > setXStronghold ( x ) ;
levels [ 0 ] - > getLevelData ( ) - > setZStronghold ( z ) ;
levels [ 0 ] - > getLevelData ( ) - > setHasStronghold ( ) ;
app . DebugPrintf ( " === FOUND stronghold in terrain features list \n " ) ;
}
else
{
// can't find the stronghold position in the terrain feature list. Do we have to run a post-process?
app . DebugPrintf ( " === Can't find stronghold in terrain features list \n " ) ;
}
}
else
{
app . DebugPrintf ( " === Leveldata has stronghold position \n " ) ;
}
app . DebugPrintf ( " =================================== \n " ) ;
}
// printf("Post processing complete at %dms\n",System::currentTimeMillis() - startTime);
// printf("Lighting complete at %dms\n",System::currentTimeMillis() - startTime);
if ( s_bServerHalted | | ! g_NetworkManager . IsInSession ( ) ) return false ;
2026-03-02 15:53:32 +07:00
2026-03-01 12:16:08 +08:00
if ( levels [ 1 ] - > isNew )
{
levels [ 1 ] - > save ( true , mcprogress ) ;
}
if ( s_bServerHalted | | ! g_NetworkManager . IsInSession ( ) ) return false ;
if ( levels [ 2 ] - > isNew )
{
levels [ 2 ] - > save ( true , mcprogress ) ;
}
if ( s_bServerHalted | | ! g_NetworkManager . IsInSession ( ) ) return false ;
// 4J - added - immediately save newly created level, like single player game
// 4J Stu - We also want to immediately save the tutorial
if ( levels [ 0 ] - > isNew )
saveGameRules ( ) ;
2026-03-02 15:53:32 +07:00
2026-03-01 12:16:08 +08:00
if ( levels [ 0 ] - > isNew )
{
levels [ 0 ] - > save ( true , mcprogress ) ;
}
if ( s_bServerHalted | | ! g_NetworkManager . IsInSession ( ) ) return false ;
if ( levels [ 0 ] - > isNew | | levels [ 1 ] - > isNew | | levels [ 2 ] - > isNew )
{
levels [ 0 ] - > saveToDisc ( mcprogress , false ) ;
}
if ( s_bServerHalted | | ! g_NetworkManager . IsInSession ( ) ) return false ;
/*
* int r = 24 ; for ( int x = - r ; x < = r ; x + + ) {
* setProgress ( " Preparing spawn area " , ( x + r ) * 100 / ( r + r + 1 ) ) ; for ( int z
* = - r ; z < = r ; z + + ) { if ( ! running ) return ; level . cache . create ( ( level . xSpawn
* > > 4 ) + x , ( level . zSpawn > > 4 ) + z ) ; while ( running & & level . updateLights ( ) )
* ; } }
*/
endProgress ( ) ;
return true ;
}
void MinecraftServer : : setProgress ( const wstring & status , int progress )
{
progressStatus = status ;
this - > progress = progress ;
// logger.info(status + ": " + progress + "%");
}
void MinecraftServer : : endProgress ( )
{
progressStatus = L " " ;
this - > progress = 0 ;
}
void MinecraftServer : : saveAllChunks ( )
{
// logger.info("Saving chunks");
for ( unsigned int i = 0 ; i < levels . length ; i + + )
{
// 4J Stu - Due to the way save mounting is handled on XboxOne, we can actually save after the player has signed out.
# ifndef _XBOX_ONE
if ( m_bPrimaryPlayerSignedOut ) break ;
# endif
// 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat
// with the data from the nethers leveldata.
// Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting.
ServerLevel * level = levels [ levels . length - 1 - i ] ;
if ( level ) // 4J - added check as level can be NULL if we end up in stopServer really early on due to network failure
{
level - > save ( true , Minecraft : : GetInstance ( ) - > progressRenderer ) ;
// Only close the level storage when we have saved the last level, otherwise we need to recreate the region files
// when saving the next levels
if ( i = = ( levels . length - 1 ) )
{
level - > closeLevelStorage ( ) ;
}
}
}
}
// 4J-JEV: Added
void MinecraftServer : : saveGameRules ( )
{
# ifndef _CONTENT_PACKAGE
if ( app . DebugSettingsOn ( ) & & app . GetGameSettingsDebugMask ( ProfileManager . GetPrimaryPad ( ) ) & ( 1L < < eDebugSetting_DistributableSave ) )
{
// Do nothing
}
else
# endif
{
byteArray ba ;
ba . data = NULL ;
app . m_gameRules . saveGameRules ( & ba . data , & ba . length ) ;
if ( ba . data ! = NULL )
{
ConsoleSaveFile * csf = getLevel ( 0 ) - > getLevelStorage ( ) - > getSaveFile ( ) ;
FileEntry * fe = csf - > createFile ( ConsoleSavePath ( GAME_RULE_SAVENAME ) ) ;
csf - > setFilePointer ( fe , 0 , NULL , FILE_BEGIN ) ;
DWORD length ;
csf - > writeFile ( fe , ba . data , ba . length , & length ) ;
delete [ ] ba . data ;
csf - > closeHandle ( fe ) ;
}
}
}
void MinecraftServer : : Suspend ( )
{
PIXBeginNamedEvent ( 0 , " Suspending server " ) ;
m_suspending = true ;
// Get the frequency of the timer
LARGE_INTEGER qwTicksPerSec , qwTime , qwNewTime , qwDeltaTime ;
float fElapsedTime = 0.0f ;
QueryPerformanceFrequency ( & qwTicksPerSec ) ;
float fSecsPerTick = 1.0f / ( float ) qwTicksPerSec . QuadPart ;
// Save the start time
QueryPerformanceCounter ( & qwTime ) ;
if ( m_bLoaded & & ProfileManager . IsFullVersion ( ) & & ( ! StorageManager . GetSaveDisabled ( ) ) )
2026-03-02 15:53:32 +07:00
{
2026-03-01 12:16:08 +08:00
if ( players ! = NULL )
{
players - > saveAll ( NULL ) ;
}
for ( unsigned int j = 0 ; j < levels . length ; j + + )
{
if ( s_bServerHalted ) break ;
// 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat
// with the data from the nethers leveldata.
// Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting.
ServerLevel * level = levels [ levels . length - 1 - j ] ;
level - > Suspend ( ) ;
}
if ( ! s_bServerHalted )
{
saveGameRules ( ) ;
levels [ 0 ] - > saveToDisc ( NULL , true ) ;
}
}
QueryPerformanceCounter ( & qwNewTime ) ;
qwDeltaTime . QuadPart = qwNewTime . QuadPart - qwTime . QuadPart ;
fElapsedTime = fSecsPerTick * ( ( FLOAT ) ( qwDeltaTime . QuadPart ) ) ;
// 4J-JEV: Flush stats and call PlayerSessionExit.
for ( int iPad = 0 ; iPad < XUSER_MAX_COUNT ; iPad + + )
{
if ( ProfileManager . IsSignedIn ( iPad ) )
{
TelemetryManager - > RecordPlayerSessionExit ( iPad , DisconnectPacket : : eDisconnect_Quitting ) ;
}
}
m_suspending = false ;
app . DebugPrintf ( " Suspend server: Elapsed time %f \n " , fElapsedTime ) ;
PIXEndNamedEvent ( ) ;
}
bool MinecraftServer : : IsSuspending ( )
{
return m_suspending ;
}
void MinecraftServer : : stopServer ( )
{
// 4J-PB - need to halt the rendering of the data, since we're about to remove it
# ifdef __PS3__
if ( ShutdownManager : : ShouldRun ( ShutdownManager : : eServerThread ) ) // This thread will take itself out if we are shutting down
# endif
{
Minecraft : : GetInstance ( ) - > gameRenderer - > DisableUpdateThread ( ) ;
}
connection - > stop ( ) ;
app . DebugPrintf ( " Stopping server \n " ) ;
// logger.info("Stopping server");
// 4J-PB - If the primary player has signed out, then don't attempt to save anything
2026-03-02 15:53:32 +07:00
2026-03-01 12:16:08 +08:00
// also need to check for a profile switch here - primary player signs out, and another player signs in before dismissing the dash
# ifdef _DURANGO
// On Durango check if the primary user is signed in OR mid-sign-out
if ( ProfileManager . GetUser ( 0 , true ) ! = nullptr )
# else
if ( ( m_bPrimaryPlayerSignedOut = = false ) & & ProfileManager . IsSignedIn ( ProfileManager . GetPrimaryPad ( ) ) )
# endif
{
# if defined(_XBOX_ONE) || defined(__ORBIS__)
// Always save on exit! Except if saves are disabled.
if ( ! saveOnExitAnswered ( ) ) m_saveOnExit = true ;
# endif
// if trial version or saving is disabled, then don't save anything
if ( m_saveOnExit & & ProfileManager . IsFullVersion ( ) & & ( ! StorageManager . GetSaveDisabled ( ) ) )
2026-03-02 15:53:32 +07:00
{
2026-03-01 12:16:08 +08:00
if ( players ! = NULL )
{
players - > saveAll ( Minecraft : : GetInstance ( ) - > progressRenderer , true ) ;
}
// 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat
// with the data from the nethers leveldata.
// Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting.
//for (unsigned int i = levels.length - 1; i >= 0; i--)
//{
// ServerLevel *level = levels[i];
// if (level != NULL)
// {
saveAllChunks ( ) ;
// }
//}
saveGameRules ( ) ;
app . m_gameRules . unloadCurrentGameRules ( ) ;
if ( levels [ 0 ] ! = NULL ) // This can be null if stopServer happens very quickly due to network error
{
levels [ 0 ] - > saveToDisc ( Minecraft : : GetInstance ( ) - > progressRenderer , false ) ;
}
}
}
// reset the primary player signout flag
m_bPrimaryPlayerSignedOut = false ;
s_bServerHalted = false ;
// On Durango/Orbis, we need to wait for all the asynchronous saving processes to complete before destroying the levels, as that will ultimately delete
// the directory level storage & therefore the ConsoleSaveSplit instance, which needs to be around until all the sub files have completed saving.
# if defined(_DURANGO) || defined(__ORBIS__) || defined(__PSVITA__)
while ( StorageManager . GetSaveState ( ) ! = C4JStorage : : ESaveGame_Idle )
{
Sleep ( 10 ) ;
}
# endif
// 4J-PB remove the server levels
unsigned int iServerLevelC = levels . length ;
for ( unsigned int i = 0 ; i < iServerLevelC ; i + + )
{
if ( levels [ i ] ! = NULL )
{
delete levels [ i ] ;
levels [ i ] = NULL ;
}
}
# if defined(__PS3__) || defined(__ORBIS__)
// 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
// 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.
# endif
delete connection ;
connection = NULL ;
delete players ;
players = NULL ;
delete settings ;
settings = NULL ;
g_NetworkManager . ServerStopped ( ) ;
}
void MinecraftServer : : halt ( )
{
running = false ;
}
void MinecraftServer : : setMaxBuildHeight ( int maxBuildHeight )
{
this - > maxBuildHeight = maxBuildHeight ;
}
int MinecraftServer : : getMaxBuildHeight ( )
{
return maxBuildHeight ;
}
PlayerList * MinecraftServer : : getPlayers ( )
{
return players ;
}
void MinecraftServer : : setPlayers ( PlayerList * players )
{
this - > players = players ;
}
ServerConnection * MinecraftServer : : getConnection ( )
{
return connection ;
}
bool MinecraftServer : : isAnimals ( )
{
return animals ;
}
void MinecraftServer : : setAnimals ( bool animals )
{
this - > animals = animals ;
}
bool MinecraftServer : : isNpcsEnabled ( )
{
return npcs ;
}
void MinecraftServer : : setNpcsEnabled ( bool npcs )
{
this - > npcs = npcs ;
}
bool MinecraftServer : : isPvpAllowed ( )
{
return pvp ;
}
void MinecraftServer : : setPvpAllowed ( bool pvp )
{
this - > pvp = pvp ;
}
bool MinecraftServer : : isFlightAllowed ( )
{
return allowFlight ;
}
void MinecraftServer : : setFlightAllowed ( bool allowFlight )
{
this - > allowFlight = allowFlight ;
}
bool MinecraftServer : : isNetherEnabled ( )
{
return true ; //settings.getBoolean("allow-nether", true);
}
bool MinecraftServer : : isHardcore ( )
{
return false ;
}
CommandDispatcher * MinecraftServer : : getCommandDispatcher ( )
{
return commandDispatcher ;
}
extern int c0a , c0b , c1a , c1b , c1c , c2a , c2b ;
2026-03-02 15:53:32 +07:00
void MinecraftServer : : run ( int64_t seed , void * lpParameter )
2026-03-01 12:16:08 +08:00
{
NetworkGameInitData * initData = NULL ;
DWORD initSettings = 0 ;
bool findSeed = false ;
if ( lpParameter ! = NULL )
{
initData = ( NetworkGameInitData * ) lpParameter ;
initSettings = app . GetGameHostOption ( eGameHostOption_All ) ;
findSeed = initData - > findSeed ;
m_texturePackId = initData - > texturePackId ;
}
// try { // 4J - removed try/catch/finally
if ( initServer ( seed , initData , initSettings , findSeed ) )
{
ServerLevel * levelNormalDimension = levels [ 0 ] ;
// 4J-PB - Set the Stronghold position in the leveldata if there isn't one in there
Minecraft * pMinecraft = Minecraft : : GetInstance ( ) ;
LevelData * pLevelData = levelNormalDimension - > getLevelData ( ) ;
if ( pLevelData & & pLevelData - > getHasStronghold ( ) = = false )
{
2026-03-02 15:53:32 +07:00
int x , z ;
2026-03-01 12:16:08 +08:00
if ( app . GetTerrainFeaturePosition ( eTerrainFeature_Stronghold , & x , & z ) )
{
pLevelData - > setXStronghold ( x ) ;
pLevelData - > setZStronghold ( z ) ;
pLevelData - > setHasStronghold ( ) ;
}
}
2026-03-02 15:53:32 +07:00
int64_t lastTime = System : : currentTimeMillis ( ) ;
int64_t unprocessedTime = 0 ;
2026-03-01 12:16:08 +08:00
while ( running & & ! s_bServerHalted )
{
2026-03-02 15:53:32 +07:00
int64_t now = System : : currentTimeMillis ( ) ;
2026-03-01 12:16:08 +08:00
// 4J Stu - When we pause the server, we don't want to count that as time passed
// 4J Stu - TU-1 hotifx - Remove this line. We want to make sure that we tick connections at the proper rate when paused
//Fix for #13191 - The host of a game can get a message informing them that the connection to the server has been lost
//if(m_isServerPaused) lastTime = now;
2026-03-02 15:53:32 +07:00
int64_t passedTime = now - lastTime ;
2026-03-01 12:16:08 +08:00
if ( passedTime > MS_PER_TICK * 40 )
{
// logger.warning("Can't keep up! Did the system time change, or is the server overloaded?");
passedTime = MS_PER_TICK * 40 ;
}
if ( passedTime < 0 )
{
// logger.warning("Time ran backwards! Did the system time change?");
passedTime = 0 ;
}
unprocessedTime + = passedTime ;
lastTime = now ;
// 4J Added ability to pause the server
if ( ! m_isServerPaused )
{
bool didTick = false ;
if ( levels [ 0 ] - > allPlayersAreSleeping ( ) )
{
tick ( ) ;
unprocessedTime = 0 ;
}
else
{
// int tickcount = 0;
2026-03-02 15:53:32 +07:00
// int64_t beforeall = System::currentTimeMillis();
2026-03-01 12:16:08 +08:00
while ( unprocessedTime > MS_PER_TICK )
{
unprocessedTime - = MS_PER_TICK ;
2026-03-02 15:53:32 +07:00
// int64_t before = System::currentTimeMillis();
2026-03-01 12:16:08 +08:00
tick ( ) ;
2026-03-02 15:53:32 +07:00
// int64_t after = System::currentTimeMillis();
2026-03-01 12:16:08 +08:00
// PIXReportCounter(L"Server time",(float)(after-before));
// 4J Ensure that the slow queue owner keeps cycling if it's not been used in a while
int time = GetTickCount ( ) ;
if ( ( s_slowQueuePacketSent ) | | ( ( time - s_slowQueueLastTime ) > ( 2 * MINECRAFT_SERVER_SLOW_QUEUE_DELAY ) ) )
{
// app.DebugPrintf("Considering cycling: (%d) %d - %d -> %d > %d\n",s_slowQueuePacketSent, time, s_slowQueueLastTime, (time - s_slowQueueLastTime), (2*MINECRAFT_SERVER_SLOW_QUEUE_DELAY));
MinecraftServer : : cycleSlowQueueIndex ( ) ;
s_slowQueuePacketSent = false ;
s_slowQueueLastTime = time ;
}
// else
// {
// app.DebugPrintf("Not considering cycling: %d - %d -> %d > %d\n",time, s_slowQueueLastTime, (time - s_slowQueueLastTime), (2*MINECRAFT_SERVER_SLOW_QUEUE_DELAY));
// }
}
2026-03-02 15:53:32 +07:00
// int64_t afterall = System::currentTimeMillis();
2026-03-01 12:16:08 +08:00
// PIXReportCounter(L"Server time all",(float)(afterall-beforeall));
// PIXReportCounter(L"Server ticks",(float)tickcount);
}
}
else
{
// 4J Stu - TU1-hotfix
//Fix for #13191 - The host of a game can get a message informing them that the connection to the server has been lost
// The connections should tick at the same frequency even when paused
while ( unprocessedTime > MS_PER_TICK )
{
unprocessedTime - = MS_PER_TICK ;
// Keep ticking the connections to stop them timing out
connection - > tick ( ) ;
}
}
if ( MinecraftServer : : setTimeAtEndOfTick )
{
MinecraftServer : : setTimeAtEndOfTick = false ;
for ( unsigned int i = 0 ; i < levels . length ; i + + )
{
// if (i == 0 || settings->getBoolean(L"allow-nether", true)) // 4J removed - we always have nether
{
ServerLevel * level = levels [ i ] ;
level - > setTime ( MinecraftServer : : setTime ) ;
level - > setOverrideTimeOfDay ( - 1 ) ;
}
}
}
if ( MinecraftServer : : setTimeOfDayAtEndOfTick )
{
MinecraftServer : : setTimeOfDayAtEndOfTick = false ;
for ( unsigned int i = 0 ; i < levels . length ; i + + )
{
if ( i = = 0 | | settings - > getBoolean ( L " allow-nether " , true ) )
{
ServerLevel * level = levels [ i ] ;
//level->setTime( MinecraftServer::setTime );
level - > setOverrideTimeOfDay ( MinecraftServer : : setTimeOfDay ) ;
}
}
}
2026-03-02 15:53:32 +07:00
// Process delayed actions
2026-03-01 12:16:08 +08:00
eXuiServerAction eAction ;
LPVOID param ;
for ( int i = 0 ; i < XUSER_MAX_COUNT ; i + + )
{
eAction = app . GetXuiServerAction ( i ) ;
param = app . GetXuiServerActionParam ( i ) ;
switch ( eAction )
{
case eXuiServerAction_AutoSaveGame :
# if defined(_XBOX_ONE) || defined(__ORBIS__)
{
PIXBeginNamedEvent ( 0 , " Autosave " ) ;
// Get the frequency of the timer
LARGE_INTEGER qwTicksPerSec , qwTime , qwNewTime , qwDeltaTime ;
float fElapsedTime = 0.0f ;
QueryPerformanceFrequency ( & qwTicksPerSec ) ;
float fSecsPerTick = 1.0f / ( float ) qwTicksPerSec . QuadPart ;
// Save the start time
QueryPerformanceCounter ( & qwTime ) ;
if ( players ! = NULL )
{
players - > saveAll ( NULL ) ;
}
for ( unsigned int j = 0 ; j < levels . length ; j + + )
{
if ( s_bServerHalted ) break ;
// 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat
// with the data from the nethers leveldata.
// Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting.
ServerLevel * level = levels [ levels . length - 1 - j ] ;
PIXBeginNamedEvent ( 0 , " Saving level %d " , levels . length - 1 - j ) ;
level - > save ( false , NULL , true ) ;
PIXEndNamedEvent ( ) ;
}
if ( ! s_bServerHalted )
{
PIXBeginNamedEvent ( 0 , " Saving game rules " ) ;
saveGameRules ( ) ;
PIXEndNamedEvent ( ) ;
PIXBeginNamedEvent ( 0 , " Save to disc " ) ;
levels [ 0 ] - > saveToDisc ( Minecraft : : GetInstance ( ) - > progressRenderer , true ) ;
PIXEndNamedEvent ( ) ;
}
PIXEndNamedEvent ( ) ;
QueryPerformanceCounter ( & qwNewTime ) ;
qwDeltaTime . QuadPart = qwNewTime . QuadPart - qwTime . QuadPart ;
fElapsedTime = fSecsPerTick * ( ( FLOAT ) ( qwDeltaTime . QuadPart ) ) ;
app . DebugPrintf ( " Autosave: Elapsed time %f \n " , fElapsedTime ) ;
}
break ;
# endif
case eXuiServerAction_SaveGame :
app . EnterSaveNotificationSection ( ) ;
if ( players ! = NULL )
{
players - > saveAll ( Minecraft : : GetInstance ( ) - > progressRenderer ) ;
}
2026-03-02 15:53:32 +07:00
2026-03-02 15:58:20 +07:00
players - > broadcastAll ( std : : shared_ptr < UpdateProgressPacket > ( new UpdateProgressPacket ( 20 ) ) ) ;
2026-03-01 12:16:08 +08:00
for ( unsigned int j = 0 ; j < levels . length ; j + + )
{
if ( s_bServerHalted ) break ;
// 4J Stu - Save the levels in reverse order so we don't overwrite the level.dat
// with the data from the nethers leveldata.
// Fix for #7418 - Functional: Gameplay: Saving after sleeping in a bed will place player at nighttime when restarting.
ServerLevel * level = levels [ levels . length - 1 - j ] ;
level - > save ( true , Minecraft : : GetInstance ( ) - > progressRenderer , ( eAction = = eXuiServerAction_AutoSaveGame ) ) ;
2026-03-02 15:53:32 +07:00
2026-03-02 15:58:20 +07:00
players - > broadcastAll ( std : : shared_ptr < UpdateProgressPacket > ( new UpdateProgressPacket ( 33 + ( j * 33 ) ) ) ) ;
2026-03-01 12:16:08 +08:00
}
if ( ! s_bServerHalted )
{
saveGameRules ( ) ;
2026-03-02 15:53:32 +07:00
levels [ 0 ] - > saveToDisc ( Minecraft : : GetInstance ( ) - > progressRenderer , ( eAction = = eXuiServerAction_AutoSaveGame ) ) ;
2026-03-01 12:16:08 +08:00
}
app . LeaveSaveNotificationSection ( ) ;
break ;
case eXuiServerAction_DropItem :
// Find the player, and drop the id at their feet
{
2026-03-02 15:58:20 +07:00
std : : shared_ptr < ServerPlayer > player = players - > players . at ( 0 ) ;
2026-03-01 12:16:08 +08:00
size_t id = ( size_t ) param ;
2026-03-02 15:58:20 +07:00
player - > drop ( std : : shared_ptr < ItemInstance > ( new ItemInstance ( id , 1 , 0 ) ) ) ;
2026-03-01 12:16:08 +08:00
}
break ;
case eXuiServerAction_SpawnMob :
{
2026-03-02 15:58:20 +07:00
std : : shared_ptr < ServerPlayer > player = players - > players . at ( 0 ) ;
2026-03-01 12:16:08 +08:00
eINSTANCEOF factory = ( eINSTANCEOF ) ( ( size_t ) param ) ;
2026-03-02 17:10:34 +07:00
std : : shared_ptr < Mob > mob = std : : dynamic_pointer_cast < Mob > ( EntityIO : : newByEnumType ( factory , player - > level ) ) ;
2026-03-01 12:16:08 +08:00
mob - > moveTo ( player - > x + 1 , player - > y , player - > z + 1 , player - > level - > random - > nextFloat ( ) * 360 , 0 ) ;
mob - > setDespawnProtected ( ) ; // 4J added, default to being protected against despawning (has to be done after initial position is set)
player - > level - > addEntity ( mob ) ;
}
break ;
case eXuiServerAction_PauseServer :
m_isServerPaused = ( ( size_t ) param = = TRUE ) ;
if ( m_isServerPaused )
2026-03-02 15:53:32 +07:00
{
2026-03-01 12:16:08 +08:00
m_serverPausedEvent - > Set ( ) ;
}
break ;
case eXuiServerAction_ToggleRain :
2026-03-02 15:53:32 +07:00
{
2026-03-01 12:16:08 +08:00
bool isRaining = levels [ 0 ] - > getLevelData ( ) - > isRaining ( ) ;
levels [ 0 ] - > getLevelData ( ) - > setRaining ( ! isRaining ) ;
levels [ 0 ] - > getLevelData ( ) - > setRainTime ( levels [ 0 ] - > random - > nextInt ( Level : : TICKS_PER_DAY * 7 ) + Level : : TICKS_PER_DAY / 2 ) ;
}
break ;
case eXuiServerAction_ToggleThunder :
2026-03-02 15:53:32 +07:00
{
2026-03-01 12:16:08 +08:00
bool isThundering = levels [ 0 ] - > getLevelData ( ) - > isThundering ( ) ;
levels [ 0 ] - > getLevelData ( ) - > setThundering ( ! isThundering ) ;
levels [ 0 ] - > getLevelData ( ) - > setThunderTime ( levels [ 0 ] - > random - > nextInt ( Level : : TICKS_PER_DAY * 7 ) + Level : : TICKS_PER_DAY / 2 ) ;
}
break ;
case eXuiServerAction_ServerSettingChanged_Gamertags :
2026-03-02 15:58:20 +07:00
players - > broadcastAll ( std : : shared_ptr < ServerSettingsChangedPacket > ( new ServerSettingsChangedPacket ( ServerSettingsChangedPacket : : HOST_OPTIONS , app . GetGameHostOption ( eGameHostOption_Gamertags ) ) ) ) ;
2026-03-01 12:16:08 +08:00
break ;
case eXuiServerAction_ServerSettingChanged_BedrockFog :
2026-03-02 15:58:20 +07:00
players - > broadcastAll ( std : : shared_ptr < ServerSettingsChangedPacket > ( new ServerSettingsChangedPacket ( ServerSettingsChangedPacket : : HOST_IN_GAME_SETTINGS , app . GetGameHostOption ( eGameHostOption_All ) ) ) ) ;
2026-03-01 12:16:08 +08:00
break ;
case eXuiServerAction_ServerSettingChanged_Difficulty :
2026-03-02 15:58:20 +07:00
players - > broadcastAll ( std : : shared_ptr < ServerSettingsChangedPacket > ( new ServerSettingsChangedPacket ( ServerSettingsChangedPacket : : HOST_DIFFICULTY , Minecraft : : GetInstance ( ) - > options - > difficulty ) ) ) ;
2026-03-01 12:16:08 +08:00
break ;
case eXuiServerAction_ExportSchematic :
# ifndef _CONTENT_PACKAGE
app . EnterSaveNotificationSection ( ) ;
2026-03-02 15:53:32 +07:00
2026-03-02 15:58:20 +07:00
//players->broadcastAll( std::shared_ptr<UpdateProgressPacket>( new UpdateProgressPacket(20) ) );
2026-03-01 12:16:08 +08:00
if ( ! s_bServerHalted )
{
ConsoleSchematicFile : : XboxSchematicInitParam * initData = ( ConsoleSchematicFile : : XboxSchematicInitParam * ) param ;
# ifdef _XBOX
File targetFileDir ( File : : pathRoot + File : : pathSeparator + L " Schematics " ) ;
# else
File targetFileDir ( L " Schematics " ) ;
# endif
if ( ! targetFileDir . exists ( ) ) targetFileDir . mkdir ( ) ;
wchar_t filename [ 128 ] ;
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 ) ) ;
File dataFile = File ( targetFileDir , wstring ( filename ) ) ;
if ( dataFile . exists ( ) ) dataFile . _delete ( ) ;
FileOutputStream fos = FileOutputStream ( dataFile ) ;
2026-03-02 15:53:32 +07:00
DataOutputStream dos = DataOutputStream ( & fos ) ;
2026-03-01 12:16:08 +08:00
ConsoleSchematicFile : : generateSchematicFile ( & dos , levels [ 0 ] , initData - > startX , initData - > startY , initData - > startZ , initData - > endX , initData - > endY , initData - > endZ , initData - > bSaveMobs , initData - > compressionType ) ;
dos . close ( ) ;
delete initData ;
}
app . LeaveSaveNotificationSection ( ) ;
# endif
break ;
case eXuiServerAction_SetCameraLocation :
# ifndef _CONTENT_PACKAGE
{
DebugSetCameraPosition * pos = ( DebugSetCameraPosition * ) param ;
app . DebugPrintf ( " DEBUG: Player=%i \n " , pos - > player ) ;
app . DebugPrintf ( " DEBUG: Teleporting to pos=(%f.2, %f.2, %f.2), looking at=(%f.2,%f.2) \n " ,
pos - > m_camX , pos - > m_camY , pos - > m_camZ ,
2026-03-02 15:53:32 +07:00
pos - > m_yRot , pos - > m_elev
2026-03-01 12:16:08 +08:00
) ;
2026-03-02 15:58:20 +07:00
std : : shared_ptr < ServerPlayer > player = players - > players . at ( pos - > player ) ;
2026-03-01 12:16:08 +08:00
player - > debug_setPosition ( pos - > m_camX , pos - > m_camY , pos - > m_camZ ,
pos - > m_yRot , pos - > m_elev ) ;
// Doesn't work
//player->setYHeadRot(pos->m_yRot);
//player->absMoveTo(pos->m_camX, pos->m_camY, pos->m_camZ, pos->m_yRot, pos->m_elev);
}
# endif
break ;
}
2026-03-02 15:53:32 +07:00
2026-03-01 12:16:08 +08:00
app . SetXuiServerAction ( i , eXuiServerAction_Idle ) ;
}
Sleep ( 1 ) ;
}
}
//else
//{
// while (running)
// {
// handleConsoleInputs();
// Sleep(10);
// }
//}
#if 0
} catch ( Throwable t ) {
t . printStackTrace ( ) ;
logger . log ( Level . SEVERE , " Unexpected exception " , t ) ;
while ( running ) {
handleConsoleInputs ( ) ;
try {
Thread . sleep ( 10 ) ;
} catch ( InterruptedException e1 ) {
e1 . printStackTrace ( ) ;
}
}
} finally {
try {
stopServer ( ) ;
stopped = true ;
} catch ( Throwable t ) {
t . printStackTrace ( ) ;
} finally {
System : : exit ( 0 ) ;
}
}
# endif
// 4J Stu - Stop the server when the loops complete, as the finally would do
stopServer ( ) ;
stopped = true ;
}
void MinecraftServer : : broadcastStartSavingPacket ( )
{
2026-03-02 15:58:20 +07:00
players - > broadcastAll ( std : : shared_ptr < GameEventPacket > ( new GameEventPacket ( GameEventPacket : : START_SAVING , 0 ) ) ) ; ;
2026-03-01 12:16:08 +08:00
}
void MinecraftServer : : broadcastStopSavingPacket ( )
{
if ( ! s_bServerHalted )
{
2026-03-02 15:58:20 +07:00
players - > broadcastAll ( std : : shared_ptr < GameEventPacket > ( new GameEventPacket ( GameEventPacket : : STOP_SAVING , 0 ) ) ) ; ;
2026-03-01 12:16:08 +08:00
}
}
void MinecraftServer : : tick ( )
{
vector < wstring > toRemove ;
for ( AUTO_VAR ( it , ironTimers . begin ( ) ) ; it ! = ironTimers . end ( ) ; it + + )
{
int t = it - > second ;
if ( t > 0 )
{
ironTimers [ it - > first ] = t - 1 ;
}
else
{
toRemove . push_back ( it - > first ) ;
}
}
for ( unsigned int i = 0 ; i < toRemove . size ( ) ; i + + )
{
ironTimers . erase ( toRemove [ i ] ) ;
}
AABB : : resetPool ( ) ;
Vec3 : : resetPool ( ) ;
2026-03-02 15:53:32 +07:00
2026-03-01 12:16:08 +08:00
tickCount + + ;
// 4J We need to update client difficulty levels based on the servers
Minecraft * pMinecraft = Minecraft : : GetInstance ( ) ;
// 4J-PB - sending this on the host changing the difficulty in the menus
/* if(m_lastSentDifficulty != pMinecraft->options->difficulty)
{
m_lastSentDifficulty = pMinecraft - > options - > difficulty ;
2026-03-02 15:58:20 +07:00
players - > broadcastAll ( std : : shared_ptr < ServerSettingsChangedPacket > ( new ServerSettingsChangedPacket ( ServerSettingsChangedPacket : : HOST_DIFFICULTY , pMinecraft - > options - > difficulty ) ) ) ;
2026-03-01 12:16:08 +08:00
} */
for ( unsigned int i = 0 ; i < levels . length ; i + + )
{
// if (i == 0 || settings->getBoolean(L"allow-nether", true)) // 4J removed - we always have nether
{
ServerLevel * level = levels [ i ] ;
// 4J Stu - We set the levels difficulty based on the minecraft options
level - > difficulty = app . GetGameHostOption ( eGameHostOption_Difficulty ) ; //pMinecraft->options->difficulty;
# if DEBUG_SERVER_DONT_SPAWN_MOBS
level - > setSpawnSettings ( false , false ) ;
# else
level - > setSpawnSettings ( level - > difficulty > 0 & & ! Minecraft : : GetInstance ( ) - > isTutorial ( ) , animals ) ;
# endif
if ( tickCount % 20 = = 0 )
{
2026-03-02 15:58:20 +07:00
players - > broadcastAll ( std : : shared_ptr < SetTimePacket > ( new SetTimePacket ( level - > getTime ( ) ) ) , level - > dimension - > id ) ;
2026-03-01 12:16:08 +08:00
}
// #ifndef __PS3__
2026-03-02 15:53:32 +07:00
static int64_t stc = 0 ;
int64_t st0 = System : : currentTimeMillis ( ) ;
2026-03-01 12:16:08 +08:00
PIXBeginNamedEvent ( 0 , " Level tick %d " , i ) ;
( ( Level * ) level ) - > tick ( ) ;
2026-03-02 15:53:32 +07:00
int64_t st1 = System : : currentTimeMillis ( ) ;
2026-03-01 12:16:08 +08:00
PIXEndNamedEvent ( ) ;
PIXBeginNamedEvent ( 0 , " Update lights %d " , i ) ;
// 4J - used to be in a while loop, but we don't want the server locking up for a big chunk of time (could end up trying to process 1,000,000 lights...)
// Instead call this once, which will try and process up to 2000 lights per tick
// printf("lights: %d\n",level->getLightsToUpdate());
while ( level - > updateLights ( ) )
;
2026-03-02 15:53:32 +07:00
int64_t st2 = System : : currentTimeMillis ( ) ;
2026-03-01 12:16:08 +08:00
PIXEndNamedEvent ( ) ;
PIXBeginNamedEvent ( 0 , " Entity tick %d " , i ) ;
// 4J added to stop ticking entities in levels when players are not in those levels.
// 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
// 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,
// 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
if ( ( players - > getPlayerCount ( level ) > 0 ) | | ( level - > hasEntitiesToRemove ( ) ) )
{
# ifdef __PSVITA__
// AP - the PlayerList->viewDistance initially starts out at 3 to make starting a level speedy
// the problem with this is that spawned monsters are always generated on the edge of the known map
// which means they wont process (unless they are surrounded by 2 visible chunks). This means
// they wont checkDespawn so they are NEVER removed which results in monsters not spawning.
// This bit of hack will modify the view distance once the level is up and running.
int newViewDistance = 5 ;
level - > getServer ( ) - > getPlayers ( ) - > setViewDistance ( newViewDistance ) ;
level - > getTracker ( ) - > updateMaxRange ( ) ;
level - > getChunkMap ( ) - > setRadius ( level - > getServer ( ) - > getPlayers ( ) - > getViewDistance ( ) ) ;
# endif
level - > tickEntities ( ) ;
}
PIXEndNamedEvent ( ) ;
PIXBeginNamedEvent ( 0 , " Entity tracker tick " ) ;
level - > getTracker ( ) - > tick ( ) ;
PIXEndNamedEvent ( ) ;
2026-03-02 15:53:32 +07:00
int64_t st3 = System : : currentTimeMillis ( ) ;
2026-03-01 12:16:08 +08:00
// printf(">>>>>>>>>>>>>>>>>>>>>> Tick %d %d %d : %d\n", st1 - st0, st2 - st1, st3 - st2, st0 - stc );
stc = st0 ;
// #endif// __PS3__
}
}
Entity : : tickExtraWandering ( ) ; // 4J added
PIXBeginNamedEvent ( 0 , " Connection tick " ) ;
connection - > tick ( ) ;
PIXEndNamedEvent ( ) ;
PIXBeginNamedEvent ( 0 , " Players tick " ) ;
players - > tick ( ) ;
PIXEndNamedEvent ( ) ;
// 4J - removed
#if 0
for ( int i = 0 ; i < tickables . size ( ) ; i + + ) {
tickables . get ( i ) - tick ( ) ;
}
# endif
// try { // 4J - removed try/catch
handleConsoleInputs ( ) ;
// } catch (Exception e) {
// logger.log(Level.WARNING, "Unexpected exception while parsing console command", e);
// }
}
void MinecraftServer : : handleConsoleInput ( const wstring & msg , ConsoleInputSource * source )
{
consoleInput . push_back ( new ConsoleInput ( msg , source ) ) ;
}
void MinecraftServer : : handleConsoleInputs ( )
{
while ( consoleInput . size ( ) > 0 )
{
AUTO_VAR ( it , consoleInput . begin ( ) ) ;
ConsoleInput * input = * it ;
consoleInput . erase ( it ) ;
// commands->handleCommand(input); // 4J - removed - TODO - do we want equivalent of console commands?
}
}
2026-03-02 15:53:32 +07:00
void MinecraftServer : : main ( int64_t seed , void * lpParameter )
2026-03-01 12:16:08 +08:00
{
# if __PS3__
ShutdownManager : : HasStarted ( ShutdownManager : : eServerThread ) ;
# endif
server = new MinecraftServer ( ) ;
server - > run ( seed , lpParameter ) ;
delete server ;
server = NULL ;
ShutdownManager : : HasFinished ( ShutdownManager : : eServerThread ) ;
}
void MinecraftServer : : HaltServer ( bool bPrimaryPlayerSignedOut )
{
s_bServerHalted = true ;
if ( server ! = NULL )
{
m_bPrimaryPlayerSignedOut = bPrimaryPlayerSignedOut ;
server - > halt ( ) ;
}
}
File * MinecraftServer : : getFile ( const wstring & name )
{
return new File ( name ) ;
}
void MinecraftServer : : info ( const wstring & string )
{
}
void MinecraftServer : : warn ( const wstring & string )
{
}
wstring MinecraftServer : : getConsoleName ( )
{
return L " CONSOLE " ;
}
ServerLevel * MinecraftServer : : getLevel ( int dimension )
{
if ( dimension = = - 1 ) return levels [ 1 ] ;
else if ( dimension = = 1 ) return levels [ 2 ] ;
else return levels [ 0 ] ;
}
// 4J added
void MinecraftServer : : setLevel ( int dimension , ServerLevel * level )
{
if ( dimension = = - 1 ) levels [ 1 ] = level ;
else if ( dimension = = 1 ) levels [ 2 ] = level ;
else levels [ 0 ] = level ;
}
// 4J Added
bool MinecraftServer : : canSendOnSlowQueue ( INetworkPlayer * player )
{
if ( player = = NULL ) return false ;
int time = GetTickCount ( ) ;
if ( player - > GetSessionIndex ( ) = = s_slowQueuePlayerIndex & & ( time - s_slowQueueLastTime ) > MINECRAFT_SERVER_SLOW_QUEUE_DELAY )
{
// app.DebugPrintf("Slow queue OK for player #%d\n", player->GetSessionIndex());
return true ;
}
return false ;
}
void MinecraftServer : : cycleSlowQueueIndex ( )
{
if ( ! g_NetworkManager . IsInSession ( ) ) return ;
int startingIndex = s_slowQueuePlayerIndex ;
INetworkPlayer * currentPlayer = NULL ;
DWORD currentPlayerCount = 0 ;
do
{
currentPlayerCount = g_NetworkManager . GetPlayerCount ( ) ;
if ( startingIndex > = currentPlayerCount ) startingIndex = 0 ;
+ + s_slowQueuePlayerIndex ;
if ( currentPlayerCount > 0 )
{
s_slowQueuePlayerIndex % = currentPlayerCount ;
// Fix for #9530 - NETWORKING: Attempting to fill a multiplayer game beyond capacity results in a softlock for the last players to join.
// The QNet session might be ending while we do this, so do a few more checks that the player is real
currentPlayer = g_NetworkManager . GetPlayerByIndex ( s_slowQueuePlayerIndex ) ;
}
else
{
s_slowQueuePlayerIndex = 0 ;
}
} while ( g_NetworkManager . IsInSession ( ) & &
currentPlayerCount > 0 & &
s_slowQueuePlayerIndex ! = startingIndex & &
currentPlayer ! = NULL & &
currentPlayer - > IsLocal ( )
) ;
// app.DebugPrintf("Cycled slow queue index to %d\n", s_slowQueuePlayerIndex);
}
// 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
// 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
// and nothing is set up.
bool MinecraftServer : : flagEntitiesToBeRemoved ( unsigned int * flags )
{
bool removedFound = false ;
for ( unsigned int i = 0 ; i < levels . length ; i + + )
{
ServerLevel * level = levels [ i ] ;
if ( level )
{
level - > flagEntitiesToBeRemoved ( flags , & removedFound ) ;
}
}
return removedFound ;
}