Appearance
Release 36
February 11th, 2026
Appearance
February 11th, 2026
Managing the client session lifecycle is now much simpler with the new MetaplayClient.ConnectAsync() method and related MetaplaySession class. Instead of implementing multiple IMetaplayLifecycleDelegate callbacks scattered across your codebase, you can now write session handling as a single async method. The entire connection-to-gameplay-to-disconnect loop becomes a straightforward linear flow that's easier to understand, maintain, and debug. This integrates well with async loading of resources, such as with async scene loads. Error handling uses natural try/catch blocks instead of separate callback methods.
async Task SessionLoop()
{
while (true)
{
MetaplaySession session = await MetaplayClient.ConnectAsync();
// Setup game state using session.PlayerContext, session.GameConfig, etc.
session.SessionStartComplete();
ConnectionLostEvent connectionLost = await session.WaitForSessionEndAsync();
if (!connectionLost.AutoReconnectRecommended)
await ShowConnectionError(connectionLost);
}
}See Connection Management for more information.
The SDK now supports IReadOnlyList<T> in game config data classes, allowing you to make your entire game config structure immutable. An entire category of hard-to-debug bugs can now be avoided by catching accidental modifications at compile time.
[MetaSerializable]
public class QuestInfo : IGameConfigData<QuestId>
{
// Use private setters to prevent accidental modification:
[MetaMember(1)] public QuestId Id { get; private set; }
// Use read-only collections to prevent accidental modification:
[MetaMember(2)] public IReadOnlyList<QuestReward> Rewards { get; private set; }
}See Use Read-Only Data Types for more information.
Since the previous release, the Metaplay CLI has received multiple improvements to make your life better:
metaplay update sdk command helps you update the Metaplay SDK in your project. It also supports retaining changes made to the SDK files. You'll still need to follow the migration guides for each release, as previously.metaplay secrets update or overwrite an existing secret with metaplay secrets create --overwrite.You can now expand or collapse a whole subtree in the game config view with a single click. This makes it much quicker to navigate deeply nested structures. Of course this shortcut also works in experiment and game config diff views too!
MultiInsertOrIgnoreAsync() and MultiInsertOrUpdateAsync() now use multi-row VALUES syntax, achieving 10-50x speedup for small items. This significantly speeds up operations like database re-sharding.MultiplayerEntityControllerBase, helper methods DefaultGetEntityDetailsAsync() and GetEntityStateAsync() now work for Ephemeral Multiplayer Entities too.EntityActor.OnSubscriptionEndedAsync() and OnSubscriberEndedAsync() hooks for detecting subscription or subscriber being removed for any reason.HideInCallstack, making them respect the Editor's "Strip logging callstack" toggle.IReadOnlySet<> and IReadOnlyDictionary<> types. When deserializing into memory, the types are deserialized as OrderedSet<> and MetaDictionary<> respectively. These types are deterministic and compatible with game logic. In Unity, IReadOnlySet<> is polyfilled due to lack of support in Unity.ERR log level to stdout.MetaDatabase now exposes non-generic QueryPagedAsync() and QueryPagedRangeAsync() to allow paged queries of types that are only known at runtime.EntityAskAsync() and SubscribeToAsync() now include the target node name in TimeoutException.JsonSerialization.SerializeToString<T>() now includes declareRootType parameter to allow emitting a $type property for the root object value if value is a subtype of T. If value is exactly of type T, no type field is added. This makes the output of SerializeToString<T>() roundtrip deserializable with Deserialize<T>() with derived types of T.MetaplayClient.ConnectAsync() and related MetaplaySession.MetaplayConfigManager.LoadLatestGameConfigAsync() for loading the last used game config before a session has been established.mysql_pool_connections, mysql_pool_pending_requests, mysql_pool_connection_timeouts_total, mysql_pool_connection_create_seconds, mysql_pool_connection_use_seconds, and mysql_pool_connection_wait_seconds. These help you monitor database connection pool health and diagnose connection-related issues.Changed: TypeExtensions.IsCollection() now returns true for types implementing IReadOnlyCollection<>, such as IReadOnlyList<T> and IReadOnlySet<T>. Similarly, TypeExtensions.IsDictionary() returns true for IReadOnlyDictionary<,>.
Changed: Deprecate MetaplayConnection.Reconnect(). Game should instead use CloseWithError() either with a use-case specific error or with ClientTerminatedConnectionConnectionError.
Changed: Sending client too large a message is now detected as an error and session is terminated. Maximum size is controlled with ClientConnectionOptions runtime options.
Changed: Entity actors no longer log a warning for their subscription being kicked if the actor handles this by implementing OnSubscriptionKickedAsync() or OnSubscriptionEndedAsync() method.
Changed: Entity actors no longer log a warning if subscribed entity crashes if the actor handles this by implementing OnSubscriptionLostAsync() or OnSubscriptionEndedAsync() method.
Changed: Changed the default LogLevels for new projects in the localhost and offline environments to LogLevel.Information, instead of verbose.
Changed: OrderedSet<> now implements IReadOnlySet<>.
Changed: Console log colors are now enabled by default when running the server in dotnet watch mode.
Changed: Calling MetaplayClient.Update() is no longer needed and has been deprecated.
Changed: Deprecate EntityActor.OnSubscriberUnsubscribedAsync() and EntityActor.OnSubscriberTerminatedAsync() in favor of OnSubscriberEndedAsync().
Changed: In Unity Editor, BackendApi.UploadGameConfigArchiveToServerAsync() and UploadLocalizationArchiveToServerAsync() no longer return an error but instead throw on failure.
Changed: Implement down-sharding fast-path when re-sharding down to one remaining shard.
Changed: Optimized database bulk insert/upsert operations using multi-row VALUES syntax. Speeds up bulk writes by up to 10-50x for small items.
Changed: MySQL database backend now uses shared MySqlDataSource connection pools for both Dapper and Entity Framework Core operations, ensuring all connections are represented in pool metrics.
Changed: Erroneous activable states (activable states without a corresponding config item) are not sent to the client.
Changed: Removed redundant GameConfig type IGameConfigLibraryEntry (replaced with use of IGameConfigLibrary) and IGameConfigMember (replaced with use of IGameConfigEntry).
Changed: Removed special GameConfig implementation types EmptyGameConfigLibrary and BuiltinGameConfigLibrary, as the updated GameConfigLibrary implementation can cater for these use cases.
Changed: Bumped AWS SDK dependencies to address CVE GHSA-9cvc-h2w8-phrp, a low-severity vulnerability involving improper region parameter validation.
Changed: Renamed IntegrationAssembly.Gather() into IntegrationDataSet.Gather().
Changed: Updated entrypoint binary to use Go v1.25.7.
Changed: async void return type is disallowed in [MessageHandler], [EntityAskHandler] and other dispatcher methods.
Changed: Deprecated endpoint api/gamedata was removed.
Changed: Clarify error message when Facebook Login token is invalid due to token having expired.
Changed: Calling MetaplayConnection.Reconnect() no longer dispatches DisconnectedFromServer and all pending unhandled messages synchronously. Instead the messages are deferred to the next update.
Changed: MetaplayConnection.SuspendMessageProcessing() is deprecated. Use IMetaplayLifecycleDelegate.OnSessionStartedAsync() or MetaplayClient.ConnectAsync() instead to suspend messages during app init.
Changed: The ConnectionConfig in MetaplayConnection.Config is no longer public. Config should be set in initialization instead of mutating the config at runtime.
Changed: ConnectionConfig.ConnectAttemptsMaxCount has been replaced with SessionConnectTimeout.
Changed: ConnectionConfig.ConnectTimeout has been renamed to SocketConnectTimeout.
Changed: IMetaplayLifecycleDelegate.OnSessionStartedAsync() now has IMetaplayLifecycleDelegate.SessionStartedArgs parameter.
Changed: The following types have been moved from Metaplay.Unity namespace into Metaplay.Core.Session:
| Old Name | Updated Name |
|---|---|
Metaplay.Unity.MaintenanceModeState | Metaplay.Core.Session.MaintenanceModeState |
Metaplay.Unity.ConnectionLostEvent | Metaplay.Core.Session.ConnectionLostEvent |
Metaplay.Unity.ConnectionLostReason | Metaplay.Core.Session.ConnectionLostReason |
Metaplay.Unity.ConnectionStatus | Metaplay.Core.Session.ConnectionStatus |
Metaplay.Unity.ConnectionState | Metaplay.Core.Session.ConnectionState |
Metaplay.Unity.ConnectionStates.* | Metaplay.Core.Session.ConnectionState.* |
Metaplay.Unity.CannotWriteCredentialsOnDiskError | Metaplay.Core.Session.ConnectionState.ClientSideConnectionError.CannotWriteCredentialsOnDiskError |
Metaplay.Unity.MustReloginError | Metaplay.Core.Session.ConnectionState.ClientSideConnectionError.MustReloginError |
Metaplay.Unity.DirectConnectionClientError | Metaplay.Core.Session.ConnectionState.ClientSideConnectionError.DirectConnectionClientError |
Changed: Moved fields from MetaplayConnection.ConnectionStatistics.CurrentConnection.* to MetaplayConnection.ConnectionStatistics.*.
Changed: IHasNetworkDiagnosticReport is now SDK internal. To fetch network diagnostics report, use ConnectionLostEvent.NetworkDiagnosticReport.
Changed: MetaGuid now exposes a TypeConverter, and is no longer JsonConverter specific.
Changed: Moved TestHelper to be consistently in the Metaplay.Core.Tests namespace for both Server and Unity tests.
Changed: Removed MetaplaySDK.UnityTempDirectory constant. It was intended for internal use only.
Changed: Deprecated MetaplaySDKBehavior. The connection status information is now available via Menu > Metaplay > Status Window.
Changed: Adjust the maximum number of mysql database connections used by the server cluster to 75% of the database hard limit, to leave more room for other users of the database.
Changed: CsvStream has been removed, use SpreadsheetContent.ParseCsv() or SimpleCsvReader.ReadAllRows() instead. They are much faster, have better feature support, and are more robust.
FileUtil.ReadAllBytesAsync on Android throwing NullReferenceException instead of IOException if the target file was not found within the APK.Application.StartApplicationAsync() had completed.NullReferenceException during server shutdown due to a race condition in MetaplayDataProtectionProvider.TextField.SetPlaceholderText() sometimes leaving the text color incorrect after placeholder was cleared.netstat_tcp_connects_total, netstat_tcp_listen_accepts_total, netstat_tcp_connections, netstat_tcp_sent_resets_total, and netstat_tcp_established_resets_total.PlayerModel to reduce bloat, as described in Removing Offers. This state removal was already implemented for offer groups, but was mistakenly missing for individual offers.[ServerOnly] fields not being filtered on client side SharedGameConfig when game config was built in Game Config Build Window.JWKSPublicKeyCache no longer requires JWK entries to have "alg" property, as it is optional.MetaConfigId<> members within PlayerModel causing the Model Inspector to not work.DefaultGameEngineIntegration now creates PersistentDataPath and TemporaryDataPath directories on initialization, fixing credentials persistence on non-Unity clients.dotnet_memory_max_working_set metric now reports the memory size of the node if no explicit memory size limits have been set. This fixes the scaling in the Memory Usage panel in the Grafana dashboard.MySqlConnector.MySqlConnection.OnSessionStartedAsync(). Pending events are called after the async method completes.MNoSeatbelts component in @metaplay/meta-ui-next for displaying standardized warnings before dangerous or irreversible actions. This is a replacement for the old MetaNoSeatbelts component from @metaplay/meta-ui, which has now been removed. See migration notes for more details.MLazyLoader component in @metaplay/meta-ui-next for lazy-loading content with customizable loading states. This is a replacement for the old MetaLazyLoader component from @metaplay/meta-ui, which has now been removed. See migration notes for more details.Segments/List and Segments/Details. You can use these to add custom components or replace existing ones in the segments views.MInputSingleSelectDropdown, MInputText and MInputNumber now support a small size variant for use in compact UI layouts.eslint-plugin-vue-pug to enable standard Vue ESLint rules in Pug templates. This adds new rules like vue/no-undef-components and vue/define-props-declaration that may produce warnings in your dashboard code. See the migration guide for details.eslint-config-love base rules.MCallout now supports an icon slot for custom title icons.MInputHintMessage now supports multi-line hints.MDateTime tooltips no longer show milliseconds for improved readability.MInputSingleSelectRadio now has a slight gap between vertical radio options for improved readability.GameConfig/Diff to GameConfigs/Diff to keep it consistent with other related placements.metatoast notification system was deprecated in R29 and replaced with the useNotifications system. The old system has now been completely removed.getGameData and getGameDataSubscriptionOptions were removed./players/:playerId/:incidentId to /playerIncidents/:playerId/:incidentId. This was done to give it a route name that is consistent with the rest of the dashboard, and to ensure that the correct menu item is highlighted in the sidebar when player incidents are viewed.MInputText no longer accepts undefined for its modelValue prop. Initialize the bound value to an empty string instead.GameConfigActionPublish and LocalizationActionPublish to better communicate how rollouts work.MListItem now emits a click event if the clickable prop is set.MInputNumber component now behaves correctly when the input is empty and the up/down arrow buttons are used.MPageOverviewCard now correctly wraps long text in titles and subtitles and truncates long IDs.MInputDateTime incorrectly limiting minimum and maximum times if the range had a timezone offset and straddled midnight.MButton now displays a placeholder text if used only with the icon slot and no label.IMetaplayLifecycleDelegate to react to connection lifecycle changes. Instead, the samples manage lifecycle with an async control loop, using ConnectAsync().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.9.1 or later for cloud deployments.
Migration Steps:
Update your metaplay-project.yaml to the latest Helm chart version.
serverChartVersion: 0.x.y
serverChartVersion: 0.9.1Roll 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.
It's a good idea to run the Metaplay integration test suite on your project before and after upgrading to the latest SDK version.
MyProject$ metaplay test integrationYou should get a clean test run before starting the upgrade process to know that your project is in a good state, and know that any test failures after the upgrade are related to the upgrade itself.
The following core SDK changes affect all Metaplay projects:
All server and tool builds depending on MetaplaySDK now target .NET 10 SDK. To use .NET 10, game server projects must be updated to target this version.
Staying in the Old Version
In this release, MetaplaySDK continues to support .NET 9, in case updating your own projects proves to be problematic. Support for .NET 9 will be dropped in the next release. Note that you still need to update global.json and install .NET 10 even if you continue targeting .NET9.
Migration Steps:
Install .NET 10 SDK.
Update the sdk entry in the global.json file of your project to specify at least version 10.0.100:
{
"sdk": {
"version": "9.0.100",
"version": "10.0.100",
"rollForward": "latestFeature"
}
}Update the dotnetRuntimeVersion entry in the metaplay-project.yaml file to specify 10.0:
dotnetRuntimeVersion: "9.0"
dotnetRuntimeVersion: "10.0"Edit the TargetFramework property in all .csproj files to specify net10.0:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<OutputType>Exe</OutputType>
...Verify that your code continues to build against .NET 10:
MyProject$ metaplay build serverIReadOnlyList<T>Game config data classes should be immutable to prevent accidental modifications at runtime. The SDK now supports IReadOnlyList<T> for game config collections, which is safer than raw arrays (T[]) or List<T>.
Migration Steps:
In your game config data classes, replace array and list types with IReadOnlyList<T>:
[MetaSerializable]
public class QuestInfo : IGameConfigData<QuestId>
{
[MetaMember(1)] public QuestId Id { get; private set; }
[MetaMember(2)] public QuestReward[] Rewards { get; private set; }
[MetaMember(3)] public List<QuestObjective> Objectives { get; private set; }
[MetaMember(2)] public IReadOnlyList<QuestReward> Rewards { get; private set; }
[MetaMember(3)] public IReadOnlyList<QuestObjective> Objectives { get; private set; }
}For more details, see Use Read-Only Data Types.
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:
Update the package.json to update your project's dependencies.
package.json file directly from the MetaplaySDK/Frontend/DefaultDashboard directory. Next, restore the name property inside the file to that of your project. This is typically of the form "name": "<projectName>-dashboard".Copy and overwrite the following files from the MetaplaySDK/Frontend/DefaultDashboard directory to your dashboard project:
vue-compat.d.tsThe minimum Node.js version has been bumped from 20 to 24. Ensure you have Node 24.13.0 (the latest 24.x LTS at the time of writing) or later installed before proceeding.
Migration Steps:
To check your current Node version, run:
node --versionIf you are below version 24, update Node using nvm as follows:
nvm install 24.13.0
nvm use 24.13.0To ensure that your dashboard project has the correct dependencies, you will need to clear the existing cached files and recreate them.
Migration Steps:
metaplay update cli or installing the CLI using instructions at Metaplay CLI.metaplay dev clean-dashboard-artifacts from within your project folder. This clears any currently installed dependencies and built files.pnpm-lock.yaml file from the root of your project. This clears the cached dependency versions.metaplay dev dashboard from within your project folder. This recreates all of the above files and folders with the correct dependencies, and runs the dashboard in development mode.We updated our eslint rules based on the most recent version of eslint-config-love. There are two rules in particular that may now catch potential errors in your code: no-useless-assignment and eqeqeq
We have also added eslint-plugin-vue-pug which enables Vue ESLint rules in Pug templates, many of which have auto-fixes available. The full set currently enabled in our shared config is listed below. All are worth being aware of, but start by fixing error rules first.
| Rule | Severity | Description |
|---|---|---|
vue/no-undef-components | error | Disallow use of undefined components in templates |
vue/define-props-declaration | warn | Enforce declaration style of defineProps (TS style) |
vue/define-emits-declaration | warn | Enforce declaration style of defineEmits (type literal style) |
vue/no-unused-vars | error | Disallow unused template variables (prefix with _ to ignore) |
vue/component-api-style | warn | Enforce component API style (Composition API in this config) |
vue/component-name-in-template-casing | warn | Enforce PascalCase component names in templates |
vue/match-component-import-name | error | Require component usage names to match import names |
vue/next-tick-style | error | Enforce promise style for nextTick usage |
vue/no-duplicate-attr-inheritance | error | Require inheritAttrs: false when forwarding $attrs |
vue/no-empty-component-block | error | Disallow empty <template>, <script>, and <style> blocks |
vue/no-import-compiler-macros | error | Disallow importing Vue compiler macros |
vue/no-ref-object-reactivity-loss | warn | Disallow ref usage patterns that can lose reactivity |
vue/no-template-target-blank | error | Require rel="noopener noreferrer" with target="_blank" |
vue/no-undef-directives | error | Disallow use of undefined custom directives |
vue/no-unused-emit-declarations | warn | Disallow unused emit declarations |
vue/no-unused-properties | warn | Disallow unused component properties |
vue/no-unused-refs | warn | Disallow unused template refs |
vue/no-use-v-else-with-v-for | error | Disallow using v-else-if/v-else on the same element as v-for |
vue/no-useless-mustaches | warn | Disallow unnecessary mustache interpolations |
vue/no-v-text | error | Disallow v-text usage |
vue/padding-line-between-blocks | error | Require padding lines between blocks |
vue/prefer-define-options | error | Enforce defineOptions instead of default export options |
vue/prefer-separate-static-class | error | Require static classes to be in a separate class attribute |
vue/prefer-true-attribute-shorthand | error | Enforce shorthand boolean attributes when bound value is true |
vue/prefer-use-template-ref | error | Enforce useTemplateRef for template refs |
vue/require-macro-variable-name | error | Enforce expected variable names for Vue macros |
vue/v-for-delimiter-style | error | Enforce the in delimiter style in v-for |
Migration Steps:
Run a type-check on your dashboard project to find any linting warnings and errors.
# Run inside your dashboard folder
Backend/Dashboard$ pnpm lintReview and fix all errors.
Frontier coding agents like Claude Code and Codex are effective at fixing linting issues automatically. Tell them to run these commands for you and propose fixes!
Optionally, you can disable rules per line, file or even globally. This can help you to focus on the most important issues first.
To disable a single line, use the eslint-disable-next-line comment:
// eslint-disable-next-line vue/define-props-declaration -- [type a note for your team here about why the ignore is needed]
const props = defineProps({ ... })To disable project-wide, override in your eslint.config.js:
import MetaplayEslintConfig from '@metaplay/eslint-config/recommended'
export default [
...MetaplayEslintConfig,
{
files: ['src/**/*.vue'],
rules: {
'vue/define-props-declaration': 'off',
'vue/define-emits-declaration': 'off',
},
},
]Build
After fixing the linter errors you may have unmasked type errors in the code, so also run the build command afterwards:
Backend/Dashboard$ pnpm buildThe GameConfigDiffView UI placement was renamed from GameConfig/Diff to GameConfigs/Diff. Update existing callsites as follows:
Migration Steps:
In your game-specific.ts file, search for any uses of the GameConfig/Diff placement and update them to GameConfigs/Diff.
addUiComponent('GameConfig/Diff', {
addUiComponent('GameConfigs/Diff', {
...
})or
removeUiComponent('GameConfig/Diff', {
removeUiComponent('GameConfigs/Diff', {
...
})metatoast SystemThe metatoast system was deprecated in R29 and has now been completely removed. If you did not migrate to the new useNotifications system (ie: you are seeing errors related to undefined functions showSuccessToast and showErrorToast) then please follow the migration steps detailed in the R29 release notes.
If you have customized the list of messages that the notification ignores by editing toasts.ts directly, migrate to use the new ignoreExtraVueWarningMessages API as follows:
Migration Steps:
Remove SDK diffs from the toasts.ts file:
const traceItemsToIgnore = [
'BTooltip',
'BRow',
...
...
// METAPLAY_CHANGED: Added custom ignores.
'BDropdownItem', // BootstrapVue warning.
'BDropdown', // BootstrapVue warning.
]Use the new ignoreExtraVueWarningMessages in your gameSpecific.ts file inside the GameSpecificPlugin function:
initializationApi.ignoreExtraVueWarningMessages(
[],
[
'BDropdownItem', // BootstrapVue warning.
'BDropdown', // BootstrapVue warning.
]
) getGameData and getGameDataSubscriptionOptionsThe getGameData function and getGameDataSubscriptionOptions subscription were deprecated in R33 in favour of higher performance alternatives. In this release, both getGameData and getGameDataSubscriptionOptions have been removed. If you are still using either of these functions then you will need to migrate. See the comprehensive migration notes in R33 under the For Games With Custom LiveOps Dashboard Components section.
MetaNoSeatbelts with MNoSeatbeltsThe MetaNoSeatbelts component has been removed from @metaplay/meta-ui. Migrate both MetaNoSeatbelts and meta-no-seatbelts call sites to MNoSeatbelts from @metaplay/meta-ui-next.
| Old Prop | New Prop | Notes |
|---|---|---|
name | target-name | Prop name changed |
message | (removed) | Use default slot instead |
variant | (removed) | Removed entirely |
Migration Steps:
Update your imports:
import { MetaNoSeatbelts } from '@metaplay/meta-ui'
import { MNoSeatbelts } from '@metaplay/meta-ui-next'Update component usage in your templates:
MetaNoSeatbelts(
:name="target.name"
message="A custom warning message."
)
MNoSeatbelts(
:target-name="target.name"
) A custom warning message.MInputText Values to Empty StringsMInputText no longer accepts undefined for its modelValue prop. If you were binding a potentially undefined value, initialize it to an empty string instead.
Migration Steps:
Update your component refs to use empty strings:
const searchTerm = ref<string>()
const searchTerm = ref('') MetaLazyLoader with MLazyLoaderThe deprecated MetaLazyLoader component from @metaplay/meta-ui has been removed. Use MLazyLoader from @metaplay/meta-ui-next instead.
| Old Prop | New Prop | Notes |
|---|---|---|
placeholder-event-height | placeholder-height | Now accepts a string with units (e.g., '100px') instead of a number |
render-after-delay-in-ms | show-after-delay-in-ms | Renamed for clarity |
remain-visible | remain-visible | Default changed from true to false |
Migration Steps:
Update your imports:
import { MetaLazyLoader } from '@metaplay/meta-ui'
import { MLazyLoader } from '@metaplay/meta-ui-next'Update component usage in your templates:
MetaLazyLoader(
:placeholder-event-height="100"
:remain-visible="false"
)
// Your content here
MLazyLoader(
placeholder-height="100px"
)
// Your content hereThese 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.
MetaplayClient.Update()Update() method is now called automatically every frame. The update is run every frame before any Unity script Update() is called, ensuring the observed SDK state does not depend on Script Execution order. Calling Update manually is no longer necessary and should be removed.
Migration Steps:
Remove calls to MetaplayClient.Update().
MetaplayClient.Update(); EntityActor.OnSubscriber* Methods.EntityActor.OnSubscriberUnsubscribedAsync() and EntityActor.OnSubscriberTerminatedAsync() commonly need to perform similar actions. Having two APIs leads to duplication and forgetting some logic from one easily leads to bugs. These methods have now been deprecated in favor of OnSubscriberEndedAsync() which handles all subscriber removals.
Migration Steps:
Update OnSubscriberTerminatedAsync() by handling terminations in OnSubscriberEndedAsync():
protected override Task OnSubscriberTerminatedAsync(EntitySubscriber subscriber)
protected override Task OnSubscriberEndedAsync(EntitySubscriber subscriber, SubscriberEndCause cause)
{
Cleanup(subscriber);
switch (cause)
{
case SubscriberEndCause.TargetEntityTerminated _:
case SubscriberEndCause.Desynchronized _:
Cleanup(subscriber);
break;
}
return Task.CompletedTask;
}Update OnSubscriberUnsubscribedAsync() by handling terminations in OnSubscriberEndedAsync():
protected override Task OnSubscriberUnsubscribedAsync(EntitySubscriber subscriber, MetaMessage goodbyeMessage)
protected override Task OnSubscriberEndedAsync(EntitySubscriber subscriber, SubscriberEndCause cause)
{
// Process goodbye message
if (goodbyeMessage is SpecialCleanupMessage specialUnsubscribe)
Cleanup(subscriber, specialUnsubscribe);
switch (cause)
{
case SubscriberEndCause.Unsubscribed unsubscribed:
{
// Process goodbye message
if (unsubscribed.GoodbyeMessage is SpecialCleanupMessage specialUnsubscribe)
Cleanup(subscriber, specialUnsubscribe);
break;
}
}
return Task.CompletedTask;
}try-catch for Detecting BackendApi FailuresBackendApi.UploadGameConfigArchiveToServerAsync() and UploadLocalizationArchiveToServerAsync() no longer return a boolean to signify if the operation completed or not. Instead, the methods throw an exception on failure.
If the failure is due to the user cancelling the operation, BackendAdminApi.CancelledByUserException is thrown.
Migration Steps:
Replace error handling with a try-catch:
bool success = await BackendApi.UploadGameConfigArchiveToServerAsync();
if (!success)
try
{
await BackendApi.UploadGameConfigArchiveToServerAsync();
}
catch (BackendAdminApi.CancelledByUserException)
{
// cancelled
}
catch (Exception ex)
{
// Something went wrong
}If throwing on failure is desired, you can keep the calls as is:
await BackendApi.UploadGameConfigArchiveToServerAsync();IntegrationAssembly.Gather() to IntegrationDataSet.Gather()The factory method for creating a IntegrationDataSet was declared in IntegrationAssembly for historical reasons. The method has now been moved to IntegrationDataSet where it's more discoverable.
Migration Steps:
Update the type on call sites:
IntegrationDataSet data = IntegrationAssembly.Gather(config);
IntegrationDataSet data = IntegrationDataSet.Gather(config); MetaplayConnection.SuspendMessageProcessing()MetaplayConnection.SuspendMessageProcessing() has been deprecated since using it safely was difficult, and misuse easily cause hard-to-debug issues. The purpose of SuspendMessageProcessing() was to allow pausing messages for async application start, to avoid message listeners to be called during ongoing init. Use IMetaplayLifecycleDelegate.OnSessionStartedAsync() or MetaplayClient.ConnectAsync() instead to suspend messages during app init.
Migration Steps:
Replace SuspendMessageProcessing() by stalling OnSessionStartedAsync() until init is complete:
void Init()
{
MetaplayClient.Initialize(new MetaplayClientOptions {
...
LifecycleDelegate = this,
});
...
MetaplaySDK.MessageDispatcher.AddListener<SessionProtocol.SessionStartSuccess>(OnSessionStarted);
}
void OnSessionStarted(SessionProtocol.SessionStartSuccess message)
{
MetaplaySDK.Connection.SuspendMessageProcessing(true);
StartCoroutine(AppInit());
}
TaskCompletionSource<int> _sessionStartTcs;
Task IMetaplayLifecycleDelegate.OnSessionStartedAsync(...)
{
_sessionStartTcs = new TaskCompletionSource<int>();
StartCoroutine(AppInit());
return _sessionStartTcs.Task;
}
IEnumerator AppInit()
{
...
// when ready
MetaplaySDK.Connection.SuspendMessageProcessing(false);
_sessionStartTcs.TrySetResult(0);
}async void Message Handler MethodsMetaplaySDK now checks server-side message handler methods such as [MessageHandler] and [EntityAskHandler] are not declared as async void. The methods are disallowed for safety as an unhandled exception from such an async void method would crash the server process. Always return Task instead of void, if no return value is needed.
Migration Steps:
Update the return type of the message handlers:
[MessageHandler]
async void HandleMyMessage(MyMessage message)
async Task HandleMyMessage(MyMessage message)
{
...
}ConnectionConfigConnectionConfig is now solely defined on SDK init, and connection timeout is now declared in units of time instead in the number of retries.
Migration Steps:
Remove mutations of Connection.Config and supply values on Init.
Connection.Config.MaxSessionRetainingPauseDuration = ... MetaplayClient.Initialize(new MetaplayClientOptions
{
...
ConnectionConfig = new ConnectionConfig
{
MaxSessionRetainingPauseDuration = ...
}
}If ConnectAttemptsMaxCount has been customized, replace it with SessionConnectTimeout:
A rule of thumb is that each attempt equals to about 2 seconds. Infinite timeout is represented with InfiniteTimeSpan.
ConnectAttemptsMaxCount = 5
SessionConnectTimeout = TimeSpan.FromSeconds(10) ConnectAttemptsMaxCount = -1
SessionConnectTimeout = Timeout.InfiniteTimeSpan If ConnectTimeout has been customized, replace it with SocketConnectTimeout:
ConnectTimeout = ...
SocketConnectTimeout = ... IMetaplayLifecycleDelegate SignaturesIMetaplayLifecycleDelegate.OnSessionStartedAsync() is now receiving IMetaplayLifecycleDelegate.SessionStartedArgs argument to pass information of the session that just started.
Migration Steps:
Add the parameter to your IMetaplayLifecycleDelegate.OnSessionStartedAsync() implementation:
Task IMetaplayLifecycleDelegate.OnSessionStartedAsync()
Task IMetaplayLifecycleDelegate.OnSessionStartedAsync(IMetaplayLifecycleDelegate.SessionStartedArgs args)
{
...
}The types related to client-side session start have been moved into Metaplay.Core.Session namespace. If you use these types, you must update the references to them.
The following types have been moved from Metaplay.Unity namespace into Metaplay.Core.Session:
| Old Name | Updated Name |
|---|---|
Metaplay.Unity.MaintenanceModeState | Metaplay.Core.Session.MaintenanceModeState |
Metaplay.Unity.ConnectionLostEvent | Metaplay.Core.Session.ConnectionLostEvent |
Metaplay.Unity.ConnectionLostReason | Metaplay.Core.Session.ConnectionLostReason |
Metaplay.Unity.ConnectionStatus | Metaplay.Core.Session.ConnectionStatus |
Metaplay.Unity.ConnectionState | Metaplay.Core.Session.ConnectionState |
Metaplay.Unity.ConnectionStates.* | Metaplay.Core.Session.ConnectionState.* |
Metaplay.Unity.CannotWriteCredentialsOnDiskError | Metaplay.Core.Session.ConnectionState.ClientSideConnectionError.CannotWriteCredentialsOnDiskError |
Metaplay.Unity.MustReloginError | Metaplay.Core.Session.ConnectionState.ClientSideConnectionError.MustReloginError |
Metaplay.Unity.DirectConnectionClientError | Metaplay.Core.Session.ConnectionState.ClientSideConnectionError.DirectConnectionClientError |
Migration Steps:
Add using namespace Metaplay.Core.Session to the files using MaintenanceModeState, ConnectionLostEvent, ConnectionLostReason, ConnectionStatus, ConnectionState:
using namespace Metaplay.Core.Session;
...
MaintenanceModeState
ConnectionLostEvent
ConnectionLostReason
ConnectionStatus
ConnectionStateReplace ConnectionStates namespace path:
using namespace Metaplay.Unity.ConnectionStates;
using namespace Metaplay.Core.Session.ConnectionStates; Update path of ClientSideConnectionError errors:
Metaplay.Unity.CannotWriteCredentialsOnDiskError;
Metaplay.Core.Session.ConnectionState.ClientSideConnectionError.CannotWriteCredentialsOnDiskError;
Metaplay.Unity.MustReloginError;
Metaplay.Core.Session.ConnectionState.ClientSideConnectionError.MustReloginError;
Metaplay.Unity.DirectConnectionClientError;
Metaplay.Core.Session.ConnectionState.ClientSideConnectionError.DirectConnectionClientError; NetworkDiagnosticReport from ConnectionLostEventIHasNetworkDiagnosticReport has been made an SDK internal interface. If your code is using IHasNetworkDiagnosticReport to fetch network diagnostics report from ConnectionState, you need to change the code to retrieve the report from ConnectionLostEvent.
Migration Steps:
Update data source of NetworkDiagnosticReport:
// Poll current connection state machine
ConnectionState state = ..
NetworkDiagnosticReport reportOrNull = (state as IHasNetworkDiagnosticReport)?.NetworkDiagnosticReport;
// Retrieve ConnectionLostEvent from:
// * MetaplayClient.ConnectAsync()
// * MetaplaySession.WaitForSessionEndAsync()
// * IMetaplayLifecycleDelegate.OnSessionLost()
// * IMetaplayLifecycleDelegate.OnFailedToStartSession
ConnectionLostEvent connectionLost = ..
NetworkDiagnosticReport reportOrNull = connectionLost.NetworkDiagnosticReport;TestHelper ReferencesTestHelper has been moved to Metaplay.Core.Tests namespace. Previously, the type was defined either in Metaplay.Cloud.Application or the top-level namespace, depending if tests were built in Unity or in Dotnet context.
Migration Steps:
Update TestHelper namespace in dotnet projects:
using Metaplay.Cloud.Application;
using Metaplay.Core.Tests;
TestHelper.DoSomething();Add the namespace in Unity projects:
using Metaplay.Core.Tests;
TestHelper.DoSomething();MetaplaySDKBehaviorEditor to MetaplaySDKEditorWindow.The connection Status debugging tools have been moved from being an inspector for MetaplaySDKBehavior (MetaplaySDKBehaviorEditor) into a separate MetaplaySDKEditorWindow window. If you have customized MetaplaySDKBehaviorEditor, the customizations must be applied on MetaplaySDKEditorWindow instead.
Migration Steps:
Update inherited class and method signature in your custom editor:
[CustomEditor(typeof(MetaplaySDKBehavior))]
class MyCustomMetaplaySDKBehaviorEditor : MetaplaySDKBehaviorEditor
class MyCustomMetaplaySDKBehaviorEditor : MetaplaySDKEditorWindow
{
public override void OnInspectorGUI()
protected override void DrawGUI()
{
base.OnInspectorGUI();
base.DrawGUI();
// Custom UI code...
}IGameConfigLibraryEntry and IGameConfigMemberThe GameConfig interfaces have been clarified. All uses to the old naming must be updated.
Migration Steps:
Replace type names for IGameConfigLibraryEntry:
Dictionary<string, IGameConfigLibraryEntry> myLookup;
Dictionary<string, IGameConfigLibrary> myLookup; Replace type names for IGameConfigMember:
Dictionary<string, IGameConfigMember> myLookup;
Dictionary<string, IGameConfigEntry> myLookup; You should run the Metaplay integration test suite on your project after the SDK upgrade to make sure everything is still working as expected:
MyProject$ metaplay test integration