forked from cafeberry/cafeberry
feat: add smartcmd dedicated server
major bug: when connecting to the server a second time, it gets softlocked and keeps repeating ticking connection A\n in debug logs until it times out.
This commit is contained in:
@@ -171,6 +171,7 @@ bool CPlatformNetworkManagerStub::Initialise(CGameNetworkManager *pGameNetworkMa
|
||||
|
||||
m_bLeavingGame = false;
|
||||
m_bLeaveGameOnTick = false;
|
||||
m_bLeaveGameOnTick = false;
|
||||
m_bHostChanged = false;
|
||||
|
||||
m_bSearchResultsReady = false;
|
||||
@@ -233,53 +234,56 @@ void CPlatformNetworkManagerStub::DoWork()
|
||||
BYTE disconnectedSmallId;
|
||||
while (NetworkSocketLayer::PopDisconnectedSmallId(&disconnectedSmallId))
|
||||
{
|
||||
if (disconnectedSmallId == 0) continue;
|
||||
if (NetworkSocketLayer::IsSmallIdConnected(disconnectedSmallId)) continue;
|
||||
IQNetPlayer *qnetPlayer = m_pIQNet->GetPlayerBySmallId(disconnectedSmallId);
|
||||
if (qnetPlayer != NULL && qnetPlayer->m_smallId == disconnectedSmallId)
|
||||
IQNetPlayer* qnetPlayer = m_pIQNet->GetPlayerBySmallId(disconnectedSmallId);
|
||||
if (qnetPlayer != nullptr && qnetPlayer->m_smallId == disconnectedSmallId)
|
||||
{
|
||||
NotifyPlayerLeaving(qnetPlayer);
|
||||
qnetPlayer->m_smallId = 0;
|
||||
qnetPlayer->m_isRemote = false;
|
||||
qnetPlayer->m_isHostPlayer = false;
|
||||
//qnetPlayer->m_resolvedXuid = INVALID_XUID; // TODO: implement
|
||||
qnetPlayer->m_gamertag[0] = 0;
|
||||
qnetPlayer->SetCustomDataValue(0);
|
||||
NetworkSocketLayer::PushFreeSmallId(disconnectedSmallId);
|
||||
if (IQNet::s_playerCount > 1)
|
||||
// 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--;
|
||||
}
|
||||
}
|
||||
|
||||
BYTE joinedSmallId;
|
||||
while (NetworkSocketLayer::PopPendingJoinSmallId(&joinedSmallId))
|
||||
{
|
||||
IQNetPlayer *qnetPlayer = m_pIQNet->GetPlayerBySmallId(joinedSmallId);
|
||||
if (qnetPlayer != NULL && qnetPlayer->m_smallId == joinedSmallId)
|
||||
{
|
||||
NotifyPlayerJoined(qnetPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 1; i < MINECRAFT_NET_MAX_PLAYERS; i++)
|
||||
{
|
||||
IQNetPlayer *qp = &IQNet::m_player[i];
|
||||
if (qp->GetCustomDataValue() == 0)
|
||||
continue;
|
||||
INetworkPlayer *np = (INetworkPlayer *)qp->GetCustomDataValue();
|
||||
Socket *sock = np->GetSocket();
|
||||
if (sock != NULL && sock->isClosing())
|
||||
{
|
||||
NetworkSocketLayer::CloseConnectionBySmallId((BYTE)i);
|
||||
}
|
||||
// NOTE: Do NOT call PushFreeSmallId here. The old PlayerConnection's
|
||||
// write thread may still be alive (it dies in PlayerList::tick when
|
||||
// m_smallIdsToClose is processed). If we recycle the smallId now,
|
||||
// AcceptThread can reuse it for a new connection, and the old write
|
||||
// thread's getPlayer() lookup will resolve to the NEW player, sending
|
||||
// stale game packets to the new client's TCP socket — corrupting its
|
||||
// login handshake (bad packet id crash). PushFreeSmallId and
|
||||
// ClearSocketForSmallId are called from PlayerList::tick after the
|
||||
// old Connection threads are dead.
|
||||
//
|
||||
// Clear chunk visibility flags for this system so rejoin gets fresh chunk state.
|
||||
SystemFlagRemoveBySmallId(disconnectedSmallId);
|
||||
}
|
||||
}
|
||||
if (_iQNetStubState == QNET_STATE_GAME_PLAY && !m_pIQNet->IsHost())
|
||||
|
||||
// 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 (!NetworkSocketLayer::IsConnected() && !g_NetworkManager.IsLeavingGame())
|
||||
if (!NetworkSocketLayer::IsConnected())
|
||||
{
|
||||
if (app.GetDisconnectReason() == DisconnectPacket::eDisconnect_None)
|
||||
app.SetDisconnectReason(DisconnectPacket::eDisconnect_Quitting);
|
||||
app.SetAction(ProfileManager.GetPrimaryPad(), eAppAction_ExitWorld, (void *)TRUE);
|
||||
if (!m_bLeaveGameOnTick)
|
||||
{
|
||||
m_bLeaveGameOnTick = true;
|
||||
g_NetworkManager.HandleDisconnect(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bLeaveGameOnTick = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -354,6 +358,7 @@ bool CPlatformNetworkManagerStub::LeaveGame(bool bMigrateHost)
|
||||
if( m_bLeavingGame ) return true;
|
||||
|
||||
m_bLeavingGame = true;
|
||||
m_bLeaveGameOnTick = false;
|
||||
NetworkSocketLayer::StopAdvertising();
|
||||
|
||||
// If we are the host wait for the game server to end
|
||||
@@ -397,6 +402,7 @@ void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame,
|
||||
localUsersMask |= GetLocalPlayerMask( g_NetworkManager.GetPrimaryPad() );
|
||||
|
||||
m_bLeavingGame = false;
|
||||
m_bLeaveGameOnTick = false;
|
||||
|
||||
m_pIQNet->HostGame();
|
||||
|
||||
@@ -418,9 +424,25 @@ void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame,
|
||||
if (!NetworkSocketLayer::IsActive())
|
||||
NetworkSocketLayer::HostGame(port);
|
||||
|
||||
const wchar_t *hostName = IQNet::m_player[0].m_gamertag;
|
||||
unsigned int settings = app.GetGameHostOption(eGameHostOption_All);
|
||||
NetworkSocketLayer::StartAdvertising(port, hostName, settings, 0, 0, MINECRAFT_NET_VERSION);
|
||||
if (NetworkSocketLayer::IsActive())
|
||||
{
|
||||
// For Dedicated Server, refer to `lan-advertise` in `server.properties`
|
||||
bool enableLanAdvertising = true;
|
||||
#ifdef MINECRAFT_SERVER_BUILD
|
||||
enableLanAdvertising = g_DedicatedServerLanAdvertise;
|
||||
#endif
|
||||
|
||||
if (enableLanAdvertising)
|
||||
{
|
||||
const wchar_t* hostName = IQNet::m_player[0].m_gamertag;
|
||||
unsigned int settings = app.GetGameHostOption(eGameHostOption_All);
|
||||
NetworkSocketLayer::StartAdvertising(port, hostName, settings, 0, 0, MINECRAFT_NET_VERSION);
|
||||
}
|
||||
else
|
||||
{
|
||||
NetworkSocketLayer::StopAdvertising();
|
||||
}
|
||||
}
|
||||
//#endif
|
||||
}
|
||||
|
||||
@@ -626,6 +648,7 @@ CPlatformNetworkManagerStub::PlayerFlags::PlayerFlags(INetworkPlayer *pNetworkPl
|
||||
this->flags = new unsigned char [ count / 8 ];
|
||||
memset( this->flags, 0, count / 8 );
|
||||
this->count = count;
|
||||
this->m_smallId = (pNetworkPlayer && pNetworkPlayer->IsLocal()) ? 256 : (pNetworkPlayer ? (int)pNetworkPlayer->GetSmallId() : -1);
|
||||
}
|
||||
CPlatformNetworkManagerStub::PlayerFlags::~PlayerFlags()
|
||||
{
|
||||
@@ -663,6 +686,23 @@ void CPlatformNetworkManagerStub::SystemFlagRemovePlayer(INetworkPlayer *pNetwor
|
||||
}
|
||||
}
|
||||
|
||||
// Clear chunk flags for a system when they disconnect (by smallId). Call even when we don't find the player,
|
||||
// so we always clear and the smallId can be reused without stale "chunk seen" state.
|
||||
void CPlatformNetworkManagerStub::SystemFlagRemoveBySmallId(int smallId)
|
||||
{
|
||||
if (smallId < 0) return;
|
||||
for (unsigned int i = 0; i < m_playerFlags.size(); i++)
|
||||
{
|
||||
if (m_playerFlags[i]->m_smallId == smallId)
|
||||
{
|
||||
delete m_playerFlags[i];
|
||||
m_playerFlags[i] = m_playerFlags.back();
|
||||
m_playerFlags.pop_back();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerStub::SystemFlagReset()
|
||||
{
|
||||
for( unsigned int i = 0; i < m_playerFlags.size(); i++ )
|
||||
|
||||
Reference in New Issue
Block a user