mirror of
https://github.com/isledecomp/isle-portable.git
synced 2026-05-01 18:13:57 +00:00
Distinguish "room full" from "connection lost" in WebSocket error handling
Fix race condition where onerror clearing m_connectedFlag before onclose caused any network drop to be misidentified as a room-full rejection. Add m_wasEverConnected flag set once in onopen, use it in onclose to assign exit code 10 (room full) vs 11 (connection lost). Rename rejected API surface to disconnected to reflect the generalized meaning. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
74271aa189
commit
680c7c28fe
@ -1300,7 +1300,7 @@ inline bool IsleApp::Tick()
|
||||
}
|
||||
|
||||
#ifdef EXTENSIONS
|
||||
if (Extensions::IsMultiplayerRejected()) {
|
||||
if (Extensions::IsMultiplayerDisconnected()) {
|
||||
g_closed = TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ class MultiplayerExt {
|
||||
static MxBool IsClonedCharacter(const char* p_name);
|
||||
static void HandleBeforeSaveLoad();
|
||||
static void HandleSaveLoaded();
|
||||
static MxBool CheckRejected();
|
||||
static MxBool CheckDisconnected();
|
||||
|
||||
static Multiplayer::NetworkManager* GetNetworkManager();
|
||||
|
||||
@ -58,7 +58,7 @@ class MultiplayerExt {
|
||||
};
|
||||
|
||||
#ifdef EXTENSIONS
|
||||
LEGO1_EXPORT bool IsMultiplayerRejected();
|
||||
LEGO1_EXPORT bool IsMultiplayerDisconnected();
|
||||
#endif
|
||||
|
||||
namespace MP
|
||||
@ -72,7 +72,7 @@ constexpr auto HandleROIClick = &MultiplayerExt::HandleROIClick;
|
||||
constexpr auto IsClonedCharacter = &MultiplayerExt::IsClonedCharacter;
|
||||
constexpr auto HandleBeforeSaveLoad = &MultiplayerExt::HandleBeforeSaveLoad;
|
||||
constexpr auto HandleSaveLoaded = &MultiplayerExt::HandleSaveLoaded;
|
||||
constexpr auto CheckRejected = &MultiplayerExt::CheckRejected;
|
||||
constexpr auto CheckDisconnected = &MultiplayerExt::CheckDisconnected;
|
||||
#else
|
||||
constexpr decltype(&MultiplayerExt::HandleCreate) HandleCreate = nullptr;
|
||||
constexpr decltype(&MultiplayerExt::HandleWorldEnable) HandleWorldEnable = nullptr;
|
||||
@ -82,7 +82,7 @@ constexpr decltype(&MultiplayerExt::HandleROIClick) HandleROIClick = nullptr;
|
||||
constexpr decltype(&MultiplayerExt::IsClonedCharacter) IsClonedCharacter = nullptr;
|
||||
constexpr decltype(&MultiplayerExt::HandleBeforeSaveLoad) HandleBeforeSaveLoad = nullptr;
|
||||
constexpr decltype(&MultiplayerExt::HandleSaveLoaded) HandleSaveLoaded = nullptr;
|
||||
constexpr decltype(&MultiplayerExt::CheckRejected) CheckRejected = nullptr;
|
||||
constexpr decltype(&MultiplayerExt::CheckDisconnected) CheckDisconnected = nullptr;
|
||||
#endif
|
||||
} // namespace MP
|
||||
|
||||
|
||||
@ -49,7 +49,7 @@ class NetworkManager : public MxCore {
|
||||
void Connect(const char* p_roomId);
|
||||
void Disconnect();
|
||||
bool IsConnected() const;
|
||||
bool WasRejected() const;
|
||||
bool WasDisconnected() const;
|
||||
|
||||
void SetWalkAnimation(uint8_t p_walkAnimId);
|
||||
void SetIdleAnimation(uint8_t p_idleAnimId);
|
||||
@ -151,6 +151,7 @@ class NetworkManager : public MxCore {
|
||||
static const uint32_t BROADCAST_INTERVAL_MS = 66; // ~15Hz
|
||||
static const uint32_t TIMEOUT_MS = 5000; // 5 second timeout
|
||||
static const int EXIT_ROOM_FULL = 10;
|
||||
static const int EXIT_CONNECTION_LOST = 11;
|
||||
};
|
||||
|
||||
} // namespace Multiplayer
|
||||
|
||||
@ -14,7 +14,7 @@ class NetworkTransport {
|
||||
virtual void Connect(const char* p_roomId) = 0;
|
||||
virtual void Disconnect() = 0;
|
||||
virtual bool IsConnected() const = 0;
|
||||
virtual bool WasRejected() const = 0;
|
||||
virtual bool WasDisconnected() const = 0;
|
||||
|
||||
// Send binary data to all peers via relay
|
||||
virtual void Send(const uint8_t* p_data, size_t p_length) = 0;
|
||||
|
||||
@ -17,15 +17,16 @@ class WebSocketTransport : public NetworkTransport {
|
||||
void Connect(const char* p_roomId) override;
|
||||
void Disconnect() override;
|
||||
bool IsConnected() const override;
|
||||
bool WasRejected() const override;
|
||||
bool WasDisconnected() const override;
|
||||
void Send(const uint8_t* p_data, size_t p_length) override;
|
||||
size_t Receive(std::function<void(const uint8_t*, size_t)> p_callback) override;
|
||||
|
||||
private:
|
||||
std::string m_relayBaseUrl;
|
||||
int m_socketId;
|
||||
volatile int32_t m_connectedFlag; // Shared with JS main thread via Atomics
|
||||
volatile int32_t m_rejectedFlag; // Set by JS when connection is rejected (e.g. room full)
|
||||
volatile int32_t m_connectedFlag; // Shared with JS main thread via Atomics
|
||||
volatile int32_t m_disconnectedFlag; // Set by JS when connection closes (room full or lost)
|
||||
volatile int32_t m_wasEverConnected; // Set once in onopen, never cleared by error/close
|
||||
uint8_t m_recvBuf[8192];
|
||||
};
|
||||
|
||||
|
||||
@ -243,9 +243,9 @@ MxBool MultiplayerExt::IsClonedCharacter(const char* p_name)
|
||||
return s_networkManager->IsClonedCharacter(p_name) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
MxBool MultiplayerExt::CheckRejected()
|
||||
MxBool MultiplayerExt::CheckDisconnected()
|
||||
{
|
||||
if (s_networkManager && s_networkManager->WasRejected()) {
|
||||
if (s_networkManager && s_networkManager->WasDisconnected()) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -257,7 +257,7 @@ Multiplayer::NetworkManager* MultiplayerExt::GetNetworkManager()
|
||||
return s_networkManager;
|
||||
}
|
||||
|
||||
bool Extensions::IsMultiplayerRejected()
|
||||
bool Extensions::IsMultiplayerDisconnected()
|
||||
{
|
||||
return Extension<MultiplayerExt>::Call(MP::CheckRejected).value_or(FALSE);
|
||||
return Extension<MultiplayerExt>::Call(MP::CheckDisconnected).value_or(FALSE);
|
||||
}
|
||||
|
||||
@ -185,9 +185,9 @@ bool NetworkManager::IsConnected() const
|
||||
return m_transport && m_transport->IsConnected();
|
||||
}
|
||||
|
||||
bool NetworkManager::WasRejected() const
|
||||
bool NetworkManager::WasDisconnected() const
|
||||
{
|
||||
return m_transport && m_transport->WasRejected();
|
||||
return m_transport && m_transport->WasDisconnected();
|
||||
}
|
||||
|
||||
void NetworkManager::OnWorldEnabled(LegoWorld* p_world)
|
||||
|
||||
@ -9,7 +9,7 @@ namespace Multiplayer
|
||||
{
|
||||
|
||||
WebSocketTransport::WebSocketTransport(const std::string& p_relayBaseUrl)
|
||||
: m_relayBaseUrl(p_relayBaseUrl), m_socketId(-1), m_connectedFlag(0), m_rejectedFlag(0)
|
||||
: m_relayBaseUrl(p_relayBaseUrl), m_socketId(-1), m_connectedFlag(0), m_disconnectedFlag(0), m_wasEverConnected(0)
|
||||
{
|
||||
// clang-format off
|
||||
MAIN_THREAD_EM_ASM({
|
||||
@ -35,13 +35,15 @@ void WebSocketTransport::Connect(const char* p_roomId)
|
||||
|
||||
std::string url = m_relayBaseUrl + "/room/" + p_roomId;
|
||||
|
||||
m_rejectedFlag = 0;
|
||||
m_disconnectedFlag = 0;
|
||||
m_wasEverConnected = 0;
|
||||
|
||||
// clang-format off
|
||||
m_socketId = MAIN_THREAD_EM_ASM_INT({
|
||||
var url = UTF8ToString($0);
|
||||
var connPtr = $1;
|
||||
var rejPtr = $2;
|
||||
var discPtr = $2;
|
||||
var everConnPtr = $3;
|
||||
var socketId = Module._mpNextSocketId++;
|
||||
Module._mpMessageQueues[socketId] = [];
|
||||
|
||||
@ -51,6 +53,7 @@ void WebSocketTransport::Connect(const char* p_roomId)
|
||||
|
||||
ws.onopen = function() {
|
||||
Atomics.store(HEAP32, connPtr >> 2, 1);
|
||||
Atomics.store(HEAP32, everConnPtr >> 2, 1);
|
||||
};
|
||||
|
||||
ws.onmessage = function(event) {
|
||||
@ -61,13 +64,12 @@ void WebSocketTransport::Connect(const char* p_roomId)
|
||||
};
|
||||
|
||||
ws.onclose = function() {
|
||||
var wasConnected = Atomics.load(HEAP32, connPtr >> 2);
|
||||
var wasEverConnected = Atomics.load(HEAP32, everConnPtr >> 2);
|
||||
Atomics.store(HEAP32, connPtr >> 2, 0);
|
||||
if (!wasConnected) {
|
||||
// Never connected — server rejected (room full / 503)
|
||||
Atomics.store(HEAP32, rejPtr >> 2, 1);
|
||||
Module._exitCode = 10;
|
||||
}
|
||||
Atomics.store(HEAP32, discPtr >> 2, 1);
|
||||
// 10 = room full / server rejected before connecting
|
||||
// 11 = connection lost after successful connect
|
||||
Module._exitCode = wasEverConnected ? 11 : 10;
|
||||
};
|
||||
|
||||
ws.onerror = function() {
|
||||
@ -81,7 +83,7 @@ void WebSocketTransport::Connect(const char* p_roomId)
|
||||
}
|
||||
|
||||
return socketId;
|
||||
}, url.c_str(), &m_connectedFlag, &m_rejectedFlag);
|
||||
}, url.c_str(), &m_connectedFlag, &m_disconnectedFlag, &m_wasEverConnected);
|
||||
// clang-format on
|
||||
|
||||
if (m_socketId <= 0) {
|
||||
@ -105,6 +107,7 @@ void WebSocketTransport::Disconnect()
|
||||
|
||||
m_socketId = -1;
|
||||
m_connectedFlag = 0;
|
||||
m_wasEverConnected = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,9 +116,9 @@ bool WebSocketTransport::IsConnected() const
|
||||
return m_socketId > 0 && m_connectedFlag != 0;
|
||||
}
|
||||
|
||||
bool WebSocketTransport::WasRejected() const
|
||||
bool WebSocketTransport::WasDisconnected() const
|
||||
{
|
||||
return m_rejectedFlag != 0;
|
||||
return m_disconnectedFlag != 0;
|
||||
}
|
||||
|
||||
void WebSocketTransport::Send(const uint8_t* p_data, size_t p_length)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user