From cdb02fa0af08e25a1630a657e60390e10a665094 Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Fri, 6 Mar 2026 12:10:39 +0100 Subject: [PATCH] Fix: Ensure host-local socket streams created before any Connection is built --- .../Common/Network/GameNetworkManager.cpp | 11 +++++ Minecraft.World/Network/Socket.cpp | 42 +++++++++++++++---- Minecraft.World/Network/Socket.h | 1 + 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/Minecraft.Client/Build/Common/Network/GameNetworkManager.cpp b/Minecraft.Client/Build/Common/Network/GameNetworkManager.cpp index cc977f6f4..1aa62bb41 100644 --- a/Minecraft.Client/Build/Common/Network/GameNetworkManager.cpp +++ b/Minecraft.Client/Build/Common/Network/GameNetworkManager.cpp @@ -212,6 +212,17 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame } static __int64 sseed = seed; // Create static version so this will be valid until next call to this function & whilst thread is running + + // 4J Fix: Ensure host-local socket streams (s_hostInStream / s_hostOutStream) are always + // created before any Socket or Connection objects are allocated. Socket::Initialise normally + // runs on the server thread, but on certain builds the main thread can reach ClientConnection + // creation before that thread has a chance to execute, leaving the stream arrays NULL. + // Calling Initialise(NULL) here (with the server connection unknown yet) will create the + // stream objects on the very first call; all subsequent calls only reset the queue state. + // The server thread's later Initialise(connection) call will then correctly update + // s_serverConnection without touching the already-allocated stream objects. + Socket::Initialise(NULL); + ServerStoppedCreate(false); if( g_NetworkManager.IsHost() ) { diff --git a/Minecraft.World/Network/Socket.cpp b/Minecraft.World/Network/Socket.cpp index 801c6a61d..c6c16b824 100644 --- a/Minecraft.World/Network/Socket.cpp +++ b/Minecraft.World/Network/Socket.cpp @@ -16,14 +16,34 @@ Socket::SocketOutputStreamLocal *Socket::s_hostOutStream[2]; Socket::SocketInputStreamLocal *Socket::s_hostInStream[2]; ServerConnection *Socket::s_serverConnection = NULL; +void Socket::EnsureStreamsInitialised() +{ + // Thread-safe one-time initialisation via C++11 magic-statics guarantee. + // The lambda body runs exactly once no matter how many threads call concurrently. + static bool initialized = []() -> bool { + for( int i = 0; i < 2; i++ ) + { + InitializeCriticalSection(&Socket::s_hostQueueLock[i]); + s_hostOutStream[i] = new SocketOutputStreamLocal(i); + s_hostInStream[i] = new SocketInputStreamLocal(i); + } + return true; + }(); + (void)initialized; +} + void Socket::Initialise(ServerConnection *serverConnection) { s_serverConnection = serverConnection; + // Ensure the host-local stream objects exist (idempotent). + EnsureStreamsInitialised(); + // Only initialise everything else once - just setting up static data, one time xrnm things, thread for ticking sockets static bool init = false; if( init ) { + // Streams already exist – just reset queue state and re-open streams. for( int i = 0; i < 2; i++ ) { if(TryEnterCriticalSection(&s_hostQueueLock[i])) @@ -39,13 +59,8 @@ void Socket::Initialise(ServerConnection *serverConnection) return; } init = true; - - for( int i = 0; i < 2; i++ ) - { - InitializeCriticalSection(&Socket::s_hostQueueLock[i]); - s_hostOutStream[i] = new SocketOutputStreamLocal(i); - s_hostInStream[i] = new SocketInputStreamLocal(i); - } + // Streams are already guaranteed to exist via EnsureStreamsInitialised() above. + // Nothing more to do for the first call. } Socket::Socket(bool response) @@ -182,6 +197,11 @@ InputStream *Socket::getInputStream(bool isServerConnection) } else { + if( s_hostInStream[m_end] == NULL ) + { + app.DebugPrintf("SOCKET: Warning - s_hostInStream[%d] is NULL in getInputStream(); calling EnsureStreamsInitialised()\n", m_end); + EnsureStreamsInitialised(); + } return s_hostInStream[m_end]; } } @@ -216,7 +236,13 @@ Socket::SocketOutputStream *Socket::getOutputStream(bool isServerConnection) } else { - return s_hostOutStream[ 1 - m_end ]; + int outIdx = 1 - m_end; + if( s_hostOutStream[outIdx] == NULL ) + { + app.DebugPrintf("SOCKET: Warning - s_hostOutStream[%d] is NULL in getOutputStream(); calling EnsureStreamsInitialised()\n", outIdx); + EnsureStreamsInitialised(); + } + return s_hostOutStream[outIdx]; } } diff --git a/Minecraft.World/Network/Socket.h b/Minecraft.World/Network/Socket.h index b30e83962..a685fd835 100644 --- a/Minecraft.World/Network/Socket.h +++ b/Minecraft.World/Network/Socket.h @@ -117,6 +117,7 @@ public: void setPlayer(INetworkPlayer *player); public: + static void EnsureStreamsInitialised(); // 4J Fix: idempotent stream creation; safe to call before Initialise(connection) static void Initialise(ServerConnection *serverConnection); Socket(bool response = false); // 4J - Create a local socket, for end 0 or 1 of a connection Socket(INetworkPlayer *player, bool response = false, bool hostLocal = false); // 4J - Create a socket for an INetworkPlayer