Appearance
Appearance
Latest release
Release 29.4 is the latest hotfix release for Release 29.
Critical update available!
Release 29.2 contains fixes for two issues related to use of Unity's XXHash3 starting in Release 29:
Both of these were regressions in Release 29 as we started using Unity's new Burst-optimized XXHash3 implementation. The issues are also present in Release 29.1.
If you are on Release 29 or 29.1, you should immediately upgrade to Release 29.4!
You can now run multiple leagues simultaneously, each with its own rules, participants, and settings. See the Advanced Leagues Customization guide for more information.
We have migrated our tests to use the Playwright test runner. This setup is now also available for your game-specific dashboard tests. Playwright is significantly faster than the previous Cypress-based runner, allowing us to increase the built-in test coverage. Check out the Introduction to Automated Testing to get started with your own tests.
Our component library documentation is now publicly accessible both locally and on the web. Browse through a wide range of components and learn how to use them in your custom dashboard. Start exploring the Metaplay UI overview page for more information.
EntityKind
values up to 1024 are now supported (up from the previous maximum of 64).KeyManagerActor
that supports creating signature JWTs for outgoing requests.AddListener()
can be given a label
argument to allow removing all listeners with a specific label with RemoveListenersWithLabel()
.RuntimeOptionsBase.GetDependencyAsync()
which can be used in runtime options OnLoadedAsync()
to better support dependencies between runtime options classes.MetaReceiveActor._cancelTimers
helper that can be given to Akka.NET's Context.System.Scheduler.ScheduleTellOnce()
and Context.System.Scheduler.ScheduleTellRepeatedly()
to automatically cancel the timers on entity shutdown.dotnet_runtime_info
with labels for the .NET version and runtime. The runtime information is also logged when the server is started.BotClientBase
now supports making fake in-app purchases. You can use the StartFakeInAppPurchase(InAppProductId)
method for static IAPs. For dynamic-content IAPs, invoke the appropriate feature-specific action for preparing the dynamic content (e.g., PlayerPreparePurchaseMetaOffer
for MetaOffers
), and BotClientBase
will automatically make the purchase after the server has confirmed the dynamic content.MetaSerializable
types using UITK in Unity, see MetaGeneratedEditorUI
for more info.TerminalError.ProjectNameMismatchError
connection state for when the client and server do not have the same project name.game_player_actions_time
tracking the duration of each action's execution.RuntimeOptionsBase.OnLoadedAsync(RuntimeOptionsRegistry)
for accessing the existing registry for dependent options via GetDependencyAsync()
.Application.AddServices()
for introducing application-specific services to the service collection. This replaces Application.PreinitializeAsync()
that was previously used for initializing services statically.RuntimeEnvironmentInfo
container for application runtime configuration that was previously owned by RuntimeOptionsRegistry
.EphemeralMultiplayerEntityActorBase
to complement PersistedMultiplayerEntityActorBase
. Ephemeral entities behave identically to the persisted variants, except their state is not stored in the database. Instead, on each actor wakeup, they start with no state.com.unity.collections
v1.4.0, which is required to use Unity's XXHash3.MetaSerialization.ResolveMetaRefs()
now throws InvalidOperationException
instead of MetaRefResolveError
when it encounters an unresolvable MetaRef
. Accordingly, the MetaRefResolveError
type has been removed. The dedicated exception type was removed because the SDK no longer needs it, due to improvements in MetaRef
validation at game config build time.MetaSerialization.ResolveMetaRefs()
now takes the object by value instead of as ref
. Due to a change in Metaplay release 28, MetaSerialization.ResolveMetaRefs()
no longer reassigns the identities of the objects in the object tree but merely in-place mutates the MetaRef
s.EntityAsk
s to entities with no matching EntityAskHandler
fail with NoHandlerForEntityAsk
exception instead of timing out.DynamicEnum
no longer supports negative id values.update-cloudcore-versions.sh
script is now embedded in the Dockerfile.server
.GlobalStateManager
to StatsCollectorManager
.MetaplaySDK.EditorHookOnExitingPlayMode
has been removed. It was previously used for flushing the network thread log buffer associated with a current server connection on editor playmode exit. The same effect is now achieved by explicitly closing any existing connection in MetaplaySDK.OnApplicationQuit
.MetaplaySDK.ExitRequestedCallback
has been removed. Automatic MetaplayClient
deinitialization on editor playmode exit is now implemented within the default MetaplayClient
implementation.TaggedWireSerializer.GetWireType(Type)
. The type registry to use for finding MetaSerializable
types must now be explicitly passed in (use MetaSerializerTypeRegistry.TypeInfo.Specs
in most cases).MetaSerializerTypeInfo.ConstructorParameterValues
into MetaSerializableType.ConstructorParameterValues
.RUN_TESTS
flag for Dockerfile.server
as it's no longer needed.MetaDelay
unit tests that became flaky under extreme CPU starvation, as in CI environments.MetaMember
by ignoring underscores.ProjectSettings/Metaplay
by default instead of Assets/Resources/Metaplay/Editor
. The file is automatically moved to the new location when Unity is opened.PlayerModel.CurrentTick
and other model timeline tick counters have been changed from int
to long
. This change supports custom tests that run simulated gameplay over a very long time span without resetting the tick counter. The behavior of the tick counter during normal gameplay has not been changed and is still reset at the start of each game session.MetaplayCore
constructor is now optional.ClusterConfig.NodeSets
has been hidden to prevent indexing into the NodeSets, which is error-prone. To enumerate all nodesets, use the EnumerateNodeSets()
method.ShardingStrategies.CreateService()
has been removed and replaced with CreateStaticService()
and CreateDynamicService()
. See the migration guide for details..unitypackage
file instead of the package manager.MetaplayController
, AskEntityAsync()
has been renamed to EntityAskAsync()
for consistency with the equivalent method in EntityActor
. The old method still exists for the time being but is marked [Obsolete]
.MetaplayCore.Initialize()
has been rewritten to internally use a lightweight dependency injection system conceptually compatible with the .NET dependency injection framework. Core initialization by the server application no longer uses this method in favor of configuring the services into the .NET service collection via IServiceCollection.AddMetaplayCore()
.MetaplayServices
static service locator.MetaplayCore.Initialize()
now takes care of initializing the MetaSerialization system as well, removing the need for initializing serialization separately.RuntimeOptionsRegistry
is now pre-initialized early during the application configuration for the ability to read startup options prior to starting the application host.Assets/SharedCode/
) and SDK shared code (MetaplaySDK/Client/
) must now exist in different client-side assemblies. The SDK enforces this when running in the Unity Editor. For all new projects, and most existing projects, nothing is changed, since this is already the case in normal SDK installations.ForceFullDebugConfigForBots
is now only enabled by default for locally run bots.EntityConfig.EntityActorType
and PersistedEntityConfig.PersistedPayloadType
now take an EntityId
parameter.SupportedSchemaVersions
helpers in PersistedEntityConfig
. They are now found in PersistedEntityActor
.IEntityAsker
to IEntityMessagingApi
.PlayerActorBase.OnAssociatedEntityRefusalAsync()
no longer is given the redundant EntityId playerId
parameter.DataProtectionProvider
that throws to ensure any accidental usage is noticed.MetaSerialization
now supports null DynamicEnum
s. It used to throw NullReferenceException
.EntityActor.StartPeriodicTimer()
did not get canceled if the actor crashed, which caused dead letter warnings.PlayerActor.OnAssociatedEntityRefusalAsync
) not run for associations marked to end automatically on session end (PlayerActorBase.AddEntityAssociation(..., removeOnSessionEnd: true)
) if the initial entity rejects session with resource correction.OrderedDictionary<TKey, TValue>
used via the non-generic IDictionary
interface now allows a null key or value when the respective TKey
or TValue
type is a Nullable<>
type. Previously, it threw an ArgumentException
on null keys and values unless the type parameter was a reference type.GetEventLogTimestamp
), which could occur when a player sends a large number of PlayerAction
s in a single batch.MButtonGroupLayout
component: This component makes it easy to create consistent-looking groups of buttons. Notably, it now expands buttons to full width on narrow browsers to keep them legible. We have migrated all our button groups to use this new layout. @metaplay/playwright-configs
package: This package provides actions, fixtures, and sensible default configurations for writing tests against the LiveOps Dashboard using Playwright.MViewContainer
component that provides a container for dashboard pages and offers a consistent layout for the page content.MPageOverviewCard
component that provides a high-level overview of a dashboard page and offers quick access to key information and actions.useNotifications
composable that provides a way to show and manage notifications to the user.api.players.view_developers
for viewing the list of developer players. This allows you to decide who can see the list of developer players in a more granular manner. Previously, this was behind the api.players.view
permission, meaning that any user who could view players could also view developer players. If you use custom roles, you will need to add this new permission. Check the migration guide for more details.MTwoColumnLayout
component, which provides a two-column layout for structuring dashboard views and components.MThreeColumnLayout
component, which offers a three-column layout for structuring dashboard views and components.MetaUiNext
components: MetaPageContainer
is replaced by MViewContainer
.MetaPageHeaderCard
is replaced by MPageOverviewCard
.MetaAuthTooltip
component has been removed as it was deprecated in favor of using the permission
prop in new components.PollingPolicyOnceOnly
would never fetch data if the client was offline. The initial fetch would fail, and the poller would never retry. The poller now continues to retry fetching the data until it succeeds once.MTooltip
hover bug where the tooltip would disappear when the mouse hovered over it. It now stays open as expected.EntityKind
allowed value ranges have been expanded to use the [100, 300] range that is now the default for the games. The Idler sample also uses the old [30, 50] range to show how multiple ranges can be used.EntityKindCloudGame
(which was intended to contain the server-only EntityKinds) has been merged into EntityKindGame
. The separation was causing unnecessary confusion for very little gain. You can still keep the separation in your project if you want.Customizing the Dashboard
sections of the documentation have been refreshed. The section now includes information about our latest tooling, component libraries, and up-to-date code snippets for customizing the dashboard.While we strive to resolve all issues, the following are known issues in this release:
⚠️ Crash on 32-bit Android Devices
Releases 29.0 and 29.1 contain a crash bug on 32-bit ARM devices.
The root cause is that Unity's Burst-optimized Unity.Collections.xxHash3
algorithm implementation relies on unaligned memory accesses which cause crashes on 32-bit ARM devices that only allow aligned memory accesses.
Release 29.2 includes a fix for this issue!
Grafana heatmaps render incorrectly when viewing time windows that span events from both Release 29 and older releases. This issue is caused by a change in the underlying Prometheus library, which has altered the formatting of floating-point numbers in heatmap buckets (e.g., 10 vs. 10.0). As a result, Grafana encounters difficulties when rendering heatmaps containing values in both formats within the same time window, leading to inaccurate visualizations.
To view the data correctly, adjust the time window to display results from only one SDK version at a time, i.e., either Release 29 events only or Release 28 (or older) events.
The ESLint plugin used in the LiveOps Dashboard project is scheduled to reach EoL during this release cycle. Unfortunately, the ESLint open-source community has not yet released compatible versions of all the underlying shared configurations.
We are looking to update to ESLint 9.x as soon as the ecosystem is ready.
This release deprecates support for Unity 2021.3 as it has reached end-of-life and is no longer supported by Unity. The lowest supported Unity version is now 2022.3. While this release still works with Unity 2021.3, we plan to remove support for it in R31, tentatively scheduled for December 2024.
Still using 2021.3?
If you cannot reasonably upgrade to 2022.3 or later in the near future, please let us know.
Please apply the following changes to your project to ensure compatibility with the latest Metaplay SDK.
Backward-Incompatible Changes
Bump your game's MetaplayCoreOptions.supportedLogicVersions
to force a synchronized update of your game client and server.
The Metaplay SDK now requires Helm chart v0.6.3 for any cloud deployments.
Migration Steps:
Update your CI pipelines for all your environments to use version 0.6.3 of the Helm chart.
When using GitHub Action scripts, make the following change:
jobs:
...
build-and-deploy-server:
...
with:
helm-chart-version: "0.x.y"
helm-chart-version: "0.6.3"
With other CI systems, apply the following change (or the equivalent in your CI system):
export HELM_CHART_VERSION="0.x.y"
export HELM_CHART_VERSION="0.6.3"
Roll out the change to each environment by running the CI job to build and deploy a new version. You can do this for each environment separately, whenever is a good time for you.
Premium SDK Update Support
If your support contract includes Metaplay-provided SDK updates, all the following steps have already been applied to your project. You can skip this migration guide!
This guide offers step-by-step instructions for migrating your project to the latest version of the Metaplay SDK. You can skip the migrations steps for features you are not using in your project.
The following core SDK changes affect all Metaplay projects:
Metaplay's built-in database schema has changed, and you need to apply the migration steps to your project.
Migration Steps:
Generate the database schema migration code with:
# Install or update the EFCore tool:
Backend/Server$ dotnet tool install -g dotnet-ef
# Then, generate the migration code:
Backend/Server$ dotnet ef migrations add MetaplayRelease29
Then, add the generated files to your project's source control. For example, using Git:
Backend/Server$ git add .
Backend/Server$ git commit -m "Database schema migrations"
The migration steps will be automatically applied when you deploy the updated game server into an environment.
With the expanded range of values allowed by EntityKind
, you can now use the range [100, 300) in your game. You should also merge the EntityKindCloudGame
into EntityKindGame
as the separation mainly caused confusion, as we did in our samples.
Migration Steps:
The following example shows how to do the same in your game:
[EntityKindregistry(40, 50)] // Old range for the shared EntityKind registry
[EntityKindRegistry(30, 50)] // Old range for the unified registry
[EntityKindRegistry(100, 300)] // New supported range
public static class EntityKindGame
{
// Old EntityKindCloudGame values stay in the [30, 40) range
<Move old EntityKindCloudGame values here>
// Old EntityKindGame values stay in the [40, 50) range
<Old EntityKindGame values>
// New entities can be added in the [100, 300) range
public static readonly EntityKind NewEntity = EntityKind.FromValue(100);
}
Bug alert!
Do not change the values of any of your existing EntityKind
s! If the EntityKind
s are persisted anywhere, their meaning would change when deserializing.
Update all code using the now-obsolete EntityKindCloudGame.Xyz
to EntityKindGame.Xyz
.
DoSomething(EntityKindCloudGame.MyEntityKind);
DoSomething(EntityKindGame.MyEntityKind);
The Metaplay environment config file has been moved to ProjectSettings/Metaplay
.
Migration Steps:
EnvironmentConfigs.json
into your source control.The following LiveOps Dashboard changes affect projects that have a game-specific dashboard project:
As usual, we have updated the underlying dependencies and configurations of the LiveOps Dashboard. This causes changes to several configuration files, which you will need to update in your dashboard project. We use the MetaplaySDK/Frontend/DefaultDashboard
folder as the source of truth for these files.
Migration Steps:
.eslintrc.cjs
. This updates the ESLint configuration for the project..gitignore
. This updates the Git ignore file for the project.tsconfig.node.json
. This updates the TypeScript configuration for the project.package.json
. This updates the dependencies and test scripts for the project. Doing so will overwrite some changes to the file that you will now need to revert. This is best done using a file diff tool: name
property inside the file to that of your project. This is typically of the form <projectName>-dashboard
.playwright.config.ts
file. This adds Playwright support to the project.tests
folder across. This folder includes sample Playwright tests.cypress.config.ts
file if it exists. This file is no longer needed.To ensure that your dashboard project has the correct dependencies, you will need to clear the existing cached files and recreate them.
You should ensure that you have Node version 20.16.0 (the latest 20.x version at the time of writing) installed. To check the current version, run node --version
. If you are using nvm
, you can update Node with:
# Install Node 20.16.0 on Node Version Manager
nvm install 20.16.0
# Use the installed version
nvm use 20.16.0
Migration Steps:
git clean -fdx ':(glob)**/node_modules/*'
from the root of your repository. This clears any currently installed dependencies.git clean -fdx ':(glob)**/dist/*'
from the root of your repository. This clears any previously built files.pnpm-lock.yaml
file from the root of your repository. This clears the cached dependency versions.pnpm install
in your 'Dashboard' folder. This recreates all of the above files and folders with the correct dependencies.MetaUiNext
ComponentsAs part of this release, the following components have been removed in favor of the new MetaUiNext
components:
MetaPageContainer
is replaced by MViewContainer
.MetaPageHeaderCard
is replaced by MPageOverviewCard
.MetaAuthTooltip
has been removed as it was removed in favor of using the permission
prop in new components.Unfortunately, the migration is not automatic, so you will need to manually replace the removed components with the new ones in your dashboard project.
Migration Steps:
Import the new components from the MetaUiNext
package:
import { MPageOverviewCard, MViewContainer } from '@metaplay/meta-ui-next'
Replace the removed components with the new components and update the props accordingly.
meta-page-container(
MViewContainer(
:is-loading="!ExampleData"
:meta-api-error="exampleError"
:error="exampleError"
:alerts="exampleAlerts"
permission="example-permission"
)
meta-page-header-card(
MPageOverviewCard(
:id="exampleID"
:title="exampleTitle"
:subtitle="exampleSubtitle"
:is-loading="!ExampleData"
:alerts="exampleAlerts"
:error="exampleError"
data-testid="example-data-testid"
)
MetaAuthTooltip(:permission="example-permission")
MButton Example Button
MButton(:permission="example-permission") Example Button
For detailed examples of how the new components work, check out our new component documentation.
useNotifications
ComposableshowSuccessToast
and showErrorToast
have been removed in favor of the new useNotifications
composable to show notification-style messages to dashboard users.
Migration Steps:
Update the imports to use the new useNotifications
composable:
import { showSuccessToast, showErrorToast } from '@metaplay/meta-ui'
import { useNotifications } from '@metaplay/meta-ui-next'
Destructure the composable to use the new showSuccessNotification
and showErrorNotification
methods:
showSuccessToast('Success message')
showErrorToast('Error message')
const { showSuccessNotification, showErrorNotification } = useNotifications()
showSuccessNotification('Success message')
showErrorNotification('Error message')
If you have been using the Cypress test runner for your dashboard tests, you will need to migrate your tests to the Playwright test runner. The new @metaplay/playwright-configs
package provides actions, fixtures, and sensible default configurations for writing tests against the LiveOps Dashboard using Playwright.
You can check if you have any of your own Cypress tests by looking inside the gamespecific_tests/e2e
folder. If you do, then you'll need to follow the migration steps below.
If you need to continue running your existing Cypress tests instead of migrating them, please contact us, and we can help you set up Cypress in parallel with Playwright.
Migration Steps:
gamespecific_tests/e2e/TESTNAME.cy.ts
to tests/e2e/TESTNAME.spec.ts
.gamespecific_tests
folder from your dashboard folder when done. This folder is no longer needed.If you have customized the role-to-permission table in your Options.base.yaml
, you need to add two additional permissions to the table.
The following new dashboard admin permissions have been introduced:
api.system.view_telemetry_messages
controls who can view the telemetry messages from the Metaplay portalapi.players.view_developers
controls who can view the developer player listMigration Steps:
Specify which roles should have access to these permissions in your Backend/Server/Config/Options.*.yaml
(whichever file you use to configure the access). You can modify the roles as needed.
AdminApi:
Permissions:
...
api.system.view_telemetry_messages: [ game-admin, game-viewer ]
api.players.view_developers: [ game-admin, game-viewer, customer-support-senior ]
The following changes affect projects that have implemented game-specific entities.
ShardingStrategies.CreateService()
The ShardingStrategies.CreateService()
has been replaced with CreateStaticService()
and CreateDynamicService()
to enable services that work with dynamic node scaling.
Migration Steps:
In your EntityConfig
declarations replace the use of ShardingStrategies
.CreateService()`:
[EntityConfig]
internal sealed class MyEntityConfig : EphemeralEntityConfig
{
public override IShardingStrategy ShardingStrategy => ShardingStrategies.CreateService();
// Use this *ONLY* if you use `EntityActor.GetAssociatedServiceEntityId()` to locate the entity:
public override IShardingStrategy ShardingStrategy => ShardingStrategies.CreateStaticService();
// In most cases you should use the dynamic service strategy:
public override IShardingStrategy ShardingStrategy => ShardingStrategies.CreateDynamicService();
}
Note that the node placement rule given in EntityConfig
s NodeSetPlacement
applies in both cases.
The following core SDK changes affect projects that have implemented the leagues system:
ClientSlotCore.PlayerDivision
has been renamed to ClientSlotCore.PlayerDivisionLegacy
and deprecated. Create a game-specific league client slot in ClientSlotGame.cs
and use that instead.
Update your constructor calls to LeagueClient
to include the new ClientSlot
.
Increment the schema version and add a migration in PlayerModel.cs
to move the old client slot data to the new client slot:
[MigrationFromVersion(fromVersion: 8)] // Replace 8 with the previous schema version in your project
void MigrateLeagues()
{
// Migrate league client state
#pragma warning disable CS0618 // Type or member is obsolete
if (PlayerSubClientStates.TryGetValue(ClientSlotCore.PlayerDivisionLegacy, out PlayerSubClientStateBase idlerState))
{
PlayerSubClientStates.Remove(ClientSlotCore.PlayerDivisionLegacy);
PlayerSubClientStates[ClientSlotGame.IdlerLeague] = idlerState;
} else {
PlayerSubClientStates[ClientSlotGame.IdlerLeague] = new PlayerSubClientState();
}
#pragma warning restore CS0618 // Type or member is obsolete
}
The PlayerActor needs to be updated to use the new league integration handler methods for communicating with the League system. Refer to step 5 of the Leagues Quickstart Guide and "Joining the League" and "Division Scoring" sections of the Customizing Leagues guides for more information. The gist of it is, that any methods in PlayerActorBase
that interact with the League system have now been moved to ILeagueIntegrationHandler
and LeagueComponentBase
. Any calls to these methods need to be updated to call the new methods on the integration handler of the league.
The LeagueManagerOptions
class has been moved to the game side. Implement an options class for your leagues that inherits from LeagueManagerOptionsBase
, and add the [RuntimeOptions]
attribute to it. LeagueManagerActorBase
and PlayerDivisionActorBase
now need the correct options class as a generic parameter.
[RuntimeOptions("Leagues", true, "Options for the leagues manager service.")]
public class LeagueManagerOptions : LeagueManagerOptionsBase { }
Change the game-side LeagueManagerConfig
to inherit from LeagueManagerConfigBase
instead of PersistedEntityConfig
. You can leave the config class empty.
[EntityConfig]
public class LeagueManagerConfig : LeagueManagerConfigBase { }
The game-side DivisionConfig
can now be left completely empty by default.
[EntityConfig]
// Base class handles everything.
public class DivisionConfig : DivisionEntityConfigBase { }
You'll have to implement a LeagueManagerRegistry
that implements the LeagueInfos
property. The property should return a list of all the leagues in your game in the form of LeagueInfo
objects. For example in Idler:
public class IdlerLeagueManagerRegistry : LeagueManagerRegistry
{
public override IReadOnlyList<LeagueInfo> LeagueInfos { get; } = new LeagueInfo[]
{
new LeagueInfo(
leagueManagerId: EntityId.Create(EntityKindCloudCore.LeagueManager,
0), // Value here should be the same as leagueId in the PlayerActor league integration. The ids must start from 0 and increase by 1 for each league.
clientSlot: ClientSlotGame.IdlerLeague,
participantKind: EntityKindCore.Player,
managerActorType: typeof(MyLeagueManagerActor),
optionsType: typeof(MyLeagueManagerOptions),
divisionActorType: typeof(MyPlayerDivisionActor))
};
}
The LeagueRankDetails
class has a new DivisionParticipantCount
property that needs to be set in the GetRankDetails(int rank, int season)
method of your LeagueManagerActor
. This also means that you can now vary the number of participants per rank. You can set this value to be the same as DivisionDesiredParticipantCount
in the old options class.
DivisionDesiredParticpantCount
and DivisionMaxParticipantCount
have been removed from the LeagueManagerOptions
. You should remove them from your options yaml files.
The following changes only affect projects that have custom handling of the networking error states on the client.
TerminalError.ProjectNameMismatchError
ErrorA new network error condition TerminalError.ProjectNameMismatchError
has been added to allow better handling of accidentally connecting to non-matching game servers. You need to handle this in case you have customized the error handling in your game.
Migration Steps:
TerminalError.ProjectNameMismatchError
. You can use the same code as you already use for handling TerminalError.InvalidGameMagic
as the two trigger on the same condition.The following only affects projects that have registered parsers for custom types using the RegisterCustomParseFunc<>()
method.
Registering custom parsing for game config types via the ConfigParserProvider.RegisterParsers()
method needs to be updated to register the parsers to the ConfigParser instance that is provided as the single argument to the method.
If you need a ConfigParser
instance in other contexts, you can use ConfigParser.Instance
to resolve the instance.
Migration Steps:
Update your RegisterParsers()
method to accept ConfigParser
as an argument and invoke the parsing methods method via the passed-in parser. For example:
public override void RegisterParsers()
public override void RegisterParsers(ConfigParser parser)
{
ConfigParser.RegisterCustomParseFunc<PlayerReward>(ParsePlayerReward);
parser.RegisterCustomParseFunc<PlayerReward>(ParsePlayerReward);
}
Update the signature of your custom parse methods to take the ConfigParser
as an argument and invoke the parsing method via the passed-in parser. For example:
static PlayerReward ParsePlayerReward(ConfigLexer lexer)
static PlayerReward ParsePlayerReward(ConfigParser parser, ConfigLexer lexer)
{
...
ProducerTypeId producerTypeId = ConfigParser.Parse<ProducerTypeId>(lexer);
ProducerTypeId producerTypeId = parser.Parse<ProducerTypeId>(lexer);
...
}
Note that the old parse method signature continues to be supported and can be used if you have no need to recursively invoke the parser.
If you make use of the Metaplay SDK's Game Config Build Window in the Unity editor, follow the steps below.
The GameConfigBuildWindowMenuItem.cs
file is no longer necessary and can be removed.
Migration Steps:
GameConfigBuildWindowMenuItem.cs
file from your project.If you make use of the Metaplay SDK outside of the standard applications (game client, server, or botclient), you need to update the Metaplay core and serializer initialization.
Metaplay now automatically handles the SDK initialization and thus no longer needs to be explicitly initialized. The only exception is initialization code using Unity's [InitializeOnLoad]
or [InitializeOnLoadMethod]
attribute, if the Metaplay SDK is used within the code.
Migration Steps:
Remove any calls to MetaplayCore.Initialize()
from within the Unity editor. They are now redundant and should be removed.
Replace any other existing MetaplayCore
and MetaSerialization
initialization with a single call:
MetaplayCore.Initialize(...);
MetaSerialization.Initialize(...);
MetaplayCore.Initialize(services =>
services.ConfigureMetaSerialization("<ApplicationName>"));
Substitute <ApplicationName>
with an appropriate name for the application, for example "MyGameUtility"
.
Ensure Metaplay SDK is initialized in code using Unity's [InitializeOnLoad]
or [InitializeOnLoadMethod]
attribute, if you use Metaplay SDK within them. For example:
[InitializeOnLoadMethod]
static void MyInitializationMethod()
{
// Make sure to initialize Metaplay SDK before using it:
MetaplayCoreEditor.EnsureInitialized();
... some code using the Metaplay SDK
}
In case your project is one that started using Metaplay a long time ago, before the proper separation of the SharedCode/
directory into its own assembly, you'll need to now apply that change.
If your project is affected, you will see the following error in the Unity Editor log and when trying to enter play mode: Metaplay client SDK and user-side game logic code have the same assembly: [...]
.
Metaplay now requires the game-specific shared code (e.g. Assets/SharedCode/
) and SDK-side shared code (MetaplaySDK/Client/
) are in separate Unity assemblies.
Migration Steps:
Remove the .asmref
in your shared-code folder (e.g. Assets/SharedCode/
) which refers to the Metaplay
assembly, and replace it with the file SharedCode.asmdef
with the JSON contents shown in the error message.
Fix any potential inverse dependencies that may exist in case you have modified the Metaplay SDK code.
These changes affect you in case you happen to use any of the APIs changed. You can build your project to get a list of any incompatibilities instead of going through the list one item at a time.
SchemaVersion
Helpers in PersistedEntityConfig
The SupportedSchemaVersions
, CurrentSchemaVersion
, and OldestSupportedSchemaVersion
helpers in PersistedEntityConfig
have been moved to PersistedEntityActor
for convenience.
Migration Steps:
Replace any usage of the above properties using the new helpers.
void MethodInMyPersistedActor()
{
_log.Debug("Current schema version: {CurrentSchemaVersion}", _entityConfig.CurrentSchemaVersion);
_log.Debug("Current schema version: {CurrentSchemaVersion}", CurrentSchemaVersion);
}
Outside of entity context, the schema versions can be found with SchemaMigrationRegistry.Instance.GetSchemaMigrator<TPersistedPayload>().SupportedSchemaVersions
.
ref
from Calls to MetaSerialization.ResolveMetaRefs()
The MetaSerialization.ResolveMetaRefs()
method's object obj
or T value
parameter is no longer taken by reference (ref object obj
or ref T value
). In your calls to this method, remove the ref
keyword from the argument.
Migration Steps:
Remove the ref
from all calls to the method:
MetaSerialization.ResolveMetaRefs(..., ref myObject, ...);
MetaSerialization.ResolveMetaRefs(..., myObject, ...);
IEntityAsker
To IEntityMessagingApi
The IEntityAsker
interface has been renamed to IEntityMessagingApi
.
Migration Steps:
Update all references to IEntityAsker
to IEntityMessagingApi
:
IEntityAsker asker = ...;
IEntityMessagingApi messaging = ...;
EntityConfig.EntityActorType
and PersistedEntityConfig.PersistedPayloadType
The following properties were replaced with methods that now take an EntityId
parameter.
EntityConfig.EntityActorType
property was replaced with EntityConfig.GetActorTypeForEntity(EntityId)
.PersistedEntityConfig.PersistedPayloadType
property was replaced with PersistedEntityConfig.GetPersistedPayloadType(EntityId)
.Migration Steps:
Update any code using the old properties with the new properties:
// Update usage of EntityActorType
Type type = _entityConfig.EntityActorType;
Type type = _entityConfig.GetActorTypeForEntity(_entityId);
// And usage of PersistedPayloadType
Type type = _entityConfig.PersistedPayloadType;
Type type = _entityConfig.GetPersistedPayloadType(_entityId);
MetaplayController.AskEntityAsync()
The AskEntityAsync()
method in the MetaplayController
API controller base class has been renamed to EntityAskAsync()
. The new name is consistent with the equivalent method in EntityActor
.
Migration Steps:
Change your AskEntityAsync()
calls to use the new name EntityAskAsync()
. The old method still exists as well but is marked as [Obsolete]
so it will produce compiler warnings.
void MyControllerMethod()
{
await AskEntityAsync<...>(...);
await EntityAskAsync<...>(...);
}
RuntimeEnvironmentInfo
Instead of RuntimeOptionsRegistry
Server runtime environment information that was previously available in RuntimeOptionsRegistry
is now in RuntimeEnvironmentInfo
. This concerns members IsRunningInContainer
, ApplicationName
, and EnvironmentFamily
.
Migration Steps:
Update accesses to RuntimeOptionsRegistry.<Member>
to RuntimeEnvironmentInfo.Instance.<Member>
. For example:
if (RuntimeOptionsRegistry.EnvironmentFamily == EnvironmentFamily.Development)
if (RuntimeEnvironmentInfo.Instance.EnvironmentFamily == EnvironmentFamily.Development)
CurrentTick
in PlayerModel
and other entity models has been changed from int
to long
to allow longer simulations to be done during the lifetime of the PlayerModel
. If you use the tick counters in your game code, you may need to update your code to use long
.
These are the parts of the SDK's API that have changed:
CurrentTick
in all entity models: player, guild, league division, and custom multiplayer entity models.DebugForceSetModelTime(MetaTime timeAtFirstTick, long currentTick)
in PlayerModelBase
.OnPostTick(long tick)
in PersistedMultiplayerEntityActorBase
.Migration Steps:
Update any code using int
s to operate on CurrentTick
to use long
instead. For example:
int ticksToTimerFinish = timer.FinishesAt - CurrentTick;
long ticksToTimerFinish = timer.FinishesAt - CurrentTick;
Update any persisted members [MetaMember(...)]
storing tick values from int
to long
, if you have any. Any persisted data can be safely read into the new type long
.
[MetaMember(5)] public int MyTick { get; set; }
[MetaMember(5)] public long MyTick { get; set; }
Update any invocations of DebugForceSetModelTime()
to pass in a long
typed tick value, if you have such calls.
Update OnPostTick()
signature in classes inheriting PersistedMultiplayerEntityActorBase
:
protected override void OnPostTick(int tick)
protected override void OnPostTick(long tick)
{
}
Released on: August 28th, 2024
This release only affects games using the Leagues feature. If you're not using the feature, you can ignore this update.
No additional migration steps are required for this release.
Released on September 3rd, 2024
While the crash issue only affects Android and other ARMv7 games, the incorrect checksum mismatches affects all games.
No additional migration steps are required for this release.
Released on September 9th, 2024
If you are using ARM-based devices to make docker builds of the game server, this release fixes the issue in Release 29 where the builds would fail.
Vue 3.5 (and related language tools) update caused LiveOps Dashboard builds to fail with type errors. This release fixes the issue by updating our components to be compatible with the updated tooling.
No additional migration steps are required for this release.
Released on September 12th, 2024
Storybook 8.3 update caused LiveOps Dashboard builds to fail with type errors. This release fixes the issue by updating our components to be compatible with the updated tooling.
The following LiveOps Dashboard changes affect projects that have a game-specific dashboard project:
Release 29.3 caused LiveOps Dashboard builds to fail with type errors. This release fixes the issue by updating our components to be compatible with the updated tooling. This causes changes to a configuration file, which you will need to update in your dashboard project. We use the MetaplaySDK/Frontend/DefaultDashboard
folder as the source of truth for these files.
Migration Steps:
package.json
. This updates the dependencies and test scripts for the project. Doing so will overwrite some changes to the file that you will now need to revert. This is best done using a file diff tool: name
property inside the file to that of your project. This is typically of the form <projectName>-dashboard
.