diff --git a/Library/include/CSP/Common/NetworkEventData.h b/Library/include/CSP/Common/NetworkEventData.h index ce8fa9bd6..8b67eb43e 100644 --- a/Library/include/CSP/Common/NetworkEventData.h +++ b/Library/include/CSP/Common/NetworkEventData.h @@ -152,6 +152,23 @@ class CSP_API SequenceChangedNetworkEventData : public NetworkEventData csp::common::Optional HotspotData = nullptr; }; +// @brief Data for an event signalling the completion of an async operation. +// This is general purpose event data that can be used by any system exposing async operations. +class CSP_API AsyncCallCompletedEventData : public NetworkEventData +{ +public: + /// @brief The name of the async operation that has been completed. + csp::common::String OperationName; + + /// @brief An Id related to the async operation that has been completed. + /// This could for example be a group Id, if this were an async duplicate group operation. + csp::common::String ReferenceId; + + /// @brief The type that the Id represents. + /// In the previous example this would be "GroupId". + csp::common::String ReferenceType; +}; + // TODO, this should not be here. It's not an event data, it's just a type for a callback used in the AssetSystem. // The annoyance is that ChangeType is defined for these EventDatas, gotta break that at some point. // I don't really know where this should go at the moment. diff --git a/Library/include/CSP/Multiplayer/MultiPlayerConnection.h b/Library/include/CSP/Multiplayer/MultiPlayerConnection.h index 18bddcf17..3db864f79 100644 --- a/Library/include/CSP/Multiplayer/MultiPlayerConnection.h +++ b/Library/include/CSP/Multiplayer/MultiPlayerConnection.h @@ -138,7 +138,8 @@ class CSP_API MultiplayerConnection /// @brief Start the connection and register to start receiving updates from the server. /// Connect should be called after LogIn and before EnterSpace. /// @param Callback ErrorCodeCallbackHandler : a callback with failure state. - CSP_NO_EXPORT void Connect(ErrorCodeCallbackHandler Callback, [[maybe_unused]] const csp::common::String& MultiplayerUri, const csp::common::String& AccessToken, const csp::common::String& DeviceId); + CSP_NO_EXPORT void Connect(ErrorCodeCallbackHandler Callback, [[maybe_unused]] const csp::common::String& MultiplayerUri, + const csp::common::String& AccessToken, const csp::common::String& DeviceId); /// @brief Indicates whether the multiplayer connection is established /// @return bool : true if connected, false otherwise @@ -153,8 +154,8 @@ class CSP_API MultiplayerConnection CSP_NO_EXPORT csp::multiplayer::NetworkEventManagerImpl* GetNetworkEventManager() { return NetworkEventManager; } /// @brief Getter for the NetworkEventBus - /// @return NetworkEventBus* : pointer to the NetworkEventBus - CSP_NO_EXPORT NetworkEventBus* GetEventBusPtr() { return EventBusPtr; } + /// @return NetworkEventBus& : reference to the NetworkEventBus + CSP_NO_EXPORT NetworkEventBus& GetEventBus() { return *EventBus; } /// @brief Disconnect the multiplayer and provide a reason /// @param Reason csp::common::String& : the reason to disconnect @@ -243,7 +244,7 @@ class CSP_API MultiplayerConnection class csp::multiplayer::IWebSocketClient* WebSocketClient; class NetworkEventManagerImpl* NetworkEventManager; - class NetworkEventBus* EventBusPtr; + class NetworkEventBus* EventBus; csp::common::LogSystem& LogSystem; diff --git a/Library/include/CSP/Multiplayer/NetworkEventBus.h b/Library/include/CSP/Multiplayer/NetworkEventBus.h index 359bc01e7..feeb5ed71 100644 --- a/Library/include/CSP/Multiplayer/NetworkEventBus.h +++ b/Library/include/CSP/Multiplayer/NetworkEventBus.h @@ -199,8 +199,9 @@ class CSP_API NetworkEventBus SequenceChanged, // Unpacks to SequenceChangedNetworkEventData or SequenceHotspotChangedEventData (Better if there was a seperate event for // each.) AccessControlChanged, // Unpacks to AccessControlChangedNetworkEventData - GeneralPurposeEvent // Unpacks to NetworkEventData (Base type). An external event unknown to us that may have been registered with any string - // value. + GeneralPurposeEvent, // Unpacks to NetworkEventData (Base type). An external event unknown to us that may have been registered with any string + // value. + AsyncCallCompleted // Unpacks to AsyncCallCompletedEventData }; static NetworkEvent NetworkEventFromString(const csp::common::String& EventString); @@ -223,7 +224,7 @@ class CSP_API NetworkEventBus static inline const std::unordered_map CustomDeserializationEventMap { { NetworkEvent::AssetDetailBlobChanged, "AssetDetailBlobChanged" }, { NetworkEvent::Conversation, "Conversation" }, { NetworkEvent::SequenceChanged, "SequenceChanged" }, - { NetworkEvent::AccessControlChanged, "AccessControlChanged" } }; + { NetworkEvent::AccessControlChanged, "AccessControlChanged" }, { NetworkEvent::AsyncCallCompleted, "AsyncCallCompleted" } }; CSP_END_IGNORE diff --git a/Library/include/CSP/Systems/Assets/AssetSystem.h b/Library/include/CSP/Systems/Assets/AssetSystem.h index 5b1a71283..0bbd2c4e9 100644 --- a/Library/include/CSP/Systems/Assets/AssetSystem.h +++ b/Library/include/CSP/Systems/Assets/AssetSystem.h @@ -354,15 +354,13 @@ class CSP_API AssetSystem : public SystemBase /// @brief Registers the system to listen for the named event. void RegisterSystemCallback() override; - /// @brief Deregisters the system from listening for the named event. - void DeregisterSystemCallback() override; /// @brief Deserialises the event values of the system. /// @param EventValues std::vector : event values to deserialise CSP_NO_EXPORT void OnAssetDetailBlobChangedEvent(const csp::common::NetworkEventData& NetworkEventData); private: AssetSystem(); // This constructor is only provided to appease the wrapper generator and should not be used - CSP_NO_EXPORT AssetSystem(csp::web::WebClient* InWebClient, csp::multiplayer::NetworkEventBus* InEventBus, common::LogSystem& LogSystem); + CSP_NO_EXPORT AssetSystem(csp::web::WebClient* WebClient, csp::multiplayer::NetworkEventBus& EventBus, common::LogSystem& LogSystem); ~AssetSystem(); CSP_ASYNC_RESULT void DeleteAssetCollectionById(const csp::common::String& AssetCollectionId, NullResultCallback Callback); diff --git a/Library/include/CSP/Systems/HotspotSequence/HotspotSequenceSystem.h b/Library/include/CSP/Systems/HotspotSequence/HotspotSequenceSystem.h index 3e89767d9..c027a95c4 100644 --- a/Library/include/CSP/Systems/HotspotSequence/HotspotSequenceSystem.h +++ b/Library/include/CSP/Systems/HotspotSequence/HotspotSequenceSystem.h @@ -46,7 +46,7 @@ class CSP_API HotspotSequenceSystem : public SystemBase CSP_END_IGNORE HotspotSequenceSystem(csp::systems::SequenceSystem* SequenceSystem, csp::systems::SpaceSystem* SpaceSystem, - csp::multiplayer::NetworkEventBus* InEventBus, csp::common::LogSystem& LogSystem); + csp::multiplayer::NetworkEventBus& EventBus, csp::common::LogSystem& LogSystem); /// @brief Create a Hotspot group /// @param GroupName csp::common::String : The unique grouping name /// @param HotspotIds csp::common::Array : set of Hotspot ids to add to the group @@ -100,8 +100,6 @@ class CSP_API HotspotSequenceSystem : public SystemBase /// @brief Registers the system to listen for the named event. void RegisterSystemCallback() override; - /// @brief Deregisters the system from listening for the named event. - void DeregisterSystemCallback() override; /// @brief Deserialises the event values of the system. /// @param EventValues std::vector : event values to deserialise CSP_NO_EXPORT void OnSequenceChangedEvent(const csp::common::NetworkEventData& NetworkEventData); diff --git a/Library/include/CSP/Systems/Sequence/SequenceSystem.h b/Library/include/CSP/Systems/Sequence/SequenceSystem.h index 944099c7f..c2594196c 100644 --- a/Library/include/CSP/Systems/Sequence/SequenceSystem.h +++ b/Library/include/CSP/Systems/Sequence/SequenceSystem.h @@ -119,15 +119,13 @@ class CSP_API SequenceSystem : public SystemBase /// @brief Registers the system to listen for the named event. void RegisterSystemCallback() override; - /// @brief Deregisters the system from listening for the named event. - void DeregisterSystemCallback() override; /// @brief Deserialises the event values of the system. /// @param EventValues std::vector : event values to deserialise CSP_NO_EXPORT void OnSequenceChangedEvent(const csp::common::NetworkEventData& NetworkEventData); private: SequenceSystem(); // This constructor is only provided to appease the wrapper generator and should not be used - SequenceSystem(csp::web::WebClient* InWebClient, csp::multiplayer::NetworkEventBus* InEventBus, csp::common::LogSystem& LogSystem); + SequenceSystem(csp::web::WebClient* WebClient, csp::multiplayer::NetworkEventBus& EventBus, csp::common::LogSystem& LogSystem); ~SequenceSystem(); csp::services::ApiBase* SequenceAPI; diff --git a/Library/include/CSP/Systems/Spaces/SpaceSystem.h b/Library/include/CSP/Systems/Spaces/SpaceSystem.h index edc886f49..593edf5b7 100644 --- a/Library/include/CSP/Systems/Spaces/SpaceSystem.h +++ b/Library/include/CSP/Systems/Spaces/SpaceSystem.h @@ -55,6 +55,8 @@ CSP_END_IGNORE namespace csp::systems { +class UserSystem; + /// @ingroup Space System /// @brief Public facing system that allows interfacing with Magnopus Connected Services' concept of a Group. /// Offers methods for creating, deleting and joining spaces. @@ -343,22 +345,63 @@ class CSP_API CSP_NO_DISPOSE SpaceSystem : public SystemBase /// @param Callback NullResultCallback : callback when asynchronous task finishes CSP_ASYNC_RESULT void DeleteSpaceGeoLocation(const csp::common::String& SpaceId, NullResultCallback Callback); - /// @brief Duplicate an existing space and assign it to the current user + /// @brief Duplicate an existing space and assign it to the current user. + /// This is a synchronous operation and can have a high execution time for complex spaces. If the user disconnects while waiting for the operation + /// to complete, the duplicate space request will be cancelled. + /// \deprecated Use DuplicateSpaceAsync() instead. This method performs a synchronous duplication of a Space which can timeout and fail for + /// complex Spaces or if the backend services are under excessive load. /// @param SpaceId csp::common::String : Id of the space to duplicate. /// @param NewName csp::common::String : A unique name for the duplicated space. /// @param NewAttributes csp::systems::SpaceAttributes : Attributes to apply to the duplicated space. - /// @param MemberGroupIds csp::common::Array : An optional array of group (space) IDs to copy users from. + /// @param MemberGroupIds csp::common::Array : An optional array of group (space) IDs. Members of these groups will be added + /// to the duplicated space with the same roles. /// @param ShallowCopy bool : If true, the duplicated space will reference the assets of the original space. Otherwise, all assets will be /// duplicated. - /// @param Callback NullResultCallback : callback when asynchronous task finishes + /// @param Callback SpaceResultCallback : callback when asynchronous task finishes. CSP_ASYNC_RESULT void DuplicateSpace(const csp::common::String& SpaceId, const csp::common::String& NewName, SpaceAttributes NewAttributes, const csp::common::Optional>& MemberGroupIds, bool ShallowCopy, SpaceResultCallback Callback); + /// @brief Duplicate an existing space and assign it to the current user. + /// This is an asynchronous operation. If the user disconnects while waiting for the operation to complete it will continue unaffected. Please + /// subcribe to the AsyncCallCompletedCallback via @ref SpaceSystem::SetAsyncCallCompletedCallback() to be notified when the duplication operation + /// is complete. The AsyncCallCompletedEventData returned by the AsyncCallCompletedCallback will contain the following information: + /// - OperationName: "DuplicateSpaceAsync". + /// - ReferenceId: Id of the newly duplicated Space. + /// - ReferenceType: "GroupId". + /// @param SpaceId csp::common::String : Id of the space to duplicate. + /// @param NewName csp::common::String : A unique name for the duplicated space. + /// @param NewAttributes csp::systems::SpaceAttributes : Attributes to apply to the duplicated space. + /// @param MemberGroupIds csp::common::Array : An optional array of group (space) IDs. Members of these groups will be added + /// to the duplicated space with the same roles. + /// @param ShallowCopy bool : If true, the duplicated space will reference the assets of the original space. Otherwise, all assets will be + /// duplicated. + /// @param Callback NullResultCallback : callback when asynchronous task is successfully received by the backend services. + CSP_ASYNC_RESULT void DuplicateSpaceAsync(const csp::common::String& SpaceId, const csp::common::String& NewName, SpaceAttributes NewAttributes, + const csp::common::Optional>& MemberGroupIds, bool ShallowCopy, NullResultCallback Callback); + ///@} + /// @brief The callback for receiving an alert when an async operation is completed. + /// Currently this callback is only being used for the DuplicateSpaceAsync operation. + /// A callback can be set via @ref SpaceSystem::SetAsyncCallCompletedCallback(). + typedef std::function AsyncCallCompletedCallbackHandler; + + /// @brief Sets a callback for the async call completed event. Triggered when an async call to DuplicateSpace is completed. + /// @param Callback AsyncCallCompletedCallbackHandler: Callback to receive data concerning the Space duplication. + CSP_EVENT void SetAsyncCallCompletedCallback(AsyncCallCompletedCallbackHandler Callback); + + /// @brief Deserialises the AsyncCallCompleted event values. + /// The AsyncCallCompletedEventData returned by the AsyncCallCompletedCallback will contain the following information: + /// - OperationName: "DuplicateSpaceAsync". + /// - ReferenceId: Id of the newly duplicated Space. + /// - ReferenceType: "GroupId". + /// @param EventValues std::vector : event values to deserialise + CSP_NO_EXPORT void OnAsyncCallCompletedEvent(const csp::common::NetworkEventData& NetworkEventData); + private: SpaceSystem(); // This constructor is only provided to appease the wrapper generator and should not be used - SpaceSystem(csp::web::WebClient* InWebClient, csp::common::LogSystem& LogSystem); + SpaceSystem( + csp::web::WebClient* WebClient, csp::multiplayer::NetworkEventBus& EventBus, UserSystem* UserSystem, csp::common::LogSystem& LogSystem); ~SpaceSystem(); // Space Metadata @@ -376,6 +419,8 @@ class CSP_API CSP_NO_DISPOSE SpaceSystem : public SystemBase void GetSpaceGeoLocationInternal(const csp::common::String& SpaceId, SpaceGeoLocationResultCallback Callback); + AsyncCallCompletedCallbackHandler AsyncCallCompletedCallback; + // CreateSpace Continuations async::task CreateSpaceGroupInfo(const csp::common::String& Name, const csp::common::String& Description, SpaceAttributes Attributes, const csp::common::Optional>& Tags); @@ -399,6 +444,8 @@ class CSP_API CSP_NO_DISPOSE SpaceSystem : public SystemBase auto AddUserToSpaceIfNecessary(SpaceResultCallback Callback, SpaceSystem& SpaceSystem); auto FireEnterSpaceEvent(Space& OutCurrentSpace); + UserSystem* UserSystem; + csp::services::ApiBase* GroupAPI; csp::services::ApiBase* SpaceAPI; Space CurrentSpace; diff --git a/Library/include/CSP/Systems/SystemBase.h b/Library/include/CSP/Systems/SystemBase.h index 3ee1897f2..9fbcefc53 100644 --- a/Library/include/CSP/Systems/SystemBase.h +++ b/Library/include/CSP/Systems/SystemBase.h @@ -49,13 +49,17 @@ namespace csp::systems { /// @brief Base class for all Connected Spaces Platform Systems, which enforces passing of a WebClient or NetworkEventBus instance in the constructor /// of each System. +/// @invariant EventBusPtr can never be null. The NetworkEventBus is owned by the MultiplayerConnection and persists for it's lifetime. It is passed +/// to each system (which derive from SystemBase) by reference to their ctor. This ref is dereferenced before being passed to the SystemBase ctor. +/// @invariant LogSystem can never be null. The LogSystem is owned by the SystemsManager and persists for it's lifetime. It is passed to +/// each system (which derive from SystemBase) by reference to their ctor. This ref is dereferenced before being passed to the SystemBase ctor. class CSP_API CSP_NO_DISPOSE SystemBase { friend class csp::multiplayer::MultiplayerConnection; protected: - CSP_NO_EXPORT SystemBase(csp::web::WebClient* InWebClient, csp::multiplayer::NetworkEventBus* InEventBus, csp::common::LogSystem* LogSystem); - CSP_NO_EXPORT SystemBase(csp::multiplayer::NetworkEventBus* InEventBus, csp::common::LogSystem* LogSystem); + CSP_NO_EXPORT SystemBase(csp::web::WebClient* InWebClient, csp::multiplayer::NetworkEventBus* EventBus, csp::common::LogSystem* LogSystem); + CSP_NO_EXPORT SystemBase(csp::multiplayer::NetworkEventBus* EventBus, csp::common::LogSystem* LogSystem); csp::web::WebClient* WebClient; csp::multiplayer::NetworkEventBus* EventBusPtr; @@ -67,9 +71,6 @@ class CSP_API CSP_NO_DISPOSE SystemBase /// @brief Registers the system to listen for the default event. virtual void RegisterSystemCallback(); - /// @brief Deregisters the system from listening for the default event. - virtual void DeregisterSystemCallback(); - protected: // EM, June2025: Having this on system base makes it quite simple to quit using the singleton macros in all the systems if we wanted to. // Should NOT be null. The only reason this is a pointer is because we can't get the wrapper gen work required to support reference injections diff --git a/Library/include/CSP/Systems/SystemsManager.h b/Library/include/CSP/Systems/SystemsManager.h index 6091127f8..5e992d633 100644 --- a/Library/include/CSP/Systems/SystemsManager.h +++ b/Library/include/CSP/Systems/SystemsManager.h @@ -198,7 +198,6 @@ class CSP_API SystemsManager csp::web::WebClient* WebClient; csp::multiplayer::MultiplayerConnection* MultiplayerConnection; - csp::multiplayer::NetworkEventBus* NetworkEventBus; std::shared_ptr RealtimeEngine; UserSystem* UserSystem; SpaceSystem* SpaceSystem; diff --git a/Library/include/CSP/Systems/Users/UserSystem.h b/Library/include/CSP/Systems/Users/UserSystem.h index 3cb690482..dc82a2493 100644 --- a/Library/include/CSP/Systems/Users/UserSystem.h +++ b/Library/include/CSP/Systems/Users/UserSystem.h @@ -291,8 +291,6 @@ class CSP_API UserSystem : public SystemBase /// @brief Registers the system to listen for the named event. void RegisterSystemCallback() override; - /// @brief Deregisters the system from listening for the named event. - void DeregisterSystemCallback() override; /// @brief Deserialises the event values of the system. /// @param EventValues std::vector : event values to deserialise CSP_NO_EXPORT void OnAccessControlChangedEvent(const csp::common::NetworkEventData& NetworkEventData); @@ -308,7 +306,7 @@ class CSP_API UserSystem : public SystemBase // Emergency Fix: We have a circular dependency issue due to SignalR requiring the AuthContext for construction. To get around this // we pass nullptr to the UserSystem ctor for the NetworkEventBus, and then call this method to set it after the NetworkEventBus has been // constructed. - void SetNetworkEventBus(csp::multiplayer::NetworkEventBus* EventBus); + void SetNetworkEventBus(csp::multiplayer::NetworkEventBus& EventBus); [[nodiscard]] bool EmailCheck(const std::string& Email) const; diff --git a/Library/src/Multiplayer/Election/ClientElectionManager.cpp b/Library/src/Multiplayer/Election/ClientElectionManager.cpp index 35516bdc0..96993ed9f 100644 --- a/Library/src/Multiplayer/Election/ClientElectionManager.cpp +++ b/Library/src/Multiplayer/Election/ClientElectionManager.cpp @@ -484,21 +484,21 @@ bool ClientElectionManager::IsConnected() const void ClientElectionManager::BindNetworkEvents() { - NetworkEventBus* NetworkEventBus = OnlineRealtimeEnginePtr->GetMultiplayerConnectionInstance()->GetEventBusPtr(); + NetworkEventBus& NetworkEventBus = OnlineRealtimeEnginePtr->GetMultiplayerConnectionInstance()->GetEventBus(); - NetworkEventBus->ListenNetworkEvent(csp::multiplayer::NetworkEventRegistration("CSPInternal::ClientElectionManager", ClientElectionMessage), + NetworkEventBus.ListenNetworkEvent(csp::multiplayer::NetworkEventRegistration("CSPInternal::ClientElectionManager", ClientElectionMessage), [this](const csp::common::NetworkEventData& NetworkEventData) { this->OnClientElectionEvent(NetworkEventData.EventValues); }); - NetworkEventBus->ListenNetworkEvent(csp::multiplayer::NetworkEventRegistration("CSPInternal::ClientElectionManager", RemoteRunScriptMessage), + NetworkEventBus.ListenNetworkEvent(csp::multiplayer::NetworkEventRegistration("CSPInternal::ClientElectionManager", RemoteRunScriptMessage), [this](const csp::common::NetworkEventData& NetworkEventData) { this->OnRemoteRunScriptEvent(NetworkEventData.EventValues); }); } void ClientElectionManager::UnBindNetworkEvents() { - NetworkEventBus* NetworkEventBus = OnlineRealtimeEnginePtr->GetMultiplayerConnectionInstance()->GetEventBusPtr(); + NetworkEventBus& NetworkEventBus = OnlineRealtimeEnginePtr->GetMultiplayerConnectionInstance()->GetEventBus(); - NetworkEventBus->StopListenNetworkEvent(csp::multiplayer::NetworkEventRegistration("CSPInternal::ClientElectionManager", ClientElectionMessage)); - NetworkEventBus->StopListenNetworkEvent(csp::multiplayer::NetworkEventRegistration("CSPInternal::ClientElectionManager", RemoteRunScriptMessage)); + NetworkEventBus.StopListenNetworkEvent(csp::multiplayer::NetworkEventRegistration("CSPInternal::ClientElectionManager", ClientElectionMessage)); + NetworkEventBus.StopListenNetworkEvent(csp::multiplayer::NetworkEventRegistration("CSPInternal::ClientElectionManager", RemoteRunScriptMessage)); } void ClientElectionManager::OnClientElectionEvent(const csp::common::Array& Data) diff --git a/Library/src/Multiplayer/MultiplayerConnection.cpp b/Library/src/Multiplayer/MultiplayerConnection.cpp index 3bc8add18..50aede1af 100644 --- a/Library/src/Multiplayer/MultiplayerConnection.cpp +++ b/Library/src/Multiplayer/MultiplayerConnection.cpp @@ -149,7 +149,7 @@ MultiplayerConnection::MultiplayerConnection(csp::common::LogSystem& LogSystem, , Connected(false) , MultiplayerHubMethods(MultiplayerHubMethodMap()) { - EventBusPtr = new NetworkEventBus(this, LogSystem); + EventBus = new NetworkEventBus(this, LogSystem); } MultiplayerConnection::~MultiplayerConnection() @@ -170,7 +170,7 @@ MultiplayerConnection::~MultiplayerConnection() delete (Connection); delete (WebSocketClient); delete (NetworkEventManager); - delete (EventBusPtr); + delete (EventBus); } } @@ -191,7 +191,7 @@ MultiplayerConnection::MultiplayerConnection(const MultiplayerConnection& InBoun DisconnectionCallback = InBoundConnection.DisconnectionCallback; ConnectionCallback = InBoundConnection.ConnectionCallback; NetworkInterruptionCallback = InBoundConnection.NetworkInterruptionCallback; - EventBusPtr = InBoundConnection.EventBusPtr; + EventBus = InBoundConnection.EventBus; Connected = (InBoundConnection.Connected) ? true : false; } @@ -373,7 +373,7 @@ void MultiplayerConnection::Connect(ErrorCodeCallbackHandler Callback, [[maybe_u BindOnRequestToSendObject(); BindOnRequestToDisconnect(); - EventBusPtr->StartEventMessageListening(); + EventBus->StartEventMessageListening(); // We register the network interruption callback as a wrapper because we want to unwrap any signalR exceptions. RegisterNetworkInterruptedCallback(Connection, LogSystem, NetworkInterruptionCallback); diff --git a/Library/src/Multiplayer/NetworkEventBus.cpp b/Library/src/Multiplayer/NetworkEventBus.cpp index 53dc339df..706999d45 100644 --- a/Library/src/Multiplayer/NetworkEventBus.cpp +++ b/Library/src/Multiplayer/NetworkEventBus.cpp @@ -34,7 +34,16 @@ namespace csp::multiplayer constexpr const uint64_t ALL_CLIENTS_ID = std::numeric_limits::max(); -NetworkEventBus::~NetworkEventBus() { } +NetworkEventBus::~NetworkEventBus() +{ + // Clean up all registered listeners. + // The NetworkEventBus is owned by the MultiplayerConnection which is one of the last systems to be destroyed by the Systems Manager. + auto Registrations = AllRegistrations(); + for (const NetworkEventRegistration& Registration : Registrations) + { + StopListenNetworkEvent(Registration); + } +} NetworkEventBus::NetworkEventBus(MultiplayerConnection* InMultiplayerConnection, csp::common::LogSystem& LogSystem) : LogSystem(LogSystem) @@ -262,6 +271,8 @@ std::unique_ptr NetworkEventBus::DeserialiseForEv return std::make_unique(csp::multiplayer::DeserializeAccessControlChangedEvent(EventValues, LogSystem)); case NetworkEvent::GeneralPurposeEvent: return std::make_unique(csp::multiplayer::DeserializeGeneralPurposeEvent(EventValues, LogSystem)); + case NetworkEvent::AsyncCallCompleted: + return std::make_unique(csp::multiplayer::DeserializeAsyncCallCompletedEvent(EventValues, LogSystem)); default: throw std::invalid_argument( fmt::format("DeserialiseForEventType: unknown enum value {}", static_cast>(EventType))); diff --git a/Library/src/Multiplayer/NetworkEventSerialisation.cpp b/Library/src/Multiplayer/NetworkEventSerialisation.cpp index b5044edbd..07df91c8c 100644 --- a/Library/src/Multiplayer/NetworkEventSerialisation.cpp +++ b/Library/src/Multiplayer/NetworkEventSerialisation.cpp @@ -418,4 +418,17 @@ csp::common::SequenceChangedNetworkEventData DeserializeSequenceHotspotChangedEv return ParsedEvent; } + +csp::common::AsyncCallCompletedEventData DeserializeAsyncCallCompletedEvent( + const std::vector& EventValues, csp::common::LogSystem& LogSystem) +{ + csp::common::AsyncCallCompletedEventData ParsedEvent {}; + PopulateCommonEventData(EventValues, ParsedEvent, LogSystem); + + ParsedEvent.OperationName = ParsedEvent.EventValues[0].GetString(); + ParsedEvent.ReferenceId = ParsedEvent.EventValues[1].GetString(); + ParsedEvent.ReferenceType = ParsedEvent.EventValues[2].GetString(); + + return ParsedEvent; +} } diff --git a/Library/src/Multiplayer/NetworkEventSerialisation.h b/Library/src/Multiplayer/NetworkEventSerialisation.h index 4686d3a91..80a3bf031 100644 --- a/Library/src/Multiplayer/NetworkEventSerialisation.h +++ b/Library/src/Multiplayer/NetworkEventSerialisation.h @@ -60,4 +60,9 @@ csp::common::SequenceChangedNetworkEventData DeserializeSequenceChangedEvent( csp::common::SequenceChangedNetworkEventData DeserializeSequenceHotspotChangedEvent( const std::vector& EventValues, csp::common::LogSystem& LogSystem); +// Specialized deserializataion for events triggered when an async call completes. +// Some methods take an AsyncCall boolean argument that allows the operation to be completed asyncronously rather than syncronously. +csp::common::AsyncCallCompletedEventData DeserializeAsyncCallCompletedEvent( + const std::vector& EventValues, csp::common::LogSystem& LogSystem); + } // namespace csp::multiplayer diff --git a/Library/src/Systems/Assets/AssetSystem.cpp b/Library/src/Systems/Assets/AssetSystem.cpp index 1db8acf43..7e1a21cd5 100644 --- a/Library/src/Systems/Assets/AssetSystem.cpp +++ b/Library/src/Systems/Assets/AssetSystem.cpp @@ -358,13 +358,13 @@ AssetSystem::AssetSystem() { } -AssetSystem::AssetSystem(web::WebClient* InWebClient, multiplayer::NetworkEventBus* InEventBus, common::LogSystem& LogSystem) - : SystemBase(InWebClient, InEventBus, &LogSystem) +AssetSystem::AssetSystem(web::WebClient* WebClient, multiplayer::NetworkEventBus& EventBus, common::LogSystem& LogSystem) + : SystemBase(WebClient, &EventBus, &LogSystem) { - PrototypeAPI = new chs::PrototypeApi(InWebClient); - AssetDetailAPI = new chs::AssetDetailApi(InWebClient); + PrototypeAPI = new chs::PrototypeApi(WebClient); + AssetDetailAPI = new chs::AssetDetailApi(WebClient); - FileManager = new web::RemoteFileManager(InWebClient); + FileManager = new web::RemoteFileManager(WebClient); RegisterSystemCallback(); } @@ -375,8 +375,6 @@ AssetSystem::~AssetSystem() delete (AssetDetailAPI); delete (PrototypeAPI); - - DeregisterSystemCallback(); } void AssetSystem::DeleteAssetCollectionById(const csp::common::String& AssetCollectionId, NullResultCallback Callback) @@ -1801,16 +1799,6 @@ void AssetSystem::RegisterSystemCallback() [this](const csp::common::NetworkEventData& NetworkEventData) { this->OnAssetDetailBlobChangedEvent(NetworkEventData); }); } -void AssetSystem::DeregisterSystemCallback() -{ - if (EventBusPtr) - { - - EventBusPtr->StopListenNetworkEvent(csp::multiplayer::NetworkEventRegistration("CSPInternal::AssetSystem", - csp::multiplayer::NetworkEventBus::StringFromNetworkEvent(csp::multiplayer::NetworkEventBus::NetworkEvent::AssetDetailBlobChanged))); - } -} - void AssetSystem::OnAssetDetailBlobChangedEvent(const csp::common::NetworkEventData& NetworkEventData) { if (!AssetDetailBlobChangedCallback && !MaterialChangedCallback) diff --git a/Library/src/Systems/Conversation/ConversationSystemInternal.cpp b/Library/src/Systems/Conversation/ConversationSystemInternal.cpp index 9c58d0342..dcef5422b 100644 --- a/Library/src/Systems/Conversation/ConversationSystemInternal.cpp +++ b/Library/src/Systems/Conversation/ConversationSystemInternal.cpp @@ -335,17 +335,16 @@ namespace } ConversationSystemInternal::ConversationSystemInternal(systems::AssetSystem* AssetSystem, systems::SpaceSystem* SpaceSystem, - systems::UserSystem* UserSystem, multiplayer::NetworkEventBus* NetworkEventBus, csp::common::LogSystem& LogSystem) - : SystemBase(NetworkEventBus, &LogSystem) + systems::UserSystem* UserSystem, multiplayer::NetworkEventBus& EventBus, csp::common::LogSystem& LogSystem) + : SystemBase(&EventBus, &LogSystem) , AssetSystem { AssetSystem } , SpaceSystem { SpaceSystem } , UserSystem { UserSystem } - , NetworkEventBus { NetworkEventBus } { RegisterSystemCallback(); } -ConversationSystemInternal::~ConversationSystemInternal() { DeregisterSystemCallback(); } +ConversationSystemInternal::~ConversationSystemInternal() { } void ConversationSystemInternal::CreateConversation(const common::String& Message, StringResultCallback Callback) { @@ -384,7 +383,7 @@ void ConversationSystemInternal::CreateConversation(const common::String& Messag multiplayer::MessageInfo MessageInfo = ConversationSystemHelpers::GetConversationInfoFromConversationAssetCollection(AddCommentContainerResult.GetAssetCollection()); - SendConversationEvent(multiplayer::ConversationEventType::NewConversation, MessageInfo, NetworkEventBus, SignalRCallback); + SendConversationEvent(multiplayer::ConversationEventType::NewConversation, MessageInfo, EventBusPtr, SignalRCallback); }; const auto UniqueAssetCollectionName = ConversationSystemHelpers::GetUniqueConversationContainerAssetCollectionName(SpaceId, UserId); @@ -437,7 +436,7 @@ void ConversationSystemInternal::DeleteConversation(const common::String& Conver multiplayer::MessageInfo MessageInfo = ConversationSystemHelpers::GetConversationInfoFromConversationAssetCollection(ConversationAssetCollection); - SendConversationEvent(multiplayer::ConversationEventType::DeleteConversation, MessageInfo, NetworkEventBus, SignalRCallback); + SendConversationEvent(multiplayer::ConversationEventType::DeleteConversation, MessageInfo, EventBusPtr, SignalRCallback); }; AssetSystem->GetAssetCollectionById(ConversationId, GetConversationCallback); @@ -472,7 +471,7 @@ void ConversationSystemInternal::AddMessage( }; const multiplayer::MessageInfo& MessageInfo = MessageResultCallbackResult.GetMessageInfo(); - SendConversationEvent(multiplayer::ConversationEventType::NewMessage, MessageInfo, NetworkEventBus, SignalRCallback); + SendConversationEvent(multiplayer::ConversationEventType::NewMessage, MessageInfo, EventBusPtr, SignalRCallback); }; multiplayer::MessageInfo MessageInfo(ConversationId, false, Message); @@ -531,7 +530,7 @@ void ConversationSystemInternal::DeleteMessage(const common::String& Conversatio this->AssetSystem->DeleteAssetCollection(MessageAssetCollection, DeleteAssetCollectionCallback); }; - SendConversationEvent(multiplayer::ConversationEventType::DeleteMessage, Info, NetworkEventBus, SignalRCallback); + SendConversationEvent(multiplayer::ConversationEventType::DeleteMessage, Info, EventBusPtr, SignalRCallback); }; AssetSystem->GetAssetCollectionById(MessageId, GetMessageCallback); @@ -633,7 +632,7 @@ void ConversationSystemInternal::UpdateConversation( auto UpdatedInfo = systems::ConversationSystemHelpers::GetConversationInfoFromConversationAssetCollection( GetUpdatedConversationResult.GetAssetCollection()); - SendConversationEvent(multiplayer::ConversationEventType::ConversationInformation, UpdatedInfo, NetworkEventBus, SignalRCallback); + SendConversationEvent(multiplayer::ConversationEventType::ConversationInformation, UpdatedInfo, EventBusPtr, SignalRCallback); }; const AssetCollection& ConversationAssetCollection = GetConversationResult.GetAssetCollection(); @@ -722,7 +721,7 @@ void ConversationSystemInternal::UpdateMessage(const common::String& /*Conversat INVOKE_IF_NOT_NULL(Callback, Result); }; - SendConversationEvent(multiplayer::ConversationEventType::MessageInformation, Result.GetMessageInfo(), NetworkEventBus, SignalRCallback); + SendConversationEvent(multiplayer::ConversationEventType::MessageInformation, Result.GetMessageInfo(), EventBusPtr, SignalRCallback); }; const AssetCollection& MessageAssetCollection = GetMessageResult.GetAssetCollection(); @@ -889,7 +888,7 @@ void ConversationSystemInternal::SetConversationAnnotation(const csp::common::St "Failed to update message asset collection metadata.", {}, {}, {})) .then(SetMessageAssetCollection(ConversationAssetCollection)) // 7. Send multiplayer event - .then(SendConversationEvent(multiplayer::ConversationEventType::SetConversationAnnotation, ConversationAssetCollection, NetworkEventBus)) + .then(SendConversationEvent(multiplayer::ConversationEventType::SetConversationAnnotation, ConversationAssetCollection, EventBusPtr)) .then(common::continuations::AssertRequestSuccessOrErrorFromMultiplayerErrorCode( "ConversationSystemInternal::SetConversationAnnotation, successfully sent multiplayer event", MakeInvalid(), *LogSystem)) @@ -916,7 +915,7 @@ void ConversationSystemInternal::DeleteConversationAnnotation(const csp::common: .then(RemoveAnnotationMetadata(AssetSystem)) .then(SetMessageAssetCollection(ConversationAssetCollection)) // 3. Send multiplayer event - .then(SendConversationEvent(multiplayer::ConversationEventType::DeleteConversationAnnotation, ConversationAssetCollection, NetworkEventBus)) + .then(SendConversationEvent(multiplayer::ConversationEventType::DeleteConversationAnnotation, ConversationAssetCollection, EventBusPtr)) .then(common::continuations::AssertRequestSuccessOrErrorFromMultiplayerErrorCode( "ConversationSystemInternal::DeleteAnnotation, successfully sent multiplayer event", MakeInvalid(), *LogSystem)) // 4. Delete annoation asset @@ -1039,7 +1038,7 @@ void ConversationSystemInternal::SetAnnotation(const csp::common::String& Conver "Failed to update message asset collection metadata.", {}, {}, {})) .then(SetMessageAssetCollection(MessageAssetCollection)) // 7. Send multiplayer event - .then(SendConversationMessageEvent(multiplayer::ConversationEventType::SetAnnotation, MessageAssetCollection, NetworkEventBus)) + .then(SendConversationMessageEvent(multiplayer::ConversationEventType::SetAnnotation, MessageAssetCollection, EventBusPtr)) .then(common::continuations::AssertRequestSuccessOrErrorFromMultiplayerErrorCode( "ConversationSystemInternal::SetAnnotation, successfully sent multiplayer event", MakeInvalid(), *LogSystem)) @@ -1070,7 +1069,7 @@ void ConversationSystemInternal::DeleteAnnotation( .then(RemoveAnnotationMetadata(AssetSystem)) .then(SetMessageAssetCollection(MessageAssetCollection)) // 3. Send multiplayer event - .then(SendConversationMessageEvent(multiplayer::ConversationEventType::DeleteAnnotation, MessageAssetCollection, NetworkEventBus)) + .then(SendConversationMessageEvent(multiplayer::ConversationEventType::DeleteAnnotation, MessageAssetCollection, EventBusPtr)) .then(common::continuations::AssertRequestSuccessOrErrorFromMultiplayerErrorCode( "ConversationSystemInternal::DeleteAnnotation, successfully sent multiplayer event", MakeInvalid(), *LogSystem)) // 4. Delete annoation asset @@ -1154,15 +1153,6 @@ void ConversationSystemInternal::RegisterSystemCallback() }); } -void ConversationSystemInternal::DeregisterSystemCallback() -{ - if (EventBusPtr) - { - EventBusPtr->StopListenNetworkEvent(csp::multiplayer::NetworkEventRegistration("CSPInternal::ConversationSystemInternal", - csp::multiplayer::NetworkEventBus::StringFromNetworkEvent(csp::multiplayer::NetworkEventBus::NetworkEvent::Conversation))); - } -} - void ConversationSystemInternal::FlushEvents() { for (auto It = std::begin(Events); It != std::end(Events);) diff --git a/Library/src/Systems/Conversation/ConversationSystemInternal.h b/Library/src/Systems/Conversation/ConversationSystemInternal.h index e68073ada..2ec70b87b 100644 --- a/Library/src/Systems/Conversation/ConversationSystemInternal.h +++ b/Library/src/Systems/Conversation/ConversationSystemInternal.h @@ -50,7 +50,7 @@ class CSP_API ConversationSystemInternal : public SystemBase CSP_END_IGNORE ConversationSystemInternal(csp::systems::AssetSystem* AssetSystem, csp::systems::SpaceSystem* SpaceSystem, csp::systems::UserSystem* UserSystem, - csp::multiplayer::NetworkEventBus* InEventBus, csp::common::LogSystem& LogSystem); + csp::multiplayer::NetworkEventBus& EventBus, csp::common::LogSystem& LogSystem); ~ConversationSystemInternal(); @@ -111,8 +111,6 @@ class CSP_API ConversationSystemInternal : public SystemBase /// @brief Registers the system to listen for the named event. void RegisterSystemCallback() override; - /// @brief Deregisters the system from listening for the named event. - void DeregisterSystemCallback() override; // Attempt to flush any events that haven't been sent. // They may fail to send in situtations where the conversation component hasn't been created before the creation event fires. @@ -125,8 +123,6 @@ class CSP_API ConversationSystemInternal : public SystemBase csp::systems::SpaceSystem* SpaceSystem; csp::systems::UserSystem* UserSystem; - csp::multiplayer::NetworkEventBus* NetworkEventBus; - std::unordered_set Components; std::vector> Events; }; diff --git a/Library/src/Systems/HotspotSequence/HotspotSequenceSystem.cpp b/Library/src/Systems/HotspotSequence/HotspotSequenceSystem.cpp index 2f0e5ff4d..a1e63b586 100644 --- a/Library/src/Systems/HotspotSequence/HotspotSequenceSystem.cpp +++ b/Library/src/Systems/HotspotSequence/HotspotSequenceSystem.cpp @@ -88,8 +88,8 @@ namespace } // namespace HotspotSequenceSystem::HotspotSequenceSystem(csp::systems::SequenceSystem* SequenceSystem, csp::systems::SpaceSystem* SpaceSystem, - csp::multiplayer::NetworkEventBus* NetworkEventBus, csp::common::LogSystem& LogSystem) - : SystemBase(NetworkEventBus, &LogSystem) + csp::multiplayer::NetworkEventBus& EventBus, csp::common::LogSystem& LogSystem) + : SystemBase(&EventBus, &LogSystem) { this->SequenceSystem = SequenceSystem; this->SpaceSystem = SpaceSystem; @@ -263,8 +263,6 @@ HotspotSequenceSystem::~HotspotSequenceSystem() { SpaceSystem = nullptr; SequenceSystem = nullptr; - - DeregisterSystemCallback(); } void HotspotSequenceSystem::RemoveItemFromGroups(const csp::common::String& ItemID, csp::systems::NullResultCallback /*Callback*/) @@ -343,15 +341,6 @@ void HotspotSequenceSystem::RegisterSystemCallback() [this](const csp::common::NetworkEventData& NetworkEventData) { this->OnSequenceChangedEvent(NetworkEventData); }); } -void HotspotSequenceSystem::DeregisterSystemCallback() -{ - if (EventBusPtr) - { - EventBusPtr->StopListenNetworkEvent(csp::multiplayer::NetworkEventRegistration("CSPInternal::HotspotSequenceSystem", - csp::multiplayer::NetworkEventBus::StringFromNetworkEvent(csp::multiplayer::NetworkEventBus::NetworkEvent::SequenceChanged))); - } -} - void HotspotSequenceSystem::OnSequenceChangedEvent(const csp::common::NetworkEventData& NetworkEventData) { // This may be either a hotspot sequence event or a regular sequence event.. we're only interested in hotspot. diff --git a/Library/src/Systems/Sequence/SequenceSystem.cpp b/Library/src/Systems/Sequence/SequenceSystem.cpp index cbfda1cb1..fefb879ea 100644 --- a/Library/src/Systems/Sequence/SequenceSystem.cpp +++ b/Library/src/Systems/Sequence/SequenceSystem.cpp @@ -303,20 +303,15 @@ SequenceSystem::SequenceSystem() { } -SequenceSystem::SequenceSystem(web::WebClient* InWebClient, multiplayer::NetworkEventBus* InEventBus, csp::common::LogSystem& LogSystem) - : SystemBase(InWebClient, InEventBus, &LogSystem) +SequenceSystem::SequenceSystem(web::WebClient* WebClient, multiplayer::NetworkEventBus& EventBus, csp::common::LogSystem& LogSystem) + : SystemBase(WebClient, &EventBus, &LogSystem) { - SequenceAPI = new chs::SequenceApi(InWebClient); + SequenceAPI = new chs::SequenceApi(WebClient); RegisterSystemCallback(); } -SequenceSystem::~SequenceSystem() -{ - delete (SequenceAPI); - - DeregisterSystemCallback(); -} +SequenceSystem::~SequenceSystem() { delete (SequenceAPI); } void SequenceSystem::SetSequenceChangedCallback(SequenceChangedCallbackHandler Callback) { @@ -343,15 +338,6 @@ void SequenceSystem::RegisterSystemCallback() [this](const csp::common::NetworkEventData& NetworkEventData) { this->OnSequenceChangedEvent(NetworkEventData); }); } -void SequenceSystem::DeregisterSystemCallback() -{ - if (EventBusPtr) - { - EventBusPtr->StopListenNetworkEvent(csp::multiplayer::NetworkEventRegistration("CSPInternal::SequenceSystem", - csp::multiplayer::NetworkEventBus::StringFromNetworkEvent(csp::multiplayer::NetworkEventBus::NetworkEvent::SequenceChanged))); - } -} - void SequenceSystem::OnSequenceChangedEvent(const csp::common::NetworkEventData& NetworkEventData) { // This may be either a hotspot sequence event or a regular sequence event.. we're only interested in non-hotspot. diff --git a/Library/src/Systems/Spaces/SpaceSystem.cpp b/Library/src/Systems/Spaces/SpaceSystem.cpp index 4dbc0ad42..e2df42149 100644 --- a/Library/src/Systems/Spaces/SpaceSystem.cpp +++ b/Library/src/Systems/Spaces/SpaceSystem.cpp @@ -58,6 +58,31 @@ namespace constexpr const int MAX_SPACES_RESULTS = 100; +// Construct a new DuplicateSpaceOptions dto request object. This function is called by both DuplicateSpace and DuplicateSpaceAsync methods. +// The only difference is in the value they pass for the AsyncCall parameter. +std::shared_ptr ConstructDuplicateSpaceOptions(csp::systems::UserSystem* UserSystem, const String& SpaceId, + const String& NewName, csp::systems::SpaceAttributes NewAttributes, const Optional>& MemberGroupIds, bool ShallowCopy, + bool AsyncCall) +{ + auto Request = std::make_shared(); + Request->SetSpaceId(SpaceId); + Request->SetNewGroupOwnerId(UserSystem->GetLoginState().UserId); + Request->SetNewUniqueName(NewName); + Request->SetDiscoverable(HasFlag(NewAttributes, csp::systems::SpaceAttributes::IsDiscoverable)); + Request->SetRequiresInvite(HasFlag(NewAttributes, csp::systems::SpaceAttributes::RequiresInvite)); + Request->SetShallowCopy(ShallowCopy); + Request->SetAsyncCall(AsyncCall); + + if (MemberGroupIds.HasValue()) + { + auto MemberGroupIdsVec = Convert(MemberGroupIds); + + Request->SetMemberGroupIds(*MemberGroupIdsVec); + } + + return Request; +} + } // namespace namespace csp::systems @@ -65,20 +90,27 @@ namespace csp::systems SpaceSystem::SpaceSystem() : SystemBase(nullptr, nullptr, nullptr) + , UserSystem(nullptr) , GroupAPI(nullptr) , SpaceAPI(nullptr) { } -SpaceSystem::SpaceSystem(csp::web::WebClient* InWebClient, csp::common::LogSystem& LogSystem) - : SystemBase(InWebClient, nullptr, &LogSystem) +SpaceSystem::SpaceSystem( + csp::web::WebClient* WebClient, multiplayer::NetworkEventBus& EventBus, csp::systems::UserSystem* UserSystem, csp::common::LogSystem& LogSystem) + : SystemBase(WebClient, &EventBus, &LogSystem) + , UserSystem(UserSystem) , CurrentSpace() { - GroupAPI = new chs::GroupApi(InWebClient); - SpaceAPI = new chsaggregation::SpaceApi(InWebClient); + GroupAPI = new chs::GroupApi(WebClient); + SpaceAPI = new chsaggregation::SpaceApi(WebClient); } -SpaceSystem::~SpaceSystem() { delete (GroupAPI); } +SpaceSystem::~SpaceSystem() +{ + delete (GroupAPI); + delete (SpaceAPI); +} /* CreateSpace Continuations */ async::task SpaceSystem::CreateSpaceGroupInfo( @@ -715,7 +747,6 @@ void SpaceSystem::DeleteSpace(const csp::common::String& SpaceId, NullResultCall void SpaceSystem::GetSpaces(SpacesResultCallback Callback) { - const auto* UserSystem = SystemsManager::Get().GetUserSystem(); const String InUserId = UserSystem->GetLoginState().UserId; csp::services::ResponseHandlerPtr ResponseHandler @@ -1925,37 +1956,67 @@ void SpaceSystem::DeleteSpaceGeoLocation(const csp::common::String& SpaceId, Nul void SpaceSystem::DuplicateSpace(const String& SpaceId, const String& NewName, SpaceAttributes NewAttributes, const Optional>& MemberGroupIds, bool ShallowCopy, SpaceResultCallback Callback) { - auto Request = std::make_shared(); - Request->SetSpaceId(SpaceId); - Request->SetNewGroupOwnerId(SystemsManager::Get().GetUserSystem()->GetLoginState().UserId); - Request->SetNewUniqueName(NewName); - Request->SetDiscoverable(HasFlag(NewAttributes, csp::systems::SpaceAttributes::IsDiscoverable)); - Request->SetRequiresInvite(HasFlag(NewAttributes, csp::systems::SpaceAttributes::RequiresInvite)); - Request->SetShallowCopy(ShallowCopy); + auto Request = ConstructDuplicateSpaceOptions(UserSystem, SpaceId, NewName, NewAttributes, MemberGroupIds, ShallowCopy, false); - if (MemberGroupIds.HasValue()) - { - std::vector GroupIds; - GroupIds.reserve(MemberGroupIds->Size()); + csp::services::ResponseHandlerPtr ResponseHandler + = SpaceAPI->CreateHandler(Callback, nullptr); - for (size_t i = 0; i < MemberGroupIds->Size(); ++i) + static_cast(SpaceAPI)->spacesSpaceIdDuplicatePost( { - GroupIds.push_back(MemberGroupIds->operator[](i)); - } + SpaceId, // spaceId + false, // asyncCall + Request // RequestBody + }, + ResponseHandler // ResponseHandler + ); +} - Request->SetMemberGroupIds(GroupIds); - } +void SpaceSystem::DuplicateSpaceAsync(const String& SpaceId, const String& NewName, SpaceAttributes NewAttributes, + const Optional>& MemberGroupIds, bool ShallowCopy, NullResultCallback Callback) +{ + auto Request = ConstructDuplicateSpaceOptions(UserSystem, SpaceId, NewName, NewAttributes, MemberGroupIds, ShallowCopy, true); csp::services::ResponseHandlerPtr ResponseHandler - = SpaceAPI->CreateHandler(Callback, nullptr); + = SpaceAPI->CreateHandler(Callback, nullptr); static_cast(SpaceAPI)->spacesSpaceIdDuplicatePost( { SpaceId, // spaceId - false, // asyncCall + true, // asyncCall Request // RequestBody }, ResponseHandler // ResponseHandler ); } + +void SpaceSystem::SetAsyncCallCompletedCallback(AsyncCallCompletedCallbackHandler Callback) +{ + AsyncCallCompletedCallback = std::move(Callback); + + if (!AsyncCallCompletedCallback) + { + CSP_LOG_ERROR_MSG("Error: The AsyncCallCompletedCallback handler has not been set and the SpaceSystem has not been registered with the " + "AsyncCallCompleted event. Please call 'SetAsyncCallCompletedCallback()' with a valid AsyncCallCompletedCallbackHandler."); + return; + } + + EventBusPtr->ListenNetworkEvent( + csp::multiplayer::NetworkEventRegistration("CSPInternal::SpaceSystem", + csp::multiplayer::NetworkEventBus::StringFromNetworkEvent(csp::multiplayer::NetworkEventBus::NetworkEvent::AsyncCallCompleted)), + [this](const csp::common::NetworkEventData& NetworkEventData) { this->OnAsyncCallCompletedEvent(NetworkEventData); }); +} + +void SpaceSystem::OnAsyncCallCompletedEvent(const csp::common::NetworkEventData& NetworkEventData) +{ + if (!AsyncCallCompletedCallback) + { + return; + } + + const csp::common::AsyncCallCompletedEventData& AsyncCallCompletedEventData + = static_cast(NetworkEventData); + + AsyncCallCompletedCallback(AsyncCallCompletedEventData); +} + } // namespace csp::systems diff --git a/Library/src/Systems/SystemBase.cpp b/Library/src/Systems/SystemBase.cpp index 1134ab7e1..31342fbd6 100644 --- a/Library/src/Systems/SystemBase.cpp +++ b/Library/src/Systems/SystemBase.cpp @@ -28,34 +28,25 @@ SystemBase::SystemBase() { } -SystemBase::SystemBase(csp::web::WebClient* InWebClient, csp::multiplayer::NetworkEventBus* InEventBus, csp::common::LogSystem* LogSystem) +SystemBase::SystemBase(csp::web::WebClient* InWebClient, csp::multiplayer::NetworkEventBus* EventBus, csp::common::LogSystem* LogSystem) : WebClient(InWebClient) - , EventBusPtr(InEventBus) + , EventBusPtr(EventBus) , LogSystem(LogSystem) { } -SystemBase::SystemBase(csp::multiplayer::NetworkEventBus* InEventBus, csp::common::LogSystem* LogSystem) +SystemBase::SystemBase(csp::multiplayer::NetworkEventBus* EventBus, csp::common::LogSystem* LogSystem) : WebClient(nullptr) - , EventBusPtr(InEventBus) + , EventBusPtr(EventBus) , LogSystem(LogSystem) { } -SystemBase::~SystemBase() -{ - DeregisterSystemCallback(); - EventBusPtr = nullptr; -} +SystemBase::~SystemBase() { EventBusPtr = nullptr; } void SystemBase::RegisterSystemCallback() { // Do nothing. } -void SystemBase::DeregisterSystemCallback() -{ - // Do nothing. -} - } // namespace csp::systems diff --git a/Library/src/Systems/SystemsManager.cpp b/Library/src/Systems/SystemsManager.cpp index 00c869d93..ea828df94 100644 --- a/Library/src/Systems/SystemsManager.cpp +++ b/Library/src/Systems/SystemsManager.cpp @@ -102,7 +102,7 @@ ExternalServiceProxySystem* SystemsManager::GetExternalServicesProxySystem() { r csp::multiplayer::MultiplayerConnection* SystemsManager::GetMultiplayerConnection() { return MultiplayerConnection; } -csp::multiplayer::NetworkEventBus* SystemsManager::GetEventBus() { return NetworkEventBus; } +csp::multiplayer::NetworkEventBus* SystemsManager::GetEventBus() { return &MultiplayerConnection->GetEventBus(); } csp::multiplayer::OnlineRealtimeEngine* SystemsManager::MakeOnlineRealtimeEngine() { @@ -130,7 +130,6 @@ csp::common::IRealtimeEngine* SystemsManager::MakeRealtimeEngine(csp::common::Re SystemsManager::SystemsManager() : WebClient(nullptr) , MultiplayerConnection(nullptr) - , NetworkEventBus(nullptr) , RealtimeEngine(nullptr) , UserSystem(nullptr) , SpaceSystem(nullptr) @@ -184,17 +183,15 @@ void SystemsManager::CreateSystems(csp::multiplayer::ISignalRConnection* SignalR MultiplayerConnection = new csp::multiplayer::MultiplayerConnection(*LogSystem, *SignalRConnection); - NetworkEventBus = MultiplayerConnection->GetEventBusPtr(); - // Set the NetworkEventBus now that it has been initialized. - UserSystem->SetNetworkEventBus(NetworkEventBus); + UserSystem->SetNetworkEventBus(MultiplayerConnection->GetEventBus()); VoipSystem = new csp::systems::VoipSystem(); // SystemBase inheritors - SpaceSystem = new csp::systems::SpaceSystem(WebClient, *LogSystem); - AssetSystem = new csp::systems::AssetSystem(WebClient, NetworkEventBus, *LogSystem); + SpaceSystem = new csp::systems::SpaceSystem(WebClient, MultiplayerConnection->GetEventBus(), UserSystem, *LogSystem); + AssetSystem = new csp::systems::AssetSystem(WebClient, MultiplayerConnection->GetEventBus(), *LogSystem); AnchorSystem = new csp::systems::AnchorSystem(WebClient, *LogSystem); PointOfInterestSystem = new csp::systems::PointOfInterestInternalSystem(WebClient, *LogSystem); ApplicationSettingsSystem = new csp::systems::ApplicationSettingsSystem(WebClient, *LogSystem); @@ -204,9 +201,10 @@ void SystemsManager::CreateSystems(csp::multiplayer::ISignalRConnection* SignalR EventTicketingSystem = new csp::systems::EventTicketingSystem(WebClient, *LogSystem); ECommerceSystem = new csp::systems::ECommerceSystem(WebClient, *LogSystem); QuotaSystem = new csp::systems::QuotaSystem(WebClient, *LogSystem); - SequenceSystem = new csp::systems::SequenceSystem(WebClient, NetworkEventBus, *LogSystem); - HotspotSequenceSystem = new csp::systems::HotspotSequenceSystem(SequenceSystem, SpaceSystem, NetworkEventBus, *LogSystem); - ConversationSystem = new csp::systems::ConversationSystemInternal(AssetSystem, SpaceSystem, UserSystem, NetworkEventBus, *LogSystem); + SequenceSystem = new csp::systems::SequenceSystem(WebClient, MultiplayerConnection->GetEventBus(), *LogSystem); + HotspotSequenceSystem = new csp::systems::HotspotSequenceSystem(SequenceSystem, SpaceSystem, MultiplayerConnection->GetEventBus(), *LogSystem); + ConversationSystem + = new csp::systems::ConversationSystemInternal(AssetSystem, SpaceSystem, UserSystem, MultiplayerConnection->GetEventBus(), *LogSystem); AnalyticsSystem = new csp::systems::AnalyticsSystem(WebClient, &(csp::CSPFoundation::GetClientUserAgentInfo()), *LogSystem); ExternalServiceProxySystem = new csp::systems::ExternalServiceProxySystem(WebClient, *LogSystem); } diff --git a/Library/src/Systems/Users/UserSystem.cpp b/Library/src/Systems/Users/UserSystem.cpp index 5fefde205..8a9db3b4e 100644 --- a/Library/src/Systems/Users/UserSystem.cpp +++ b/Library/src/Systems/Users/UserSystem.cpp @@ -199,13 +199,11 @@ UserSystem::~UserSystem() delete (ProfileAPI); delete (AuthenticationAPI); delete (StripeAPI); - - DeregisterSystemCallback(); } -void UserSystem::SetNetworkEventBus(csp::multiplayer::NetworkEventBus* EventBus) +void UserSystem::SetNetworkEventBus(csp::multiplayer::NetworkEventBus& EventBus) { - EventBusPtr = EventBus; + EventBusPtr = &EventBus; RegisterSystemCallback(); } @@ -929,15 +927,6 @@ void UserSystem::RegisterSystemCallback() [this](const csp::common::NetworkEventData& NetworkEventData) { this->OnAccessControlChangedEvent(NetworkEventData); }); } -void UserSystem::DeregisterSystemCallback() -{ - if (EventBusPtr) - { - EventBusPtr->StopListenNetworkEvent(csp::multiplayer::NetworkEventRegistration("CSPInternal::UserSystem", - csp::multiplayer::NetworkEventBus::StringFromNetworkEvent(csp::multiplayer::NetworkEventBus::NetworkEvent::AccessControlChanged))); - } -} - void UserSystem::OnAccessControlChangedEvent(const csp::common::NetworkEventData& NetworkEventData) { if (!UserPermissionsChangedCallback) diff --git a/Tests/src/PublicAPITests/AssetSystemTests.cpp b/Tests/src/PublicAPITests/AssetSystemTests.cpp index 06f7be466..eefb86203 100644 --- a/Tests/src/PublicAPITests/AssetSystemTests.cpp +++ b/Tests/src/PublicAPITests/AssetSystemTests.cpp @@ -2065,7 +2065,6 @@ CSP_PUBLIC_TEST(CSPEngine, AssetSystemTests, AssetProcessGracefulFailureCallback auto* SpaceSystem = SystemsManager.GetSpaceSystem(); auto* AssetSystem = SystemsManager.GetAssetSystem(); auto* Connection = SystemsManager.GetMultiplayerConnection(); - auto* NetworkEventBus = SystemsManager.GetEventBus(); const char* TestSpaceName = "CSP-UNITTEST-SPACE-MAG"; const char* TestSpaceDescription = "CSP-UNITTEST-SPACEDESC-MAG"; @@ -2115,8 +2114,9 @@ CSP_PUBLIC_TEST(CSPEngine, AssetSystemTests, AssetProcessGracefulFailureCallback csp::common::ReplicatedValue Param4 = ""; csp::common::ReplicatedValue Param5 = ""; - NetworkEventBus->SendNetworkEventToClient(NetworkEventBus::StringFromNetworkEvent(NetworkEventBus::NetworkEvent::AssetDetailBlobChanged), - { Param1, Param2, Param3, Param4, Param5 }, Connection->GetClientId(), [](ErrorCode Error) { EXPECT_EQ(Error, ErrorCode::None); }); + SystemsManager.GetEventBus()->SendNetworkEventToClient( + NetworkEventBus::StringFromNetworkEvent(NetworkEventBus::NetworkEvent::AssetDetailBlobChanged), { Param1, Param2, Param3, Param4, Param5 }, + Connection->GetClientId(), [](ErrorCode Error) { EXPECT_EQ(Error, ErrorCode::None); }); // Wait for message WaitForCallback(AssetDetailBlobChangedCallbackCalled); diff --git a/Tests/src/PublicAPITests/ConversationSystemTests.cpp b/Tests/src/PublicAPITests/ConversationSystemTests.cpp index 8eb586077..446619274 100644 --- a/Tests/src/PublicAPITests/ConversationSystemTests.cpp +++ b/Tests/src/PublicAPITests/ConversationSystemTests.cpp @@ -18,8 +18,8 @@ #include "CSP/Common/Optional.h" #include "CSP/Multiplayer/Components/ConversationSpaceComponent.h" #include "CSP/Multiplayer/MultiPlayerConnection.h" -#include "CSP/Multiplayer/SpaceEntity.h" #include "CSP/Multiplayer/OnlineRealtimeEngine.h" +#include "CSP/Multiplayer/SpaceEntity.h" #include "CSP/Systems/Spaces/Space.h" #include "CSP/Systems/Spaces/UserRoles.h" #include "CSP/Systems/SystemsManager.h" @@ -72,7 +72,6 @@ CSP_PUBLIC_TEST(CSPEngine, ConversationSystemTests, ConversationSystemEventTest) auto* UserSystem = SystemsManager.GetUserSystem(); auto* SpaceSystem = SystemsManager.GetSpaceSystem(); auto* Connection = SystemsManager.GetMultiplayerConnection(); - auto* NetworkEventBus = SystemsManager.GetEventBus(); // Log in csp::common::String UserId; @@ -118,7 +117,7 @@ CSP_PUBLIC_TEST(CSPEngine, ConversationSystemTests, ConversationSystemEventTest) bool EventSent = false; - NetworkEventBus->SendNetworkEventToClient(NetworkEventBus::StringFromNetworkEvent(NetworkEventBus::NetworkEvent::Conversation), + SystemsManager.GetEventBus()->SendNetworkEventToClient(NetworkEventBus::StringFromNetworkEvent(NetworkEventBus::NetworkEvent::Conversation), csp::systems::ConversationSystemHelpers::MessageInfoToReplicatedValueArray(Params.MessageType, Params.MessageInfo), Connection->GetClientId(), [&EventSent](csp::multiplayer::ErrorCode) { EventSent = true; }); @@ -153,7 +152,7 @@ CSP_PUBLIC_TEST(CSPEngine, ConversationSystemTests, ConversationSystemEventTest) bool EventSent = false; - NetworkEventBus->SendNetworkEventToClient(NetworkEventBus::StringFromNetworkEvent(NetworkEventBus::NetworkEvent::Conversation), + SystemsManager.GetEventBus()->SendNetworkEventToClient(NetworkEventBus::StringFromNetworkEvent(NetworkEventBus::NetworkEvent::Conversation), csp::systems::ConversationSystemHelpers::MessageInfoToReplicatedValueArray(Params.MessageType, Params.MessageInfo), Connection->GetClientId(), [&EventSent](csp::multiplayer::ErrorCode) { EventSent = true; }); @@ -185,7 +184,6 @@ CSP_PUBLIC_TEST(CSPEngine, ConversationSystemTests, ConversationSystemEventDelay auto* UserSystem = SystemsManager.GetUserSystem(); auto* SpaceSystem = SystemsManager.GetSpaceSystem(); auto* Connection = SystemsManager.GetMultiplayerConnection(); - auto* NetworkEventBus = SystemsManager.GetEventBus(); // Log in csp::common::String UserId; @@ -210,7 +208,7 @@ CSP_PUBLIC_TEST(CSPEngine, ConversationSystemTests, ConversationSystemEventDelay bool EventSent = false; - NetworkEventBus->SendNetworkEventToClient(NetworkEventBus::StringFromNetworkEvent(NetworkEventBus::NetworkEvent::Conversation), + SystemsManager.GetEventBus()->SendNetworkEventToClient(NetworkEventBus::StringFromNetworkEvent(NetworkEventBus::NetworkEvent::Conversation), csp::systems::ConversationSystemHelpers::MessageInfoToReplicatedValueArray(Params.MessageType, Params.MessageInfo), Connection->GetClientId(), [&EventSent](csp::multiplayer::ErrorCode) { EventSent = true; }); diff --git a/Tests/src/PublicAPITests/EventBusTests.cpp b/Tests/src/PublicAPITests/EventBusTests.cpp index 3386e6743..bba4d3375 100644 --- a/Tests/src/PublicAPITests/EventBusTests.cpp +++ b/Tests/src/PublicAPITests/EventBusTests.cpp @@ -68,22 +68,21 @@ CSP_PUBLIC_TEST(CSPEngine, EventBusTests, RegisterDeregister) { using namespace csp::multiplayer; auto& SystemsManager = csp::systems::SystemsManager::Get(); - auto* NetworkEventBus = SystemsManager.GetEventBus(); const char* ReceiverId = "TestReceiverId"; const char* EventName = "TestEventName"; - const csp::common::Array InitialRegisteredEvents = NetworkEventBus->AllRegistrations(); - NetworkEventBus->ListenNetworkEvent( + const csp::common::Array InitialRegisteredEvents = SystemsManager.GetEventBus()->AllRegistrations(); + SystemsManager.GetEventBus()->ListenNetworkEvent( NetworkEventRegistration { ReceiverId, EventName }, [](const csp::common::NetworkEventData& /*NetworkEventData*/) {}); - const csp::common::Array AddedRegistration = NetworkEventBus->AllRegistrations(); + const csp::common::Array AddedRegistration = SystemsManager.GetEventBus()->AllRegistrations(); EXPECT_TRUE(AddedRegistration.Size() == InitialRegisteredEvents.Size() + 1); EXPECT_TRUE(AddedRegistration.ToList().Contains(NetworkEventRegistration { ReceiverId, EventName })); - NetworkEventBus->StopListenNetworkEvent(NetworkEventRegistration { ReceiverId, EventName }); + SystemsManager.GetEventBus()->StopListenNetworkEvent(NetworkEventRegistration { ReceiverId, EventName }); - const csp::common::Array RemovedRegistration = NetworkEventBus->AllRegistrations(); + const csp::common::Array RemovedRegistration = SystemsManager.GetEventBus()->AllRegistrations(); EXPECT_TRUE(RemovedRegistration.Size() == InitialRegisteredEvents.Size()); EXPECT_FALSE(RemovedRegistration.ToList().Contains(NetworkEventRegistration { ReceiverId, EventName })); } @@ -92,7 +91,6 @@ CSP_PUBLIC_TEST(CSPEngine, EventBusTests, RegisterDeregisterMulti) { using namespace csp::multiplayer; auto& SystemsManager = csp::systems::SystemsManager::Get(); - auto* NetworkEventBus = SystemsManager.GetEventBus(); const char* ReceiverId = "TestReceiverId"; const char* EventName = "TestEventName"; @@ -102,20 +100,20 @@ CSP_PUBLIC_TEST(CSPEngine, EventBusTests, RegisterDeregisterMulti) const char* EventName3 = "TestEventName3"; - const csp::common::Array InitialRegisteredEvents = NetworkEventBus->AllRegistrations(); - NetworkEventBus->ListenNetworkEvent( + const csp::common::Array InitialRegisteredEvents = SystemsManager.GetEventBus()->AllRegistrations(); + SystemsManager.GetEventBus()->ListenNetworkEvent( NetworkEventRegistration { ReceiverId, EventName }, [](const csp::common::NetworkEventData& /*NetworkEventData*/) {}); - NetworkEventBus->ListenNetworkEvent( + SystemsManager.GetEventBus()->ListenNetworkEvent( NetworkEventRegistration { ReceiverId, EventName2 }, [](const csp::common::NetworkEventData& /*NetworkEventData*/) {}); - NetworkEventBus->ListenNetworkEvent( + SystemsManager.GetEventBus()->ListenNetworkEvent( NetworkEventRegistration { ReceiverId, EventName3 }, [](const csp::common::NetworkEventData& /*NetworkEventData*/) {}); - NetworkEventBus->ListenNetworkEvent( + SystemsManager.GetEventBus()->ListenNetworkEvent( NetworkEventRegistration { ReceiverId2, EventName }, [](const csp::common::NetworkEventData& /*NetworkEventData*/) {}); - NetworkEventBus->ListenNetworkEvent( + SystemsManager.GetEventBus()->ListenNetworkEvent( NetworkEventRegistration { ReceiverId2, EventName2 }, [](const csp::common::NetworkEventData& /*NetworkEventData*/) {}); - NetworkEventBus->ListenNetworkEvent( + SystemsManager.GetEventBus()->ListenNetworkEvent( NetworkEventRegistration { ReceiverId2, EventName3 }, [](const csp::common::NetworkEventData& /*NetworkEventData*/) {}); - const csp::common::Array AddedRegistration = NetworkEventBus->AllRegistrations(); + const csp::common::Array AddedRegistration = SystemsManager.GetEventBus()->AllRegistrations(); EXPECT_TRUE(AddedRegistration.Size() == InitialRegisteredEvents.Size() + 6); EXPECT_TRUE(AddedRegistration.ToList().Contains(NetworkEventRegistration { ReceiverId, EventName })); @@ -125,9 +123,9 @@ CSP_PUBLIC_TEST(CSPEngine, EventBusTests, RegisterDeregisterMulti) EXPECT_TRUE(AddedRegistration.ToList().Contains(NetworkEventRegistration { ReceiverId2, EventName2 })); EXPECT_TRUE(AddedRegistration.ToList().Contains(NetworkEventRegistration { ReceiverId2, EventName3 })); - NetworkEventBus->StopListenNetworkEvent(NetworkEventRegistration { ReceiverId, EventName }); + SystemsManager.GetEventBus()->StopListenNetworkEvent(NetworkEventRegistration { ReceiverId, EventName }); - const csp::common::Array RemovedRegistration = NetworkEventBus->AllRegistrations(); + const csp::common::Array RemovedRegistration = SystemsManager.GetEventBus()->AllRegistrations(); EXPECT_TRUE(RemovedRegistration.Size() == InitialRegisteredEvents.Size() + 5); EXPECT_FALSE(RemovedRegistration.ToList().Contains(NetworkEventRegistration { ReceiverId, EventName })); EXPECT_TRUE(RemovedRegistration.ToList().Contains(NetworkEventRegistration { ReceiverId, EventName2 })); @@ -136,9 +134,9 @@ CSP_PUBLIC_TEST(CSPEngine, EventBusTests, RegisterDeregisterMulti) EXPECT_TRUE(RemovedRegistration.ToList().Contains(NetworkEventRegistration { ReceiverId2, EventName2 })); EXPECT_TRUE(RemovedRegistration.ToList().Contains(NetworkEventRegistration { ReceiverId2, EventName3 })); - NetworkEventBus->StopListenAllNetworkEvents(ReceiverId2); + SystemsManager.GetEventBus()->StopListenAllNetworkEvents(ReceiverId2); - const csp::common::Array RemovedAllTestReceivedOneRegistrations = NetworkEventBus->AllRegistrations(); + const csp::common::Array RemovedAllTestReceivedOneRegistrations = SystemsManager.GetEventBus()->AllRegistrations(); EXPECT_TRUE(RemovedAllTestReceivedOneRegistrations.Size() == InitialRegisteredEvents.Size() + 2); EXPECT_FALSE(RemovedAllTestReceivedOneRegistrations.ToList().Contains(NetworkEventRegistration { ReceiverId, EventName })); EXPECT_TRUE(RemovedAllTestReceivedOneRegistrations.ToList().Contains(NetworkEventRegistration { ReceiverId, EventName2 })); @@ -155,7 +153,6 @@ CSP_PUBLIC_TEST(CSPEngine, EventBusTests, RejectNullEvent) RAIIMockLogger MockLogger {}; auto& SystemsManager = csp::systems::SystemsManager::Get(); - auto* NetworkEventBus = SystemsManager.GetEventBus(); const csp::common::String Error = "Error: Expected non-null callback."; EXPECT_CALL(MockLogger.MockLogCallback, Call(csp::common::LogLevel::Error, Error)).Times(1); @@ -163,8 +160,8 @@ CSP_PUBLIC_TEST(CSPEngine, EventBusTests, RejectNullEvent) const char* ReceiverId = "TestReceiverId"; const char* EventName = "TestEventName"; - NetworkEventBus->ListenNetworkEvent(NetworkEventRegistration { ReceiverId, EventName }, nullptr); - auto AllRegistrations = NetworkEventBus->AllRegistrations(); + SystemsManager.GetEventBus()->ListenNetworkEvent(NetworkEventRegistration { ReceiverId, EventName }, nullptr); + auto AllRegistrations = SystemsManager.GetEventBus()->AllRegistrations(); EXPECT_FALSE(std::any_of(AllRegistrations.begin(), AllRegistrations.end(), [ReceiverId](const NetworkEventRegistration& Registration) { return Registration.EventReceiverId == ReceiverId; })); } @@ -176,7 +173,6 @@ CSP_PUBLIC_TEST(CSPEngine, EventBusTests, RejectDuplicateRegistration) RAIIMockLogger MockLogger {}; auto& SystemsManager = csp::systems::SystemsManager::Get(); - auto* NetworkEventBus = SystemsManager.GetEventBus(); const char* ReceiverId = "TestReceiverId"; const char* EventName = "TestEventName"; @@ -198,22 +194,22 @@ CSP_PUBLIC_TEST(CSPEngine, EventBusTests, RejectDuplicateRegistration) .c_str(); EXPECT_CALL(MockLogger.MockLogCallback, Call(csp::common::LogLevel::Warning, Error)).Times(1); - const auto StartSize = NetworkEventBus->AllRegistrations().Size(); + const auto StartSize = SystemsManager.GetEventBus()->AllRegistrations().Size(); - NetworkEventBus->ListenNetworkEvent( + SystemsManager.GetEventBus()->ListenNetworkEvent( NetworkEventRegistration { ReceiverId, EventName }, [](const csp::common::NetworkEventData& /*NetworkEventData*/) {}); - EXPECT_EQ(NetworkEventBus->AllRegistrations().Size(), StartSize + 1); - NetworkEventBus->ListenNetworkEvent( + EXPECT_EQ(SystemsManager.GetEventBus()->AllRegistrations().Size(), StartSize + 1); + SystemsManager.GetEventBus()->ListenNetworkEvent( NetworkEventRegistration { ReceiverId2, EventName }, [](const csp::common::NetworkEventData& /*NetworkEventData*/) {}); - EXPECT_EQ(NetworkEventBus->AllRegistrations().Size(), StartSize + 2); - NetworkEventBus->ListenNetworkEvent( + EXPECT_EQ(SystemsManager.GetEventBus()->AllRegistrations().Size(), StartSize + 2); + SystemsManager.GetEventBus()->ListenNetworkEvent( NetworkEventRegistration { ReceiverId, EventName2 }, [](const csp::common::NetworkEventData& /*NetworkEventData*/) {}); - EXPECT_EQ(NetworkEventBus->AllRegistrations().Size(), StartSize + 3); + EXPECT_EQ(SystemsManager.GetEventBus()->AllRegistrations().Size(), StartSize + 3); // This one should be rejected - NetworkEventBus->ListenNetworkEvent( + SystemsManager.GetEventBus()->ListenNetworkEvent( NetworkEventRegistration { ReceiverId2, EventName }, [](const csp::common::NetworkEventData& /*NetworkEventData*/) {}); - EXPECT_EQ(NetworkEventBus->AllRegistrations().Size(), StartSize + 3); + EXPECT_EQ(SystemsManager.GetEventBus()->AllRegistrations().Size(), StartSize + 3); } CSP_PUBLIC_TEST(CSPEngine, EventBusTests, RejectUnknownDeregistration) @@ -223,7 +219,6 @@ CSP_PUBLIC_TEST(CSPEngine, EventBusTests, RejectUnknownDeregistration) RAIIMockLogger MockLogger {}; auto& SystemsManager = csp::systems::SystemsManager::Get(); - auto* NetworkEventBus = SystemsManager.GetEventBus(); const char* ReceiverId = "TestReceiverId"; const char* EventName = "TestEventName"; @@ -236,15 +231,14 @@ CSP_PUBLIC_TEST(CSPEngine, EventBusTests, RejectUnknownDeregistration) EXPECT_CALL(MockLogger.MockLogCallback, Call(csp::common::LogLevel::Verbose, Error)).Times(1); EXPECT_CALL(MockLogger.MockLogCallback, Call(csp::common::LogLevel::Log, Error1)).Times(1); - NetworkEventBus->StopListenNetworkEvent(NetworkEventRegistration { ReceiverId, EventName }); - NetworkEventBus->StopListenAllNetworkEvents(ReceiverId); + SystemsManager.GetEventBus()->StopListenNetworkEvent(NetworkEventRegistration { ReceiverId, EventName }); + SystemsManager.GetEventBus()->StopListenAllNetworkEvents(ReceiverId); } CSP_PUBLIC_TEST(CSPEngine, EventBusTests, SingleEventSingleReciever) { auto& SystemsManager = csp::systems::SystemsManager::Get(); auto* UserSystem = SystemsManager.GetUserSystem(); - auto* NetworkEventBus = SystemsManager.GetEventBus(); auto* Connection = SystemsManager.GetMultiplayerConnection(); const char* ReceiverId = "TestReceiverId"; @@ -268,11 +262,11 @@ CSP_PUBLIC_TEST(CSPEngine, EventBusTests, SingleEventSingleReciever) const csp::common::Array ValsToSend = { csp::common::ReplicatedValue { TestValValue }, csp::common::ReplicatedValue { 1.0f } }; - NetworkEventBus->ListenNetworkEvent(csp::multiplayer::NetworkEventRegistration(ReceiverId, EventName), + SystemsManager.GetEventBus()->ListenNetworkEvent(csp::multiplayer::NetworkEventRegistration(ReceiverId, EventName), [&NetworkEventPromise](const csp::common::NetworkEventData& NetworkEventData) { NetworkEventPromise.set_value(NetworkEventData.EventValues); }); - NetworkEventBus->SendNetworkEventToClient(EventName, ValsToSend, Connection->GetClientId(), ErrorCallback); + SystemsManager.GetEventBus()->SendNetworkEventToClient(EventName, ValsToSend, Connection->GetClientId(), ErrorCallback); const csp::common::Array ReceivedVals = NetworkEventFuture.get(); EXPECT_EQ(ReceivedVals.Size(), 2); @@ -286,7 +280,6 @@ CSP_PUBLIC_TEST(CSPEngine, EventBusTests, SingleEventMultiReciever) { auto& SystemsManager = csp::systems::SystemsManager::Get(); auto* UserSystem = SystemsManager.GetUserSystem(); - auto* NetworkEventBus = SystemsManager.GetEventBus(); auto* Connection = SystemsManager.GetMultiplayerConnection(); const char* ReceiverId = "TestReceiverId"; @@ -312,18 +305,18 @@ CSP_PUBLIC_TEST(CSPEngine, EventBusTests, SingleEventMultiReciever) const csp::common::Array ValsToSend = { csp::common::ReplicatedValue { TestValValue }, csp::common::ReplicatedValue { 1.0f } }; - NetworkEventBus->ListenNetworkEvent(csp::multiplayer::NetworkEventRegistration(ReceiverId, EventName), + SystemsManager.GetEventBus()->ListenNetworkEvent(csp::multiplayer::NetworkEventRegistration(ReceiverId, EventName), [&NetworkEventPromise](const csp::common::NetworkEventData& NetworkEventData) { NetworkEventPromise.set_value(NetworkEventData.EventValues); }); std::promise> NetworkEventPromise1; std::future> NetworkEventFuture1 = NetworkEventPromise1.get_future(); - NetworkEventBus->ListenNetworkEvent(csp::multiplayer::NetworkEventRegistration(ReceiverId2, EventName), + SystemsManager.GetEventBus()->ListenNetworkEvent(csp::multiplayer::NetworkEventRegistration(ReceiverId2, EventName), [&NetworkEventPromise1](const csp::common::NetworkEventData& NetworkEventData) { NetworkEventPromise1.set_value(NetworkEventData.EventValues); }); - NetworkEventBus->SendNetworkEventToClient(EventName, ValsToSend, Connection->GetClientId(), ErrorCallback); + SystemsManager.GetEventBus()->SendNetworkEventToClient(EventName, ValsToSend, Connection->GetClientId(), ErrorCallback); // Both recievers should recieve this event const csp::common::Array ReceivedVals = NetworkEventFuture.get(); @@ -339,7 +332,6 @@ CSP_PUBLIC_TEST(CSPEngine, EventBusTests, MultiEventSingleReceiver) { auto& SystemsManager = csp::systems::SystemsManager::Get(); auto* UserSystem = SystemsManager.GetUserSystem(); - auto* NetworkEventBus = SystemsManager.GetEventBus(); auto* Connection = SystemsManager.GetMultiplayerConnection(); const char* ReceiverId = "TestReceiverId"; @@ -368,14 +360,14 @@ CSP_PUBLIC_TEST(CSPEngine, EventBusTests, MultiEventSingleReceiver) const csp::common::Array ValsToSend = { csp::common::ReplicatedValue { TestValValue }, csp::common::ReplicatedValue { 1.0f } }; - NetworkEventBus->ListenNetworkEvent(csp::multiplayer::NetworkEventRegistration(ReceiverId, EventName), + SystemsManager.GetEventBus()->ListenNetworkEvent(csp::multiplayer::NetworkEventRegistration(ReceiverId, EventName), [&NetworkEventPromise](const csp::common::NetworkEventData& NetworkEventData) { NetworkEventPromise.set_value(NetworkEventData.EventValues); }); - NetworkEventBus->ListenNetworkEvent(csp::multiplayer::NetworkEventRegistration(ReceiverId, EventName2), + SystemsManager.GetEventBus()->ListenNetworkEvent(csp::multiplayer::NetworkEventRegistration(ReceiverId, EventName2), [&NetworkEventPromise1](const csp::common::NetworkEventData& NetworkEventData) { NetworkEventPromise1.set_value(NetworkEventData.EventValues); }); - NetworkEventBus->SendNetworkEventToClient(EventName, ValsToSend, Connection->GetClientId(), ErrorCallback); + SystemsManager.GetEventBus()->SendNetworkEventToClient(EventName, ValsToSend, Connection->GetClientId(), ErrorCallback); const csp::common::Array ReceivedVals = NetworkEventFuture.get(); EXPECT_EQ(ReceivedVals.Size(), 2); @@ -387,7 +379,7 @@ CSP_PUBLIC_TEST(CSPEngine, EventBusTests, MultiEventSingleReceiver) // The other event should not have been recieved as it has not been fired EXPECT_TRUE(NetworkEventFuture1.wait_for(std::chrono::milliseconds { 0 }) != std::future_status::ready); - NetworkEventBus->SendNetworkEventToClient(EventName2, ValsToSend, Connection->GetClientId(), ErrorCallback); + SystemsManager.GetEventBus()->SendNetworkEventToClient(EventName2, ValsToSend, Connection->GetClientId(), ErrorCallback); const csp::common::Array ReceivedVals1 = NetworkEventFuture1.get(); EXPECT_EQ(ReceivedVals1.Size(), 2); } @@ -412,7 +404,6 @@ CSP_PUBLIC_TEST(DISABLED_CSPEngine, EventBusTests, TestMulticastEventToAllClient // Spin up 2 other clients auto& SystemsManager = csp::systems::SystemsManager::Get(); auto* UserSystem = SystemsManager.GetUserSystem(); - auto* NetworkEventBus = SystemsManager.GetEventBus(); auto* Connection = SystemsManager.GetMultiplayerConnection(); auto* SpaceSystem = SystemsManager.GetSpaceSystem(); @@ -461,7 +452,7 @@ CSP_PUBLIC_TEST(DISABLED_CSPEngine, EventBusTests, TestMulticastEventToAllClient const char* PintRequestEventName = "EventPingRequest"; const char* PingResponseEventName = "EventPingResponse"; - NetworkEventBus->ListenNetworkEvent(csp::multiplayer::NetworkEventRegistration(ReceiverId, PingResponseEventName), + SystemsManager.GetEventBus()->ListenNetworkEvent(csp::multiplayer::NetworkEventRegistration(ReceiverId, PingResponseEventName), [&ReceivedPings, &TwoPingsResponsePromise](const csp::common::NetworkEventData& /*NetworkEventData*/) { std::cout << "Received Event Bus Ping." << std::endl; @@ -473,7 +464,7 @@ CSP_PUBLIC_TEST(DISABLED_CSPEngine, EventBusTests, TestMulticastEventToAllClient }); // Send the ping event to all clients - NetworkEventBus->SendNetworkEvent(PintRequestEventName, {}, [](ErrorCode Error) { ASSERT_EQ(Error, ErrorCode::None); }); + SystemsManager.GetEventBus()->SendNetworkEvent(PintRequestEventName, {}, [](ErrorCode Error) { ASSERT_EQ(Error, ErrorCode::None); }); // Expect to have had two responses auto Status = TwoPingsResponseFuture.wait_for(30s); diff --git a/Tests/src/PublicAPITests/SpaceSystemTests.cpp b/Tests/src/PublicAPITests/SpaceSystemTests.cpp index f10aed788..1bc41a8d2 100644 --- a/Tests/src/PublicAPITests/SpaceSystemTests.cpp +++ b/Tests/src/PublicAPITests/SpaceSystemTests.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -3222,16 +3223,26 @@ CSP_PUBLIC_TEST(CSPEngine, SpaceSystemTests, DuplicateSpaceTest) auto [Result] = AWAIT_PRE(SpaceSystem, DuplicateSpace, RequestPredicate, Space.Id, UniqueSpaceName, SpaceAttributes::Private, nullptr, true); - EXPECT_EQ(Result.GetResultCode(), EResultCode::Success); + ASSERT_EQ(Result.GetResultCode(), EResultCode::Success); const auto& NewSpace = Result.GetSpace(); - EXPECT_NE(NewSpace.Id, Space.Id); - EXPECT_EQ(NewSpace.Name, UniqueSpaceName); - EXPECT_EQ(NewSpace.Description, Space.Description); - EXPECT_EQ(NewSpace.Attributes, SpaceAttributes::Private); - EXPECT_EQ(NewSpace.OwnerId, UserId); - EXPECT_NE(Space.OwnerId, UserId); + ASSERT_NE(NewSpace.Id, Space.Id); + ASSERT_EQ(NewSpace.Name, UniqueSpaceName); + ASSERT_EQ(NewSpace.Description, Space.Description); + ASSERT_EQ(NewSpace.Attributes, SpaceAttributes::Private); + ASSERT_EQ(NewSpace.OwnerId, UserId); + ASSERT_NE(Space.OwnerId, UserId); + + // Ensure we can enter the newly duplicated Space + std::unique_ptr RealtimeEngine { SystemsManager.MakeOnlineRealtimeEngine() }; + RealtimeEngine->SetEntityFetchCompleteCallback([](uint32_t) {}); + + auto [EnterResult] = AWAIT_PRE(SpaceSystem, EnterSpace, RequestPredicate, NewSpace.Id, RealtimeEngine.get()); + ASSERT_EQ(EnterResult.GetResultCode(), csp::systems::EResultCode::Success); + + auto [ExitSpaceResult] = AWAIT_PRE(SpaceSystem, ExitSpace, RequestPredicate); + ASSERT_EQ(ExitSpaceResult.GetResultCode(), csp::systems::EResultCode::Success); // Delete duplicated space DeleteSpace(SpaceSystem, NewSpace.Id); @@ -3247,6 +3258,107 @@ CSP_PUBLIC_TEST(CSPEngine, SpaceSystemTests, DuplicateSpaceTest) // Log out LogOut(UserSystem); } + +CSP_PUBLIC_TEST(CSPEngine, SpaceSystemTests, DuplicateSpaceAsyncTest) +{ + SetRandSeed(); + + auto& SystemsManager = ::SystemsManager::Get(); + auto* UserSystem = SystemsManager.GetUserSystem(); + auto* SpaceSystem = SystemsManager.GetSpaceSystem(); + + const char* TestSpaceName = "CSP-TEST-SPACE"; + const char* TestSpaceDescription = "CSP-TEST-SPACEDESC"; + + char UniqueSpaceName[256]; + SPRINTF(UniqueSpaceName, "%s-%s", TestSpaceName, GetUniqueString().c_str()); + + String UserId; + + // Create default and alt users + csp::systems::Profile DefaultUser = CreateTestUser(); + csp::systems::Profile AlternativeUser = CreateTestUser(); + + // Log in + LogIn(UserSystem, UserId, DefaultUser.Email, GeneratedTestAccountPassword); + + // Create space + Array UserRoles(1); + UserRoles[0].UserEmail = AlternativeUser.Email; + UserRoles[0].UserRole = SpaceUserRole::User; + InviteUserRoleInfoCollection InviteInfo; + InviteInfo.InviteUserRoleInfos = UserRoles; + + ::Space Space; + CreateSpace(SpaceSystem, UniqueSpaceName, TestSpaceDescription, SpaceAttributes::Private, nullptr, InviteInfo, nullptr, nullptr, Space); + + // Log out and log in as alt user + LogOut(UserSystem); + LogIn(UserSystem, UserId, AlternativeUser.Email, GeneratedTestAccountPassword); + + String NewSpaceId; + + // Attempt to duplicate space asynchrously + { + std::promise> AsyncCallCompletedPromise; + std::future> AsyncCallCompletedFuture = AsyncCallCompletedPromise.get_future(); + + auto AsyncCallCompletedCallback = [&](const csp::common::AsyncCallCompletedEventData& NetworkEventData) { + AsyncCallCompletedPromise.set_value({ NetworkEventData.OperationName, NetworkEventData.ReferenceId, NetworkEventData.ReferenceType }); + }; + + SpaceSystem->SetAsyncCallCompletedCallback(AsyncCallCompletedCallback); + + SPRINTF(UniqueSpaceName, "%s-%s", TestSpaceName, GetUniqueString().c_str()); + + auto [Result] + = AWAIT_PRE(SpaceSystem, DuplicateSpaceAsync, RequestPredicate, Space.Id, UniqueSpaceName, SpaceAttributes::Private, nullptr, true); + + ASSERT_EQ(Result.GetResultCode(), EResultCode::Success); + + // It is possible for this test to take an extended period of time to complete if the backend services are under load. + // We are therefore setting a timeout on waiting for the async callback to be received. + const std::chrono::seconds TimeoutDuration(30); + + std::future_status Status = AsyncCallCompletedFuture.wait_for(TimeoutDuration); + + ASSERT_NE(Status, std::future_status::timeout) << "The DuplicateSpaceAsync operation timed out after 30 seconds."; + + std::tuple AsyncCallResult = AsyncCallCompletedFuture.get(); + + String OperationName = std::get<0>(AsyncCallResult); + String ReferenceType = std::get<2>(AsyncCallResult); + NewSpaceId = std::get<1>(AsyncCallResult); + + ASSERT_TRUE(OperationName == "DuplicateSpaceAsync"); + ASSERT_TRUE(ReferenceType == "GroupId"); + } + + // Ensure we can enter the newly duplicated Space + { + std::unique_ptr RealtimeEngine { SystemsManager.MakeOnlineRealtimeEngine() }; + RealtimeEngine->SetEntityFetchCompleteCallback([](uint32_t) {}); + + auto [EnterResult] = AWAIT_PRE(SpaceSystem, EnterSpace, RequestPredicate, NewSpaceId, RealtimeEngine.get()); + ASSERT_EQ(EnterResult.GetResultCode(), csp::systems::EResultCode::Success); + + auto [ExitSpaceResult] = AWAIT_PRE(SpaceSystem, ExitSpace, RequestPredicate); + ASSERT_EQ(ExitSpaceResult.GetResultCode(), csp::systems::EResultCode::Success); + } + + // Delete duplicated space + DeleteSpace(SpaceSystem, NewSpaceId); + + // Log out and log in as default user to clean up original space + LogOut(UserSystem); + LogIn(UserSystem, UserId, DefaultUser.Email, GeneratedTestAccountPassword); + + // Delete space + DeleteSpace(SpaceSystem, Space.Id); + + // Log out + LogOut(UserSystem); +} namespace CSPEngine {