Skip to content

Commit 641d1f3

Browse files
committed
[OF-1784] feat: Adding MultiplayerSystem
This adds a multiplayey system where we can put functionality from the chs multiplayer service api. We may want to make other system abstractions in the future, as this api contains a lot of different functionality. For now, only the functions needed for testing leader election have been implemented
1 parent 51464fc commit 641d1f3

File tree

12 files changed

+815
-4
lines changed

12 files changed

+815
-4
lines changed
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright 2025 Magnopus LLC
3+
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include "CSP/CSPCommon.h"
20+
#include "CSP/Systems/Multiplayer/Scope.h"
21+
#include "CSP/Systems/Multiplayer/ScopeLeader.h"
22+
#include "CSP/Systems/SystemBase.h"
23+
24+
#include <memory>
25+
26+
namespace csp::services
27+
{
28+
29+
class ApiBase;
30+
31+
} // namespace csp::services
32+
33+
namespace csp::web
34+
{
35+
36+
class WebClient;
37+
38+
} // namespace csp::web
39+
40+
namespace csp::systems
41+
{
42+
43+
/// @ingroup Multiplayer System
44+
/// @brief Public facing system that allows interfacing with Magnopus Connected Services' multiplayer api.
45+
/// Offers methods for managing realtime state via REST calls.
46+
class CSP_API CSP_NO_DISPOSE MultiplayerSystem : public SystemBase
47+
{
48+
public:
49+
CSP_NO_EXPORT MultiplayerSystem(csp::web::WebClient* WebClient, csp::common::LogSystem& LogSystem);
50+
MultiplayerSystem(); // This constructor is only provided to appease the wrapper generator and should not be used
51+
~MultiplayerSystem();
52+
53+
/// @brief Gets all scopes associated with the given space id.
54+
/// @param SpaceId const csp::common::String& : The id of the space we want to get scopes for.
55+
/// @param Callback csp::systems::ScopesResultCallback : Callback when asynchronous task finishes.
56+
/// @pre Must already have entered the space of the SpaceId parameter.
57+
CSP_ASYNC_RESULT void GetScopesBySpace(const csp::common::String& SpaceId, ScopesResultCallback Callback);
58+
59+
/// @brief Updates Data on a scope
60+
/// @param ScopeId const csp::common::String& : The id of the scope we want to update.
61+
/// @param Scope const csp::systems::Scope& : Scope containing new values for the given id.
62+
/// @param Callback csp::systems::ScopesResultCallback : Callback when asynchronous task finishes.
63+
/// @pre Must already have entered the space of the SpaceId parameter.
64+
CSP_ASYNC_RESULT void UpdateScopeById(const csp::common::String& ScopeId, const csp::systems::Scope& Scope, ScopeResultCallback Callback);
65+
66+
/// @brief Gets details about a scope leader.
67+
/// @param ScopeId const csp::common::String& : The id of the scope we want to get scope leader details about.
68+
/// @param Callback csp::systems::ScopesResultCallback : Callback when asynchronous task finishes.
69+
/// @pre "ManagedLeaderElection" should be set to true on the scope, otherwise this function will fail with a EResultCode::Failed response.
70+
/// @pre Must already have entered the space of the SpaceId parameter.
71+
CSP_ASYNC_RESULT void GetScopeLeader(const csp::common::String& ScopeId, ScopeLeaderResultCallback Callback);
72+
73+
/// @brief Starts leader election for the given scope.
74+
/// This should not need to be called outside of testing, as leader election is done automatically.
75+
/// @param ScopeId const csp::common::String& : The id of the scope we want to start leader election for.
76+
/// @param UserIdsToExclude const csp::common::Optional<csp::common::Array<csp::common::String>>& : A list of user ids we don't want to consider
77+
/// for election.
78+
/// @param Callback csp::systems::ScopesResultCallback : Callback when asynchronous task finishes.
79+
/// @pre "ManagedLeaderElection" should be set to true on the scope, otherwise this function will fail with a EResultCode::Failed response.
80+
/// @pre Must already have entered the space of the SpaceId parameter.
81+
CSP_ASYNC_RESULT void PerformLeaderElectionInScope(const csp::common::String& ScopeId,
82+
const csp::common::Optional<csp::common::Array<csp::common::String>>& UserIdsToExclude, NullResultCallback Callback);
83+
84+
private:
85+
csp::services::ApiBase* ScopeLeaderApi;
86+
csp::services::ApiBase* ScopesApi;
87+
};
88+
89+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
* Copyright 2025 Magnopus LLC
3+
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include "CSP/CSPCommon.h"
20+
#include "CSP/Systems/SystemsResult.h"
21+
#include "CSP/Systems/WebService.h"
22+
23+
#include <functional>
24+
25+
namespace csp::services
26+
{
27+
28+
CSP_START_IGNORE
29+
template <typename T, typename U, typename V, typename W> class ApiResponseHandler;
30+
CSP_END_IGNORE
31+
32+
} // namespace csp::services
33+
34+
namespace csp::services::generated::multiplayerservice
35+
{
36+
class ScopeDto;
37+
}
38+
39+
namespace csp::systems
40+
{
41+
42+
/// @ingroup Multiplayer System
43+
/// @brief Enum representing the scopes pub/sub model type
44+
/// Object: used in object scopes, each object is published to its own channel, and
45+
/// client subscribes to the channels of only the objects they can see
46+
/// Global: used in global scopes, all objects are published to a single channel,
47+
/// client subscribes to the channel and can see everything in the channel/scope
48+
enum class PubSubModelType
49+
{
50+
Object,
51+
Global
52+
};
53+
54+
/// @ingroup Multiplayer System
55+
/// @brief Data representation for a scope in a space.
56+
/// Scopes represent different channels in a space which objects can exist in.
57+
/// This allows csp/mcs to only reason about objects in specific scopes.
58+
class CSP_API Scope
59+
{
60+
public:
61+
csp::common::String Id;
62+
csp::common::String ReferenceId;
63+
csp::common::String ReferenceType;
64+
csp::common::String Name;
65+
PubSubModelType PubSubType = PubSubModelType::Global;
66+
double SolveRadius = 0.0;
67+
bool ManagedLeaderElection = false;
68+
};
69+
70+
void DtoToScope(const csp::services::generated::multiplayerservice::ScopeDto& Dto, csp::systems::Scope& ScopeLeader);
71+
72+
/// @ingroup Multiplayer System
73+
/// @brief Contains details about an async operation which returns a scope.
74+
/// If the ResultCode is successful, this will contain a valid scope.
75+
class CSP_API ScopeResult : public ResultBase
76+
{
77+
/** @cond DO_NOT_DOCUMENT */
78+
CSP_START_IGNORE
79+
template <typename T, typename U, typename V, typename W> friend class csp::services::ApiResponseHandler;
80+
CSP_END_IGNORE
81+
/** @endcond */
82+
83+
public:
84+
/// @brief Returns the scope if this result is successful.
85+
/// @return const Scope& : The scope retrieved by this result.
86+
const Scope& GetScope() const;
87+
88+
private:
89+
ScopeResult(void*) {};
90+
91+
void OnResponse(const csp::services::ApiResponseBase* ApiResponse) override;
92+
93+
Scope Scope;
94+
};
95+
96+
typedef std::function<void(const ScopeResult& Result)> ScopeResultCallback;
97+
98+
/// @ingroup Multiplayer System
99+
/// @brief Contains details about an async operation which returns an array of scopes.
100+
/// If the ResultCode is successful, this will contain a valid array of scopes.
101+
class CSP_API ScopesResult : public ResultBase
102+
{
103+
/** @cond DO_NOT_DOCUMENT */
104+
CSP_START_IGNORE
105+
template <typename T, typename U, typename V, typename W> friend class csp::services::ApiResponseHandler;
106+
CSP_END_IGNORE
107+
/** @endcond */
108+
109+
public:
110+
/// @brief Returns the an array of scopes if this result is successful.
111+
/// @return const csp::common::Array<Scope>& : The array of scopes retrieved by this result.
112+
const csp::common::Array<Scope>& GetScopes() const;
113+
114+
private:
115+
ScopesResult(void*) {};
116+
117+
void OnResponse(const csp::services::ApiResponseBase* ApiResponse) override;
118+
119+
csp::common::Array<Scope> Scopes;
120+
};
121+
122+
typedef std::function<void(const ScopesResult& Result)> ScopesResultCallback;
123+
124+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2025 Magnopus LLC
3+
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include "CSP/CSPCommon.h"
20+
#include "CSP/Systems/SystemsResult.h"
21+
#include "CSP/Systems/WebService.h"
22+
23+
#include <functional>
24+
25+
namespace csp::services
26+
{
27+
28+
CSP_START_IGNORE
29+
template <typename T, typename U, typename V, typename W> class ApiResponseHandler;
30+
CSP_END_IGNORE
31+
32+
} // namespace csp::services
33+
34+
namespace csp::services::generated::multiplayerservice
35+
{
36+
class ScopeLeaderDto;
37+
}
38+
39+
namespace csp::systems
40+
{
41+
42+
/// @ingroup Multiplayer System
43+
/// @brief Data representation for a scope leader.
44+
/// A scope leader represents a user which owns a specific scope in a space.
45+
/// The scope leader will run scripts and other operations for the scope.
46+
class CSP_API ScopeLeader
47+
{
48+
public:
49+
csp::common::String ScopeId;
50+
csp::common::String ScopeLeaderUserId;
51+
bool ElectionInProgress = false;
52+
};
53+
54+
void DtoToScopeLeader(const csp::services::generated::multiplayerservice::ScopeLeaderDto& Dto, csp::systems::ScopeLeader& ScopeLeader);
55+
56+
/// @ingroup Multiplayer System
57+
/// @brief Contains details about an async operation which returns a scope leader.
58+
/// If the ResultCode is successful, this will contain a valid scope leader.
59+
class CSP_API ScopeLeaderResult : public ResultBase
60+
{
61+
/** @cond DO_NOT_DOCUMENT */
62+
CSP_START_IGNORE
63+
template <typename T, typename U, typename V, typename W> friend class csp::services::ApiResponseHandler;
64+
CSP_END_IGNORE
65+
/** @endcond */
66+
67+
public:
68+
/// @brief Returns the scope leader if this result is successful.
69+
/// @return const ScopeLeader& : The scope leader retrieved by this result.
70+
const ScopeLeader& GetScopeLeader() const;
71+
72+
private:
73+
ScopeLeaderResult(void*) {};
74+
75+
void OnResponse(const csp::services::ApiResponseBase* ApiResponse) override;
76+
77+
ScopeLeader Leader;
78+
};
79+
80+
typedef std::function<void(const ScopeLeaderResult& Result)> ScopeLeaderResultCallback;
81+
}

Library/include/CSP/Systems/SystemsManager.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class HotspotSequenceSystem;
5353
class ConversationSystemInternal;
5454
class AnalyticsSystem;
5555
class ExternalServiceProxySystem;
56+
class MultiplayerSystem;
5657

5758
} // namespace csp::systems
5859

@@ -169,6 +170,8 @@ class CSP_API SystemsManager
169170
/// @return ExternalServiceProxySystem : pointer to the external services proxy system class.
170171
ExternalServiceProxySystem* GetExternalServicesProxySystem();
171172

173+
MultiplayerSystem* GetMultiplayerSystem();
174+
172175
csp::multiplayer::MultiplayerConnection* GetMultiplayerConnection();
173176

174177
csp::multiplayer::NetworkEventBus* GetEventBus();
@@ -220,6 +223,7 @@ class CSP_API SystemsManager
220223
ConversationSystemInternal* ConversationSystem;
221224
AnalyticsSystem* AnalyticsSystem;
222225
ExternalServiceProxySystem* ExternalServiceProxySystem;
226+
MultiplayerSystem* MultiplayerSystem;
223227
};
224228

225229
} // namespace csp::systems

Library/src/ExplicitTypes.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "CSP/Systems/Assets/Asset.h"
2626
#include "CSP/Systems/Assets/AssetCollection.h"
2727
#include "CSP/Systems/Assets/GLTFMaterial.h"
28+
#include "CSP/Systems/Multiplayer/Scope.h"
2829
#include "CSP/Systems/Spaces/Site.h"
2930
#include "CSP/Systems/Spaces/Space.h"
3031
#include "CSP/Systems/Spaces/UserRoles.h"
@@ -62,6 +63,7 @@ template class CSP_API csp::common::Array<csp::systems::Site>;
6263
template class CSP_API csp::common::Array<csp::systems::Space>;
6364
template class CSP_API csp::common::Array<csp::systems::UserRoleInfo>;
6465
template class CSP_API csp::common::Array<csp::systems::Material*>;
66+
template class CSP_API csp::common::Array<csp::systems::Scope>;
6567

6668
// csp::common::List
6769
template class CSP_API csp::common::List<csp::common::String>;

0 commit comments

Comments
 (0)