Appearance
Release 28
June 7th, 2024
Appearance
June 7th, 2024

Backend/PrebuiltDashboard that will be used by local runs and docker image builds when present.
MetaplayCoreOptions.supportedLogicVersions to force a synchronized update of your game client and server.pnpm-lock.yaml file generated by earlier versions of PNPM is deprecated and must be re-generated in your project. npm i -g pnpm@9.pnpm install to re-generate the pnpm-lock.yaml file.INFO
Note: If you have a service contract signed with Metaplay, your SDK integration has already been updated to reflect any backward-incompatible API changes in the Metaplay SDK in the SDK upgrade pull request of your project. Otherwise, please see the Migration Guide for a full list of changes, along with the HelloWorld and Idler samples for references on how to apply the changes.
We have updated both the infrastructure and game server Helm charts and bumped the minimum requirements. Please consider updating to the latest versions during your next maintenance window.
infra-modules v0.2.14. metaplay-gameserver v0.6.1. The minimum required versions are infra-modules v0.2.9 and metaplay-gameserver v0.6.0.

MInputDateTime component is completely re-engineered from scratch for better usability. It now separates the date and time into two separate inputs. The new input only accepts and shows UTC times. 
MInputDuration component now shows preview times in UTC instead of local time and has a new, clearer UX for inputting "empty" durations. 
MetaEventStreamCard now has a more comprehensive set of utilities for filtering or highlighting events. It also has new props for pre-filtering and highlighting events. This makes it easier to make custom preset projections of and event stream (like player events) while still allowing manual control to search the events further. 

Frontend/DefaultDashboard now contains a vanilla LiveOps Dashboard project that we use to both create the pre-built dashboard and as a template to scaffold your own, custom dashboard project.MInputText now has a @blur:modelValue event that can be used to trigger input validation.MModal component offers a simplified and more intuitive API for displaying read-only modals that do not require user interaction.MInputDate and MInputTime components for picking dates and times separately.MInputHintMessage component for consistently styled hint messages in input components./test/generatedUi. You can now test generated forms and views for different types.?scroll-to-data-testid=ID_HERE to scroll to a specific element with the given data-testid attribute. This implementation works with lazy-loaded content.MInputNumber has a new step prop to control the increment/decrement value.MInputText and MInputNumber now have an optional clear button that can be used to more easily clear the input field. MInputText has a new showClearButton prop to show the button, while MInputNumber uses the already existing allowUndefined and clearOnZero to show the button.SubscriptionOptions type so all useSubscription() call sites will have typings for the return data. This reduces bugs by making the return data typings easier to maintain./api/userInfo instead of directly accessing the OAuth2 provider endpoint.MetaButton is replaced by MButton, MIconButton and MTextButton.MetaClipboardCopy is replaced by MClipboardCopy.MetaActionModalButton is replaced by MActionModalButton or a combination of either the MButton, MIconButton or MTextButton with an MActionModal component.MetaCollapse is replaced by the MCollapse component.BCollapse and CollapsePlugin have been removed in favour of the new MCollapse component.Frontend/E2ETests.MCollapse component to support additional controls for managing the initial state of the collapsible section. Now, you have the option to set the initial state as open or hide the collapsible section entirely.MInputDateTime component is now a wrapper for the new MInputDate and MInputTime components.getIsProductionEnvironment(), which didn't work as expected, and replaced with getEnvironmentFamily() instead. This new function allows the code to check if it is deployed into a production environment with getEnvironmentFamily() === EnvironmentFamily.Production.onlyClose property in the MActionModal component has been deprecated in favor of the MModal component. This was an optional property used to create modals that do not trigger an action when closed.@metaplay/meta-ui package for type hints and easier future migration.@metaplay/meta-ui package for type hints and easier future migration.constructExactTimeCompactString() now say "3mo" instead of "3m" when shortening months to avoid confusion with minutes.MListItem and MErrorCallout now have a max-height in their main content slots to prevent accidental large strings from breaking the layout.MetaplaySDK.LocalizationManager.SetCurrentLanguage() now throws an exception if you try to change to a language that is not defined in the Languages game config library.noDeselect prop from MetaInputSelect as it doesn't make sense to allow deselection in this way.MInputDateTime.PlayerPurchaseHistoryCard item collapse in MetaListCard no longer persists when navigating between pages.MButton component, where the middle click event within the Live-Ops Dashboard didn't trigger the opening of a new window.MModal and MActionModal not emitting hide event when closed by pressing the escape key.MInputNumber being too aggressive in clamping non-allowed values while typing. Input validation now happens only on focus change instead of on value change.Metaplay/Create Default Game Config Build Window menu item.None to disable the checksum verification. This can be used when the overhead of checksum calculations is too expensive and the game logic can be trusted to never cause a divergence between the client and server.--build-arg CHISELED_IMAGE= argument. We only recommend this to ease transition to the safer and smaller chiseled images.Player runtime options. See the migration notes for the required actions. Now you can also override these options per player session for advanced debugging.EntityActor.UnsubscribeFromAsync() now takes optional MetaMessage goodbyeMessage that is delivered to EntityActor.OnSubscriberUnsubscribedAsync().HttpUtil methods now have an optional argument for authentication and an optional CancellationToken argument.Dockerfile.server.dockerignore.MetaValidateInRangeAttribute now supports unsigned integers and values up to the range of Int64. Additionally a new MetaValidateInRangeFloatAttribute supports floats and doubles.DeletionStatus PlayerModel property.MetaSerializable object using the constructor now allows the use of optional parameters. This is especially useful when adding new MetaMembers, allowing you to deserialize and specify default values when deserialize old instances.MetaGameConfigBuildConstructor attribute. GameConfigKeyValues are currently not supported, support coming in a later update.LocalServer utility for Unity Editor for launching and managing the game server process. See "Menu -> Metaplay -> Local Server".[BigQueryAnalyticsRecursionLimit] attribute allows recursion of types in BigQuery formatted analytics events.ActiveEnvironmentIdOverride to MetaplayClientOptions which is used when calling MetaplayClient.Initialize(). This allows easily changing the active environment at init-time.IncludeSoldOutOffers, which you can set to false to stop sold-out offers from being included in offer groups. By default, this is true (matching the old behavior), meaning that sold-out offers can get included in offer groups.StartServicesAsync() (and corresponding StopServicesAsync()) hook is available in ServerMain, for initializing custom services before entity clustering.dotnet-counters, dotnet-dump, dotnet-gcdump and dotnet-trace) are no longer installed by default in the images. This reduces image sizes by 125MB. The tools can be installed with dotnet tool install --global <tool-name>.AdminApiOptions.JwtConfiguration.BearerTokenSource. It gets used in case the standard Authorization header is not specified.FileUtil.Write*() methods explicitly reset file creation time to avoid "file tunneling" of metadata from the overwritten file.WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE and READ_PHONE_STATE from the API level 4.MySqlDataSource object and logging has been enabled to help diagnose database-related issues.Enqueue(Server)Action() and Execute(Server)Action() methods cannot be called unsafely from background threads.GameConfigBase.PopulateConfigEntries() has been fully removed. ModelListener interfaces now have default empty implementations for the listener methods. Therefore, there's no longer the need to define empty implementations for methods that wouldn't be used otherwise. The affected interfaces are IPlayerModelClientListenerCore, IPlayerModelServerListenerCore, IGuildModelClientListenerCore, IGuildModelServerListenerCore, IDivisionModelClientListenerCore, IDivisionModelServerListenerCore.EntityActor.StartPeriodicTimer() and EntityActor.StartRandomizedPeriodicTimer() require handler method to exists and be declared with [CommandHandler].EntityActor.StartPeriodicTimer() and EntityActor.StartRandomizedPeriodicTimer() takes more time than the timer's interval is (i.e. new work is scheduled faster than executed), new timer invocations triggered before the completion of the handler are dropped and a warning is logged.shard declaration in helm values, service nodeset is now the first nodeset. This makes entities with NodeSetPlacement.All have EntityIds with the value of 0 be placed on the service node.MetaMessages being routed to a destination node that does not exist or is not available are now logged at Warning level. If an entity keeps sending these unroutable messages more than once a second on average, the log level for this entity's warnings get temporarily throttled to Verbose level.EntityShardId now contains a separate NodeSetIndex and NodeIndex.EntityIds created with ManualShardingStrategy.CreateEntityId() have changed as the EntityShardId encoding above has changed. If you store EntityIds generated with ManualShardingStrategy, they need to be regenerated. Note that this is extremely unlikely as ManualShardingStrategy cannot be used for Persisted Entities, and storing the EntityIds of Transient Entities is useless. The EntityID with the zero value continues to be valid.EntityActor.GetAssociatedServiceEntityId(EntityKind targetEntityKind) is now only allowed for targetEntityKinds that have ServiceShardingStrategy.Application.Instance.ClusterConfig is removed. Use RuntimeOptionsRegistry.Instance.GetCurrent<ClusteringOptions>().ClusterConfig instead.Metaplay.Attributes containing attributes which are shared between the server and client.GameConfigBuildTemplate base class no longer requires knowing the build parameters type statically, which allows using context-dependent build parameter types and allows the default implementation to work with user-defined build parameters classes.MetaRef<> are now resolved in place, this causes the HashCode to change and as such MetaRef<> should not be used as keys in dictionaries or as elements in hash based collections (e.g. HashSet or OrderedSet).playerId parameter from AuthenticateSocialAuthenticationClaimAsync(). In general the process of authenticating should now care about the current or claimed playerId, and requiring some playerId in this method has proven to be confusing.DefaultEnvironmentConfigProvider.ClearActiveEnvironmentClientOverride() has been removed. The DefaultEnvironmentConfigProvider no longer persists the runtime active environment override to PlayerPrefs.--Environment:ExitOnLogError for the BotClient in integration tests to help catch more errors.Assets/ directory to be in the project root.ConfigArchives with entries named in a particular way. This was caused due to the entries being mistakenly sorted in different order on Unity and on the server, causing a sanity check to fail. Now, the server no longer cares which order the archive entries are in.1 can now be used as action codes for ModelActions./isReady endpoint now returns 503 Service Unavailable (instead of 500 Internal Server Error) when the node is not yet ready.MessageDispatcher handler was a method of a generic class.PlayerActor crash if enqueued ServerAction enqueued another ServerAction via ServerListener after a game session had ended.Nullable<T> with a null value are no longer dropped.string/int/double_value fields in event_params RECORDs. This behavior now matches the documentation.StartApplicationAsync() is now called only after the cluster has properly initialized, i.e., all entity shards are running.PersistedMultiplayerEntityActorBase.AddEntityAssociation() in OnClientSessionHandshake() now adds the association as expected.GameConfigBuildIntegration.GetAvailableBuildSources(string sourcePropertyInBuildParams) has been removed. It was obsoleted in release 26 by being renamed to GetAvailableGameConfigBuildSources().AppTooLongSuspended timeout which triggers when a frame took too long to execute has been removed, this is instead handled by the server kicking a client when the client has not responded for 60 seconds by default.http://localhost:5550 when running locally.2022.3.28f1.SharedCode/ folder has been renamed from CloudCore to SharedCode to clarify its purpose.INFO
Note: If you have a service contract signed with Metaplay, your SDK integration has already been updated!
Required migration steps for C# code.
Add the MetaplaySDK\Backend\Attributes\Metaplay.Attributes.csproj project as an existing project to the Metaplay folder in your MyGame-Server solution file.
MetaplaySDK samples have renamed Backend/CloudCore/CloudCore.csproj to CloudCore/SharedCode/SharedCode.csproj to clarify the project mostly contains the Assets/SharedCode folder. For consistency, it is recommeded that the you update the project name too.
First, you need to update project file and folder name. Then you update the paths Backend Solution file (Note that GUIDs may be different, and the existing GUIDs should not be modified):
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CloudCore", "CloudCore\CloudCore.csproj", "{D3CE2986-5A54-4300-9AA8-5E0D3434B69F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharedCode", "SharedCode\SharedCode.csproj", "{D3CE2986-5A54-4300-9AA8-5E0D3434B69F}"Finally, you need to change all project references in the Server, BotClient and other Backend Projects:
- <ProjectReference Include="..\CloudCore\CloudCore.csproj" />
+ <ProjectReference Include="..\SharedCode\SharedCode.csproj" />TIP
To find all project references, you can use git grep "CloudCore.csproj"
In order to clarify the purpose of the two different ways to create instances of ConfigArchive, its public constructors have been replaced with static creation methods. Replace your ConfigArchive constructor invocations as follows.
Creating a ConfigArchive from newly-built data:
- new ConfigArchive(creationTime, entries)
+ ConfigArchive.CreateFromBuild(creationTime, entries)Creating a ConfigArchive from data that was loaded from a previously-serialized archive:
- new ConfigArchive(contentHash, createdAt, entries)
+ ConfigArchive.CreateLoaded(contentHash, createdAt, entries)The actual difference between the two methods is that CreateFromBuild sorts the given entries by name and computes the contentHash automatically, whereas CreateLoaded keeps the entries in the given order and the contentHash as given.
DefaultEnvironmentConfigProvider.ClearActiveEnvironmentClientOverride() has been removed. The runtime active environment override is no longer persisted in PlayerPrefs and is instead an in-memory override that applies for the current lifecycle of the app. The new, recommended way to use the runtime active environment override is to provide ActiveEnvironmentIdOverride in MetaplayClientOptions when calling MetaplayClient.Initialize.
The member ClientDebugConfig in class EnvironmentConfig has been removed and is replaced with ClientLoggingConfig that only configures the per-environment client logging. If you have custom configuration for the client logging options, please update the environment the logging options in your enviroment configuration.
The configuration of EnablePlayerConsistencyChecks and PlayerChecksumGranularity is now done on the server side. If you have custom configuration for these options then migrate them into the appropriate Options.xxx.yaml files hosted in the server Config directory. The default options provided by the SDK remain unchanged:
ClientConsistencyChecks: Enabled for local and development environments, disabled otherwise.ChecksumGranularity: PerOperation for local and development environments, PerActionSingleTickPerFrame for other environments.For example, to override checksum granularity to PerActionSingleTickPerFrame also in development environments you would add this to Options.dev.yaml:
Player:
DebugConfig:
ChecksumGranularity: PerActionSingleTickPerFrameThe signature of the MetaplayClientCreatePlayerContextFunc and the default implementation DefaultCreatePlayerContext has been changed. The parameters currentOperation, enableConsistencyChecks and checksumGranularity have been replaced with the single parameter initialState received from the server. The old parameters are now accessible from initialState, highlighting that they are now part of server configuration. If you have a custom implementation of IPlayerClientContext, update your code to read these parameters from the passed in initialState.
If you create an instance of DefaultPlayerClientContext in your BotClient.HandleStartSession(), you'll need to remove the arguments for currentOperation, enableConsistencyChecks and checksumGranularity. For the new initialPlayerState parameter, pass in success.PlayerState (where success is the SessionProtocol.SessionStartSuccess success to HandleStartSession().)
To provide better error handling, PopulateConfigEntries has been removed. If you need to pass custom data to the game config, we recommend doing so by providing an implementation of GameConfigBuildTemplate<TSharedConfig, TServerConfig, TBuildParameters>.GetEntryBuilder(Type configType, string entryName). This method does the processing during at build time, reducing the amount of work that needs to be done when the game client imports the game config.
For example, the Idler sample imports a JSON file as part of the build process:
public class IdlerGameConfigBuild : GameConfigBuildTemplate<SharedGameConfig, ServerGameConfig, IdlerGameConfigBuildParameters>
{
protected override ConfigEntryBuilder? GetEntryBuilder(Type configType, string entryName)
{
if (!ShouldBuildEntry(entryName))
return null;
if (configType == typeof(SharedGameConfig))
{
if (entryName == "OpaqueSourceTest")
{
if (!BuildParameters.HasLocalFileSource)
return null;
return CustomEntryBuildSingleSource<byte[]>(
// Fetch data from the OpaqueSourceTest defined in the build parameters
GetGenericFetchFunc("OpaqueSourceTest.json", BuildParameters.OpaqueDataSource),
(builder, data) =>
{
// Deserialize from json to the target type.
JsonSerializer serializer = JsonSerialization.CreateSerializer(
new JsonSerialization.Options(JsonSerialization.Options.DefaultOptions)
{
TraverseGameConfigDataType = typeof(OpaqueSourceTestInfo),
});
List<OpaqueSourceTestInfo> list = JsonSerialization.Deserialize<List<OpaqueSourceTestInfo>>(data, serializer);
// Convert to a type that the SDK can consume, if your data type supports variants, they can also be passed here.
List<VariantConfigItem<OpaqueSourceTestId,OpaqueSourceTestInfo>> items = list.Select(x => new VariantConfigItem<OpaqueSourceTestId, OpaqueSourceTestInfo>(x, null, null, null)).ToList();
// Finally, assign the result to the game config builder
builder.AssignLibraryBuildResult(nameof(SharedGameConfig.OpaqueSourceTest),
items,
null);
});
}
}
// For every other entry, return the default builder.
return base.GetEntryBuilder(configType, entryName);
}
}Another option is to add binary data to the game config. To do this, add the data as part of the build process:
public class IdlerGameConfigBuild : GameConfigBuildTemplate<SharedGameConfig, ServerGameConfig, IdlerGameConfigBuildParameters>
{
protected override Task Build(IGameConfigBuilder<SharedGameConfig> shared, IGameConfigBuilder<ServerGameConfig> server, IdlerGameConfigBuildParameters buildParams)
{
byte[] bytes = ...;
shared.AddCustomArchiveEntry(ConfigArchiveEntry.FromBlob("CustomEntry", bytes));
return base.Build(shared, server, buildParams);
}
}This can then be loaded in the GameConfig.PopulateCustomConfigData(GameConfigImportParams importParams) method as follow:
public class SharedGameConfig : SharedGameConfigBase
{
public override void PopulateCustomConfigData(GameConfigImportParams importParams)
{
ReadOnlyMemory<byte> bytes = importParams.Resources.BaselineArchive.GetEntryBytes("CustomEntry");
// Parse your byte data and assign it to a property in your game config class
}
}You can instead manually resolve references at runtime. For example, replace your MetaRef collection with a collection containing StringId (or the Id of your config items):
- [MetaMember(1)] public OrderedSet<MetaRef<ProducerInfo>> DiscountedProducers { get; private set; }
+ [MetaMember(1)] public OrderedSet<ProducerTypeId> DiscountedProducers { get; private set; }You can then access the item from the relevant game config library:
foreach (ProducerTypeId id in DiscountedProducers)
ProducerInfo info = GameConfig.Producers[id];Another option is to use a normal list in the your game config, then construct the hash based collection in the IGameConfigPostLoad.PostLoad() method:
public class HappyHourInfo : IMetaActivableConfigData<HappyHourId>, IGameConfigPostLoad
{
[MetaMember(1)] private List<MetaRef<ProducerInfo>> discountedProducers { get; set; }
public OrderedSet<MetaRef<ProducerInfo>> DiscountedProducers { get; private set; }
public void PostLoad()
{
DiscountedProducers = new OrderedSet<MetaRef<ProducerInfo>>(discountedProducers);
}
}Timers started with StartPeriodicTimer and StartRandomizedPeriodicTimer require a handler for the scheduled message. The existence of the handler is checked when either method is called.
All handlers using the Akka's Receive<> methods must be changed to use [CommandHandler]. Code in the form of:
protected Task Initialize()
{
StartRandomizedPeriodicTimer(TickInterval, Tick.Instance);
}
protected override void RegisterHandlers()
{
ReceiveAsync<Tick>(ReceiveTick);
...
}
async Task ReceiveTick(Tick _)
{
...
}Should be converted to:
protected Task Initialize()
{
StartRandomizedPeriodicTimer(TickInterval, Tick.Instance);
}
[CommandHandler]
async Task HandleTick(Tick _)
{
...
}The PlayerActorBase.GetSessionStartAssociatedEntities() has been removed. Instead the actor keeps track of all entities that have been added with AddEntityAssociation, and those are sent to new sessions automatically. To port existing code from GetSessionStartAssociatedEntities:
EntityActor.OnClientSessionHandshakeAsync() call AddEntityAssociation(.., removeOnSessionEnd: true) for every associated entity as was called in GetSessionStartAssociatedEntities.GetSessionStartAssociatedEntities was added with AddEntityAssociation, use removeOnSessionEnd: true to maintain the expected, session lifetime.The PersistedMultiplayerEntityActorBase.GetSessionStartAssociatedEntities() has been removed. Instead the actor keeps track of all entities that have been added with AddEntityAssociation, and those are sent to new sessions automatically. To port existing code from GetSessionStartAssociatedEntities:
AddEntityAssociation to add an association. This was required previously too.RemoveEntityAssociation to add an association. This was required previously too.EntityActor.Initialize() call AddEntityAssociation for every associated entity as was called in GetSessionStartAssociatedEntities.Replace void OnSubscriberLost with Task OnSubscriberLostAsync:
- override void OnSubscriberLost(EntitySubscriber subscriber)
+ override Task OnSubscriberLostAsync(EntitySubscriber subscriber)
{
...
+ return Task.CompletedTask;
}Replace void OnSubscriberTerminated with Task OnSubscriberTerminatedAsync:
- override void OnSubscriberTerminated(EntitySubscriber subscriber)
+ override Task OnSubscriberTerminatedAsync(EntitySubscriber subscriber)
{
...
+ return Task.CompletedTask;
}Replace void OnSubscriberUnsubscribed(EntitySubscriber subscriber) with Task OnSubscriberUnsubscribedAsyncAsync(EntitySubscriber subscriber, MetaMessage goodbyeMessage):
- override void OnSubscriberUnsubscribed(EntitySubscriber subscriber)
+ override Task OnSubscriberUnsubscribedAsync(EntitySubscriber subscriber, MetaMessage goodbyeMessage)
{
...
+ return Task.CompletedTask;
}Rename Task OnSubscriptionLost to Task OnSubscriptionLostAsync:
- override Task OnSubscriptionLost(EntitySubscription subscription)
+ override Task OnSubscriptionLostAsync(EntitySubscription subscription)
{
...
}Rename Task OnSubscriptionKicked to Task OnSubscriptionKickedAsync:
- override Task OnSubscriptionKicked(EntitySubscription subscription)
+ override Task OnSubscriptionKickedAsync(EntitySubscription subscription)
{
...
}The attribute marking an "anonymous" AdminApi HTTP endpoint (i.e., an endpoint that can be accessed as long as the user is authenticated to the dashboard) is changed. If you are using the [AllowAnonymous] endpoint for any custom AdminApi endpoints, replace it like as follows:
[HttpGet("hello")]
- [AllowAnonymous]
+ [RequirePermission(MetaplayPermissions.Anyone)]
public ActionResult GetHello() { ... }Required migration steps for the dashboard project.
Ensure that you have Node version 20.12.0 or higher installed. We recommend using the latest LTS version of Node 20 which, at the time of writing, is 20.14.0.
Changes to several configuration files have been made during this release. The MetaplaySDK/Frontend/DefaultDashboard folder is to be considered the source of truth for these files. You will need to make the following changes to files in your Dashboard folder, based on the source files in MetaplaySDK/Frontend/DefaultDashboard:
.eslintrc.cjs. The file introduces new ESLint configurations.vite.config.ts. The file introduces new Vite configurations.tsconfig.app.json. The file contains TypeScript configurations for the browser app.tsconfig.json. The file introduces new configurations. "../tsconfig.libraries.json" to point to this folder within your own repository's structure. Typically this will be "../../MetaplaySDK/Frontend/tsconfig.libraries.json"tsconfig.node.json. The file introduces new TypeScript configs for the Node tooling.cypress.config.ts.package.json has new dependencies and an updated lint script. You should copy over the source file from DefaultDashboard and overwrite your existing file. 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.You can now remove the following two obsolete files from your Dashboard's root folder:
meta-components.d.ts file.cypress.config.js then you should remove it. Please note the exact filename..eslintrc.js then you should remove it. Please note the exact filename.Delete all node_modules folders from your project's repository. npm can sometimes get confused about stale dependencies when we make big changes to structure or configuration, as we did above. You can remove all modules with the command git clean -fdx ':(glob)**/node_modules/*' from anywhere inside your repository.
Run command pnpm i from your Dashboard directory to freshly re-install all required modules.
Run command pnpm lint from your Dashboard directory to get more opinionated TypeScript checks on code quality.
Finally, run pnpm build to build the Dashboard. Depending on how much customisation you have made to your project, you may now see warnings or errors related to component changes and deprecations inside the Metaplay SDK. The following migration guides will help you to fix these.
To make imports more consistent, the import scope has changed for some components. This will cause build errors of the form: Cannot find module '@metaplay/core/src/components/generatedui/components/MetaGeneratedForm.vue' or its corresponding type declarations.ts-plugin(2307) You can fix this by changing the import statement to be of the form: @metaplay/core' i.e. The import now appears at the root of the library and the internal structure of the library is no longer visible to external code. This simplification results in import statements of the form: import { MetaGeneratedForm } from '@metaplay/core'.
The components that are known to be affected are:
MInputDuration Component Changes The MInputDuration component's allowZeroDuration property has been removed in favor of the new allowEmpty property. The component's UX has been updated to make this clearer to the user. Additionally, please consider using hintMessage to provide additional context to the user of what an empty value means in practice. Maybe a feature has been disabled?
To migrate, replace the allowZeroDuration property with allowEmpty as follows, and update your surrounding code to handle undefined outputs from the component:
MInputDuration(
:model-value="duration"
@update:model-value="duration = $event"
- allowZeroDuration
+ allowEmpty
+ :hintMessage="!duration ? 'Feature is disabled' : undefined"
...
)
// Selected duration can now be either Luxon.Duration or undefined
const duration: Ref<Duration | undefined> = ref()MetaEventStreamCard Component Changes The MetaEventStreamCard component has been updated to include new props for pre-filtering and highlighting events. These replace the previous, similar props and overall should make the component easier to use. Please update your callsites to the new props as follows:
meta-event-stream-card(
title="Some events",
:eventStream="eventStream",
- tooltip="Tooltips got removed as a bad UX pattern. Please use a descriptive card title instead."
- searchIsFilter
+ utilitiesMode="filter" <- "highlight" (default, like before) or "filter"
- :initialSearchString="initialSearchString"
- :freezeSearch="freezeSearch"
+ :searchPreHighlight="searchPreHighlight" <- A search string that permanently highlights events, like before (not editable by the user)
- :initialKeywordFilters="initialKeywordFilters"
- :freezeKeywordFilters="freezeKeywordFilters"
+ :keywordPreFilters="keywordPreFilters" <- Array of keyword strings to pre-filter events by, like before (not editable by the user)
- :initialEventTypeFilters="initialEventTypeFilters"
- :freezeEventTypeFilters="freezeEventTypeFilters"
+ :eventTypePreFilters="eventTypePreFilters" <- Array of event type strings to pre-filter events by, like before (not editable by the user)
...
)doesHavePermission() Changed The doesHavePermission() function moved from gameServerApiStore to usePermissions. Please update your callsites:
-import { useGameServerApiStore } from '@metaplay/game-server-api'
-const gameServerApiStore = useGameServerAPIStore()
-const hasPermission = gameServerApiStore.doesHavePermission('xxx')
+import { usePermissions } from '@metaplay/meta-ui-next'
+const permissions = usePermissions()
+const hasPermission = permissions.doesHavePermission('xxx')MetaButton is replaced by MButton, MIconButton and MTextButton.MetaClipboardCopy is replaced by MClipboardCopy.MetaActionModalButton is replaced by MActionModalButton or a combination of either the MButton, MIconButton or MTextButton with an MActionModal component.MetaCollapse is replaced by the MCollapse component.For detailed examples of how the new components work, run pnpm storybook within the MetaplaySDK/NodePackages/MetaUiNext folder.
The b-collapse and BCollapsePlugin have now been removed. To ensure continued functionality, update all game-specific references to use the new MCollapse component. If you prefer to continue using the b-collapse component, you can import the library into your game-specific dashboard project and maintain it as a custom dependency.
The MetaInputSelect component no longer supports the noDeselect property. This property had no useful effect and is now ignored. You can safely remove the prop from any of your MetaInputSelect components that use it.
The MActionModal component no longer supports the onlyClose property. To achieve the same functionality, replace instances of the MActionModal with the onlyClose property to the new MModal component as follows:
- MActionModal(
+ MModal(
title="modalTitle"
ref="modalRef"
- :action="async () => {}"
- only-close
...
)The official Vue extension for VS Code has matured to the point that we now highly recommend using it. Take the following steps to ensure that you are using the latest version:
A problem that could arise during the steps mentioned above where VS Code pins the Vue extension to 1.8.27 despite you trying to upgrade to the latest 2.0.xx version.
Released on: July 11th, 2024
Dashboard: The latest version of TypeScript (version "5.5") contains bugs in its handling of generics. We use generics heavily inside our UI components and this latest version causes errors when building the Dashboard. Our project dependencies allowed for the latest version of TypeScript to be installed if you created a new project or updated (with npm update) an existing one. To fix this we have pinned TypeScript back to version "5.4". We will update to the latest version of TypeScript once these issues have been addressed.
Dashboard: Fixed getEnvironmentFamily to return the correct value.
Dashboard: Fixed player search to stop it from breaking when trying to display uninitialized players.
Dashboard: Improved formatting of game config validation log messages.
SDK: Fixed login potentially getting stuck a Metaplay Guild transaction is pending.
SDK: Improved caching of the GameConfig List endpoint, causing potential old data to be display.
SDK: Fixed dashboard "game configs updated" and "server restarted" popups not working with newest infra version. The long-poll SSE channel used to communicate the state gets incorrectly buffered in the ingress proxy.
package.json has new dependencies and an updated lint script. You should copy over the package.json from DefaultDashboard and overwrite your package.json. 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.