Appearance
Appearance
Using Logic Versions, you can implement backward-compatible support so multiple client versions can connect to the server. Although you can only deploy one server version at a time, it can support multiple client versions.
The Logic Versions track the range of implementations the client and the server support. The Version, therefore, should be bumped up whenever changes are made to the deterministic simulation being run on the client and the server to avoid two players having the same Version but different results on a given operation.
You can define which client versions work with the current server by modifying the range of Logic Versions in MetaplayCoreOptions.supportedLogicVersions
. You can, in turn, define the client's Logic Version in MetaplayCoreOptions.clientLogicVersion
. By default, the server will support only a single client version, but it's possible to support multiple versions simultaneously with a bit of extra work.
You can change the Active Logic Version Range in the LiveOps Dashboard. This range determines which client Logic Versions can connect to the server. Even if a client is within the Supported Logic Version Range, it won't be able to connect to the server unless the Active Logic Version Range includes that client's Logic Version.
To change the Active Logic Version Range, go to the LiveOps Dashboard and click the "Settings" button. Navigate to the "Client Compatibility Settings" section and click the "Edit Settings" button. You can then change the Active Logic Version Range to include the desired Logic Versions.
Tread Carefully!
If you roll back the Active Logic Version Range to a lower value than before, you may experience data loss or unexpected behavior.
You can also enable auto-upgrading of the Active Logic Version Range by setting the System.AutoUpgradeLogicVersion
runtime option to true
. This will automatically update the Active Logic Version Range to the highest Supported Logic Version when the server starts.
Suppose you want to roll out a new feature and bump up the current LogicVersion
. Here are the steps necessary to support both the old and new clients:
clientLogicVersion
.You can specify the clientLogicVersion
on the client and supportedLogicVersions
on the server separately in your options:
public MetaplayCoreOptions Options => new MetaplayCoreOptions(
...
// Specify client version (only support version 6)
clientLogicVersion: 6,
// Server accepts multiple client versions (5 and 6)
supportedLogicVersions: new MetaVersionRange(5, 6),
...);
You can find the definition of MetaplayCoreOptions
in Assets/SharedCode/GlobalOptions.cs
.
Although sending new actions from the client is safe, you need to be careful when adding new actions to the server. Executing a server action on a player with an incompatible logic version will cause the player to be kicked and their data to be updated to the minimum required logic version. Therefore, it's important to consider that your users might be on different Logic Versions when executing actions.
To trigger behavior changes based on the Logic Version, you can use regular if
statements in PlayerAction
s or in the PlayerModel
tick:
// On the server, this will check what the client version is.
if (playerModel.LogicVersion >= 6) {
// Implementation of feature X
} else {
// Old implementation before feature X, in case some old behavior was removed
}
Be Careful!
It's important that the server is still able to reproduce the old game logic identically for all client versions connecting to the server.
You can add or remove all MetaSerializable
members (like PlayerModel
members, for example) in new Logic Versions, but they must be marked with the [AddedInVersion(...)]
and [RemovedInVersion(...)]
attributes to ensure the model checksums still get computed correctly across all versions:
class PlayerModel ... {
// Example of adding a new member. The member is not checksummed when dealing
// with clients with LogicVersion<=5.
[MetaMember(123)]
[AddedInVersion(6)]
string NewMemberRequiredByFeatureX;
// Example of soft-removing a member that is no longer used by LogicVersion>=6
// but is kept around for compatibility with LogicVersion<=5.
[MetaMember(124)]
[RemovedInVersion(6)]
string SomeDeprecatedMember;
}
You can also do the same for classes. However, serializing or deserializing a class that isn't available in the player's Logic Version will throw an exception.
[MetaSerializable]
abstract class BaseClass {
...
}
[AddedInVersion(6)]
[MetaSerializableDerived(1)]
class NewlyAddedClass : BaseClass {
...
}
We also provide some utilities to check whether a given instance is compatible with the Logic Version. This is especially useful in cases where the data is coming from a different system or you're targeting a group of users that can be on different Versions of the client.
class PlayerActor : ... {
void DoSomething()
{
if (MetaSerializationUtil.IsInstanceCompatibleWithLogicVersion<NewlyAddedClass>(newlyAddedClass, ClientLogicVersion))
{
// Implementation of the feature
}
else
{
// Fallback for players that do not support this feature
}
}
}
The dashboard can communicate that certain features are not available to a given player or a group of players. For built-in features, we've already taken care of this when you're using the AddedInVersion
or RemovedInVersion
attributes.
Manually disabling components when you know the feature is not available to a player is the easiest option. However, that is not always feasible. For example, when using GeneratedForms
, you often don't know whether a type is available. We provide two built-in solutions for this:
First, when you know which player you're targeting and what logic version they're on at the moment, passing the player's logic version to the generated form will disable components known to be unavailable.
meta-generated-form(
v-model="mail"
:typeName="'Metaplay.Core.InGameMail.MetaInGameMail'"
:forcedLocalization="playerData.model.language"
:page="'PlayerActionSendMail'"
class="!tw-overflow-x-hidden"
:logic-version="playerData.model.logicVersion"
@status="isValid = $event"
)
The other option helps when you target a group of players, like with Broadcasts or LiveOps Events. We will show a warning if a member or type is only available to a subset of players. Remember that we won't stop you from executing these actions, and as such, you should handle this gracefully. For example, through segmentation or your server implementation, you can verify that a player is compatible with the action you're executing.
To enable this, you can just set is-targeting-multiple-players
on a GeneratedForm
:
meta-generated-form(
typeName="Metaplay.Server.NotificationCampaign.NotificationContent"
:value="campaignFormInfo.content"
:page="'NotificationFormButton'"
class="tw-mt-3"
@input="campaignFormInfo.content = $event"
@status="isContentValid = $event"
is-targeting-multiple-players
)
There are a few things to keep in mind to keep the experience of working with multiple client versions flowing smoothly:
PlayerModel
is incompatible with the current Logic Version, as this can happen because an incompatible type was accidentally added to the PlayerModel or an incompatible ServerAction was executed against the PlayerModel. In these cases, we'll forcefully update the player to the minimum required Logic Version that supports the types.TerminalError.LogicVersionDowngrade
being thrown, which should be handled in the client by showing an error message telling the player to update their client.If you have any issues, please don't hesitate to send us a message.
Client Patch Versions
within a given Logic Version, see Client Patches for details.