mirror of
https://github.com/isledecomp/isle-portable.git
synced 2026-05-02 02:23:56 +00:00
Add WebSocket reconnection with exponential backoff
Automatically reconnect when the WebSocket connection is lost (e.g. phone sleep, alt-tab, network blip) instead of exiting the game. - Add reconnection state machine (CONNECTED → RECONNECTING → CONNECTED or DISCONNECTED) with exponential backoff (1s→30s cap, 10 max attempts) - Add OnConnectionStatusChanged callback (connected/reconnecting/failed/ rejected) to PlatformCallbacks, with Emscripten CustomEvent dispatch and native SDL_Log implementations - Add WorldStateSync::ResetForReconnect() to clear session state - Only exit the game on room-full rejection (WasRejected); connection loss is handled internally by the state machine - Rename WasDisconnected→WasRejected, CheckDisconnected→CheckRejected, IsMultiplayerDisconnected→IsMultiplayerRejected through the full call chain for accurate naming - Remove Module._exitCode from JS onclose; use C++ callback + sessionStorage for room-full signaling instead - Clean up EXIT_CONNECTION_LOST constant (obsolete) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
176aef1d90
commit
982957ee5e
@ -1300,7 +1300,7 @@ inline bool IsleApp::Tick()
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef EXTENSIONS
|
#ifdef EXTENSIONS
|
||||||
if (Extensions::IsMultiplayerDisconnected()) {
|
if (Extensions::IsMultiplayerRejected()) {
|
||||||
g_closed = TRUE;
|
g_closed = TRUE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,7 +45,7 @@ class MultiplayerExt {
|
|||||||
static MxBool IsClonedCharacter(const char* p_name);
|
static MxBool IsClonedCharacter(const char* p_name);
|
||||||
static void HandleBeforeSaveLoad();
|
static void HandleBeforeSaveLoad();
|
||||||
static void HandleSaveLoaded();
|
static void HandleSaveLoaded();
|
||||||
static MxBool CheckDisconnected();
|
static MxBool CheckRejected();
|
||||||
|
|
||||||
static Multiplayer::NetworkManager* GetNetworkManager();
|
static Multiplayer::NetworkManager* GetNetworkManager();
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ class MultiplayerExt {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#ifdef EXTENSIONS
|
#ifdef EXTENSIONS
|
||||||
LEGO1_EXPORT bool IsMultiplayerDisconnected();
|
LEGO1_EXPORT bool IsMultiplayerRejected();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace MP
|
namespace MP
|
||||||
@ -72,7 +72,7 @@ constexpr auto HandleROIClick = &MultiplayerExt::HandleROIClick;
|
|||||||
constexpr auto IsClonedCharacter = &MultiplayerExt::IsClonedCharacter;
|
constexpr auto IsClonedCharacter = &MultiplayerExt::IsClonedCharacter;
|
||||||
constexpr auto HandleBeforeSaveLoad = &MultiplayerExt::HandleBeforeSaveLoad;
|
constexpr auto HandleBeforeSaveLoad = &MultiplayerExt::HandleBeforeSaveLoad;
|
||||||
constexpr auto HandleSaveLoaded = &MultiplayerExt::HandleSaveLoaded;
|
constexpr auto HandleSaveLoaded = &MultiplayerExt::HandleSaveLoaded;
|
||||||
constexpr auto CheckDisconnected = &MultiplayerExt::CheckDisconnected;
|
constexpr auto CheckRejected = &MultiplayerExt::CheckRejected;
|
||||||
#else
|
#else
|
||||||
constexpr decltype(&MultiplayerExt::HandleCreate) HandleCreate = nullptr;
|
constexpr decltype(&MultiplayerExt::HandleCreate) HandleCreate = nullptr;
|
||||||
constexpr decltype(&MultiplayerExt::HandleWorldEnable) HandleWorldEnable = 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::IsClonedCharacter) IsClonedCharacter = nullptr;
|
||||||
constexpr decltype(&MultiplayerExt::HandleBeforeSaveLoad) HandleBeforeSaveLoad = nullptr;
|
constexpr decltype(&MultiplayerExt::HandleBeforeSaveLoad) HandleBeforeSaveLoad = nullptr;
|
||||||
constexpr decltype(&MultiplayerExt::HandleSaveLoaded) HandleSaveLoaded = nullptr;
|
constexpr decltype(&MultiplayerExt::HandleSaveLoaded) HandleSaveLoaded = nullptr;
|
||||||
constexpr decltype(&MultiplayerExt::CheckDisconnected) CheckDisconnected = nullptr;
|
constexpr decltype(&MultiplayerExt::CheckRejected) CheckRejected = nullptr;
|
||||||
#endif
|
#endif
|
||||||
} // namespace MP
|
} // namespace MP
|
||||||
|
|
||||||
|
|||||||
@ -30,6 +30,12 @@ class NameBubbleRenderer;
|
|||||||
|
|
||||||
class NetworkManager : public MxCore {
|
class NetworkManager : public MxCore {
|
||||||
public:
|
public:
|
||||||
|
enum ConnectionState {
|
||||||
|
STATE_DISCONNECTED,
|
||||||
|
STATE_CONNECTED,
|
||||||
|
STATE_RECONNECTING
|
||||||
|
};
|
||||||
|
|
||||||
NetworkManager();
|
NetworkManager();
|
||||||
~NetworkManager() override;
|
~NetworkManager() override;
|
||||||
|
|
||||||
@ -49,7 +55,7 @@ class NetworkManager : public MxCore {
|
|||||||
void Connect(const char* p_roomId);
|
void Connect(const char* p_roomId);
|
||||||
void Disconnect();
|
void Disconnect();
|
||||||
bool IsConnected() const;
|
bool IsConnected() const;
|
||||||
bool WasDisconnected() const;
|
bool WasRejected() const;
|
||||||
|
|
||||||
void SetWalkAnimation(uint8_t p_walkAnimId);
|
void SetWalkAnimation(uint8_t p_walkAnimId);
|
||||||
void SetIdleAnimation(uint8_t p_idleAnimId);
|
void SetIdleAnimation(uint8_t p_idleAnimId);
|
||||||
@ -114,6 +120,10 @@ class NetworkManager : public MxCore {
|
|||||||
void RemoveRemotePlayer(uint32_t p_peerId);
|
void RemoveRemotePlayer(uint32_t p_peerId);
|
||||||
void RemoveAllRemotePlayers();
|
void RemoveAllRemotePlayers();
|
||||||
|
|
||||||
|
void CheckConnectionState();
|
||||||
|
void AttemptReconnect();
|
||||||
|
void ResetStateAfterReconnect();
|
||||||
|
|
||||||
void NotifyPlayerCountChanged();
|
void NotifyPlayerCountChanged();
|
||||||
void EnforceDisableNPCs();
|
void EnforceDisableNPCs();
|
||||||
|
|
||||||
@ -149,8 +159,18 @@ class NetworkManager : public MxCore {
|
|||||||
bool m_lastCameraEnabled;
|
bool m_lastCameraEnabled;
|
||||||
bool m_wasInRestrictedArea;
|
bool m_wasInRestrictedArea;
|
||||||
|
|
||||||
|
ConnectionState m_connectionState;
|
||||||
|
bool m_wasRejected;
|
||||||
|
std::string m_roomId;
|
||||||
|
uint32_t m_reconnectAttempt;
|
||||||
|
uint32_t m_reconnectDelay;
|
||||||
|
uint32_t m_nextReconnectTime;
|
||||||
|
|
||||||
static const uint32_t BROADCAST_INTERVAL_MS = 66; // ~15Hz
|
static const uint32_t BROADCAST_INTERVAL_MS = 66; // ~15Hz
|
||||||
static const uint32_t TIMEOUT_MS = 5000; // 5 second timeout
|
static const uint32_t TIMEOUT_MS = 5000; // 5 second timeout
|
||||||
|
static const uint32_t RECONNECT_INITIAL_DELAY_MS = 1000;
|
||||||
|
static const uint32_t RECONNECT_MAX_DELAY_MS = 30000;
|
||||||
|
static const uint32_t RECONNECT_MAX_ATTEMPTS = 10;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Multiplayer
|
} // namespace Multiplayer
|
||||||
|
|||||||
@ -7,9 +7,6 @@
|
|||||||
namespace Multiplayer
|
namespace Multiplayer
|
||||||
{
|
{
|
||||||
|
|
||||||
static const int EXIT_ROOM_FULL = 10;
|
|
||||||
static const int EXIT_CONNECTION_LOST = 11;
|
|
||||||
|
|
||||||
class NetworkTransport {
|
class NetworkTransport {
|
||||||
public:
|
public:
|
||||||
virtual ~NetworkTransport() = default;
|
virtual ~NetworkTransport() = default;
|
||||||
|
|||||||
@ -3,6 +3,11 @@
|
|||||||
namespace Multiplayer
|
namespace Multiplayer
|
||||||
{
|
{
|
||||||
|
|
||||||
|
static const int CONNECTION_STATUS_CONNECTED = 0;
|
||||||
|
static const int CONNECTION_STATUS_RECONNECTING = 1;
|
||||||
|
static const int CONNECTION_STATUS_FAILED = 2;
|
||||||
|
static const int CONNECTION_STATUS_REJECTED = 3;
|
||||||
|
|
||||||
class PlatformCallbacks {
|
class PlatformCallbacks {
|
||||||
public:
|
public:
|
||||||
virtual ~PlatformCallbacks() = default;
|
virtual ~PlatformCallbacks() = default;
|
||||||
@ -19,6 +24,9 @@ class PlatformCallbacks {
|
|||||||
|
|
||||||
// Called when the allow-customization setting changes.
|
// Called when the allow-customization setting changes.
|
||||||
virtual void OnAllowCustomizeChanged(bool p_enabled) = 0;
|
virtual void OnAllowCustomizeChanged(bool p_enabled) = 0;
|
||||||
|
|
||||||
|
// Called when the connection status changes (connected, reconnecting, failed).
|
||||||
|
virtual void OnConnectionStatusChanged(int p_status) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Multiplayer
|
} // namespace Multiplayer
|
||||||
|
|||||||
@ -13,6 +13,7 @@ class EmscriptenCallbacks : public PlatformCallbacks {
|
|||||||
void OnThirdPersonChanged(bool p_enabled) override;
|
void OnThirdPersonChanged(bool p_enabled) override;
|
||||||
void OnNameBubblesChanged(bool p_enabled) override;
|
void OnNameBubblesChanged(bool p_enabled) override;
|
||||||
void OnAllowCustomizeChanged(bool p_enabled) override;
|
void OnAllowCustomizeChanged(bool p_enabled) override;
|
||||||
|
void OnConnectionStatusChanged(int p_status) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Multiplayer
|
} // namespace Multiplayer
|
||||||
|
|||||||
@ -13,6 +13,7 @@ class NativeCallbacks : public PlatformCallbacks {
|
|||||||
void OnThirdPersonChanged(bool p_enabled) override;
|
void OnThirdPersonChanged(bool p_enabled) override;
|
||||||
void OnNameBubblesChanged(bool p_enabled) override;
|
void OnNameBubblesChanged(bool p_enabled) override;
|
||||||
void OnAllowCustomizeChanged(bool p_enabled) override;
|
void OnAllowCustomizeChanged(bool p_enabled) override;
|
||||||
|
void OnConnectionStatusChanged(int p_status) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Multiplayer
|
} // namespace Multiplayer
|
||||||
|
|||||||
@ -48,6 +48,9 @@ class WorldStateSync {
|
|||||||
// Returns TRUE if the local action should be suppressed (non-host).
|
// Returns TRUE if the local action should be suppressed (non-host).
|
||||||
MxBool HandleSkyLightMutation(uint8_t p_entityType, uint8_t p_changeType);
|
MxBool HandleSkyLightMutation(uint8_t p_entityType, uint8_t p_changeType);
|
||||||
|
|
||||||
|
// Resets session state for reconnection (peer ID, sequence, host, pending events).
|
||||||
|
void ResetForReconnect();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ApplySkyLightState(const char* p_skyColor, int p_lightPos);
|
void ApplySkyLightState(const char* p_skyColor, int p_lightPos);
|
||||||
void SendSnapshotRequest();
|
void SendSnapshotRequest();
|
||||||
|
|||||||
@ -251,9 +251,9 @@ MxBool MultiplayerExt::IsClonedCharacter(const char* p_name)
|
|||||||
return s_networkManager->IsClonedCharacter(p_name) ? TRUE : FALSE;
|
return s_networkManager->IsClonedCharacter(p_name) ? TRUE : FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
MxBool MultiplayerExt::CheckDisconnected()
|
MxBool MultiplayerExt::CheckRejected()
|
||||||
{
|
{
|
||||||
if (s_networkManager && s_networkManager->WasDisconnected()) {
|
if (s_networkManager && s_networkManager->WasRejected()) {
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,7 +265,7 @@ Multiplayer::NetworkManager* MultiplayerExt::GetNetworkManager()
|
|||||||
return s_networkManager;
|
return s_networkManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Extensions::IsMultiplayerDisconnected()
|
bool Extensions::IsMultiplayerRejected()
|
||||||
{
|
{
|
||||||
return Extension<MultiplayerExt>::Call(MP::CheckDisconnected).value_or(FALSE);
|
return Extension<MultiplayerExt>::Call(MP::CheckRejected).value_or(FALSE);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,7 +49,9 @@ NetworkManager::NetworkManager()
|
|||||||
m_sequence(0), m_lastBroadcastTime(0), m_lastValidActorId(0), m_localAllowRemoteCustomize(true),
|
m_sequence(0), m_lastBroadcastTime(0), m_lastValidActorId(0), m_localAllowRemoteCustomize(true),
|
||||||
m_inIsleWorld(false), m_registered(false), m_pendingToggleThirdPerson(false), m_pendingToggleNameBubbles(false),
|
m_inIsleWorld(false), m_registered(false), m_pendingToggleThirdPerson(false), m_pendingToggleNameBubbles(false),
|
||||||
m_pendingWalkAnim(-1), m_pendingIdleAnim(-1), m_pendingEmote(-1), m_pendingToggleAllowCustomize(false),
|
m_pendingWalkAnim(-1), m_pendingIdleAnim(-1), m_pendingEmote(-1), m_pendingToggleAllowCustomize(false),
|
||||||
m_disableAllNPCs(false), m_showNameBubbles(true), m_lastCameraEnabled(false), m_wasInRestrictedArea(false)
|
m_disableAllNPCs(false), m_showNameBubbles(true), m_lastCameraEnabled(false), m_wasInRestrictedArea(false),
|
||||||
|
m_connectionState(STATE_DISCONNECTED), m_wasRejected(false), m_reconnectAttempt(0), m_reconnectDelay(0),
|
||||||
|
m_nextReconnectTime(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,6 +68,7 @@ static ThirdPersonCamera::Controller* GetCamera()
|
|||||||
MxResult NetworkManager::Tickle()
|
MxResult NetworkManager::Tickle()
|
||||||
{
|
{
|
||||||
ProcessPendingRequests();
|
ProcessPendingRequests();
|
||||||
|
CheckConnectionState();
|
||||||
|
|
||||||
if (m_disableAllNPCs) {
|
if (m_disableAllNPCs) {
|
||||||
EnforceDisableNPCs();
|
EnforceDisableNPCs();
|
||||||
@ -171,12 +174,15 @@ void NetworkManager::Shutdown()
|
|||||||
void NetworkManager::Connect(const char* p_roomId)
|
void NetworkManager::Connect(const char* p_roomId)
|
||||||
{
|
{
|
||||||
if (m_transport) {
|
if (m_transport) {
|
||||||
|
m_roomId = p_roomId;
|
||||||
m_transport->Connect(p_roomId);
|
m_transport->Connect(p_roomId);
|
||||||
|
m_connectionState = STATE_CONNECTED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkManager::Disconnect()
|
void NetworkManager::Disconnect()
|
||||||
{
|
{
|
||||||
|
m_connectionState = STATE_DISCONNECTED;
|
||||||
if (m_transport) {
|
if (m_transport) {
|
||||||
m_transport->Disconnect();
|
m_transport->Disconnect();
|
||||||
}
|
}
|
||||||
@ -188,9 +194,9 @@ bool NetworkManager::IsConnected() const
|
|||||||
return m_transport && m_transport->IsConnected();
|
return m_transport && m_transport->IsConnected();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NetworkManager::WasDisconnected() const
|
bool NetworkManager::WasRejected() const
|
||||||
{
|
{
|
||||||
return m_transport && m_transport->WasDisconnected();
|
return m_wasRejected;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkManager::OnWorldEnabled(LegoWorld* p_world)
|
void NetworkManager::OnWorldEnabled(LegoWorld* p_world)
|
||||||
@ -322,6 +328,88 @@ void NetworkManager::EnforceDisableNPCs()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NetworkManager::CheckConnectionState()
|
||||||
|
{
|
||||||
|
if (!m_transport || m_connectionState == STATE_DISCONNECTED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_connectionState == STATE_CONNECTED) {
|
||||||
|
if (!m_transport->WasDisconnected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_transport->WasRejected()) {
|
||||||
|
// Room full on initial connect - flag for game loop to exit
|
||||||
|
m_wasRejected = true;
|
||||||
|
m_connectionState = STATE_DISCONNECTED;
|
||||||
|
|
||||||
|
if (m_callbacks) {
|
||||||
|
m_callbacks->OnConnectionStatusChanged(CONNECTION_STATUS_REJECTED);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connection lost - enter reconnection
|
||||||
|
m_connectionState = STATE_RECONNECTING;
|
||||||
|
RemoveAllRemotePlayers();
|
||||||
|
m_reconnectAttempt = 0;
|
||||||
|
m_reconnectDelay = RECONNECT_INITIAL_DELAY_MS;
|
||||||
|
m_nextReconnectTime = SDL_GetTicks() + m_reconnectDelay;
|
||||||
|
|
||||||
|
if (m_callbacks) {
|
||||||
|
m_callbacks->OnConnectionStatusChanged(CONNECTION_STATUS_RECONNECTING);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STATE_RECONNECTING
|
||||||
|
if (m_transport->IsConnected()) {
|
||||||
|
ResetStateAfterReconnect();
|
||||||
|
m_connectionState = STATE_CONNECTED;
|
||||||
|
|
||||||
|
if (m_callbacks) {
|
||||||
|
m_callbacks->OnConnectionStatusChanged(CONNECTION_STATUS_CONNECTED);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t now = SDL_GetTicks();
|
||||||
|
if (now < m_nextReconnectTime) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_reconnectAttempt >= RECONNECT_MAX_ATTEMPTS) {
|
||||||
|
// Give up - stay alive but without multiplayer
|
||||||
|
m_connectionState = STATE_DISCONNECTED;
|
||||||
|
|
||||||
|
if (m_callbacks) {
|
||||||
|
m_callbacks->OnConnectionStatusChanged(CONNECTION_STATUS_FAILED);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AttemptReconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkManager::AttemptReconnect()
|
||||||
|
{
|
||||||
|
m_reconnectAttempt++;
|
||||||
|
m_transport->Disconnect();
|
||||||
|
m_transport->Connect(m_roomId.c_str());
|
||||||
|
m_reconnectDelay = SDL_min(m_reconnectDelay * 2, RECONNECT_MAX_DELAY_MS);
|
||||||
|
m_nextReconnectTime = SDL_GetTicks() + m_reconnectDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkManager::ResetStateAfterReconnect()
|
||||||
|
{
|
||||||
|
m_localPeerId = 0;
|
||||||
|
m_hostPeerId = 0;
|
||||||
|
m_sequence = 0;
|
||||||
|
m_lastBroadcastTime = 0;
|
||||||
|
m_worldSync.ResetForReconnect();
|
||||||
|
}
|
||||||
|
|
||||||
void NetworkManager::ProcessPendingRequests()
|
void NetworkManager::ProcessPendingRequests()
|
||||||
{
|
{
|
||||||
ThirdPersonCamera::Controller* cam = GetCamera();
|
ThirdPersonCamera::Controller* cam = GetCamera();
|
||||||
|
|||||||
@ -50,6 +50,20 @@ void EmscriptenCallbacks::OnAllowCustomizeChanged(bool p_enabled)
|
|||||||
DispatchBoolEvent("allowCustomizeChanged", p_enabled);
|
DispatchBoolEvent("allowCustomizeChanged", p_enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmscriptenCallbacks::OnConnectionStatusChanged(int p_status)
|
||||||
|
{
|
||||||
|
// clang-format off
|
||||||
|
MAIN_THREAD_EM_ASM({
|
||||||
|
var canvas = Module.canvas;
|
||||||
|
if (canvas) {
|
||||||
|
canvas.dispatchEvent(new CustomEvent('connectionStatusChanged', {
|
||||||
|
detail: { status: $0 }
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}, p_status);
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Multiplayer
|
} // namespace Multiplayer
|
||||||
|
|
||||||
#endif // __EMSCRIPTEN__
|
#endif // __EMSCRIPTEN__
|
||||||
|
|||||||
@ -44,8 +44,6 @@ void WebSocketTransport::Connect(const char* p_roomId)
|
|||||||
var connPtr = $1;
|
var connPtr = $1;
|
||||||
var discPtr = $2;
|
var discPtr = $2;
|
||||||
var everConnPtr = $3;
|
var everConnPtr = $3;
|
||||||
var exitRoomFull = $4;
|
|
||||||
var exitConnLost = $5;
|
|
||||||
var socketId = Module._mpNextSocketId++;
|
var socketId = Module._mpNextSocketId++;
|
||||||
Module._mpMessageQueues[socketId] = [];
|
Module._mpMessageQueues[socketId] = [];
|
||||||
|
|
||||||
@ -66,10 +64,8 @@ void WebSocketTransport::Connect(const char* p_roomId)
|
|||||||
};
|
};
|
||||||
|
|
||||||
ws.onclose = function() {
|
ws.onclose = function() {
|
||||||
var wasEverConnected = Atomics.load(HEAP32, everConnPtr >> 2);
|
|
||||||
Atomics.store(HEAP32, connPtr >> 2, 0);
|
Atomics.store(HEAP32, connPtr >> 2, 0);
|
||||||
Atomics.store(HEAP32, discPtr >> 2, 1);
|
Atomics.store(HEAP32, discPtr >> 2, 1);
|
||||||
Module._exitCode = wasEverConnected ? exitConnLost : exitRoomFull;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ws.onerror = function() {
|
ws.onerror = function() {
|
||||||
@ -83,7 +79,7 @@ void WebSocketTransport::Connect(const char* p_roomId)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return socketId;
|
return socketId;
|
||||||
}, url.c_str(), &m_connectedFlag, &m_disconnectedFlag, &m_wasEverConnected, EXIT_ROOM_FULL, EXIT_CONNECTION_LOST);
|
}, url.c_str(), &m_connectedFlag, &m_disconnectedFlag, &m_wasEverConnected);
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
if (m_socketId <= 0) {
|
if (m_socketId <= 0) {
|
||||||
|
|||||||
@ -32,6 +32,26 @@ void NativeCallbacks::OnAllowCustomizeChanged(bool p_enabled)
|
|||||||
SDL_Log("[Multiplayer] Allow customization: %s", p_enabled ? "enabled" : "disabled");
|
SDL_Log("[Multiplayer] Allow customization: %s", p_enabled ? "enabled" : "disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NativeCallbacks::OnConnectionStatusChanged(int p_status)
|
||||||
|
{
|
||||||
|
const char* statusStr = "unknown";
|
||||||
|
switch (p_status) {
|
||||||
|
case CONNECTION_STATUS_CONNECTED:
|
||||||
|
statusStr = "connected";
|
||||||
|
break;
|
||||||
|
case CONNECTION_STATUS_RECONNECTING:
|
||||||
|
statusStr = "reconnecting";
|
||||||
|
break;
|
||||||
|
case CONNECTION_STATUS_FAILED:
|
||||||
|
statusStr = "failed";
|
||||||
|
break;
|
||||||
|
case CONNECTION_STATUS_REJECTED:
|
||||||
|
statusStr = "rejected (room full)";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
SDL_Log("[Multiplayer] Connection status: %s", statusStr);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Multiplayer
|
} // namespace Multiplayer
|
||||||
|
|
||||||
#endif // !__EMSCRIPTEN__
|
#endif // !__EMSCRIPTEN__
|
||||||
|
|||||||
@ -66,6 +66,15 @@ void WorldStateSync::ApplySkyLightState(const char* p_skyColor, int p_lightPos)
|
|||||||
VariableTable()->SetVariable("lightposition", buf);
|
VariableTable()->SetVariable("lightposition", buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WorldStateSync::ResetForReconnect()
|
||||||
|
{
|
||||||
|
m_localPeerId = 0;
|
||||||
|
m_sequence = 0;
|
||||||
|
m_isHost = false;
|
||||||
|
m_snapshotRequested = false;
|
||||||
|
m_pendingWorldEvents.clear();
|
||||||
|
}
|
||||||
|
|
||||||
void WorldStateSync::OnHostChanged()
|
void WorldStateSync::OnHostChanged()
|
||||||
{
|
{
|
||||||
if (!m_isHost) {
|
if (!m_isHost) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user