Refactor WASM exports and add push-based player count events

Move WASM exports (mp_set_walk_animation, mp_set_idle_animation,
mp_trigger_emote) from multiplayer.cpp into a dedicated
wasm_exports.cpp, added to the isle target (guarded by both
EMSCRIPTEN and ISLE_EXTENSIONS) so the linker keeps the symbols.

Replace the polling mp_get_player_count export with push-based
playerCountChanged CustomEvents dispatched from NetworkManager.
The count only reflects players visible in the Isle world: null
when the local player is outside Isle, filtered by remote player
worldId when inside. Remove the now-unused GetPlayerCount() method.
This commit is contained in:
Christian Semmler 2026-03-02 15:26:27 -08:00
parent 3e85941cbc
commit e47d2dab67
No known key found for this signature in database
GPG Key ID: 086DAA1360BEEE5C
5 changed files with 88 additions and 48 deletions

View File

@ -15,8 +15,6 @@ endif()
if (EMSCRIPTEN)
add_compile_options(-pthread)
add_link_options(-sUSE_WEBGL2=1 -sMIN_WEBGL_VERSION=2 -sALLOW_MEMORY_GROWTH=1 -sMAXIMUM_MEMORY=2gb -sUSE_PTHREADS=1 -sPROXY_TO_PTHREAD=1 -sOFFSCREENCANVAS_SUPPORT=1 -sPTHREAD_POOL_SIZE_STRICT=0 -sFORCE_FILESYSTEM=1 -sWASMFS=1 -sEXIT_RUNTIME=1)
add_link_options("-sEXPORTED_FUNCTIONS=[\"_main\",\"_mp_set_walk_animation\",\"_mp_set_idle_animation\",\"_mp_trigger_emote\",\"_mp_get_player_count\"]")
add_link_options("-sEXPORTED_RUNTIME_METHODS=[\"ccall\",\"cwrap\"]")
set(SDL_PTHREADS ON CACHE BOOL "Enable SDL pthreads" FORCE)
endif()
@ -595,6 +593,11 @@ if (ISLE_BUILD_APP)
ISLE/emscripten/messagebox.cpp
ISLE/emscripten/window.cpp
)
if(ISLE_EXTENSIONS)
target_sources(isle PRIVATE
extensions/src/multiplayer/wasm_exports.cpp
)
endif()
target_compile_definitions(isle PRIVATE "ISLE_EMSCRIPTEN_HOST=\"${ISLE_EMSCRIPTEN_HOST}\"")
set_property(TARGET isle PROPERTY SUFFIX ".html")
endif()

View File

@ -44,7 +44,6 @@ class NetworkManager : public MxCore {
void SetWalkAnimation(uint8_t p_index);
void SetIdleAnimation(uint8_t p_index);
void SendEmote(uint8_t p_emoteId);
int GetPlayerCount() const;
void OnWorldEnabled(LegoWorld* p_world);
void OnWorldDisabled(LegoWorld* p_world);
@ -71,6 +70,7 @@ class NetworkManager : public MxCore {
void RemoveRemotePlayer(uint32_t p_peerId);
void RemoveAllRemotePlayers();
void NotifyPlayerCountChanged();
int8_t DetectLocalVehicleType();
// Serialize and send a fixed-size message via the transport

View File

@ -125,42 +125,3 @@ bool Extensions::IsMultiplayerRejected()
{
return Extension<MultiplayerExt>::Call(CheckRejected).value_or(FALSE);
}
#ifdef __EMSCRIPTEN__
extern "C" {
EMSCRIPTEN_KEEPALIVE void mp_set_walk_animation(int index)
{
Multiplayer::NetworkManager* mgr = MultiplayerExt::GetNetworkManager();
if (mgr) {
mgr->SetWalkAnimation(static_cast<uint8_t>(index));
}
}
EMSCRIPTEN_KEEPALIVE void mp_set_idle_animation(int index)
{
Multiplayer::NetworkManager* mgr = MultiplayerExt::GetNetworkManager();
if (mgr) {
mgr->SetIdleAnimation(static_cast<uint8_t>(index));
}
}
EMSCRIPTEN_KEEPALIVE void mp_trigger_emote(int index)
{
Multiplayer::NetworkManager* mgr = MultiplayerExt::GetNetworkManager();
if (mgr) {
mgr->SendEmote(static_cast<uint8_t>(index));
}
}
EMSCRIPTEN_KEEPALIVE int mp_get_player_count()
{
Multiplayer::NetworkManager* mgr = MultiplayerExt::GetNetworkManager();
if (mgr) {
return mgr->GetPlayerCount();
}
return 0;
}
} // extern "C"
#endif

View File

@ -11,6 +11,9 @@
#include <SDL3/SDL_stdinc.h>
#include <SDL3/SDL_timer.h>
#include <vector>
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
using namespace Multiplayer;
@ -145,6 +148,8 @@ void NetworkManager::OnWorldEnabled(LegoWorld* p_world)
}
}
}
NotifyPlayerCountChanged();
}
}
@ -160,6 +165,8 @@ void NetworkManager::OnWorldDisabled(LegoWorld* p_world)
for (auto& [peerId, player] : m_remotePlayers) {
player->SetVisible(false);
}
NotifyPlayerCountChanged();
}
}
@ -338,6 +345,7 @@ void NetworkManager::HandleJoin(const PlayerJoinMsg& p_msg)
}
CreateAndSpawnPlayer(peerId, p_msg.actorId);
NotifyPlayerCountChanged();
}
void NetworkManager::HandleLeave(const PlayerLeaveMsg& p_msg)
@ -356,6 +364,7 @@ void NetworkManager::HandleState(const PlayerStateMsg& p_msg)
}
CreateAndSpawnPlayer(peerId, p_msg.actorId);
NotifyPlayerCountChanged();
it = m_remotePlayers.find(peerId);
}
@ -367,12 +376,20 @@ void NetworkManager::HandleState(const PlayerStateMsg& p_msg)
it = m_remotePlayers.find(peerId);
}
int8_t oldWorldId = it->second->GetWorldId();
it->second->UpdateFromNetwork(p_msg);
bool bothInIsle = m_inIsleWorld && (p_msg.worldId == (int8_t) LegoOmni::e_act1);
if (it->second->IsSpawned()) {
it->second->SetVisible(bothInIsle);
}
bool wasInIsle = (oldWorldId == (int8_t) LegoOmni::e_act1);
bool nowInIsle = (p_msg.worldId == (int8_t) LegoOmni::e_act1);
if (m_inIsleWorld && wasInIsle != nowInIsle) {
NotifyPlayerCountChanged();
}
}
void NetworkManager::HandleHostAssign(const HostAssignMsg& p_msg)
@ -413,12 +430,6 @@ void NetworkManager::SendEmote(uint8_t p_emoteId)
SendMessage(msg);
}
int NetworkManager::GetPlayerCount() const
{
// +1 for the local player
return static_cast<int>(m_remotePlayers.size()) + 1;
}
void NetworkManager::HandleEmote(const EmoteMsg& p_msg)
{
uint32_t peerId = p_msg.header.peerId;
@ -434,6 +445,7 @@ void NetworkManager::RemoveRemotePlayer(uint32_t p_peerId)
if (it != m_remotePlayers.end()) {
it->second->Despawn();
m_remotePlayers.erase(it);
NotifyPlayerCountChanged();
}
}
@ -443,6 +455,32 @@ void NetworkManager::RemoveAllRemotePlayers()
player->Despawn();
}
m_remotePlayers.clear();
NotifyPlayerCountChanged();
}
void NetworkManager::NotifyPlayerCountChanged()
{
#ifdef __EMSCRIPTEN__
int count = -1;
if (m_inIsleWorld) {
count = 1; // local player
for (auto& [peerId, player] : m_remotePlayers) {
if (player->GetWorldId() == (int8_t) LegoOmni::e_act1) {
count++;
}
}
}
// clang-format off
MAIN_THREAD_EM_ASM({
var canvas = Module.canvas;
if (canvas) {
canvas.dispatchEvent(new CustomEvent('playerCountChanged', {
detail: { count: $0 < 0 ? null : $0 }
}));
}
}, count);
// clang-format on
#endif
}
int8_t NetworkManager::DetectLocalVehicleType()

View File

@ -0,0 +1,38 @@
#ifdef __EMSCRIPTEN__
#include "extensions/multiplayer.h"
#include "extensions/multiplayer/networkmanager.h"
#include <emscripten.h>
using namespace Extensions;
extern "C" {
EMSCRIPTEN_KEEPALIVE void mp_set_walk_animation(int index)
{
Multiplayer::NetworkManager* mgr = MultiplayerExt::GetNetworkManager();
if (mgr) {
mgr->SetWalkAnimation(static_cast<uint8_t>(index));
}
}
EMSCRIPTEN_KEEPALIVE void mp_set_idle_animation(int index)
{
Multiplayer::NetworkManager* mgr = MultiplayerExt::GetNetworkManager();
if (mgr) {
mgr->SetIdleAnimation(static_cast<uint8_t>(index));
}
}
EMSCRIPTEN_KEEPALIVE void mp_trigger_emote(int index)
{
Multiplayer::NetworkManager* mgr = MultiplayerExt::GetNetworkManager();
if (mgr) {
mgr->SendEmote(static_cast<uint8_t>(index));
}
}
} // extern "C"
#endif