Something went wrong. Try again.
the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
Something went wrong. Try again.
63 kB · 1953 lines
C++
at main
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954#include "stdafx.h"//#include "Minecraft.h"
#include <ctime>
#include "ConsoleInput.h"#include "DerivedServerLevel.h"#include "DispenserBootstrap.h"#include "EntityTracker.h"#include "MinecraftServer.h"#include "Options.h"#include "PlayerList.h"#include "ServerChunkCache.h"#include "ServerConnection.h"#include "ServerLevel.h"#include "ServerLevelListener.h"#include "Settings.h"#include "..\Minecraft.World\Command.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"#include "PlayerConnection.h"#ifdef _XBOX_ONE#include "Durango\Network\NetworkPlayerDurango.h"#endif
#define DEBUG_SERVER_DONT_SPAWN_MOBS 0
//4J AddedMinecraftServer *MinecraftServer::server = NULL;bool MinecraftServer::setTimeAtEndOfTick = false;__int64 MinecraftServer::setTime = 0;bool MinecraftServer::setTimeOfDayAtEndOfTick = false;__int64 MinecraftServer::setTimeOfDay = 0;bool MinecraftServer::m_bPrimaryPlayerSignedOut=false;bool MinecraftServer::s_bServerHalted=false;bool MinecraftServer::s_bSaveOnExitAnswered=false;#ifdef _ACK_CHUNK_SEND_THROTTLINGbool MinecraftServer::s_hasSentEnoughPackets = false;__int64 MinecraftServer::s_tickStartTime = 0;vector<INetworkPlayer *> MinecraftServer::s_sentTo;#elseint MinecraftServer::s_slowQueuePlayerIndex = 0;int MinecraftServer::s_slowQueueLastTime = 0;bool MinecraftServer::s_slowQueuePacketSent = false;#endif
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; playerIdleTimeout = 0; m_postUpdateThread = NULL; forceGameType = false;
commandDispatcher = new ServerCommandDispatcher();
DispenserBootstrap::bootStrap();}
MinecraftServer::~MinecraftServer(){}
bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DWORD initSettings, bool findSeed){ // 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"); //motd.replace('�', '$');
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. }
__int64 levelNanoTime = System::nanoTime();
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); if (pLevelType == NULL) { 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""); __int64 levelSeed = (new Random())->nextLong(); if (levelSeedString.length() > 0) { long newSeed = _fromString<__int64>(levelSeedString); 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 creationint 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(); Vec3::CreateNewThreadStorage(); 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);}
bool MinecraftServer::loadLevel(LevelStorageSource *storageSource, const wstring& name, __int64 levelSeed, LevelType *pLevelType, NetworkGameInitData *initData){ // 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 shared_ptr<McRegionLevelStorage> storage = nullptr; bool levelChunksNeedConverted = false; if( initData->saveData != NULL ) { // We are loading a file from disk with the data passed in
#ifdef SPLIT_SAVES ConsoleSaveFileOriginal oldFormatSave( initData->saveData->saveName, initData->saveData->data, initData->saveData->fileSize, false, initData->savePlatform ); ConsoleSaveFile* pSave = new ConsoleSaveFileSplit( &oldFormatSave );
//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
storage = shared_ptr<McRegionLevelStorage>(new McRegionLevelStorage(pSave, File(L"."), name, true)); } 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"" ); }
storage = shared_ptr<McRegionLevelStorage>(new McRegionLevelStorage(newFormatSave, File(L"."), name, true));#else storage = shared_ptr<McRegionLevelStorage>(new McRegionLevelStorage(new ConsoleSaveFileOriginal( L"" ), File(L"."), name, true));#endif }
// McRegionLevelStorage *storage = new McRegionLevelStorage(new ConsoleSaveFile( L"" ), L"", L"", 0); // original // McRegionLevelStorage *storage = new McRegionLevelStorage(File(L"."), name, true); // TODO 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);
if(app.getLevelGenerationOptions() != NULL) { LevelGenerationOptions *mapOptions = app.getLevelGenerationOptions(); levels[i]->getLevelData()->setHasBeenInCreative(mapOptions->getLevelHasBeenInCreative() ); }
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();
__int64 startTime = System::currentTimeMillis();
// 4J Stu - Added this to temporarily make starting games on vita faster#ifdef __PSVITA__ int r = 48;#else int r = 196; #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); }
__int64 lastTime = System::currentTimeMillis();#ifdef _LARGE_WORLDS if(app.GetGameNewWorldSize() > levels[0]->getLevelData()->getXZSizeOld()) { if(!app.GetGameNewWorldSizeUseMoat()) // check the moat settings to see if we should be overwriting the edge tiles { overwriteBordersForNewWorldSize(levels[0]); } // we're always overwriting hell edges int oldHellSize = levels[0]->getLevelData()->getXZHellSizeOld(); overwriteHellBordersForNewWorldSize(levels[1], oldHellSize); }#endif
// 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 __int64 lastStorageTickTime = System::currentTimeMillis();
// 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 __int64 lastStorageTickTime = System::currentTimeMillis(); 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); // __int64 now = System::currentTimeMillis(); // 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()) { int x,z; 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;
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();
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;}
#ifdef _LARGE_WORLDSvoid MinecraftServer::overwriteBordersForNewWorldSize(ServerLevel* level){ // recreate the chunks round the border (2 chunks or 32 blocks deep), deleting any player data from them app.DebugPrintf("Expanding level size\n"); int oldSize = level->getLevelData()->getXZSizeOld(); // top int minVal = -oldSize/2; int maxVal = (oldSize/2)-1; for(int xVal = minVal; xVal <= maxVal; xVal++) { int zVal = minVal; level->cache->overwriteLevelChunkFromSource(xVal, zVal); level->cache->overwriteLevelChunkFromSource(xVal, zVal+1); } // bottom for(int xVal = minVal; xVal <= maxVal; xVal++) { int zVal = maxVal; level->cache->overwriteLevelChunkFromSource(xVal, zVal); level->cache->overwriteLevelChunkFromSource(xVal, zVal-1); } // left for(int zVal = minVal; zVal <= maxVal; zVal++) { int xVal = minVal; level->cache->overwriteLevelChunkFromSource(xVal, zVal); level->cache->overwriteLevelChunkFromSource(xVal+1, zVal); } // right for(int zVal = minVal; zVal <= maxVal; zVal++) { int xVal = maxVal; level->cache->overwriteLevelChunkFromSource(xVal, zVal); level->cache->overwriteLevelChunkFromSource(xVal-1, zVal); }}
void MinecraftServer::overwriteHellBordersForNewWorldSize(ServerLevel* level, int oldHellSize){ // recreate the chunks round the border (1 chunk or 16 blocks deep), deleting any player data from them app.DebugPrintf("Expanding level size\n"); // top int minVal = -oldHellSize/2; int maxVal = (oldHellSize/2)-1; for(int xVal = minVal; xVal <= maxVal; xVal++) { int zVal = minVal; level->cache->overwriteHellLevelChunkFromSource(xVal, zVal, minVal, maxVal); } // bottom for(int xVal = minVal; xVal <= maxVal; xVal++) { int zVal = maxVal; level->cache->overwriteHellLevelChunkFromSource(xVal, zVal, minVal, maxVal); } // left for(int zVal = minVal; zVal <= maxVal; zVal++) { int xVal = minVal; level->cache->overwriteHellLevelChunkFromSource(xVal, zVal, minVal, maxVal); } // right for(int zVal = minVal; zVal <= maxVal; zVal++) { int xVal = maxVal; level->cache->overwriteHellLevelChunkFromSource(xVal, zVal, minVal, maxVal); }}
#endif
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: Addedvoid 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())) { 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(bool didInit){
// 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
// 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. Also don't save anything if we didn't actually get through the server initialisation. if(m_saveOnExit && ProfileManager.IsFullVersion() && (!StorageManager.GetSaveDisabled()) && didInit) { 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::isCommandBlockEnabled(){ return false; //settings.getBoolean("enable-command-block", false);}
bool MinecraftServer::isNetherEnabled(){ return true; //settings.getBoolean("allow-nether", true);}
bool MinecraftServer::isHardcore(){ return false;}
int MinecraftServer::getOperatorUserPermissionLevel(){ return Command::LEVEL_OWNERS; //settings.getInt("op-permission-level", Command.LEVEL_OWNERS);}
CommandDispatcher *MinecraftServer::getCommandDispatcher(){ return commandDispatcher;}
Pos *MinecraftServer::getCommandSenderWorldPosition(){ return new Pos(0, 0, 0);}
Level *MinecraftServer::getCommandSenderWorld(){ return levels[0];}
int MinecraftServer::getSpawnProtectionRadius(){ return 16;}
bool MinecraftServer::isUnderSpawnProtection(Level *level, int x, int y, int z, shared_ptr<Player> player){ if (level->dimension->id != 0) return false; //if (getPlayers()->getOps()->empty()) return false; if (getPlayers()->isOp(player->getName())) return false; if (getSpawnProtectionRadius() <= 0) return false;
Pos *spawnPos = level->getSharedSpawnPos(); int xd = Mth::abs(x - spawnPos->x); int zd = Mth::abs(z - spawnPos->z); int dist = max(xd, zd);
return dist <= getSpawnProtectionRadius();}
void MinecraftServer::setForceGameType(bool forceGameType){ this->forceGameType = forceGameType;}
bool MinecraftServer::getForceGameType(){ return forceGameType;}
__int64 MinecraftServer::getCurrentTimeMillis(){ return System::currentTimeMillis();}
int MinecraftServer::getPlayerIdleTimeout(){ return playerIdleTimeout;}
void MinecraftServer::setPlayerIdleTimeout(int playerIdleTimeout){ this->playerIdleTimeout = playerIdleTimeout;}
extern int c0a, c0b, c1a, c1b, c1c, c2a, c2b;void MinecraftServer::run(__int64 seed, void *lpParameter){ 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 bool didInit = false; if (initServer(seed, initData, initSettings,findSeed)) { didInit = true; 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) { int x,z; if(app.GetTerrainFeaturePosition(eTerrainFeature_Stronghold,&x,&z)) { pLevelData->setXStronghold(x); pLevelData->setZStronghold(z); pLevelData->setHasStronghold(); } }
__int64 lastTime = getCurrentTimeMillis(); __int64 unprocessedTime = 0; while (running && !s_bServerHalted) { __int64 now = getCurrentTimeMillis();
// 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;
__int64 passedTime = now - lastTime; 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; // __int64 beforeall = System::currentTimeMillis(); while (unprocessedTime > MS_PER_TICK) { unprocessedTime -= MS_PER_TICK; chunkPacketManagement_PreTick();// __int64 before = System::currentTimeMillis(); tick();// __int64 after = System::currentTimeMillis();// PIXReportCounter(L"Server time",(float)(after-before));
chunkPacketManagement_PostTick(); }// __int64 afterall = System::currentTimeMillis();// 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->setGameTime( MinecraftServer::setTime ); } } } 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->setDayTime( MinecraftServer::setTimeOfDay ); } } }
// Process delayed actions 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); }
players->broadcastAll( shared_ptr<UpdateProgressPacket>( new UpdateProgressPacket(20) ) );
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));
players->broadcastAll( shared_ptr<UpdateProgressPacket>( new UpdateProgressPacket(33 + (j*33) ) ) ); } if( !s_bServerHalted ) { saveGameRules();
levels[0]->saveToDisc(Minecraft::GetInstance()->progressRenderer, (eAction==eXuiServerAction_AutoSaveGame)); } app.LeaveSaveNotificationSection(); break; case eXuiServerAction_DropItem: // Find the player, and drop the id at their feet { shared_ptr<ServerPlayer> player = players->players.at(0); size_t id = (size_t) param; player->drop( shared_ptr<ItemInstance>( new ItemInstance(id, 1, 0 ) ) ); } break; case eXuiServerAction_SpawnMob: { shared_ptr<ServerPlayer> player = players->players.at(0); eINSTANCEOF factory = (eINSTANCEOF)((size_t)param); shared_ptr<Mob> mob = dynamic_pointer_cast<Mob>(EntityIO::newByEnumType(factory,player->level )); 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 ) { m_serverPausedEvent->Set(); } break; case eXuiServerAction_ToggleRain: { 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: { 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: players->broadcastAll( shared_ptr<ServerSettingsChangedPacket>( new ServerSettingsChangedPacket( ServerSettingsChangedPacket::HOST_OPTIONS, app.GetGameHostOption(eGameHostOption_Gamertags)) ) ); break; case eXuiServerAction_ServerSettingChanged_BedrockFog: players->broadcastAll( shared_ptr<ServerSettingsChangedPacket>( new ServerSettingsChangedPacket( ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS, app.GetGameHostOption(eGameHostOption_All)) ) ); break;
case eXuiServerAction_ServerSettingChanged_Difficulty: players->broadcastAll( shared_ptr<ServerSettingsChangedPacket>( new ServerSettingsChangedPacket( ServerSettingsChangedPacket::HOST_DIFFICULTY, Minecraft::GetInstance()->options->difficulty) ) ); break; case eXuiServerAction_ExportSchematic:#ifndef _CONTENT_PACKAGE app.EnterSaveNotificationSection();
//players->broadcastAll( shared_ptr<UpdateProgressPacket>( new UpdateProgressPacket(20) ) );
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); DataOutputStream dos = DataOutputStream(&fos); 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, pos->m_yRot, pos->m_elev );
shared_ptr<ServerPlayer> player = players->players.at(pos->player); 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; }
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(didInit); stopped = true;}
void MinecraftServer::broadcastStartSavingPacket(){ players->broadcastAll( shared_ptr<GameEventPacket>( new GameEventPacket(GameEventPacket::START_SAVING, 0) ) );;}
void MinecraftServer::broadcastStopSavingPacket(){ if( !s_bServerHalted ) { players->broadcastAll( shared_ptr<GameEventPacket>( new GameEventPacket(GameEventPacket::STOP_SAVING, 0) ) );; }}
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();
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; players->broadcastAll( shared_ptr<ServerSettingsChangedPacket>( new ServerSettingsChangedPacket( ServerSettingsChangedPacket::HOST_DIFFICULTY, pMinecraft->options->difficulty) ) ); }*/
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) { players->broadcastAll( shared_ptr<SetTimePacket>( new SetTimePacket(level->getGameTime(), level->getDayTime(), level->getGameRules()->getBoolean(GameRules::RULE_DAYLIGHT) ) ), level->dimension->id); } // #ifndef __PS3__ static __int64 stc = 0; __int64 st0 = System::currentTimeMillis(); PIXBeginNamedEvent(0,"Level tick %d",i); ((Level *)level)->tick(); __int64 st1 = System::currentTimeMillis(); PIXEndNamedEvent(); PIXBeginNamedEvent(0,"Update lights %d",i);
__int64 st2 = System::currentTimeMillis(); 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();
__int64 st3 = System::currentTimeMillis(); // 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? }}
void MinecraftServer::main(__int64 seed, void *lpParameter){#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 addedvoid MinecraftServer::setLevel(int dimension, ServerLevel *level){ if (dimension == -1) levels[1] = level; else if (dimension == 1) levels[2] = level; else levels[0] = level;}
#if defined _ACK_CHUNK_SEND_THROTTLINGbool MinecraftServer::chunkPacketManagement_CanSendTo(INetworkPlayer *player){ if( s_hasSentEnoughPackets ) return false; if( player == NULL ) return false;
for( int i = 0; i < s_sentTo.size(); i++ ) { if( s_sentTo[i]->IsSameSystem(player) ) { return false; } }
#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) return ( player->GetOutstandingAckCount() < 3 );#else return ( player->GetOutstandingAckCount() < 2 );#endif}
void MinecraftServer::chunkPacketManagement_DidSendTo(INetworkPlayer *player){ __int64 currentTime = System::currentTimeMillis();
if( ( currentTime - s_tickStartTime ) >= MAX_TICK_TIME_FOR_PACKET_SENDS ) { s_hasSentEnoughPackets = true;// app.DebugPrintf("Sending, setting enough packet flag: %dms\n",currentTime - s_tickStartTime); } else {// app.DebugPrintf("Sending, more time: %dms\n",currentTime - s_tickStartTime); }
player->SentChunkPacket();
s_sentTo.push_back(player);}
void MinecraftServer::chunkPacketManagement_PreTick(){// app.DebugPrintf("*************************************************************************************************************************************************************************\n"); s_hasSentEnoughPackets = false; s_tickStartTime = System::currentTimeMillis(); s_sentTo.clear();
vector< shared_ptr<PlayerConnection> > *players = connection->getPlayers();
if( players->size() ) { vector< shared_ptr<PlayerConnection> > playersOrig = *players; players->clear();
do { int longestTime = 0; AUTO_VAR(playerConnectionBest,playersOrig.begin()); for( AUTO_VAR(it, playersOrig.begin()); it != playersOrig.end(); it++) { int thisTime = 0; INetworkPlayer *np = (*it)->getNetworkPlayer(); if( np ) { thisTime = np->GetTimeSinceLastChunkPacket_ms(); }
if( thisTime > longestTime ) { playerConnectionBest = it; longestTime = thisTime; } } players->push_back(*playerConnectionBest); playersOrig.erase(playerConnectionBest); } while ( playersOrig.size() > 0 ); }}
void MinecraftServer::chunkPacketManagement_PostTick(){}
#else// 4J Addedbool MinecraftServer::chunkPacketManagement_CanSendTo(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::chunkPacketManagement_DidSendTo(INetworkPlayer *player){ s_slowQueuePacketSent = true;}
void MinecraftServer::chunkPacketManagement_PreTick(){}
void MinecraftServer::chunkPacketManagement_PostTick(){ // 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));// }}
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);}#endif
// 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;}