Stormancer.Server.Plugins.Party
5.2.0.57-pre
See the version list below for details.
dotnet add package Stormancer.Server.Plugins.Party --version 5.2.0.57-pre
NuGet\Install-Package Stormancer.Server.Plugins.Party -Version 5.2.0.57-pre
<PackageReference Include="Stormancer.Server.Plugins.Party" Version="5.2.0.57-pre" />
paket add Stormancer.Server.Plugins.Party --version 5.2.0.57-pre
#r "nuget: Stormancer.Server.Plugins.Party, 5.2.0.57-pre"
// Install Stormancer.Server.Plugins.Party as a Cake Addin #addin nuget:?package=Stormancer.Server.Plugins.Party&version=5.2.0.57-pre&prerelease // Install Stormancer.Server.Plugins.Party as a Cake Tool #tool nuget:?package=Stormancer.Server.Plugins.Party&version=5.2.0.57-pre&prerelease
Overview
This module adds player parties related features to a Stormancer application. It contains a Server application plugin (the Nuget package), and a C++ client plugin (Party.hpp).
A player party is a group any player can create, which can be joined by other players afterward. A gamefinder is set as part of the party creation settings and can be changed afterwards.
When all members of a party are in the ready state (set by calling PartyApi::updatePlayerStatus
), the selected gamefinder is executed, with the current party data (including party settings, member list and member data) as argument.
Main features:
- Maintains on all party members as well as on the server an updated list of the party members
- Maintains a party leader, with leadership automatically passed to another player in case of leader disconnection
- Synchronizes custom party settings (expressed as a string). Only the leader can update the party settings.
- Synchronizes custom data (as a string) for all members. Each party member can only update its own data.
- Generates invitation codes to enable players to join in a crossplay game.
- Synchronizes an extensible state, for instance filled by the Gamesession system so that players joining a party already in a gamesession can detect that and join the gamesession.
- Integrates with the invitation systems of gaming platforms like Steam, PSN, XBoxLive, Nintendo Online and Epic Store to provide a seamless experience for players running the game on the same platform, while retaining crossplay capabilities in the same party.
- Provides search and filtering capabilities based on Lucene.
The party system uses Stormancer scenes as its core abstraction (a party is a component of a scene). This makes the party system highly scalable and extensible as developers can easily add additional behaviors to a party scene.
Creating a party
client->DependencyResolver.resolve<Stormancer::PartyApi>()->createIfNotJoined().then([]()
{
...
});
Joining a party
Invitation codes
Parties can be joined in a cross platform way by using invitation codes Invitation codes can be customized in the server configuration, or code:
"party":{
"authorizedInvitationCodeCharacters":"01234567890" // defaults to "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"
"invitationCodeLength" : 4 //defaults to 6
}
To generate an invitation code, the party leader calls pplx::task<std::string> PartyApi::createInvitationCode(pplx::cancellation_token ct)
. Calling the method again invalidates the previous code.
The current invitation code can be disabled without generating a new one by calling pplx::task<void> PartyApi::cancelInvitationCode(pplx::cancellation_token ct = pplx::cancellation_token::none())
To join a party using a code, users call pplx::task<void> joinPartyByInvitationCode(const std::string& invitationCode)
and can optionally provide custom member data.
Invitations
Platform integration
The party integrates with the platform plugins (steam.hpp, epic.hpp, etc...) to process player invitations. Each platform requires specific setup described in the relevant documentation.
Additionnally, the game must register a callback to the OnInvitationReceived
event by calling Subscription PartyApi::subscribeOnInvitationReceived(std::function<void(PartyInvitation)> callback)
and storing the Subscription
object returned by the method
for as long as the game should listen to the event (Most of the time as long as the game is running).
Additionally, a list of currently pending invitations can also be retrieved by calling PartyApi::getPendingInvitations()
.
When an invitation is received, the game should either cancel it, or process it by calling PartyInvitation::acceptAndJoinParty()
optionally providing custom memberData or PartyInvitation::decline()
Furthermore, a list or currently pending invitations can be retrieved by calling PartyApi::getPendingInvitations()
Gamefinder integration
Gamefinders take player parties as arguments for matchmaking. A gamefinder id is associated with the party on party creation by setting PartyCreationOptions::gameFinder
and is stored in the party settings.
After party creation the party leader can change it by updating the party settings.
Once all player have set their state as ready by calling updatePlayerStatus(PartyUserStatus playerStatus)
with playerStatus set as PartyUserStatus::Ready
, the party automatically invokes the configured gamefinder.
Customizing ready state changes
The party system supports various ways in which gamefinder integration can be customized.
Validating party state before setting the player state as ready
The state of a player can be validated when they try setting themselves as ready by implementing IPartyEventHandler.OnUpdatingPlayerReadyState
. This method can perform validation and set a custom error string to be sent back to the client in case of failure.
Customizing ready reset behavior
By default, the ready state resets to NotReady
if any of the following conditions are met:
- The list of players in the party changes.
- Party settings are updated.
- The custom data of any party members is updated.
- Matchmaking is in progress
This behavior can be customized through two mechanisms:
- Party configuration
- The Party
IPartyEventHandler
extensibility point.
Note that preventing resetting the ready state also disables automated matchmaking cancellation in all the situations that would have reset it, for instance if a player in the party disconnects while matchmaking is in progress. Be carefult, as data about the parties are sent to the matchmaker only when matchmaking starts, they may become out of synch in this case. Manually resetting the ready state still cancels matchmaking as only automated reset is disabled. You may use a custom validator to reject data modification while in matchmaking.
Party Configuration
The list of events the ready state should reset in can be set in the party configuration. ResetPlayerReadyStateMode.None
and ResetPlayerReadyStateMode.All
are convenient shortcuts. By default, the option is set as `ResetPlayerReadyStateMode.All
public class TestPlugin : IHostPlugin
{
public void Build(HostPluginBuildContext ctx)
{
ctx.HostStarting += (IHost host) =>
{
host.ConfigurePlayerParty(p => p.ResetPlayerReadyStateOn(ResetPlayerReadyStateMode.PartySettingsUpdated | ResetPlayerReadyStateMode.PartyMemberDataUpdated | ResetPlayerReadyStateMode.PartyMembersListUpdated));
}
}
}
It's also possible to provide a custom lambda to drive the reset logic. For instance :
public class TestPlugin : IHostPlugin
{
public void Build(HostPluginBuildContext ctx)
{
ctx.HostStarting += (IHost host) =>
{
var value = ResetPlayerReadyStateMode.All; //Always reset
host.ConfigurePlayerParty(p => p.ResetPlayerReadyStateOn(ctx =>
{
switch (ctx.EventType)
{
case PartyMemberReadyStateResetEventType.PartySettingsUpdated:
if ((value & ResetPlayerReadyStateMode.PartySettingsUpdated) != 0)
{
ctx.ShouldReset = true;
}
break;
case PartyMemberReadyStateResetEventType.PartyMemberDataUpdated:
if ((value & ResetPlayerReadyStateMode.PartyMemberDataUpdated) != 0)
{
ctx.ShouldReset = true;
}
break;
case PartyMemberReadyStateResetEventType.PartyMembersListUpdated:
if ((value & ResetPlayerReadyStateMode.PartyMembersListUpdated) != 0)
{
ctx.ShouldReset = true;
}
break;
}
}));
}
}
}
IPartyEventHandler implementation.
Implement IPartyEventHandler.OnPlayerReadyStateReset
in an event handler to control when the the ready state should be reset depending on custom logic.
Joining the current gamesession
When joining a party, it's possible to know if it is currently in a gamession, and join this gamesession.
If not used by the application, this behavior can be disabled by setting to application configuration value party.enableGameSessionPartyStatus
to false.
The following methods of PartyApi
enables clients to determine if their party is in a gamesession, and if it's the case, to get a connection token to it that can be consumed by the GameSession client API.
/// <summary>
/// Gets a boolean indicating if the party is currently in a gamesession.
/// </summary>
/// <returns></returns>
virtual bool isInGameSession() = 0;
/// <summary>
/// If the party is in a gamesession, gets a token to connect to it.
/// </summary>
/// <param name="ct"></param>
/// <returns></returns>
virtual pplx::task<std::string> getCurrentGameSessionConnectionToken(pplx::cancellation_token ct = pplx::cancellation_token::none()) = 0;
Party Search
Once a party is created, the party leader can index it into a scalable Lucene server-side index for search. To do that, call UpdatePartySettings
and provide a valid json document in the indexedDocument
field.
struct PartySettings
{
std::string gameFinderName;
std::string customData;
bool onlyLeaderCanInvite = true;
bool isJoinable = true;
std::unordered_map<std::string, std::string> publicServerData; // Not in MSGPACK_DEFINE because cannot be set by the client
/// <summary>
/// Json document used to search the party.
/// </summary>
/// <remarks>
/// Must be a valid json object.
/// The party is not searchable if set to empty or an invalid json object.
/// The content of the document are indexed using the field paths as keys, with '.' as separator.
///
/// For example, the following document:
/// {
/// "maxPlayers":3,
/// "gamemode":{
/// "map":"level3-a",
/// "extraFooEnabled":true
/// }
/// }
///
/// will be indexed with the following keys:
/// - "numplayers": 3 (numeric)
/// - "gamemode.map":"level3-a" (string)
/// - "gamemode.extraFooEnabled":true (bool)
///
/// To enable search without filtering, set indexedDocument to an empty json object '{}'.
/// </remarks>
std::string indexedDocument;
MSGPACK_DEFINE(gameFinderName, customData, onlyLeaderCanInvite, isJoinable, indexedDocument);
};
Parties can be searched by calling PartyApi::Search()
with a json Stormancer filter as the argument. Only clauses compatible with the Lucene query parser are supported (see Stormancer.Server.Plugins.Queries).
{
"bool":{
"must":[
{
"term":"numplayers",
"value":2
},
{
"term":"gamemode.map",
"value":"level3-a"
}]
}
Validating party settings and party member data
Party settings and party member data can be validated by creating a class implementing IPartyEventHandler
and the methods OnUpdatingSettings
and OnUpdatingPartyMemberData
. Both APIs support denying the update and providing an error string sent back to the caller.
Remarks and common issues
Setting the party member as not ready do not always prevent the player from entering matchmaking
If all party members set their state as ready, the party will enter game finding. Thanks to latency, there is an unavoidable small timeframe when a player could set itself as not ready too late to prevent that from happening. In this case, they will enter game finding even though they tried to stop the process. Your game must take account of this possibility and use the party and game finding states as reported by the corresponding Stormancer APIs as authoritative source of truth.
Generally speaking using local predictions to update the client UI without latency is OK, but be aware that these predictions might be wrong in rare situations and that rollbacks could happen.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net8.0
- Stormancer.Abstractions.Server.GameFinder (>= 1.0.0-pre)
- Stormancer.NewtonsoftJson (>= 0.1.1.1-pre)
- Stormancer.Server.Plugins.GameFinder (>= 8.1.1.17-pre)
- Stormancer.Server.Plugins.GameSession (>= 6.2.1.71-pre)
- Stormancer.Server.Plugins.Profile (>= 4.1.2-pre)
- Stormancer.Server.Plugins.Users (>= 10.0.2.13-pre)
- Stormancer.Server.Plugins.Utilities (>= 3.2.0.6-pre)
NuGet packages (5)
Showing the top 5 NuGet packages that depend on Stormancer.Server.Plugins.Party:
Package | Downloads |
---|---|
Stormancer.Server.Plugins.Steam
Steam integration for Stormancer server applications. |
|
Stormancer.Server.Plugins.Friends
Provides a friend system to Stormancer applications. |
|
Stormancer.Server.Plugins.Epic
Provides Epic Games integration to Stormancer server applications. |
|
Stormancer.Server.Plugins.PartyMerging
Enables party merging according to customizable algorithms. |
|
Stormancer.Server.Plugins.SampleApp
The core features of the Stormancer sample application in a nuget package. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
5.2.0.87-pre | 43 | 11/20/2024 |
5.2.0.86-pre | 171 | 9/30/2024 |
5.2.0.85-pre | 77 | 9/30/2024 |
5.2.0.83-pre | 124 | 9/27/2024 |
5.2.0.76-pre | 112 | 9/26/2024 |
5.2.0.75-pre | 102 | 9/26/2024 |
5.2.0.74-pre | 176 | 9/8/2024 |
5.2.0.73-pre | 94 | 9/6/2024 |
5.2.0.71-pre | 128 | 7/29/2024 |
5.2.0.69-pre | 93 | 7/26/2024 |
5.2.0.58-pre | 99 | 7/24/2024 |
5.2.0.57-pre | 122 | 7/9/2024 |
5.2.0.53-pre | 91 | 7/3/2024 |
5.2.0.52-pre | 99 | 6/19/2024 |
5.2.0.51-pre | 96 | 6/18/2024 |
5.2.0.50-pre | 94 | 6/17/2024 |
5.2.0.49-pre | 87 | 6/14/2024 |
5.2.0.45-pre | 101 | 6/10/2024 |
5.2.0.44-pre | 114 | 5/12/2024 |
5.2.0.43-pre | 77 | 5/12/2024 |
5.2.0.42-pre | 123 | 4/22/2024 |
5.2.0.41-pre | 100 | 4/17/2024 |
5.2.0.39-pre | 108 | 4/16/2024 |
5.2.0.38-pre | 115 | 4/8/2024 |
5.2.0.37-pre | 79 | 4/8/2024 |
5.2.0.36-pre | 130 | 4/2/2024 |
5.2.0.35-pre | 95 | 3/14/2024 |
5.2.0.34-pre | 121 | 3/11/2024 |
5.2.0.30-pre | 99 | 2/23/2024 |
5.2.0.29-pre | 187 | 12/12/2023 |
5.2.0.28-pre | 110 | 12/11/2023 |
5.2.0.27-pre | 111 | 12/11/2023 |
5.2.0.26-pre | 147 | 12/1/2023 |
5.2.0.25-pre | 132 | 11/24/2023 |
5.2.0.18-pre | 141 | 11/20/2023 |
5.2.0.15-pre | 202 | 10/27/2023 |
5.2.0.14-pre | 147 | 10/20/2023 |
5.2.0.13-pre | 136 | 10/19/2023 |
5.2.0.12-pre | 140 | 10/18/2023 |
5.2.0.11-pre | 152 | 10/12/2023 |
5.2.0.10-pre | 127 | 10/10/2023 |
5.2.0.8-pre | 127 | 10/5/2023 |
5.2.0.4-pre | 122 | 10/5/2023 |
5.2.0.1-pre | 122 | 10/4/2023 |
5.1.0.33-pre | 236 | 8/18/2023 |
5.1.0.32-pre | 207 | 8/1/2023 |
5.1.0.31-pre | 160 | 8/1/2023 |
5.1.0.30-pre | 207 | 7/5/2023 |
5.1.0.29-pre | 148 | 7/5/2023 |
5.1.0.28-pre | 165 | 6/29/2023 |
5.1.0.27-pre | 137 | 6/27/2023 |
5.1.0.25-pre | 182 | 6/27/2023 |
5.1.0.24-pre | 132 | 6/27/2023 |
5.1.0.23-pre | 195 | 6/26/2023 |
5.1.0.22-pre | 159 | 6/23/2023 |
5.1.0.21-pre | 135 | 6/21/2023 |
5.1.0.20-pre | 142 | 6/21/2023 |
5.1.0.18-pre | 141 | 6/21/2023 |
5.1.0.17-pre | 154 | 6/16/2023 |
5.1.0.16-pre | 143 | 6/15/2023 |
5.1.0.15-pre | 167 | 6/13/2023 |
5.1.0.14-pre | 153 | 6/6/2023 |
5.1.0.13-pre | 161 | 6/5/2023 |
5.1.0.12-pre | 152 | 5/26/2023 |
5.1.0.11-pre | 153 | 5/25/2023 |
5.1.0.10-pre | 135 | 5/25/2023 |
5.1.0.9-pre | 151 | 5/25/2023 |
5.1.0.8-pre | 136 | 5/24/2023 |
5.1.0.7-pre | 146 | 5/24/2023 |
5.1.0.6-pre | 146 | 5/23/2023 |
5.1.0.5-pre | 138 | 5/22/2023 |
5.1.0.4-pre | 135 | 5/22/2023 |
5.1.0.3-pre | 134 | 5/17/2023 |
5.1.0.2-pre | 143 | 5/14/2023 |
5.1.0.1-pre | 109 | 5/12/2023 |
5.1.0-pre | 142 | 5/11/2023 |
5.0.1.9-pre | 147 | 5/10/2023 |
5.0.1.8-pre | 164 | 5/9/2023 |
5.0.1.7 | 268 | 5/9/2023 |
5.0.1.7-pre | 135 | 4/30/2023 |
5.0.1.6-pre | 141 | 4/25/2023 |
5.0.1.5-pre | 139 | 4/24/2023 |
5.0.1.4-pre | 163 | 4/21/2023 |
5.0.1.3-pre | 142 | 4/21/2023 |
5.0.1.2-pre | 154 | 4/21/2023 |
5.0.1.1-pre | 154 | 4/17/2023 |
5.0.1-pre | 159 | 4/11/2023 |
5.0.0.2 | 340 | 4/4/2023 |
5.0.0.2-pre | 225 | 3/7/2023 |
5.0.0.1-pre | 181 | 1/17/2023 |
5.0.0-pre | 177 | 1/17/2023 |
4.5.1-pre | 180 | 1/12/2023 |
4.5.0-pre | 179 | 1/10/2023 |
4.4.0.3-pre | 173 | 1/4/2023 |
4.4.0.2-pre | 161 | 1/4/2023 |
4.4.0.1-pre | 152 | 1/3/2023 |
4.4.0-pre | 171 | 1/3/2023 |
4.3.3.1-pre | 170 | 12/21/2022 |
4.3.3-pre | 163 | 12/7/2022 |
4.3.2-pre | 143 | 12/6/2022 |
4.3.1.13 | 864 | 10/26/2022 |
4.3.1.12 | 935 | 9/28/2022 |
4.3.1.12-pre | 211 | 9/23/2022 |
4.3.1.9-pre | 188 | 9/23/2022 |
4.3.1.7-pre | 197 | 9/19/2022 |
4.3.1.6-pre | 214 | 9/12/2022 |
4.3.1.4-pre | 376 | 8/2/2022 |
4.3.1.3-pre | 205 | 7/19/2022 |
4.3.1-pre | 237 | 7/1/2022 |
4.3.0.1-pre | 196 | 9/20/2022 |
4.3.0-pre | 218 | 6/9/2022 |
4.2.0.18-pre | 281 | 5/3/2022 |
4.2.0.17-pre | 201 | 5/2/2022 |
4.2.0.16-pre | 209 | 4/29/2022 |
4.2.0.14-pre | 214 | 4/21/2022 |
4.2.0.13-pre | 229 | 4/14/2022 |
4.2.0.12-pre | 174 | 4/13/2022 |
4.2.0.11-pre | 197 | 4/12/2022 |
4.2.0.10-pre | 202 | 3/23/2022 |
4.2.0.2-pre | 181 | 3/22/2022 |
4.1.4.4-pre | 230 | 3/15/2022 |
4.1.4.3-pre | 217 | 2/28/2022 |
4.1.4.2 | 721 | 2/7/2022 |
4.1.4.1 | 714 | 2/1/2022 |
4.1.4.1-pre | 196 | 2/1/2022 |
4.1.4-pre | 235 | 1/27/2022 |
4.1.3-pre | 184 | 1/26/2022 |
4.1.2.5-pre | 215 | 1/26/2022 |
4.1.2.1 | 711 | 1/21/2022 |
4.1.1.6 | 752 | 1/4/2022 |
4.1.1.4 | 425 | 12/15/2021 |
4.1.1.4-pre | 261 | 10/29/2021 |
4.1.1.1-pre | 277 | 10/28/2021 |
4.1.1-pre | 316 | 10/18/2021 |
4.1.0.5 | 457 | 10/14/2021 |
4.1.0.5-pre | 369 | 10/1/2021 |
4.1.0.4-pre | 253 | 9/20/2021 |
4.1.0-pre | 259 | 9/19/2021 |
4.0.4.2 | 581 | 5/23/2021 |
4.0.4.2-pre | 458 | 5/12/2021 |
4.0.3 | 598 | 4/29/2021 |
4.0.2 | 422 | 4/19/2021 |
4.0.2-pre | 253 | 4/12/2021 |
4.0.1 | 559 | 3/1/2021 |
4.0.0.2 | 618 | 12/9/2020 |
4.0.0.1 | 615 | 12/4/2020 |
3.2.2.4 | 654 | 10/20/2020 |
3.2.2.4-pre | 327 | 10/5/2020 |
3.2.2.3-pre | 358 | 9/23/2020 |
3.2.2.2-pre | 374 | 9/16/2020 |
3.2.1-pre | 399 | 8/29/2020 |
3.2.0.4-pre | 333 | 8/21/2020 |
3.2.0.3-pre | 361 | 8/20/2020 |
3.1.0 | 833 | 5/27/2020 |
3.1.0-pre | 522 | 5/20/2020 |
3.0.0.1-pre | 606 | 4/2/2020 |
3.0.0-pre | 420 | 4/2/2020 |
2.2.0.1-pre | 464 | 3/29/2020 |
2.1.0-pre | 455 | 3/12/2020 |
2.0.0.1-pre | 355 | 3/6/2020 |
2.0.0 | 859 | 2/21/2020 |
2.0.0-pre | 494 | 2/22/2020 |
1.1.0 | 696 | 1/29/2020 |
1.0.0.3 | 625 | 1/4/2020 |
1.0.0.2 | 621 | 1/4/2020 |
1.0.0.1 | 636 | 1/4/2020 |
Changed
*******
- Updated dependency to Users to support new abstraction lib.
- Remove current gamesession in party when the gamesession scene shuts down.
- If party does not exist, CreateConnectionToken returns 'notFound'
- Removes an error if a user has no profile when entering the party
- Improved Error logs in case of gamefinder failures.
Fixed
*****
- Use PeriodicTimer in PartyAnalyticsWorker to fix an high usage CPU issue.
- Don't create an error log if the party of a player was already destroyed when a gamesession try to update its party state.
- Fixed issue in party size check that prevented the connecting player from being taken into account.
- Made the plugin compatible with Users 9.0.0.
- Don't create a new reservation cleanup loop each time a PartyService is created.
Added
*****
- Added Party Id to PartySettingsUpdateDto
- Added LocalPlayerCount to party user data to enable party members to be associated to several local players.
- Added default implementations to several methods of `IPartyEventHandler`
- Added TryGetGamesessionId extension method to party.
- Added extensibility point IPartyEventHandler.OnCreatingReservation
- Added a section on common issues in the documentation.
- Added a reason argument to the method 'kick from party'.
- Check the crossplay options when joining the party.