Merge branch 'smartcmd:main' into main

This commit is contained in:
Alexandra-Myers 2026-03-15 17:13:05 -04:00 committed by GitHub
commit 77f64b9389
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 388 additions and 493 deletions

View file

@ -658,7 +658,7 @@ bool MinecraftServer::initServer(int64_t seed, NetworkGameInitData *initData, DW
setFlightAllowed(GetDedicatedServerBool(settings, L"allow-flight", true));
// 4J Stu - Enabling flight to stop it kicking us when we use it
#ifdef _DEBUG_MENUS_ENABLED
#if (defined _DEBUG_MENUS_ENABLED && defined _DEBUG)
setFlightAllowed(true);
#endif
@ -1716,330 +1716,345 @@ void MinecraftServer::setPlayerIdleTimeout(int playerIdleTimeout)
extern int c0a, c0b, c1a, c1b, c1c, c2a, c2b;
void MinecraftServer::run(int64_t seed, void *lpParameter)
{
NetworkGameInitData *initData = nullptr;
DWORD initSettings = 0;
bool findSeed = false;
NetworkGameInitData *initData = nullptr;
DWORD initSettings = 0;
bool findSeed = false;
if(lpParameter != nullptr)
{
initData = static_cast<NetworkGameInitData *>(lpParameter);
initSettings = app.GetGameHostOption(eGameHostOption_All);
findSeed = initData->findSeed;
m_texturePackId = initData->texturePackId;
}
// try { // 4J - removed try/catch/finally
bool didInit = false;
{
initData = static_cast<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();
{
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();
}
}
{
pLevelData->setXStronghold(x);
pLevelData->setZStronghold(z);
pLevelData->setHasStronghold();
}
}
int64_t lastTime = getCurrentTimeMillis();
int64_t unprocessedTime = 0;
while (running && !s_bServerHalted)
{
int64_t now = getCurrentTimeMillis();
int64_t lastTime = getCurrentTimeMillis();
int64_t unprocessedTime = 0;
while (running && !s_bServerHalted)
{
int64_t 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
// 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_t 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;
int64_t 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
// 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_t beforeall = System::currentTimeMillis();
while (unprocessedTime > MS_PER_TICK)
{
unprocessedTime -= MS_PER_TICK;
chunkPacketManagement_PreTick();
// int64_t before = System::currentTimeMillis();
tick();
// int64_t after = System::currentTimeMillis();
// PIXReportCounter(L"Server time",(float)(after-before));
{
bool didTick = false;
if (levels[0]->allPlayersAreSleeping())
{
tick();
unprocessedTime = 0;
}
else
{
// int tickcount = 0;
// int64_t beforeall = System::currentTimeMillis();
while (unprocessedTime > MS_PER_TICK)
{
unprocessedTime -= MS_PER_TICK;
chunkPacketManagement_PreTick();
// int64_t before = System::currentTimeMillis();
tick();
// int64_t after = System::currentTimeMillis();
// PIXReportCounter(L"Server time",(float)(after-before));
chunkPacketManagement_PostTick();
}
// int64_t afterall = System::currentTimeMillis();
// PIXReportCounter(L"Server time all",(float)(afterall-beforeall));
// PIXReportCounter(L"Server ticks",(float)tickcount);
}
}
else
{
// 4J Stu - TU1-hotfix
chunkPacketManagement_PostTick();
}
// int64_t 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();
}
}
// 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];
{
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 || GetDedicatedServerBool(settings, L"allow-nether", true))
{
ServerLevel *level = levels[i];
{
MinecraftServer::setTimeOfDayAtEndOfTick = false;
for (unsigned int i = 0; i < levels.length; i++)
{
if (i == 0 || GetDedicatedServerBool(settings, L"allow-nether", true))
{
ServerLevel *level = levels[i];
level->setDayTime( MinecraftServer::setTimeOfDay );
}
}
}
}
}
}
// Process delayed actions
eXuiServerAction eAction;
LPVOID param;
// Process delayed actions
eXuiServerAction eAction;
LPVOID param;
for(int i=0;i<XUSER_MAX_COUNT;i++)
{
eAction = app.GetXuiServerAction(i);
param = app.GetXuiServerActionParam(i);
{
eAction = app.GetXuiServerAction(i);
param = app.GetXuiServerActionParam(i);
switch(eAction)
{
case eXuiServerAction_AutoSaveGame:
{
case eXuiServerAction_AutoSaveGame:
#if defined(_XBOX_ONE) || defined(__ORBIS__) || defined(MINECRAFT_SERVER_BUILD)
{
#if defined(_XBOX_ONE) || defined(__ORBIS__)
{
PIXBeginNamedEvent(0,"Autosave");
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;
// 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 != nullptr)
{
players->saveAll(nullptr);
}
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, nullptr, 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;
// Save the start time
QueryPerformanceCounter(&qwTime);
#endif
case eXuiServerAction_SaveGame:
app.EnterSaveNotificationSection();
if (players != nullptr)
{
players->saveAll(Minecraft::GetInstance()->progressRenderer);
}
players->broadcastAll(std::make_shared<UpdateProgressPacket>(20));
if (players != nullptr)
{
players->saveAll(nullptr);
}
for (unsigned int j = 0; j < levels.length; j++)
{
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];
#if defined(_XBOX_ONE) || defined(__ORBIS__)
PIXBeginNamedEvent(0, "Saving level %d", levels.length - 1 - j);
#endif
level->save(false, nullptr, true);
#if defined(_XBOX_ONE) || defined(__ORBIS__)
PIXEndNamedEvent();
#endif
}
if (!s_bServerHalted)
{
#if defined(_XBOX_ONE) || defined(__ORBIS__)
PIXBeginNamedEvent(0, "Saving game rules");
#endif
saveGameRules();
#if defined(_XBOX_ONE) || defined(__ORBIS__)
PIXEndNamedEvent();
PIXBeginNamedEvent(0, "Save to disc");
#endif
levels[0]->saveToDisc(Minecraft::GetInstance()->progressRenderer, true);
#if defined(_XBOX_ONE) || defined(__ORBIS__)
PIXEndNamedEvent();
#endif
}
#if defined(_XBOX_ONE) || defined(__ORBIS__)
PIXEndNamedEvent();
QueryPerformanceCounter(&qwNewTime);
qwDeltaTime.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart;
fElapsedTime = fSecsPerTick * ((FLOAT)(qwDeltaTime.QuadPart));
app.DebugPrintf("Autosave: Elapsed time %f\n", fElapsedTime);
#endif
}
break;
#endif
case eXuiServerAction_SaveGame:
app.EnterSaveNotificationSection();
if (players != nullptr)
{
players->saveAll(Minecraft::GetInstance()->progressRenderer);
}
players->broadcastAll(std::make_shared<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];
// 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(std::make_shared<UpdateProgressPacket>(33 + (j * 33)));
}
players->broadcastAll(std::make_shared<UpdateProgressPacket>(33 + (j * 33)));
}
if( !s_bServerHalted )
{
saveGameRules();
{
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);
}
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(std::make_shared<ItemInstance>(id, 1, 0));
}
break;
case eXuiServerAction_SpawnMob:
{
shared_ptr<ServerPlayer> player = players->players.at(0);
eINSTANCEOF factory = static_cast<eINSTANCEOF>((size_t)param);
player->drop(std::make_shared<ItemInstance>(id, 1, 0));
}
break;
case eXuiServerAction_SpawnMob:
{
shared_ptr<ServerPlayer> player = players->players.at(0);
eINSTANCEOF factory = static_cast<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:
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(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_OPTIONS, app.GetGameHostOption(eGameHostOption_Gamertags)));
break;
case eXuiServerAction_ServerSettingChanged_BedrockFog:
players->broadcastAll(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS, app.GetGameHostOption(eGameHostOption_All)));
break;
{
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(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_OPTIONS, app.GetGameHostOption(eGameHostOption_Gamertags)));
break;
case eXuiServerAction_ServerSettingChanged_BedrockFog:
players->broadcastAll(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS, app.GetGameHostOption(eGameHostOption_All)));
break;
case eXuiServerAction_ServerSettingChanged_Difficulty:
players->broadcastAll(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_DIFFICULTY, Minecraft::GetInstance()->options->difficulty));
break;
case eXuiServerAction_ExportSchematic:
case eXuiServerAction_ServerSettingChanged_Difficulty:
players->broadcastAll(std::make_shared<ServerSettingsChangedPacket>(ServerSettingsChangedPacket::HOST_DIFFICULTY, Minecraft::GetInstance()->options->difficulty));
break;
case eXuiServerAction_ExportSchematic:
#ifndef _CONTENT_PACKAGE
app.EnterSaveNotificationSection();
app.EnterSaveNotificationSection();
//players->broadcastAll( shared_ptr<UpdateProgressPacket>( new UpdateProgressPacket(20) ) );
if( !s_bServerHalted )
{
ConsoleSchematicFile::XboxSchematicInitParam *initData = static_cast<ConsoleSchematicFile::XboxSchematicInitParam *>(param);
{
ConsoleSchematicFile::XboxSchematicInitParam *initData = static_cast<ConsoleSchematicFile::XboxSchematicInitParam *>(param);
#ifdef _XBOX
File targetFileDir(File::pathRoot + File::pathSeparator + L"Schematics");
File targetFileDir(File::pathRoot + File::pathSeparator + L"Schematics");
#else
File targetFileDir(L"Schematics");
File targetFileDir(L"Schematics");
#endif
if(!targetFileDir.exists()) targetFileDir.mkdir();
wchar_t filename[128];
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();
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();
delete initData;
}
app.LeaveSaveNotificationSection();
#endif
break;
case eXuiServerAction_SetCameraLocation:
break;
case eXuiServerAction_SetCameraLocation:
#ifndef _CONTENT_PACKAGE
{
DebugSetCameraPosition *pos = static_cast<DebugSetCameraPosition *>(param);
{
DebugSetCameraPosition *pos = static_cast<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_camX, pos->m_camY, pos->m_camZ,
pos->m_yRot, pos->m_elev
);
shared_ptr<ServerPlayer> player = players->players.at(pos->player);
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
// 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;
}
break;
}
app.SetXuiServerAction(i,eXuiServerAction_Idle);
}
}
Sleep(1);
}
}
Sleep(1);
}
}
//else
//{
//{
// while (running)
// {
// {
// handleConsoleInputs();
// Sleep(10);
// Sleep(10);
// }
//}
#if 0
@ -2066,9 +2081,9 @@ void MinecraftServer::run(int64_t seed, void *lpParameter)
}
#endif
// 4J Stu - Stop the server when the loops complete, as the finally would do
stopServer(didInit);
stopped = true;
// 4J Stu - Stop the server when the loops complete, as the finally would do
stopServer(didInit);
stopped = true;
}
void MinecraftServer::broadcastStartSavingPacket()
@ -2376,6 +2391,9 @@ bool MinecraftServer::chunkPacketManagement_CanSendTo(INetworkPlayer *player)
{
if( player == nullptr ) return false;
#ifdef MINECRAFT_SERVER_BUILD
return true;
#else
int time = GetTickCount();
DWORD currentPlayerCount = g_NetworkManager.GetPlayerCount();
if( currentPlayerCount == 0 ) return false;
@ -2387,6 +2405,7 @@ bool MinecraftServer::chunkPacketManagement_CanSendTo(INetworkPlayer *player)
}
return false;
#endif
}
void MinecraftServer::chunkPacketManagement_DidSendTo(INetworkPlayer *player)

View file

@ -146,7 +146,8 @@ void ServerPlayer::flagEntitiesToBeRemoved(unsigned int *flags, bool *removedFou
if( ( *removedFound ) == false )
{
*removedFound = true;
memset(flags, 0, 2048/32);
// before this left 192 bytes uninitialized!!!!!
memset(flags, 0, (2048 / 32) * sizeof(unsigned int));
}
for(int index : entitiesToRemove)
@ -376,6 +377,9 @@ void ServerPlayer::doChunkSendingTick(bool dontDelayChunks)
// connection->done);
// }
#ifdef MINECRAFT_SERVER_BUILD
if (dontDelayChunks || (canSendToPlayer && !connection->done))
#else
if( dontDelayChunks ||
(canSendToPlayer &&
#ifdef _XBOX_ONE
@ -390,21 +394,22 @@ void ServerPlayer::doChunkSendingTick(bool dontDelayChunks)
#endif
//(tickCount - lastBrupSendTickCount) > (connection->getNetworkPlayer()->GetCurrentRtt()>>4) &&
!connection->done) )
{
lastBrupSendTickCount = tickCount;
okToSend = true;
MinecraftServer::chunkPacketManagement_DidSendTo(connection->getNetworkPlayer());
#endif
{
lastBrupSendTickCount = tickCount;
okToSend = true;
MinecraftServer::chunkPacketManagement_DidSendTo(connection->getNetworkPlayer());
// static unordered_map<wstring,int64_t> mapLastTime;
// int64_t thisTime = System::currentTimeMillis();
// int64_t lastTime = mapLastTime[connection->getNetworkPlayer()->GetUID().toString()];
// app.DebugPrintf(" - OK to send (%d ms since last)\n", thisTime - lastTime);
// mapLastTime[connection->getNetworkPlayer()->GetUID().toString()] = thisTime;
}
else
{
// app.DebugPrintf(" - <NOT OK>\n");
}
// static unordered_map<wstring,int64_t> mapLastTime;
// int64_t thisTime = System::currentTimeMillis();
// int64_t lastTime = mapLastTime[connection->getNetworkPlayer()->GetUID().toString()];
// app.DebugPrintf(" - OK to send (%d ms since last)\n", thisTime - lastTime);
// mapLastTime[connection->getNetworkPlayer()->GetUID().toString()] = thisTime;
}
else
{
// app.DebugPrintf(" - <NOT OK>\n");
}
}
if (okToSend)

View file

@ -121,7 +121,6 @@ static WINDOWPLACEMENT g_wpPrev = { sizeof(g_wpPrev) };
struct Win64LaunchOptions
{
int screenMode;
bool serverMode;
bool fullscreen;
};
@ -207,13 +206,9 @@ static Win64LaunchOptions ParseLaunchOptions()
{
Win64LaunchOptions options = {};
options.screenMode = 0;
options.serverMode = false;
g_Win64MultiplayerJoin = false;
g_Win64MultiplayerPort = WIN64_NET_DEFAULT_PORT;
g_Win64DedicatedServer = false;
g_Win64DedicatedServerPort = WIN64_NET_DEFAULT_PORT;
g_Win64DedicatedServerBindIP[0] = 0;
int argc = 0;
LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
@ -226,17 +221,6 @@ static Win64LaunchOptions ParseLaunchOptions()
options.screenMode = argv[1][0] - L'0';
}
for (int i = 1; i < argc; ++i)
{
if (_wcsicmp(argv[i], L"-server") == 0)
{
options.serverMode = true;
break;
}
}
g_Win64DedicatedServer = options.serverMode;
for (int i = 1; i < argc; ++i)
{
if (_wcsicmp(argv[i], L"-name") == 0 && (i + 1) < argc)
@ -247,15 +231,8 @@ static Win64LaunchOptions ParseLaunchOptions()
{
char ipBuf[256];
CopyWideArgToAnsi(argv[++i], ipBuf, sizeof(ipBuf));
if (options.serverMode)
{
strncpy_s(g_Win64DedicatedServerBindIP, sizeof(g_Win64DedicatedServerBindIP), ipBuf, _TRUNCATE);
}
else
{
strncpy_s(g_Win64MultiplayerIP, sizeof(g_Win64MultiplayerIP), ipBuf, _TRUNCATE);
g_Win64MultiplayerJoin = true;
}
strncpy_s(g_Win64MultiplayerIP, sizeof(g_Win64MultiplayerIP), ipBuf, _TRUNCATE);
g_Win64MultiplayerJoin = true;
}
else if (_wcsicmp(argv[i], L"-port") == 0 && (i + 1) < argc)
{
@ -263,10 +240,7 @@ static Win64LaunchOptions ParseLaunchOptions()
const long port = wcstol(argv[++i], &endPtr, 10);
if (endPtr != argv[i] && *endPtr == 0 && port > 0 && port <= 65535)
{
if (options.serverMode)
g_Win64DedicatedServerPort = static_cast<int>(port);
else
g_Win64MultiplayerPort = static_cast<int>(port);
g_Win64MultiplayerPort = static_cast<int>(port);
}
}
else if (_wcsicmp(argv[i], L"-fullscreen") == 0)
@ -277,36 +251,6 @@ static Win64LaunchOptions ParseLaunchOptions()
return options;
}
static BOOL WINAPI HeadlessServerCtrlHandler(DWORD ctrlType)
{
switch (ctrlType)
{
case CTRL_C_EVENT:
case CTRL_BREAK_EVENT:
case CTRL_CLOSE_EVENT:
case CTRL_SHUTDOWN_EVENT:
app.m_bShutdown = true;
MinecraftServer::HaltServer();
return TRUE;
default:
return FALSE;
}
}
static void SetupHeadlessServerConsole()
{
if (AllocConsole())
{
FILE* stream = nullptr;
freopen_s(&stream, "CONIN$", "r", stdin);
freopen_s(&stream, "CONOUT$", "w", stdout);
freopen_s(&stream, "CONOUT$", "w", stderr);
SetConsoleTitleA("Minecraft Server");
}
SetConsoleCtrlHandler(HeadlessServerCtrlHandler, TRUE);
}
void DefineActions(void)
{
// The app needs to define the actions required, and the possible mappings for these
@ -1350,161 +1294,6 @@ static Minecraft* InitialiseMinecraftRuntime()
return pMinecraft;
}
static int HeadlessServerConsoleThreadProc(void* lpParameter)
{
UNREFERENCED_PARAMETER(lpParameter);
std::string line;
while (!app.m_bShutdown)
{
if (!std::getline(std::cin, line))
{
if (std::cin.eof())
{
break;
}
std::cin.clear();
Sleep(50);
continue;
}
wstring command = trimString(convStringToWstring(line));
if (command.empty())
continue;
MinecraftServer* server = MinecraftServer::getInstance();
if (server != nullptr)
{
server->handleConsoleInput(command, server);
}
}
return 0;
}
static int RunHeadlessServer()
{
SetupHeadlessServerConsole();
Settings serverSettings(new File(L"server.properties"));
const wstring configuredBindIp = serverSettings.getString(L"server-ip", L"");
const char* bindIp = "*";
if (g_Win64DedicatedServerBindIP[0] != 0)
{
bindIp = g_Win64DedicatedServerBindIP;
}
else if (!configuredBindIp.empty())
{
bindIp = wstringtochararray(configuredBindIp);
}
const int port = g_Win64DedicatedServerPort > 0 ? g_Win64DedicatedServerPort : serverSettings.getInt(L"server-port", WIN64_NET_DEFAULT_PORT);
printf("Starting headless server on %s:%d\n", bindIp, port);
fflush(stdout);
const Minecraft* pMinecraft = InitialiseMinecraftRuntime();
if (pMinecraft == nullptr)
{
fprintf(stderr, "Failed to initialise the Minecraft runtime.\n");
return 1;
}
app.SetGameHostOption(eGameHostOption_Difficulty, serverSettings.getInt(L"difficulty", 1));
app.SetGameHostOption(eGameHostOption_Gamertags, 1);
app.SetGameHostOption(eGameHostOption_GameType, serverSettings.getInt(L"gamemode", 0));
app.SetGameHostOption(eGameHostOption_LevelType, 0);
app.SetGameHostOption(eGameHostOption_Structures, serverSettings.getBoolean(L"generate-structures", true) ? 1 : 0);
app.SetGameHostOption(eGameHostOption_BonusChest, serverSettings.getBoolean(L"bonus-chest", false) ? 1 : 0);
app.SetGameHostOption(eGameHostOption_PvP, serverSettings.getBoolean(L"pvp", true) ? 1 : 0);
app.SetGameHostOption(eGameHostOption_TrustPlayers, serverSettings.getBoolean(L"trust-players", true) ? 1 : 0);
app.SetGameHostOption(eGameHostOption_FireSpreads, serverSettings.getBoolean(L"fire-spreads", true) ? 1 : 0);
app.SetGameHostOption(eGameHostOption_TNT, serverSettings.getBoolean(L"tnt", true) ? 1 : 0);
app.SetGameHostOption(eGameHostOption_HostCanFly, 1);
app.SetGameHostOption(eGameHostOption_HostCanChangeHunger, 1);
app.SetGameHostOption(eGameHostOption_HostCanBeInvisible, 1);
app.SetGameHostOption(eGameHostOption_MobGriefing, 1);
app.SetGameHostOption(eGameHostOption_KeepInventory, 0);
app.SetGameHostOption(eGameHostOption_DoMobSpawning, 1);
app.SetGameHostOption(eGameHostOption_DoMobLoot, 1);
app.SetGameHostOption(eGameHostOption_DoTileDrops, 1);
app.SetGameHostOption(eGameHostOption_NaturalRegeneration, 1);
app.SetGameHostOption(eGameHostOption_DoDaylightCycle, 1);
MinecraftServer::resetFlags();
g_NetworkManager.HostGame(0, false, true, MINECRAFT_NET_MAX_PLAYERS, 0);
if (!WinsockNetLayer::IsActive())
{
fprintf(stderr, "Failed to bind the server socket on %s:%d.\n", bindIp, port);
return 1;
}
g_NetworkManager.FakeLocalPlayerJoined();
NetworkGameInitData* param = new NetworkGameInitData();
param->seed = 0;
param->settings = app.GetGameHostOption(eGameHostOption_All);
g_NetworkManager.ServerStoppedCreate(true);
g_NetworkManager.ServerReadyCreate(true);
C4JThread* thread = new C4JThread(&CGameNetworkManager::ServerThreadProc, param, "Server", 256 * 1024);
thread->SetProcessor(CPU_CORE_SERVER);
thread->Run();
g_NetworkManager.ServerReadyWait();
g_NetworkManager.ServerReadyDestroy();
if (MinecraftServer::serverHalted())
{
fprintf(stderr, "The server halted during startup.\n");
g_NetworkManager.LeaveGame(false);
return 1;
}
app.SetGameStarted(true);
g_NetworkManager.DoWork();
printf("Server ready on %s:%d\n", bindIp, port);
printf("Type 'help' for server commands.\n");
fflush(stdout);
C4JThread* consoleThread = new C4JThread(&HeadlessServerConsoleThreadProc, nullptr, "Server console", 128 * 1024);
consoleThread->Run();
MSG msg = { 0 };
while (WM_QUIT != msg.message && !app.m_bShutdown && !MinecraftServer::serverHalted())
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
continue;
}
app.UpdateTime();
ProfileManager.Tick();
StorageManager.Tick();
RenderManager.Tick();
ui.tick();
g_NetworkManager.DoWork();
app.HandleXuiActions();
Sleep(10);
}
printf("Stopping server...\n");
fflush(stdout);
app.m_bShutdown = true;
MinecraftServer::HaltServer();
g_NetworkManager.LeaveGame(false);
return 0;
}
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
@ -1566,11 +1355,8 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
const Win64LaunchOptions launchOptions = ParseLaunchOptions();
ApplyScreenMode(launchOptions.screenMode);
// Ensure uid.dat exists from startup in client mode (before any multiplayer/login path).
if (!launchOptions.serverMode)
{
Win64Xuid::ResolvePersistentXuid();
}
// Ensure uid.dat exists from startup (before any multiplayer/login path).
Win64Xuid::ResolvePersistentXuid();
// If no username, let's fall back
if (g_Win64Username[0] == 0)
@ -1651,7 +1437,7 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, launchOptions.serverMode ? SW_HIDE : nCmdShow))
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
@ -1670,13 +1456,6 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
ToggleFullscreen();
}
if (launchOptions.serverMode)
{
const int serverResult = RunHeadlessServer();
CleanupDevice();
return serverResult;
}
#if 0
// Main message loop
MSG msg = {0};

View file

@ -96,9 +96,20 @@ int Entity::getSmallId()
puiUsedFlags++;
}
#ifdef MINECRAFT_SERVER_BUILD
// in mc server dedi, a server with 8+ playerrs can cause this to go wack
int fallbackId = Entity::entityCounter++;
if (entityCounter == 0x7ffffff)
{
entityCounter = 2048;
}
return fallbackId;
#else
app.DebugPrintf("Out of small entity Ids... possible leak?\n");
__debugbreak();
return -1;
#endif
}
void Entity::countFlagsForPIX()

View file

@ -16,7 +16,12 @@ using namespace std;
// 4J Stu - This value should be big enough that we don't get any crashes causes by memory overwrites,
// however it does seem way too large for what is actually needed. Needs further investigation
#ifdef MINECRAFT_SERVER_BUILD
// fixes a crash when 8+ players are present
#define LEVEL_CHUNKS_TO_UPDATE_MAX (32*32*8)
#else
#define LEVEL_CHUNKS_TO_UPDATE_MAX (19*19*8)
#endif
class Vec3;
class ChunkSource;

View file

@ -30,6 +30,65 @@ ensure_persist_file() {
ln -sfn "${persist_path}" "${runtime_path}"
}
wait_for_xvfb_ready() {
local display="$1"
local xvfb_pid="$2"
local wait_seconds="${XVFB_WAIT_SECONDS:-10}"
local wait_ticks=$((wait_seconds * 10))
local display_number="${display#:}"
display_number="${display_number%%.*}"
if [ -z "${display_number}" ] || ! [[ "${display_number}" =~ ^[0-9]+$ ]]; then
echo "[error] Invalid DISPLAY format for Xvfb wait: ${display}" >&2
return 1
fi
local socket_path="/tmp/.X11-unix/X${display_number}"
local elapsed=0
while [ "${elapsed}" -lt "${wait_ticks}" ]; do
if ! kill -0 "${xvfb_pid}" 2>/dev/null; then
echo "[error] Xvfb exited before the display became ready." >&2
if [ -f /tmp/xvfb.log ]; then
echo "[error] ---- /tmp/xvfb.log ----" >&2
tail -n 200 /tmp/xvfb.log >&2 || true
echo "[error] ----------------------" >&2
fi
return 1
fi
if [ -S "${socket_path}" ]; then
# Keep a short extra delay so Wine does not race display handshake.
sleep 0.2
if kill -0 "${xvfb_pid}" 2>/dev/null && [ -S "${socket_path}" ]; then
return 0
fi
fi
# One more liveness check after the ready probe branch.
if ! kill -0 "${xvfb_pid}" 2>/dev/null; then
echo "[error] Xvfb exited during display readiness probe." >&2
if [ -f /tmp/xvfb.log ]; then
echo "[error] ---- /tmp/xvfb.log ----" >&2
tail -n 200 /tmp/xvfb.log >&2 || true
echo "[error] ----------------------" >&2
fi
return 1
fi
sleep 0.1
elapsed=$((elapsed + 1))
done
echo "[error] Timed out waiting for Xvfb display ${display}." >&2
if [ -f /tmp/xvfb.log ]; then
echo "[error] ---- /tmp/xvfb.log ----" >&2
tail -n 200 /tmp/xvfb.log >&2 || true
echo "[error] ----------------------" >&2
fi
return 1
}
if [ ! -d "$SERVER_DIR" ]; then
echo "[error] Server directory not found: $SERVER_DIR" >&2
exit 1
@ -78,8 +137,25 @@ fi
if [ -z "${DISPLAY:-}" ]; then
export DISPLAY="${XVFB_DISPLAY:-:99}"
XVFB_SCREEN="${XVFB_SCREEN:-64x64x16}"
DISPLAY_NUMBER="${DISPLAY#:}"
DISPLAY_NUMBER="${DISPLAY_NUMBER%%.*}"
if [ -z "${DISPLAY_NUMBER}" ] || ! [[ "${DISPLAY_NUMBER}" =~ ^[0-9]+$ ]]; then
echo "[error] Invalid XVFB_DISPLAY format: ${DISPLAY}" >&2
exit 1
fi
XVFB_SOCKET="/tmp/.X11-unix/X${DISPLAY_NUMBER}"
XVFB_LOCK="/tmp/.X${DISPLAY_NUMBER}-lock"
# The check is performed assuming the same container will be restarted.
if [ -S "${XVFB_SOCKET}" ] || [ -e "${XVFB_LOCK}" ]; then
echo "[warn] Removing stale Xvfb state for ${DISPLAY} before startup." >&2
rm -f "${XVFB_SOCKET}" "${XVFB_LOCK}"
fi
Xvfb "${DISPLAY}" -nolisten tcp -screen 0 "${XVFB_SCREEN}" >/tmp/xvfb.log 2>&1 &
sleep 0.2
XVFB_PID=$!
wait_for_xvfb_ready "${DISPLAY}" "${XVFB_PID}"
echo "[info] Xvfb ready on ${DISPLAY} (pid=${XVFB_PID}, screen=${XVFB_SCREEN})"
else
echo "[info] Using existing DISPLAY=${DISPLAY}; skipping Xvfb startup"
fi
args=(