-
Notifications
You must be signed in to change notification settings - Fork 14
[OF-1784] Multiplayer System #842
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 10 commits
2388030
c6eb6f4
04ca912
7dcfcdf
00bf02c
63f0970
a22b329
917d04a
16e3767
9860b3d
43b23e1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| /* | ||
| * Copyright 2025 Magnopus LLC | ||
|
|
||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "CSP/CSPCommon.h" | ||
| #include "CSP/Systems/Multiplayer/Scope.h" | ||
| #include "CSP/Systems/Multiplayer/ScopeLeader.h" | ||
| #include "CSP/Systems/SystemBase.h" | ||
|
|
||
| #include <memory> | ||
|
|
||
| namespace csp::services | ||
| { | ||
|
|
||
| class ApiBase; | ||
|
|
||
| } // namespace csp::services | ||
|
|
||
| namespace csp::web | ||
| { | ||
|
|
||
| class WebClient; | ||
|
|
||
| } // namespace csp::web | ||
|
|
||
| namespace csp::systems | ||
| { | ||
| class SpaceSystem; | ||
|
|
||
| /// @ingroup Multiplayer System | ||
| /// @brief Public facing system that allows interfacing with Magnopus Connected Services' multiplayer api. | ||
| /// Offers methods for managing realtime state via REST calls. | ||
| class CSP_API CSP_NO_DISPOSE MultiplayerSystem : public SystemBase | ||
| { | ||
| public: | ||
| CSP_NO_EXPORT MultiplayerSystem(csp::web::WebClient* WebClient, csp::systems::SpaceSystem& SpaceSystem, csp::common::LogSystem& LogSystem); | ||
| MultiplayerSystem(); // This constructor is only provided to appease the wrapper generator and should not be used | ||
| ~MultiplayerSystem(); | ||
|
|
||
| /// @brief Gets all scopes associated with the given space id. | ||
| /// Note: These functions are currently not exported, as they are only used for testing, as we haven't fully implemented scopes within csp. | ||
| /// @param SpaceId const csp::common::String& : The id of the space we want to get scopes for. | ||
| /// @param Callback csp::systems::ScopesResultCallback : Callback when the scopes are retrieved, or on failure. | ||
| /// @pre Must already have entered the space of the SpaceId parameter. | ||
| /// A CSP error will be sent to the LogSystem if this condition is not met, with a EResultCode::Failed response. | ||
| CSP_NO_EXPORT void GetScopesBySpace(const csp::common::String& SpaceId, ScopesResultCallback Callback); | ||
|
|
||
| /// @brief Updates Data on a scope. | ||
| /// Note: These functions are currently not exported, as they are only used for testing, as we haven't fully implemented scopes within csp. | ||
| /// @param ScopeId const csp::common::String& : The id of the scope we want to update. | ||
| /// @param Scope const csp::systems::Scope& : Scope containing new values for the given id. | ||
| /// @param Callback csp::systems::ScopesResultCallback : Callback when asynchronous task finishes. | ||
| CSP_NO_EXPORT void UpdateScopeById(const csp::common::String& ScopeId, const csp::systems::Scope& Scope, ScopeResultCallback Callback); | ||
|
|
||
| /// @brief Gets details about a scope leader. | ||
MAG-ElliotMorris marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /// Note: These functions are currently not exported, as they are only used for testing, as we haven't fully implemented scopes within csp. | ||
| /// @param ScopeId const csp::common::String& : The id of the scope we want to get scope leader details about. | ||
| /// @param Callback csp::systems::ScopesResultCallback : Callback when asynchronous task finishes. | ||
| /// @pre "ManagedLeaderElection" should be set to true on the scope, otherwise this function will fail with a EResultCode::Failed response. | ||
| /// @pre Must already have entered the space of the SpaceId parameter. | ||
| CSP_NO_EXPORT void GetScopeLeader(const csp::common::String& ScopeId, ScopeLeaderResultCallback Callback); | ||
|
|
||
| /// @brief Starts leader election for the given scope. | ||
| /// Note: These functions are currently not exported, as they are only used for testing, as we haven't fully implemented scopes within csp. | ||
| /// This should not need to be called outside of testing, as leader election is done automatically. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Consider] Maybe we should do the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in: 2553f80 by setting these types NO_EXPORT, until we understand how scopes are going to be used in CSP
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Question] I was under the impression we agreed on the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in: 16e3767 |
||
| /// @param ScopeId const csp::common::String& : The id of the scope we want to start leader election for. | ||
| /// @param UserIdsToExclude const csp::common::Optional<csp::common::Array<csp::common::String>>& : A list of user ids we don't want to consider | ||
| /// for election. | ||
| /// @param Callback csp::systems::ScopesResultCallback : Callback when asynchronous task finishes. | ||
| /// @pre "ManagedLeaderElection" should be set to true on the scope, otherwise this function will fail with a EResultCode::Failed response. | ||
| /// @pre Must already have entered the space of the SpaceId parameter. | ||
| CSP_NO_EXPORT void __PerformLeaderElectionInScope(const csp::common::String& ScopeId, | ||
| const csp::common::Optional<csp::common::Array<csp::common::String>>& UserIdsToExclude, NullResultCallback Callback); | ||
|
|
||
| private: | ||
| CSP_START_IGNORE | ||
| std::unique_ptr<csp::services::ApiBase> ScopeLeaderApi; | ||
| std::unique_ptr<csp::services::ApiBase> ScopesApi; | ||
| CSP_END_IGNORE | ||
|
|
||
| csp::systems::SpaceSystem* SpaceSystem; | ||
| }; | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,142 @@ | ||
| /* | ||
| * Copyright 2025 Magnopus LLC | ||
|
|
||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "CSP/CSPCommon.h" | ||
| #include "CSP/Systems/SystemsResult.h" | ||
| #include "CSP/Systems/WebService.h" | ||
|
|
||
| #include <functional> | ||
|
|
||
| namespace csp::services | ||
| { | ||
|
|
||
| CSP_START_IGNORE | ||
| template <typename T, typename U, typename V, typename W> class ApiResponseHandler; | ||
| CSP_END_IGNORE | ||
|
|
||
| } // namespace csp::services | ||
|
|
||
| namespace csp::services::generated::multiplayerservice | ||
| { | ||
| class ScopeDto; | ||
| } | ||
|
|
||
| namespace csp::systems | ||
| { | ||
|
|
||
| /// @ingroup Multiplayer System | ||
| /// @brief Enum representing the scopes pub/sub model type | ||
| /// Object: used in object scopes, each object is published to its own channel, and | ||
| /// client subscribes to the channels of only the objects they can see | ||
| /// Global: used in global scopes, all objects are published to a single channel, | ||
| /// client subscribes to the channel and can see everything in the channel/scope | ||
| enum class PubSubModelType | ||
| { | ||
| Object, | ||
| Global | ||
| }; | ||
MAG-ChristopherAtkinson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /// @ingroup Multiplayer System | ||
| /// @brief Data representation for a scope in a space. | ||
| /// Scopes represent different channels in a space which objects can exist in. | ||
| /// This allows csp/mcs to only reason about objects in specific scopes. | ||
| class CSP_API Scope | ||
| { | ||
| public: | ||
| /// @brief The unique identifier of the scope. | ||
| /// This is set internally by MCS. | ||
| csp::common::String Id; | ||
| /// @brief The id of the object this scope relates to. | ||
| /// This is currently always the space id. | ||
| csp::common::String ReferenceId; | ||
| /// @brief The type of object this scope relates to. | ||
| /// This is currently always "GroupId", as it references the space. | ||
| csp::common::String ReferenceType; | ||
| /// @brief The name of the scope, this should be a human readable string to identify the scope. | ||
| csp::common::String Name; | ||
| /// @brief The pub/sub model of the scope. | ||
| /// This allows us define a global scope for the entire space, or a scope with a position and size. | ||
| /// See csp::systems::PubSubModelType for more details. | ||
| PubSubModelType PubSubType = PubSubModelType::Global; | ||
| /// @brief Determines the size of the scope using the radius from the object in meters. | ||
| /// This is only used when PubSubType is set to "Object". | ||
| double SolveRadius = 0.0; | ||
| /// @brief Determines whether server side leader election is enabled on this scope. | ||
| /// If this is true, MCS will automatically determine the leader for this scope. | ||
| bool ManagedLeaderElection = false; | ||
| }; | ||
|
|
||
| void DtoToScope(const csp::services::generated::multiplayerservice::ScopeDto& Dto, csp::systems::Scope& ScopeLeader); | ||
MAG-ElliotMorris marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /// @ingroup Multiplayer System | ||
| /// @brief Contains details about an async operation which returns a scope. | ||
| /// If the ResultCode is successful, this will contain a valid scope. | ||
| class CSP_API ScopeResult : public ResultBase | ||
| { | ||
| /** @cond DO_NOT_DOCUMENT */ | ||
| CSP_START_IGNORE | ||
| template <typename T, typename U, typename V, typename W> friend class csp::services::ApiResponseHandler; | ||
| CSP_END_IGNORE | ||
| /** @endcond */ | ||
|
|
||
| public: | ||
| /// @brief Returns the scope if this result is successful. | ||
| /// @return const Scope& : The scope retrieved by this result. | ||
| const Scope& GetScope() const; | ||
|
|
||
| CSP_NO_EXPORT ScopeResult(csp::systems::EResultCode ResCode, uint16_t HttpResCode); | ||
|
|
||
| private: | ||
| ScopeResult(void*) {}; | ||
|
|
||
| void OnResponse(const csp::services::ApiResponseBase* ApiResponse) override; | ||
|
|
||
| Scope Scope; | ||
| }; | ||
|
|
||
| typedef std::function<void(const ScopeResult& Result)> ScopeResultCallback; | ||
|
|
||
| /// @ingroup Multiplayer System | ||
| /// @brief Contains details about an async operation which returns an array of scopes. | ||
| /// If the ResultCode is successful, this will contain a valid array of scopes. | ||
| class CSP_API ScopesResult : public ResultBase | ||
| { | ||
| /** @cond DO_NOT_DOCUMENT */ | ||
| CSP_START_IGNORE | ||
| template <typename T, typename U, typename V, typename W> friend class csp::services::ApiResponseHandler; | ||
| CSP_END_IGNORE | ||
| /** @endcond */ | ||
|
|
||
| public: | ||
| /// @brief Returns the an array of scopes if this result is successful. | ||
| /// @return const csp::common::Array<Scope>& : The array of scopes retrieved by this result. | ||
| const csp::common::Array<Scope>& GetScopes() const; | ||
|
|
||
| CSP_NO_EXPORT ScopesResult(csp::systems::EResultCode ResCode, uint16_t HttpResCode); | ||
|
|
||
| private: | ||
| ScopesResult(void*) {}; | ||
|
|
||
| void OnResponse(const csp::services::ApiResponseBase* ApiResponse) override; | ||
|
|
||
| csp::common::Array<Scope> Scopes; | ||
| }; | ||
|
|
||
| typedef std::function<void(const ScopesResult& Result)> ScopesResultCallback; | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| /* | ||
| * Copyright 2025 Magnopus LLC | ||
|
|
||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "CSP/CSPCommon.h" | ||
| #include "CSP/Systems/SystemsResult.h" | ||
| #include "CSP/Systems/WebService.h" | ||
|
|
||
| #include <functional> | ||
|
|
||
| namespace csp::services | ||
| { | ||
|
|
||
| CSP_START_IGNORE | ||
| template <typename T, typename U, typename V, typename W> class ApiResponseHandler; | ||
| CSP_END_IGNORE | ||
|
|
||
| } // namespace csp::services | ||
|
|
||
| namespace csp::services::generated::multiplayerservice | ||
| { | ||
| class ScopeLeaderDto; | ||
| } | ||
|
|
||
| namespace csp::systems | ||
| { | ||
|
|
||
| /// @ingroup Multiplayer System | ||
| /// @brief Data representation for a scope leader. | ||
| /// A scope leader represents a user which owns a specific scope in a space. | ||
| /// The scope leader will run scripts and other operations for the scope. | ||
| class CSP_API ScopeLeader | ||
| { | ||
| public: | ||
| /// @brief The scope id the client is leader of. | ||
| csp::common::String ScopeId; | ||
| /// @brief The client id which is the leader. | ||
| csp::common::String ScopeLeaderUserId; | ||
| /// @brief Whether there is a server side election currently in progress when this object is received. | ||
| bool ElectionInProgress = false; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [fix] I won't markup every undocumented property. I'm not trying to be docs mad, I understand in these DTO types some stuff is obvious, but here for example,
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in: 2553f80
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Consider] This still isn't good enough.
and
mean practically the same thing. Think about if you were integrating against our Api. Think of a reason why you would call I suspect the answer is that you just can't know, as it is with all the other docs, and the NO_EXPORT "strategy". @MAG-SamBirley @MAG-AdamThorn It is quite frustrating to have to direct my issues at Matt here, it's not his failing. All this is a consequence from CHS not having adequate api documentation that considers workflows, as they are the only people who can answer these sorts of questions. There's only so far we can go with documenting Api's like this and it's difficult to impossible to maintain quality standards in this environment. 😢 I'm tempted to just give up on documentation for the sinkhole REST endpoints, or at least the stuff that CSP dosen't insert opinions on (most of it). It's a losing battle.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I can't answer the questions above. What makes it more difficult is that CHS are still making more changes to this on the backend |
||
| }; | ||
|
|
||
| void DtoToScopeLeader(const csp::services::generated::multiplayerservice::ScopeLeaderDto& Dto, csp::systems::ScopeLeader& ScopeLeader); | ||
MAG-ChristopherAtkinson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /// @ingroup Multiplayer System | ||
| /// @brief Contains details about an async operation which returns a scope leader. | ||
| /// If the ResultCode is successful, this will contain a valid scope leader. | ||
| class CSP_API ScopeLeaderResult : public ResultBase | ||
| { | ||
| /** @cond DO_NOT_DOCUMENT */ | ||
| CSP_START_IGNORE | ||
| template <typename T, typename U, typename V, typename W> friend class csp::services::ApiResponseHandler; | ||
| CSP_END_IGNORE | ||
| /** @endcond */ | ||
|
|
||
| public: | ||
| /// @brief Returns the scope leader if this result is successful. | ||
| /// @return const ScopeLeader& : The scope leader retrieved by this result. | ||
| const ScopeLeader& GetScopeLeader() const; | ||
|
|
||
| private: | ||
| ScopeLeaderResult(void*) {}; | ||
|
|
||
| void OnResponse(const csp::services::ApiResponseBase* ApiResponse) override; | ||
|
|
||
| ScopeLeader Leader; | ||
| }; | ||
|
|
||
| typedef std::function<void(const ScopeLeaderResult& Result)> ScopeLeaderResultCallback; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Consider] Stating what happens when this precondition is violated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in: 2553f80
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Comment] I still think this erroring behaviour is poor, an empty array of scopes is not a good marker for "You were not in the space", it could be caused by anything. I'm presuming we don't get a helpful error from CHS telling us why the request failed, which is what I'd ideally like.
[Question[ To give better errors, would we have to query CHS again to check if we're in the space beforehand? Seems like context the forwarded CHS response should be able to provide.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in: 9860b3d
Used the space system to check if we're in the space. We do this for HotspotSequenceSystem, as well.