mirror of
https://github.com/isledecomp/isle-portable.git
synced 2026-05-02 02:23:56 +00:00
Merge branch 'multiplayer' into claude/adjust-camera-switch-distance-duaQ4
This commit is contained in:
commit
37587beda4
@ -1300,7 +1300,7 @@ inline bool IsleApp::Tick()
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef EXTENSIONS
|
#ifdef EXTENSIONS
|
||||||
if (Extensions::IsMultiplayerRejected()) {
|
if (Extensions::IsMultiplayerDisconnected()) {
|
||||||
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 CheckRejected();
|
static MxBool CheckDisconnected();
|
||||||
|
|
||||||
static Multiplayer::NetworkManager* GetNetworkManager();
|
static Multiplayer::NetworkManager* GetNetworkManager();
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ class MultiplayerExt {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#ifdef EXTENSIONS
|
#ifdef EXTENSIONS
|
||||||
LEGO1_EXPORT bool IsMultiplayerRejected();
|
LEGO1_EXPORT bool IsMultiplayerDisconnected();
|
||||||
#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 CheckRejected = &MultiplayerExt::CheckRejected;
|
constexpr auto CheckDisconnected = &MultiplayerExt::CheckDisconnected;
|
||||||
#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::CheckRejected) CheckRejected = nullptr;
|
constexpr decltype(&MultiplayerExt::CheckDisconnected) CheckDisconnected = nullptr;
|
||||||
#endif
|
#endif
|
||||||
} // namespace MP
|
} // namespace MP
|
||||||
|
|
||||||
|
|||||||
@ -49,7 +49,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 WasRejected() const;
|
bool WasDisconnected() 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);
|
||||||
@ -151,6 +151,7 @@ class NetworkManager : public MxCore {
|
|||||||
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 int EXIT_ROOM_FULL = 10;
|
static const int EXIT_ROOM_FULL = 10;
|
||||||
|
static const int EXIT_CONNECTION_LOST = 11;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Multiplayer
|
} // namespace Multiplayer
|
||||||
|
|||||||
@ -14,7 +14,7 @@ class NetworkTransport {
|
|||||||
virtual void Connect(const char* p_roomId) = 0;
|
virtual void Connect(const char* p_roomId) = 0;
|
||||||
virtual void Disconnect() = 0;
|
virtual void Disconnect() = 0;
|
||||||
virtual bool IsConnected() const = 0;
|
virtual bool IsConnected() const = 0;
|
||||||
virtual bool WasRejected() const = 0;
|
virtual bool WasDisconnected() const = 0;
|
||||||
|
|
||||||
// Send binary data to all peers via relay
|
// Send binary data to all peers via relay
|
||||||
virtual void Send(const uint8_t* p_data, size_t p_length) = 0;
|
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 Connect(const char* p_roomId) override;
|
||||||
void Disconnect() override;
|
void Disconnect() override;
|
||||||
bool IsConnected() const 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;
|
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;
|
size_t Receive(std::function<void(const uint8_t*, size_t)> p_callback) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string m_relayBaseUrl;
|
std::string m_relayBaseUrl;
|
||||||
int m_socketId;
|
int m_socketId;
|
||||||
volatile int32_t m_connectedFlag; // Shared with JS main thread via Atomics
|
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_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];
|
uint8_t m_recvBuf[8192];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -243,9 +243,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::CheckRejected()
|
MxBool MultiplayerExt::CheckDisconnected()
|
||||||
{
|
{
|
||||||
if (s_networkManager && s_networkManager->WasRejected()) {
|
if (s_networkManager && s_networkManager->WasDisconnected()) {
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,7 +257,7 @@ Multiplayer::NetworkManager* MultiplayerExt::GetNetworkManager()
|
|||||||
return s_networkManager;
|
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();
|
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)
|
void NetworkManager::OnWorldEnabled(LegoWorld* p_world)
|
||||||
|
|||||||
@ -9,7 +9,7 @@ namespace Multiplayer
|
|||||||
{
|
{
|
||||||
|
|
||||||
WebSocketTransport::WebSocketTransport(const std::string& p_relayBaseUrl)
|
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
|
// clang-format off
|
||||||
MAIN_THREAD_EM_ASM({
|
MAIN_THREAD_EM_ASM({
|
||||||
@ -35,13 +35,15 @@ void WebSocketTransport::Connect(const char* p_roomId)
|
|||||||
|
|
||||||
std::string url = m_relayBaseUrl + "/room/" + p_roomId;
|
std::string url = m_relayBaseUrl + "/room/" + p_roomId;
|
||||||
|
|
||||||
m_rejectedFlag = 0;
|
m_disconnectedFlag = 0;
|
||||||
|
m_wasEverConnected = 0;
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
m_socketId = MAIN_THREAD_EM_ASM_INT({
|
m_socketId = MAIN_THREAD_EM_ASM_INT({
|
||||||
var url = UTF8ToString($0);
|
var url = UTF8ToString($0);
|
||||||
var connPtr = $1;
|
var connPtr = $1;
|
||||||
var rejPtr = $2;
|
var discPtr = $2;
|
||||||
|
var everConnPtr = $3;
|
||||||
var socketId = Module._mpNextSocketId++;
|
var socketId = Module._mpNextSocketId++;
|
||||||
Module._mpMessageQueues[socketId] = [];
|
Module._mpMessageQueues[socketId] = [];
|
||||||
|
|
||||||
@ -51,6 +53,7 @@ void WebSocketTransport::Connect(const char* p_roomId)
|
|||||||
|
|
||||||
ws.onopen = function() {
|
ws.onopen = function() {
|
||||||
Atomics.store(HEAP32, connPtr >> 2, 1);
|
Atomics.store(HEAP32, connPtr >> 2, 1);
|
||||||
|
Atomics.store(HEAP32, everConnPtr >> 2, 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
ws.onmessage = function(event) {
|
ws.onmessage = function(event) {
|
||||||
@ -61,13 +64,12 @@ void WebSocketTransport::Connect(const char* p_roomId)
|
|||||||
};
|
};
|
||||||
|
|
||||||
ws.onclose = function() {
|
ws.onclose = function() {
|
||||||
var wasConnected = Atomics.load(HEAP32, connPtr >> 2);
|
var wasEverConnected = Atomics.load(HEAP32, everConnPtr >> 2);
|
||||||
Atomics.store(HEAP32, connPtr >> 2, 0);
|
Atomics.store(HEAP32, connPtr >> 2, 0);
|
||||||
if (!wasConnected) {
|
Atomics.store(HEAP32, discPtr >> 2, 1);
|
||||||
// Never connected — server rejected (room full / 503)
|
// 10 = room full / server rejected before connecting
|
||||||
Atomics.store(HEAP32, rejPtr >> 2, 1);
|
// 11 = connection lost after successful connect
|
||||||
Module._exitCode = 10;
|
Module._exitCode = wasEverConnected ? 11 : 10;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ws.onerror = function() {
|
ws.onerror = function() {
|
||||||
@ -81,7 +83,7 @@ void WebSocketTransport::Connect(const char* p_roomId)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return socketId;
|
return socketId;
|
||||||
}, url.c_str(), &m_connectedFlag, &m_rejectedFlag);
|
}, url.c_str(), &m_connectedFlag, &m_disconnectedFlag, &m_wasEverConnected);
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
if (m_socketId <= 0) {
|
if (m_socketId <= 0) {
|
||||||
@ -105,6 +107,7 @@ void WebSocketTransport::Disconnect()
|
|||||||
|
|
||||||
m_socketId = -1;
|
m_socketId = -1;
|
||||||
m_connectedFlag = 0;
|
m_connectedFlag = 0;
|
||||||
|
m_wasEverConnected = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,9 +116,9 @@ bool WebSocketTransport::IsConnected() const
|
|||||||
return m_socketId > 0 && m_connectedFlag != 0;
|
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)
|
void WebSocketTransport::Send(const uint8_t* p_data, size_t p_length)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user