Appearance
Appearance
Using Logic Versions, you can implement backward-compatible support so multiple client versions can connect to the server. Although only one server version can be deployed at a time, it can support multiple client versions.
The Logic Versions are used to track the range of implementations the client and the server support. The Version, therefore, should be bumped whenever changes are made to the deterministic simulation 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
. The client's Logic Version is defined 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 little extra work.
The active Logic Version Range can be changed in the LiveOps Dashboard. This range determines which client 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 if the Active Logic Version Range does not include 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 behaviour.
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 have 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 they will 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.
All MetaSerializable members (e.g. PlayerModel
members) can be added and removed but 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;
}
Similarly, you can do the same for classes. However, serializing or deserializing a class that is not available for the player's LogicVersion 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 at the same time.
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 you're using GeneratedForms, you often don't know whether the type is available. We provide 2 built-in solutions for this, the first helps 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 that are known to not be available.
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 in cases where you are targeting a group of players, think Broadcasts or LiveOps Events. We will show a warning if a member or type is only available to a subset of players. Keep in mind 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 can verify that a player is indeed compatible.
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:
TerminalError.LogicVersionDowngrade
being thrown, which should be handled by 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.