isle-portable/extensions/include/extensions/multiplayer.h
foxtacles eb6d2b8728
Sync sky light (#9)
* Sync sky color and light position in multiplayer

Add ENTITY_SKY and ENTITY_LIGHT to the WorldEvent system so the host
controls sky color (hue/saturation via observatory sun/moon/palette
buttons) and light position (globe arrows) with the same
host-authoritative pattern used for plants and buildings. Non-host
players send requests to the host who applies and broadcasts. Sky/light
state is appended to the world snapshot so joining players get the
current values.

https://claude.ai/code/session_01X2cPVQEo7c92wpWA7QPPMG

* Clean up sky/light sync: remove debug logging, DRY apply logic, fix host routing

- Extract ApplySkyLightState helper to deduplicate sky/light apply code
  between RestoreSkyLightState and HandleWorldSnapshot
- Remove all SDL_Log debug calls and SDL_log.h includes
- Remove dead OnWorldEnabled method from WorldStateSync (replaced by
  OnHostChanged in OnSaveLoaded)
- Fix HandleSkyLightMutation host path: return FALSE to let local
  switch case proceed, instead of duplicating via ApplyWorldEvent
- Simplify isle.cpp HandleControl: split observatory cases into
  individual switch arms with single early-return multiplayer hook
- Add save load hooks to sync world state with multiplayer peers
- Fix player count to exclude local player without valid actor
- Support broadcast snapshots (targetPeerId=0) in relay server

---------
2026-03-08 05:55:00 +01:00

108 lines
4.3 KiB
C++

#pragma once
#include "extensions/extensions.h"
#include "mxtypes.h"
#include <map>
#include <string>
class IslePathActor;
class LegoEntity;
class LegoEventNotificationParam;
class LegoPathActor;
class LegoROI;
class LegoWorld;
namespace Multiplayer
{
class NetworkManager;
class NetworkTransport;
class PlatformCallbacks;
} // namespace Multiplayer
namespace Extensions
{
class MultiplayerExt {
public:
static void Initialize();
static void HandleCreate();
static MxBool HandleWorldEnable(LegoWorld* p_world, MxBool p_enable);
// Intercepts click notifications on plants/buildings for multiplayer routing.
// Returns TRUE if the click should be suppressed locally (non-host).
static MxBool HandleEntityNotify(LegoEntity* p_entity);
// Intercepts observatory sky/light controls for multiplayer routing.
// Returns TRUE if the local action should be suppressed (non-host).
static MxBool HandleSkyLightControl(MxU32 p_controlId);
// Handles clicks on entity-less ROIs (remote players, display actor overrides).
static MxBool HandleROIClick(LegoROI* p_rootROI, LegoEventNotificationParam& p_param);
static std::map<std::string, std::string> options;
static bool enabled;
static std::string relayUrl;
static std::string room;
static void HandleActorEnter(IslePathActor* p_actor);
static void HandleActorExit(IslePathActor* p_actor);
static void HandleCamAnimEnd(LegoPathActor* p_actor);
static MxBool ShouldInvertMovement(LegoPathActor* p_actor);
// Returns TRUE if the name belongs to a multiplayer clone (entity-less ROI).
static MxBool IsClonedCharacter(const char* p_name);
// Called before a save file is loaded. Captures current sky/light state.
static void HandleBeforeSaveLoad();
// Called after a save file is loaded. Re-syncs world state with multiplayer peers.
static void HandleSaveLoaded();
// Returns true if the multiplayer connection was rejected (e.g. room full).
static MxBool CheckRejected();
static void SetNetworkManager(Multiplayer::NetworkManager* p_networkManager);
static Multiplayer::NetworkManager* GetNetworkManager();
private:
static Multiplayer::NetworkManager* s_networkManager;
static Multiplayer::NetworkTransport* s_transport;
static Multiplayer::PlatformCallbacks* s_callbacks;
};
#ifdef EXTENSIONS
LEGO1_EXPORT bool IsMultiplayerRejected();
constexpr auto HandleCreate = &MultiplayerExt::HandleCreate;
constexpr auto HandleWorldEnable = &MultiplayerExt::HandleWorldEnable;
constexpr auto HandleEntityNotify = &MultiplayerExt::HandleEntityNotify;
constexpr auto HandleSkyLightControl = &MultiplayerExt::HandleSkyLightControl;
constexpr auto HandleROIClick = &MultiplayerExt::HandleROIClick;
constexpr auto HandleActorEnter = &MultiplayerExt::HandleActorEnter;
constexpr auto HandleActorExit = &MultiplayerExt::HandleActorExit;
constexpr auto HandleCamAnimEnd = &MultiplayerExt::HandleCamAnimEnd;
constexpr auto ShouldInvertMovement = &MultiplayerExt::ShouldInvertMovement;
constexpr auto IsClonedCharacter = &MultiplayerExt::IsClonedCharacter;
constexpr auto HandleBeforeSaveLoad = &MultiplayerExt::HandleBeforeSaveLoad;
constexpr auto HandleSaveLoaded = &MultiplayerExt::HandleSaveLoaded;
constexpr auto CheckRejected = &MultiplayerExt::CheckRejected;
#else
constexpr decltype(&MultiplayerExt::HandleCreate) HandleCreate = nullptr;
constexpr decltype(&MultiplayerExt::HandleWorldEnable) HandleWorldEnable = nullptr;
constexpr decltype(&MultiplayerExt::HandleEntityNotify) HandleEntityNotify = nullptr;
constexpr decltype(&MultiplayerExt::HandleSkyLightControl) HandleSkyLightControl = nullptr;
constexpr decltype(&MultiplayerExt::HandleROIClick) HandleROIClick = nullptr;
constexpr decltype(&MultiplayerExt::HandleActorEnter) HandleActorEnter = nullptr;
constexpr decltype(&MultiplayerExt::HandleActorExit) HandleActorExit = nullptr;
constexpr decltype(&MultiplayerExt::HandleCamAnimEnd) HandleCamAnimEnd = nullptr;
constexpr decltype(&MultiplayerExt::ShouldInvertMovement) ShouldInvertMovement = 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;
#endif
}; // namespace Extensions