diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index 9440fa8e..bfc3df23 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -1300,7 +1300,7 @@ inline bool IsleApp::Tick() } #ifdef EXTENSIONS - if (Extensions::IsMultiplayerRejected()) { + if (Extensions::IsMultiplayerDisconnected()) { g_closed = TRUE; return true; } diff --git a/extensions/include/extensions/multiplayer.h b/extensions/include/extensions/multiplayer.h index 0d3078f5..cd087c3f 100644 --- a/extensions/include/extensions/multiplayer.h +++ b/extensions/include/extensions/multiplayer.h @@ -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 diff --git a/extensions/include/extensions/multiplayer/networkmanager.h b/extensions/include/extensions/multiplayer/networkmanager.h index de82f72e..cab005be 100644 --- a/extensions/include/extensions/multiplayer/networkmanager.h +++ b/extensions/include/extensions/multiplayer/networkmanager.h @@ -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 diff --git a/extensions/include/extensions/multiplayer/networktransport.h b/extensions/include/extensions/multiplayer/networktransport.h index 5fd05db6..ea033377 100644 --- a/extensions/include/extensions/multiplayer/networktransport.h +++ b/extensions/include/extensions/multiplayer/networktransport.h @@ -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; diff --git a/extensions/include/extensions/multiplayer/platforms/emscripten/websockettransport.h b/extensions/include/extensions/multiplayer/platforms/emscripten/websockettransport.h index bd895922..533447d2 100644 --- a/extensions/include/extensions/multiplayer/platforms/emscripten/websockettransport.h +++ b/extensions/include/extensions/multiplayer/platforms/emscripten/websockettransport.h @@ -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 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]; }; diff --git a/extensions/src/multiplayer.cpp b/extensions/src/multiplayer.cpp index 18c49ec0..ae537381 100644 --- a/extensions/src/multiplayer.cpp +++ b/extensions/src/multiplayer.cpp @@ -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::Call(MP::CheckRejected).value_or(FALSE); + return Extension::Call(MP::CheckDisconnected).value_or(FALSE); } diff --git a/extensions/src/multiplayer/networkmanager.cpp b/extensions/src/multiplayer/networkmanager.cpp index bf9e45ad..d806cf0a 100644 --- a/extensions/src/multiplayer/networkmanager.cpp +++ b/extensions/src/multiplayer/networkmanager.cpp @@ -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) diff --git a/extensions/src/multiplayer/platforms/emscripten/websockettransport.cpp b/extensions/src/multiplayer/platforms/emscripten/websockettransport.cpp index 318dc5ed..fdc14c44 100644 --- a/extensions/src/multiplayer/platforms/emscripten/websockettransport.cpp +++ b/extensions/src/multiplayer/platforms/emscripten/websockettransport.cpp @@ -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)