Merge upstream/main into main

Brings in dedicated server software, README updates, and .vscode cleanup
while preserving hardcore mode, screenshot, and watermark toggle features.
This commit is contained in:
Revela
2026-03-15 03:54:37 -05:00
112 changed files with 39006 additions and 185 deletions
+23 -13
View File
@@ -1,21 +1,32 @@
#include "stdafx.h"
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
#include "..\..\Minecraft.Server\ServerLogManager.h"
#endif
//--------------------------------------------------------------------------------------
// Name: DebugSpewV()
// Desc: Internal helper function
//--------------------------------------------------------------------------------------
#ifndef _CONTENT_PACKAGE
static VOID DebugSpewV( const CHAR* strFormat, const va_list pArgList )
static VOID DebugSpewV( const CHAR* strFormat, va_list pArgList )
{
#if defined __PS3__ || defined __ORBIS__ || defined __PSVITA__
assert(0);
assert(0);
#else
CHAR str[2048];
// Use the secure CRT to avoid buffer overruns. Specify a count of
// _TRUNCATE so that too long strings will be silently truncated
// rather than triggering an error.
_vsnprintf_s( str, _TRUNCATE, strFormat, pArgList );
OutputDebugStringA( str );
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
// Dedicated server routes legacy debug spew through ServerLogger to preserve CLI prompt handling.
if (ServerRuntime::ServerLogManager::ShouldForwardClientDebugLogs())
{
ServerRuntime::ServerLogManager::ForwardClientDebugSpewLogV(strFormat, pArgList);
return;
}
#endif
CHAR str[2048];
// Use the secure CRT to avoid buffer overruns. Specify a count of
// _TRUNCATE so that too long strings will be silently truncated
// rather than triggering an error.
_vsnprintf_s( str, _TRUNCATE, strFormat, pArgList );
OutputDebugStringA( str );
#endif
}
#endif
@@ -31,10 +42,9 @@ VOID CDECL DebugPrintf( const CHAR* strFormat, ... )
#endif
{
#ifndef _CONTENT_PACKAGE
va_list pArgList;
va_start( pArgList, strFormat );
DebugSpewV( strFormat, pArgList );
va_end( pArgList );
va_list pArgList;
va_start( pArgList, strFormat );
DebugSpewV( strFormat, pArgList );
va_end( pArgList );
#endif
}
+69 -48
View File
@@ -38,6 +38,9 @@
#include "GameRules\ConsoleSchematicFile.h"
#include "..\User.h"
#include "..\..\Minecraft.World\LevelData.h"
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
#include "..\..\Minecraft.Server\ServerLogManager.h"
#endif
#include "..\..\Minecraft.World\net.minecraft.world.entity.player.h"
#include "..\EntityRenderDispatcher.h"
#include "..\..\Minecraft.World\compression.h"
@@ -240,12 +243,21 @@ void CMinecraftApp::DebugPrintf(const char *szFormat, ...)
{
#ifndef _FINAL_BUILD
char buf[1024];
va_list ap;
va_start(ap, szFormat);
vsnprintf(buf, sizeof(buf), szFormat, ap);
va_end(ap);
OutputDebugStringA(buf);
va_list ap;
va_start(ap, szFormat);
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
// Dedicated server routes client debug spew through ServerLogger so CLI output stays prompt-safe.
if (ServerRuntime::ServerLogManager::ShouldForwardClientDebugLogs())
{
ServerRuntime::ServerLogManager::ForwardClientAppDebugLogV(szFormat, ap);
va_end(ap);
return;
}
#endif
char buf[1024];
vsnprintf(buf, sizeof(buf), szFormat, ap);
va_end(ap);
OutputDebugStringA(buf);
#endif
}
@@ -253,53 +265,62 @@ void CMinecraftApp::DebugPrintf(const char *szFormat, ...)
void CMinecraftApp::DebugPrintf(int user, const char *szFormat, ...)
{
#ifndef _FINAL_BUILD
if(user == USER_NONE)
return;
char buf[1024];
va_list ap;
va_start(ap, szFormat);
vsnprintf(buf, sizeof(buf), szFormat, ap);
va_end(ap);
if(user == USER_NONE)
return;
va_list ap;
va_start(ap, szFormat);
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
// Dedicated server routes client debug spew through ServerLogger so CLI output stays prompt-safe.
if (ServerRuntime::ServerLogManager::ShouldForwardClientDebugLogs())
{
ServerRuntime::ServerLogManager::ForwardClientUserDebugLogV(user, szFormat, ap);
va_end(ap);
return;
}
#endif
char buf[1024];
vsnprintf(buf, sizeof(buf), szFormat, ap);
va_end(ap);
#ifdef __PS3__
unsigned int writelen;
sys_tty_write(SYS_TTYP_USER1 + ( user - 1 ), buf, strlen(buf), &writelen );
unsigned int writelen;
sys_tty_write(SYS_TTYP_USER1 + ( user - 1 ), buf, strlen(buf), &writelen );
#elif defined __PSVITA__
switch(user)
{
case 0:
{
SceUID tty2 = sceIoOpen("tty2:", SCE_O_WRONLY, 0);
if(tty2>=0)
{
std::string string1(buf);
sceIoWrite(tty2, string1.c_str(), string1.length());
sceIoClose(tty2);
}
}
break;
case 1:
{
SceUID tty3 = sceIoOpen("tty3:", SCE_O_WRONLY, 0);
if(tty3>=0)
{
std::string string1(buf);
sceIoWrite(tty3, string1.c_str(), string1.length());
sceIoClose(tty3);
}
}
break;
default:
OutputDebugStringA(buf);
break;
}
switch(user)
{
case 0:
{
SceUID tty2 = sceIoOpen("tty2:", SCE_O_WRONLY, 0);
if(tty2>=0)
{
std::string string1(buf);
sceIoWrite(tty2, string1.c_str(), string1.length());
sceIoClose(tty2);
}
}
break;
case 1:
{
SceUID tty3 = sceIoOpen("tty3:", SCE_O_WRONLY, 0);
if(tty3>=0)
{
std::string string1(buf);
sceIoWrite(tty3, string1.c_str(), string1.length());
sceIoClose(tty3);
}
}
break;
default:
OutputDebugStringA(buf);
break;
}
#else
OutputDebugStringA(buf);
OutputDebugStringA(buf);
#endif
#ifndef _XBOX
if(user == USER_UI)
{
ui.logDebugString(buf);
}
if(user == USER_UI)
{
ui.logDebugString(buf);
}
#endif
#endif
}
@@ -200,10 +200,12 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
#endif
int64_t seed = 0;
bool dedicatedNoLocalHostPlayer = false;
if (lpParameter != nullptr)
{
NetworkGameInitData *param = static_cast<NetworkGameInitData *>(lpParameter);
seed = param->seed;
dedicatedNoLocalHostPlayer = param->dedicatedNoLocalHostPlayer;
app.setLevelGenerationOptions(param->levelGen);
if(param->levelGen != nullptr)
@@ -359,9 +361,19 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
// PRIMARY PLAYER
vector<ClientConnection *> createdConnections;
ClientConnection *connection;
ClientConnection *connection = nullptr;
if( g_NetworkManager.IsHost() )
if( g_NetworkManager.IsHost() && dedicatedNoLocalHostPlayer )
{
app.DebugPrintf("Dedicated server mode: skipping local host client connection\n");
// Keep telemetry behavior consistent with the host path.
INT multiplayerInstanceId = TelemetryManager->GenerateMultiplayerInstanceId();
TelemetryManager->SetMultiplayerInstanceId(multiplayerInstanceId);
app.SetGameMode( eMode_Multiplayer );
}
else if( g_NetworkManager.IsHost() )
{
connection = new ClientConnection(minecraft, nullptr);
}
@@ -390,16 +402,18 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
connection = new ClientConnection(minecraft, socket);
}
if( !connection->createdOk )
if (connection != nullptr)
{
assert(false);
delete connection;
connection = nullptr;
MinecraftServer::HaltServer();
return false;
}
if( !connection->createdOk )
{
assert(false);
delete connection;
connection = nullptr;
MinecraftServer::HaltServer();
return false;
}
connection->send(std::make_shared<PreLoginPacket>(minecraft->user->name));
connection->send(std::make_shared<PreLoginPacket>(minecraft->user->name));
// Tick connection until we're ready to go. The stages involved in this are:
// (1) Creating the ClientConnection sends a prelogin packet to the server
@@ -434,9 +448,9 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
connection->close();
}
if( connection->isStarted() && !connection->isClosed() )
{
createdConnections.push_back( connection );
if( connection->isStarted() && !connection->isClosed() )
{
createdConnections.push_back( connection );
int primaryPad = ProfileManager.GetPrimaryPad();
app.SetRichPresenceContext(primaryPad,CONTEXT_GAME_STATE_BLANK);
@@ -533,13 +547,14 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
}
}
app.SetGameMode( eMode_Multiplayer );
}
else if ( connection->isClosed() || !IsInSession())
{
app.SetGameMode( eMode_Multiplayer );
}
else if ( connection->isClosed() || !IsInSession())
{
// assert(false);
MinecraftServer::HaltServer();
return false;
MinecraftServer::HaltServer();
return false;
}
}
@@ -240,7 +240,13 @@ void CPlatformNetworkManagerStub::DoWork()
qnetPlayer->m_resolvedXuid = INVALID_XUID;
qnetPlayer->m_gamertag[0] = 0;
qnetPlayer->SetCustomDataValue(0);
while (IQNet::s_playerCount > 1 && IQNet::m_player[IQNet::s_playerCount - 1].GetCustomDataValue() == 0)
// Recalculate s_playerCount as the highest active slot + 1.
// A blind decrement would hide players at higher-indexed slots when a
// lower-indexed player disconnects first: GetPlayerBySmallId scans
// [0, s_playerCount) so any slot at or above the decremented count
// becomes invisible, causing its disconnect to be missed (ghost player).
while (IQNet::s_playerCount > 1 &&
IQNet::m_player[IQNet::s_playerCount - 1].GetCustomDataValue() == 0)
IQNet::s_playerCount--;
}
// NOTE: Do NOT call PushFreeSmallId here. The old PlayerConnection's
@@ -257,6 +263,25 @@ void CPlatformNetworkManagerStub::DoWork()
SystemFlagRemoveBySmallId(disconnectedSmallId);
}
}
// Client-side host disconnect detection:
// if TCP is gone, propagate through normal network-disconnect flow so UI returns to menus.
// The processing from the Xbox version will be reused.
if (_iQNetStubState == QNET_STATE_GAME_PLAY && !m_pIQNet->IsHost() && !m_bLeavingGame)
{
if (!WinsockNetLayer::IsConnected())
{
if (!m_bLeaveGameOnTick)
{
m_bLeaveGameOnTick = true;
g_NetworkManager.HandleDisconnect(false);
}
}
else
{
m_bLeaveGameOnTick = false;
}
}
#endif
}
@@ -356,6 +381,7 @@ bool CPlatformNetworkManagerStub::LeaveGame(bool bMigrateHost)
if( m_bLeavingGame ) return true;
m_bLeavingGame = true;
m_bLeaveGameOnTick = false;
#ifdef _WINDOWS64
WinsockNetLayer::StopAdvertising();
@@ -404,6 +430,7 @@ void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame,
localUsersMask |= GetLocalPlayerMask( g_NetworkManager.GetPrimaryPad() );
m_bLeavingGame = false;
m_bLeaveGameOnTick = false;
m_pIQNet->HostGame();
@@ -433,9 +460,23 @@ void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame,
if (WinsockNetLayer::IsActive())
{
const wchar_t* hostName = IQNet::m_player[0].m_gamertag;
unsigned int settings = app.GetGameHostOption(eGameHostOption_All);
WinsockNetLayer::StartAdvertising(port, hostName, settings, 0, 0, MINECRAFT_NET_VERSION);
// For Dedicated Server, refer to `lan-advertise` in `server.properties`
bool enableLanAdvertising = true;
if (g_Win64DedicatedServer)
{
enableLanAdvertising = g_Win64DedicatedServerLanAdvertise;
}
if (enableLanAdvertising)
{
const wchar_t* hostName = IQNet::m_player[0].m_gamertag;
unsigned int settings = app.GetGameHostOption(eGameHostOption_All);
WinsockNetLayer::StartAdvertising(port, hostName, settings, 0, 0, MINECRAFT_NET_VERSION);
}
else
{
WinsockNetLayer::StopAdvertising();
}
}
#endif
//#endif
@@ -463,6 +504,7 @@ int CPlatformNetworkManagerStub::JoinGame(FriendSessionInfo* searchResult, int l
return CGameNetworkManager::JOINGAME_FAIL_GENERAL;
m_bLeavingGame = false;
m_bLeaveGameOnTick = false;
IQNet::s_isHosting = false;
m_pIQNet->ClientJoinGame();