Fix: Ensure host-local socket streams created before any Connection is built

This commit is contained in:
JuiceyDev 2026-03-06 12:10:39 +01:00
parent b589b86f40
commit cdb02fa0af
3 changed files with 46 additions and 8 deletions

View file

@ -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() )
{

View file

@ -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];
}
}

View file

@ -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