### Build Game Server Container Image Source: https://docs.metaplay.io/cloud-deployments/getting-started Builds a Docker container image for your game server using the Metaplay CLI. Requires Docker to be installed and running. ```bash MyGame$ metaplay build image ``` -------------------------------- ### Metaplay IAP Manager Initialization Logs (Steam) text Source: https://docs.metaplay.io/feature-cookbooks/in-app-purchases/getting-started-with-in-app-purchases Displays example log output indicating the Metaplay IAP manager has been created and is configured to use the server-driven purchase platform Steam. ```text // Logged when MetaplayClient is initialized: [iapManager] Created IAP manager (using server-driven purchase platform Steam) ``` -------------------------------- ### Metaplay IAP Manager Initialization Logs (Unity IAP) text Source: https://docs.metaplay.io/feature-cookbooks/in-app-purchases/getting-started-with-in-app-purchases Shows example log output from the Unity console when the Metaplay client initializes and the IAP manager is enabled using Unity IAP. It indicates the creation of the IAP manager and the subsequent initialization of the store with available products. ```text // Logged when MetaplayClient is initialized: [iapManager] Created IAP manager (using Unity IAP) ... // Logged after the session has started and the store has been initialized: [iapManager] Store initialized with 1 product, 1 of them available ``` -------------------------------- ### Run Metaplay Local Server with .NET CLI Source: https://docs.metaplay.io/introduction/introduction-to-metaplay This command allows for a quick local server setup without the need for Docker or database installations, facilitating rapid development and testing. ```bash dotnet run ``` -------------------------------- ### Start Dashboard Development Server Source: https://docs.metaplay.io/feature-cookbooks/automated-testing/advanced-system-testing Installs dependencies and starts the LiveOps Dashboard in development mode using pnpm. This is necessary if your system tests interact with the dashboard. ```bash Backend/Dashboard$ pnpm install Backend/Dashboard$ pnpm dev ``` -------------------------------- ### Deploy Game Server to Cloud Source: https://docs.metaplay.io/cloud-deployments/getting-started Deploys a built game server container image to a specified cloud environment using the Metaplay CLI. The CLI will prompt for missing details like the environment and image if not provided. ```bash MyGame$ metaplay deploy server ``` ```bash MyGame$ metaplay deploy server lovely-wombats-build-nimbly lovely-wombats-build:1755849940 ``` -------------------------------- ### SharedCode.Tests Project Structure Example Source: https://docs.metaplay.io/feature-cookbooks/automated-testing/dotnet-unit-testing Provides an example of the typical file structure within a `SharedCode.Tests` C# project. This includes the project file (`.csproj`), a global setup fixture (`SetupFixture.cs`) for Metaplay initialization, and example test files like `PlayerActionTests.cs`. ```text Backend Game-specific backend directory ├── SharedCode.Tests Shared code test C# project ├ ├── SharedCode.Tests.csproj The C# project file ├ ├── SetupFixture.cs One-time global setup fixture, initializes Metaplay ├ ├── PlayerActionTests.cs Examples for testing PlayerActions, PlayerModel, and GameConfigs ``` -------------------------------- ### Define In-App Product Configuration (Spreadsheet) Source: https://docs.metaplay.io/feature-cookbooks/in-app-purchases/getting-started-with-in-app-purchases This example shows how to define an in-app product in a spreadsheet for Metaplay. It includes columns for product ID, name, type, price, development ID, and platform-specific store IDs. You can add custom game-specific properties as needed. ```Spreadsheet ProductId #key| Name| Type| Price| NumGems| DevelopmentId| GoogleId| AppleId ---|---|---|---|---|---|---|--- GemPack1| Gem Pack| Consumable| 0.99| 50| dev.gempack_1| com.examplecompany.examplegame.gempack_1| com.examplecompany.examplegame.GemPack_1 ``` -------------------------------- ### ShopScript: Initialize and Populate Shop UI Source: https://docs.metaplay.io/feature-cookbooks/in-app-purchases/getting-started-with-in-app-purchases The ShopScript initializes the shop UI, fetches available in-app products from the Metaplay client, and instantiates UI elements for each product. It handles store availability and initialization failures. ```csharp using System.Collections; using System.Collections.Generic; using UnityEngine; using Metaplay.Unity.UnityClient; using Metaplay.Core.IAP; using Metaplay.Core.Player; using TMPro; // ShopScript class (MonoBehaviour) // Object references: // List where the product objects are spawned. public Transform ItemList; // Prefab for a single product in the shop UI. public ShopProductScript ShopProductPrefab; // Text showing the shop initialization status. public Text InitializingText; bool _shopUIIsInitialized = false; void Start() { InitializingText.text = "Initializing..."; } void Update() { TryInitializeShopUI(); } void TryInitializeShopUI() { // Only initialize the UI once. if (_shopUIIsInitialized) return; // Wait for store to initialize, or the initialization to fail. if (MetaplayClient.IAPManager.StoreIsAvailable) { // Store has been successfully initialized. _shopUIIsInitialized = true; InitializingText.gameObject.SetActive(false); // Get all configured products that are available in the store. IEnumerable products = MetaplayClient.PlayerModel .GameConfig.InAppProducts.Values .Where(productInfo => MetaplayClient.IAPManager.StoreProductIsAvailable(productInfo.ProductId)); // Create a ShopProductScript-equipped gameObject for // each of the available products. foreach (InAppProductInfo productInfo in products) { ShopProductScript productObject = Instantiate(ShopProductPrefab, ItemList); productObject.ProductId = productInfo.ProductId; } } else if (MetaplayClient.IAPManager.StoreInitFailure.HasValue) { // Store initialization failed. // Display failure info. _shopUIIsInitialized = true; InitializingText.text = $"Shop initialization failed: {MetaplayClient.IAPManager.StoreInitFailure.Value.Reason}"; } } ``` -------------------------------- ### Debug Game Server Logs Source: https://docs.metaplay.io/cloud-deployments/getting-started Retrieves the latest logs for a deployed game server from a specified environment using the Metaplay CLI for debugging purposes. ```bash MyGame$ metaplay debug logs [ENVIRONMENT] ``` -------------------------------- ### Add EF Core Migration for Web3 Features Source: https://docs.metaplay.io/feature-cookbooks/web3/getting-started-with-nfts Creates a new Entity Framework Core migration named 'InitialWeb3' to accommodate new database tables introduced by the Web3 features. This command should be run from the game's Server project directory. ```bash dotnet ef migrations add InitialWeb3 ``` -------------------------------- ### ShopProductScript: Display Product Info and Handle Purchases Source: https://docs.metaplay.io/feature-cookbooks/in-app-purchases/getting-started-with-in-app-purchases The ShopProductScript manages the UI for a single product in the shop, displaying its name, price, and reward. It also handles the purchase flow initiation and displays a purchasing indicator. ```csharp using UnityEngine; using Metaplay.Unity.UnityClient; using Metaplay.Core.IAP; using TMPro; // ShopProductScript class (MonoBehaviour) // Parameter assigned when instantiated (by ShopScript). public InAppProductId ProductId; // Object references: // Texts for showing info about the product. public Text NameText; public Text PriceText; public Text RewardText; // An indicator (e.g., a spinner) shown when // a purchase flow of this product is ongoing. public GameObject PurchasingIndicator; public void Start() { // Initialize UI for this product IAPManager.StoreProductInfo? storeProductMaybe = MetaplayClient.IAPManager.TryGetStoreProductInfo(ProductId); if (storeProductMaybe.HasValue) { IAPManager.StoreProductInfo storeProduct = storeProductMaybe.Value; InAppProductInfo productInfo = MetaplayClient.PlayerModel.GameConfig.InAppProducts[ProductId]; // Price text comes from Unity IAP. PriceText.text = storeProduct.Product.metadata.localizedPriceString; // Note: using non-localized text just for simplicity in this example. NameText.text = productInfo.Name; RewardText.text = $"{productInfo.NumGems} gems"; } } public void Update() { // Show or hide purchasing indicator based on whether this // product is currently being purchased. PurchasingIndicator.SetActive(IsPurchasing()); } public void OnClickBuy() { // Don't try to buy the product if its purchase is already ongoing. if (IsPurchasing()) return; // Start a purchase flow of the product. // This will initiate a purchase in the IAP store, and if successful, // the purchase will be validated by the server. Ultimately, // PlayerModel.OnClaimedInAppProduct will be called. MetaplayClient.IAPManager.TryBeginPurchaseProduct(ProductId); } bool IsPurchasing() { // Query the purchase flow status from the IAPFlowTracker helper. // IAPFlowTracker listens to events from IAPManager and keeps // track (in a best-effort manner) of ongoing purchases. return MetaplayClient.IAPFlowTracker.PurchaseFlowIsOngoing(ProductId); } ``` -------------------------------- ### Invoke NFT Transaction via MetaplayClient (C#) Source: https://docs.metaplay.io/feature-cookbooks/web3/getting-started-with-nfts This C# code demonstrates how to invoke an NFT transaction, specifically the hero upgrade transaction, using the MetaplayClient. It shows how to create an instance of the transaction and pass it to `MetaplayClient.NftClient.ExecuteNftTransaction`. ```csharp // Note: An NFT's token id is MetaNft.TokenId public void UpgradeHero(NftId heroTokenId) { PlayerNftTransaction transaction = new PlayerUpgradeHeroNft(heroTokenId); MetaplayClient.NftClient.ExecuteNftTransaction(transaction); } ``` -------------------------------- ### Casting Offer Types in Metaplay Source: https://docs.metaplay.io/feature-cookbooks/in-game-offers/getting-started-with-in-game-offers Demonstrates how to cast offer types when working with custom offer configurations in Metaplay. This is often necessary when extending default offer models. ```csharp MetaOfferInfoBase metaOfferInfo = ...; GameOfferInfo gameOfferInfo = metaOfferInfo as GameOfferInfo; MetaOfferGroupInfoBase metaOfferGroupInfo = ...; GameOfferGroupInfo gameOfferGroupInfo = metaOfferGroupInfo as GameOfferGroupInfo; ``` -------------------------------- ### Configure Immutable X Server Settings (YAML) Source: https://docs.metaplay.io/feature-cookbooks/web3/getting-started-with-nfts This configuration snippet shows how to set up your game server to interface with Immutable X. It includes network selection, enabling player authentication, product naming for wallet binding, an HMAC secret for security, an optional API key, and NFT collection details like the contract address and metadata folder. ```yaml Web3: # Should be either EthereumMainnet or GoerliTestnet. ImmutableXNetwork: GoerliTestnet # Enable player authentication to enable wallet binding. EnableImmutableXPlayerAuthentication: true # This product name will be displayed to the player during the # wallet binding flow. ImmutableXPlayerAuthenticationProductName: "My Game Name" # This secret should be set to an unguessable, per-game string. # This example was generated with python: # import uuid; print(uuid.uuid4()) # Generate your own random string instead of using this! ImmutableXPlayerAuthenticationChallengeHmacSecret: "35995352-df6c-4a18-ada1-992eb8a9eac7" # Optional Immutable X API key. See https://docs.x.immutable.com/docs/api-rate-limiting ImmutableXApiKey: ... NftCollections: Heroes: # Change Ledger from LocalTesting to ImmutableX. Ledger: ImmutableX # Assign your collection's actual contract address. ContractAddress: 0x0c61Baa20CfC1d5fAa3e732d7a14F3971C0c1529 MetadataFolder: "metadata/heroes" ``` -------------------------------- ### Define SharedGameConfig Libraries for Offers and OfferGroups (C#) Source: https://docs.metaplay.io/feature-cookbooks/in-game-offers/getting-started-with-in-game-offers Defines the C# configuration libraries for 'Offers' and 'OfferGroups' within the SharedGameConfig class. These libraries link to specific data sources and transformations, enabling the Metaplay SDK to manage offer configurations. ```csharp public class SharedGameConfig : SharedGameConfigBase { [GameConfigEntry("Offers")] [GameConfigEntryTransform(typeof(DefaultMetaOfferSourceConfigItem))] public GameConfigLibrary Offers { get; private set; } [GameConfigEntry("OfferGroups")] [GameConfigEntryTransform(typeof(DefaultMetaOfferGroupSourceConfigItem))] public GameConfigLibrary OfferGroups { get; private set; } } ``` -------------------------------- ### Start Metaplay Server via CLI Source: https://docs.metaplay.io/introduction/getting-started/connecting-to-the-game-server Starts the Metaplay game server in development mode using the command-line interface. Press 'q' to gracefully stop the server. ```bash MyProject$ metaplay dev server ``` -------------------------------- ### Initiate Immutable X Login in Client (C#) Source: https://docs.metaplay.io/feature-cookbooks/web3/getting-started-with-nfts This C# code snippet demonstrates how to initiate the Immutable X login process from the game client. It calls `ImmutableXLinkSdkHelper.LoginWithImmutableXAsync` within a MetaTask to handle asynchronous operations, which typically involves opening a browser wallet plugin for authentication. ```csharp public void OnClickImxLogin() { MetaTask.Run(async () => { await ImmutableXLinkSdkHelper.LoginWithImmutableXAsync(); }); } ``` -------------------------------- ### ShopOfferScript: Manage Individual Offer UI (C#) Source: https://docs.metaplay.io/feature-cookbooks/in-game-offers/getting-started-with-in-game-offers Handles the UI display for a single offer within a group. It retrieves offer details, purchase status, and displays relevant information like the offer's display name. It requires `InfoText` for displaying information and is assigned `OfferGroupId` and `OfferId` by the `ShopScript`. ```csharp // ShopOfferScript.cs // Object references. public Text InfoText; // Info about the offer and its status. // Parameters assigned by ShopScript when it spawns this offer object. public MetaOfferGroupId OfferGroupId; // The Group this offer exists in. public MetaOfferId OfferId; void Update() { PlayerModel player = MetaplayClient.PlayerModel; MetaOfferGroupInfoBase offerGroupInfo = player.GameConfig.OfferGroups[OfferGroupId]; MetaOfferInfoBase offerInfo = player.GameConfig.Offers[OfferId]; // If the offer group has expired, don't update UI. if (!player.MetaOfferGroups.IsActive(OfferGroupId, player)) return; // Get the status of the offer within the group. // This contains info such as how many times the offer has been purchased. MetaOfferStatus offerStatus = player.MetaOfferGroups.GetOfferStatus(player, offerGroupInfo, offerInfo); // Display the offer's name and remaining purchase count string infoText = offerInfo.DisplayName; ``` -------------------------------- ### Enable Web3 Features in MetaplayCoreOptions Source: https://docs.metaplay.io/feature-cookbooks/web3/getting-started-with-nfts Enables Web3 features by setting the `EnableWeb3` feature flag to true in MetaplayCoreOptions. It also enables the ImmutableXLinkClientLibrary for wallet binding. This is typically done within an IMetaplayCoreOptionsProvider implementation. ```csharp public class GlobalOptions : IMetaplayCoreOptionsProvider { public MetaplayCoreOptions GetCoreOptions() => new MetaplayCoreOptions { FeatureFlags = new FeatureFlags { EnableWeb3 = true, EnableImmutableXLinkClientLibrary = true } }; } ``` -------------------------------- ### Start Battle Entity Using Workload Scheduling Source: https://docs.metaplay.io/game-server-programming/how-to-guides/balancing-load-between-nodes Demonstrates starting a BattleActor entity using WorkloadSchedulingUtility. It creates a new entity ID on the best available node and sends setup parameters to the newly created entity. ```csharp using System.Linq; using System.Threading.Tasks; using Metaplay.Core; using Metaplay.Core.Model; // Assuming BattleEntry, BattleMemberAvatar, BattleSetupParams, EntityKindGame are defined elsewhere async Task<(EntityId, BattleSetupParams)> StartBattleAsync(BattleEntry battleEntry) { // Create an ID for a battle entity EntityId newBattleId = await WorkloadSchedulingUtility.CreateEntityIdOnBestNode(EntityKindGame.Battle); // Start entity by sending setup data to it BattleMemberAvatar[] avatars = GetAvatarsForBattleEntry(battleEntry).ToArray(); BattleSetupParams setupParams = new BattleSetupParams(avatars); _ = await EntityAskAsync(newBattleId, new InternalEntitySetupRequest(setupParams)); return (newBattleId, setupParams); } ``` -------------------------------- ### Building Environment Configs Manually in C# Source: https://docs.metaplay.io/miscellaneous/sdk-updates/release-notes/release-25-environment-configs-migration-guide Provides a C# code example for manually triggering the environment configuration build process within a custom build script. This allows for fine-grained control over when and how environment configurations are generated. ```csharp using Metaplay.Unity.Build; public static class BuildScripts { public static void PerformBuild() { // Example of calling BuildEnvironmentConfigs manually // This might be called before BuildPipeline.BuildPlayer DefaultEnvironmentConfigProvider.BuildEnvironmentConfigs("production", new string[] { "production", "common" }); // ... rest of your build logic ... } } ``` -------------------------------- ### Define In-Game Offers Configuration Source: https://docs.metaplay.io/feature-cookbooks/in-game-offers/getting-started-with-in-game-offers This configuration defines individual in-game offers, specifying their ID, display name, description, associated in-app product, rewards, and purchase limits per player. It serves as the data source for purchasable items in the game. ```csv OfferId #key| DisplayName| Description| InAppProduct| Rewards[0]| MaxPurchasesPerPlayer GemOffer1| Gem Offer 1| Small gem offer| Offer5Usd| 100 Gems| 5 GemOffer2| Gem Offer 2| Large gem offer| Offer10Usd| 200 Gems| 5 ``` -------------------------------- ### Update SharedGameConfig with Custom Config Classes (C#) Source: https://docs.metaplay.io/feature-cookbooks/in-game-offers/getting-started-with-in-game-offers Shows how to modify the 'SharedGameConfig' class to use the newly defined custom Offer and Offer Group configuration classes ('GameOfferSourceConfigItem', 'GameOfferGroupSourceConfigItem') instead of the default SDK implementations. ```csharp public class SharedGameConfig : SharedGameConfigBase { [GameConfigEntry("Offers")] [GameConfigEntryTransform(typeof(GameOfferSourceConfigItem))] // Previously, was DefaultMetaOfferSourceConfigItem // Previously, item type was DefaultMetaOfferInfo public GameConfigLibrary Offers { get; private set; } [GameConfigEntry("OfferGroups")] [GameConfigEntryTransform(typeof(GameOfferGroupSourceConfigItem))] // Previously, was DefaultMetaOfferGroupSourceConfigItem // Previously, item type was DefaultMetaOfferGroupInfo public GameConfigLibrary OfferGroups { get; private set; } } ``` -------------------------------- ### Customize PlayerModel for Offer Groups (C#) Source: https://docs.metaplay.io/feature-cookbooks/in-game-offers/getting-started-with-in-game-offers Demonstrates how to customize the 'PlayerModel' by deriving from 'PlayerModelBase' and specifying the custom 'GameOfferGroupInfo' type for handling offer groups. This ensures the player model correctly uses the extended configuration. ```csharp // OfferGroup.cs // Here, the only things that differ from DefaultPlayerMetaOfferGroupsModel // are the tag number for [MetaSerializableDerived], and the type parameter // given to PlayerMetaOfferGroupsModelBase. [MetaSerializableDerived(1)] [MetaActivableSet("OfferGroup")] public class PlayerGameOfferGroupsModel : PlayerMetaOfferGroupsModelBase { protected override MetaOfferGroupModelBase CreateActivableState(GameOfferGroupInfo info, IPlayerModelBase player) { return new DefaultMetaOfferGroupModel(info); } protected override MetaOfferPerPlayerStateBase CreateOfferState(MetaOfferInfoBase offerInfo, IPlayerModelBase player) { return new DefaultMetaOfferPerPlayerState(); } } ``` -------------------------------- ### Install and Use dotnet-gcdump Locally Source: https://docs.metaplay.io/game-server-programming/how-to-guides/troubleshooting-servers Installs the dotnet-gcdump tool globally and demonstrates collecting a heap dump from a running process by PID. Also shows how to copy the dump file from a Kubernetes pod. ```shell dotnet tool install -g dotnet-gcdump dotnet-gcdump ps dotnet-gcdump collect -p 100 kubectl cp :/ ./ --container ``` -------------------------------- ### Extend Offer Config with Custom Fields (C#) Source: https://docs.metaplay.io/feature-cookbooks/in-game-offers/getting-started-with-in-game-offers Defines custom C# classes to extend the Offer configuration by adding an 'Icon' field. It includes the runtime data class 'GameOfferInfo' and the build-time intermediate class 'GameOfferSourceConfigItem', demonstrating inheritance from SDK base classes. ```csharp // OfferInfo.cs // This is the actual runtime config data class. [MetaSerializableDerived(1)] public class GameOfferInfo : MetaOfferInfoBase { // Example custom field. [MetaMember(1)] public IconId Icon; public GameOfferInfo(){ } public GameOfferInfo(GameOfferSourceConfigItem source) : base(source) { Icon = source.Icon; } } // This is an intermediate class which is only used at config build time. // Its purpose is to help in parsing the configs, since // MetaOfferSourceConfigItemBase<> contains some fields that are non-trivially // mapped into MetaOfferInfoBase. public class GameOfferSourceConfigItem : MetaOfferSourceConfigItemBase { // Example custom field. public IconId Icon; public override GameOfferInfo ToConfigData(GameConfigBuildLog buildLog) { return new GameOfferInfo(this); } } ``` -------------------------------- ### Extend Offer Group Config with Custom Fields (C#) Source: https://docs.metaplay.io/feature-cookbooks/in-game-offers/getting-started-with-in-game-offers Illustrates extending Offer Group configurations by adding a 'ThemeColor' field using custom C# classes. It provides the runtime class 'GameOfferGroupInfo' and the build-time class 'GameOfferGroupSourceConfigItem', both inheriting from Metaplay SDK base classes. ```csharp // OfferGroupInfo.cs // Like in the previous snippet, this is the actual runtime config data class. [MetaSerializableDerived(1)] [MetaActivableConfigData("OfferGroup")] public class GameOfferGroupInfo : MetaOfferGroupInfoBase { // Example custom field. [MetaMember(1)] public Color ThemeColor; public GameOfferGroupInfo(){ } public GameOfferGroupInfo(GameOfferGroupSourceConfigItem source) : base(source) { ThemeColor = source.ThemeColor; } } // Like in the previous snippet, this is an intermediate class which is only // used at config build time. public class GameOfferGroupSourceConfigItem : MetaOfferGroupSourceConfigItemBase { // Example custom field. public Color ThemeColor; public override GameOfferGroupInfo ToConfigData(GameConfigBuildLog buildLog) { return new GameOfferGroupInfo(this); } } ``` -------------------------------- ### ShopScript: Display Active Offers in UI (C#) Source: https://docs.metaplay.io/feature-cookbooks/in-game-offers/getting-started-with-in-game-offers Manages the display of active MetaOfferGroups in a specific UI placement ('Shop'). It instantiates prefabs for individual offers, updates group status text, and handles the visibility of offers based on their activation state. Dependencies include `ShopOfferScript`, `OfferPrefab`, `OfferList`, and `GroupStatusText`. ```csharp // ShopScript.cs // Object references. public ShopOfferScript OfferPrefab; // Prefab for individual offers. public Transform OfferList; // Parent for offer game objects. public Text GroupStatusText; // Info about the status of the active group. // Keep track of the current active offer group and its offer game objects. MetaOfferGroupInfoBase _activeGroupInfo = null; List _activeGroupOfferObjects = new List(); void UpdateOffers() { PlayerModel player = MetaplayClient.PlayerModel; // Here, we only show offer groups with the placement Shop. OfferPlacementId shopPlacement = OfferPlacementId.FromString("Shop"); // Find the active offer group in the Shop placement, if any. MetaOfferGroupModelBase activeGroup = player.MetaOfferGroups .GetActiveStates(player) .FirstOrDefault(group => group.ActivableInfo.Placement == shopPlacement); MetaOfferGroupInfoBase activeGroupInfo = activeGroup?.ActivableInfo; // If the active group has changed since the last update, create new offer game objects. if (activeGroupInfo?.GroupId != _activeGroupInfo?.GroupId) { // \note activeGroup and activeGroupInfo can still be null here! _activeGroupInfo = activeGroupInfo; // Destroy offer game objects of the previously-active group. foreach (ShopOfferScript offerItem in _activeGroupOfferObjects) Destroy(offerItem.gameObject); // Spawn offer game objects for the newly-active group (if any). _activeGroupOfferObjects = new List(); if (activeGroupInfo != null) { foreach (MetaOfferInfoBase offerInfo in activeGroupInfo.Offers.MetaRefUnwrap()) { ShopOfferScript offerItem = Instantiate(OfferPrefab, parent: OfferList); offerItem.OfferGroupId = activeGroupInfo.GroupId; offerItem.OfferId = offerInfo.OfferId; _activeGroupOfferObjects.Add(offerItem); } } } // Update the UI for the active offer group. if (activeGroup != null) { // Show group expiration timer. MetaActivableState.Activation activation = activeGroup.LatestActivation.Value; string expirationText = ""; if (activation.EndAt.HasValue) expirationText = $"Expires in {activation.EndAt.Value - player.CurrentTime}"; GroupStatusText.text = expirationText; GroupStatusText.gameObject.SetActive(true); // Show or hide each individual offer based on whether it is // currently active or not. // Even if the offer group is active, individual offers in it // might be inactive due to offer-specific targeting. foreach (ShopOfferScript offerItem in _activeGroupOfferObjects) offerItem.gameObject.SetActive(activeGroup.OfferIsActive(offerItem.OfferId, player)); } else { // There is no active offer group. GroupStatusText.gameObject.SetActive(false); } } ``` -------------------------------- ### Define In-Game Offer Group Configuration Source: https://docs.metaplay.io/feature-cookbooks/in-game-offers/getting-started-with-in-game-offers This configuration groups individual offers into logical collections, specifying the group's ID, display name, description, placement in the game UI, priority, associated offers, and lifetime/cooldown periods. It dictates how offers are presented to players. ```csv GroupId #key| DisplayName| Description| Placement| Priority| Offers[0]| Offers[1]| Lifetime| Cooldown GemOffers| Gem Offers| Various gem offers| Shop| 1| GemOffer1| GemOffer2| 5h| 12h ``` -------------------------------- ### Reference Project: Example Activatables Source: https://docs.metaplay.io/miscellaneous/sdk-updates/release-notes/release-12 The reference project now includes two example features implemented as activables: "Happy Hour" and "Special Producer Event". These demonstrate practical application of the activables system. ```csharp /* Reference project: Added two example event features, implemented as activables: "Happy Hour" and "Special Producer Event". */ ``` -------------------------------- ### Trigger Purchase Action in Metaplay Source: https://docs.metaplay.io/feature-cookbooks/in-game-offers/getting-started-with-in-game-offers Initiates a purchase action for a Metaplay offer by executing a PlayerPreparePurchaseMetaOffer action. This involves retrieving offer details and providing analytics context. The code sets a flag to indicate that a purchase is pending and shows a purchase spinner. ```C# // ShopOfferScript.cs public void OnClickBuy() { PlayerModel player = MetaplayClient.PlayerModel; MetaOfferGroupInfoBase offerGroupInfo = player.GameConfig.OfferGroups[OfferGroupId]; MetaOfferInfoBase offerInfo = player.GameConfig.Offers[OfferId]; // Start preparing the purchase of the offer. MetaplayClient.PlayerContext.ExecuteAction(new PlayerPreparePurchaseMetaOffer( offerGroupInfo, offerInfo, // Specify info for analytics about the context of the purchase. // If it isn't needed, it can also be left null for now. new GamePurchaseAnalyticsContext( placement: offerGroupInfo.Placement.ToString(), group: OfferGroupId.ToString()))); // Start waiting for the server's confirmation // of the above preparation action. _purchaseIsPending = true; // Start showing the purchase spinner or other indicator. ShopUI.Instance.ShowPurchaseSpinner(); } ``` -------------------------------- ### Install and Run Playwright CLI Source: https://docs.metaplay.io/feature-cookbooks/automated-testing/advanced-system-testing Installs the Playwright CLI globally and downloads the necessary browser binaries. This is a prerequisite for running Playwright-based tests. ```bash dotnet tool install --global Microsoft.Playwright.CLI playwright install ``` -------------------------------- ### Define InAppProducts Library in C# Source: https://docs.metaplay.io/feature-cookbooks/in-app-purchases/getting-started-with-in-app-purchases Defines a `SharedGameConfig` class that includes a `GameConfigLibrary` for `InAppProducts`. This library maps `InAppProductId` to `InAppProductInfo`, enabling structured management of in-app product data within the game configuration. It relies on Metaplay's game configuration system. ```csharp using Metaplay.Core; using Metaplay.Unity; public class SharedGameConfig : SharedGameConfigBase { [GameConfigEntry("InAppProducts")] public GameConfigLibrary InAppProducts { get; private set; } } ``` -------------------------------- ### Configure NFT Collection Server Options (YAML) Source: https://docs.metaplay.io/feature-cookbooks/web3/getting-started-with-nfts Defines the server-side configuration for NFT collections, including ledger type, contract address, and metadata folder. The 'Ledger' option determines whether to use 'LocalTesting' or 'ImmutableX'. 'ContractAddress' is required for ImmutableX but not LocalTesting. 'MetadataFolder' specifies the path for collection metadata. ```yaml sWeb3: NftCollections: Heroes: Ledger: LocalTesting # Note: contract address not used in LocalTesting mode. #ContractAddress: 0x0123456789abcDEF0123456789abCDef01234567 MetadataFolder: "metadata/heroes" ``` -------------------------------- ### Dashboard: Local Development Server Start Command Source: https://docs.metaplay.io/miscellaneous/sdk-updates/release-notes/release-20 Provides the updated command to start the local Metaplay Dashboard development server. The command now originates from the project's root backend directory. ```bash # Old command location: # /MetaplaySDK/Backend/Dashboard> npm run serve # New command location: /Backend/Dashboard> npm run serve ``` -------------------------------- ### Foreign NFT Collection Configuration (YAML) Source: https://docs.metaplay.io/feature-cookbooks/web3/getting-started-with-nfts Configures a 'foreign' NFT collection where metadata is managed externally. This involves omitting S3 region and bucket name settings, as the server only needs to import existing metadata. The MetadataManagementMode is set to 'Foreign' to indicate this setup. ```yaml Web3: # ... NftCollections: Heroes: Ledger: ImmutableX ContractAddress: 0x0c61Baa2Cd5fAa3e732d7a14F3971C0c1529 MetadataFolder: "metadata/heroes" MetadataManagementMode: Foreign ``` -------------------------------- ### C# Example: Application State Manager Source: https://docs.metaplay.io/introduction/getting-started/adding-the-sdk-to-a-unity-project An example of a C# file that manages the application lifecycle for a Metaplay-powered game. This includes handling Metaplay SDK initialization, server connections, and network error management. ```csharp // ApplicationStateManager.cs // Manages Metaplay SDK integration, connection, and error handling. ``` -------------------------------- ### Initialize Metaplay Project using CLI Source: https://docs.metaplay.io/introduction/getting-started/sdk-integration-guide Initializes the Metaplay SDK in your Unity project and links it to your Metaplay Portal project. This command also adds a basic 'Hello World' example and placeholder code for player state and game logic. ```bash MyProject$ metaplay init project ``` -------------------------------- ### Install and Use Node.js Version Source: https://docs.metaplay.io/miscellaneous/sdk-updates/release-notes/release-33 Use Node Version Manager (nvm) to install and switch to the recommended Node.js version for the LiveOps Dashboard. ```bash # Install Node 22.14.0 with Node Version Manager (nvm). nvm install 22.14.0 # Use the newly installed version. nvm use 22.14.0 ``` -------------------------------- ### Player-NFT Transaction to Upgrade Hero (C#) Source: https://docs.metaplay.io/feature-cookbooks/web3/getting-started-with-nfts This C# code defines a player-NFT transaction that upgrades a hero. It handles payment in gold, increments the hero's level, and includes methods for executing, canceling, and finalizing the transaction. It uses Metaplay's transaction mechanism to ensure changes are persistent and coordinated. ```csharp [MetaSerializableDerived(1)] public class PlayerUpgradeHeroNft : PlayerNftTransaction { /// /// Which hero to upgrade. /// [MetaMember(1)] public NftId HeroId; public override IEnumerable TargetNfts => new NftKey[] { new NftKey(NftCollectionId.FromString("Heroes"), HeroId) }; PlayerUpgradeHeroNft() { } public PlayerUpgradeHeroNft(NftId heroId) { HeroId = heroId; } [MetaSerializableDerived(1)] public class Context : ContextBase { [MetaMember(1)] public int UpgradeCost; Context() {} public Context(int upgradeCost) { UpgradeCost = upgradeCost; } } public override MetaActionResult Execute(IPlayerModelBase playerBase, MetaDictionary nfts, bool commit, ref ContextBase context) { PlayerModel player = (PlayerModel)playerBase; // Note: Transactions should operate on the `nfts` parameter of Execute, // instead of on player.Nft.OwnedNfts. // `nfts` includes the NFTs specified by the `TargetNfts` property. HeroNft heroNft = (HeroNft)nfts.Values.Single(); // Calculate cost and validate int upgradeCost = heroNft.HeroType.Ref.UpgradeCost; if (player.Wallet.NumGold < upgradeCost) { // \note When Execute returns non-Success, CancelPlayer and FinalizePlayer // will not be called, and thus we don't need to set `context` here. return ActionResult.NotEnoughResources; } if (commit) { // Take payment from player, and increment hero level player.Wallet.NumGold -= upgradeCost; heroNft.Level++; } // Store in the context how much resources the player paid, so that CancelPlayer can refund it. context = new Context(upgradeCost); return MetaActionResult.Success; } public override void CancelPlayer(IPlayerModelBase playerBase, ContextBase context) { PlayerModel player = (PlayerModel)playerBase; // Give back the amount of resources paid by the player. int upgradeCost = ((Context)context).UpgradeCost; player.Wallet.NumGold += upgradeCost; } public override void FinalizePlayer(IPlayerModelBase player, ContextBase context) { } } ``` -------------------------------- ### metaplay-project.yaml Configuration Example Source: https://docs.metaplay.io/introduction/getting-started/project-configuration This YAML snippet shows a typical `metaplay-project.yaml` file, illustrating how to configure project details, directories, .NET runtime version, Helm chart versions, LiveOps dashboard, and environments. ```yaml # Configure schema to use. # yaml-language-server: $schema=../../MetaplaySDK/projectConfigSchema.json $schema: "../../MetaplaySDK/projectConfigSchema.json" # Configure project. projectID: lovely-wombats-build buildRootDir: . sdkRootDir: MetaplaySDK backendDir: Backend sharedCodeDir: UnityClient/Assets/SharedCode unityProjectDir: UnityClient # Specify .NET runtime version to build project for, only '.'. dotnetRuntimeVersion: "9.0" # Specify Helm chart versions to use for server and bot deployments. serverChartVersion: 0.8.0 botClientChartVersion: 0.4.2 # Customize Metaplay features used in the game. features: # Configure LiveOps Dashboard. dashboard: useCustom: true rootDir: Backend/Dashboard # Project environments. environments: - name: Develop humanId: lovely-wombats-build-quickly type: development stackDomain: p1.metaplay.io ``` -------------------------------- ### Dockerfile Server npm Install Logic Source: https://docs.metaplay.io/miscellaneous/sdk-updates/release-notes/release-21 The Dockerfile.server now automatically runs `npm install` if `package-lock.json` is not found, simplifying Docker build setup. This eliminates the need for manual `npm install` before building. ```dockerfile Dockerfile.server now runs `npm install` automatically if `package-lock.json` is not present. ``` -------------------------------- ### Install and Use Node.js 22.14.0 with NVM Source: https://docs.metaplay.io/miscellaneous/sdk-updates/release-notes/release-32 This snippet demonstrates how to install and switch to Node.js version 22.14.0 using the Node Version Manager (nvm). Ensure nvm is installed before running these commands. ```bash # Install Node 22.14.0 with Node Version Manager (nvm). nvm install 22.14.0 # Use the new version. nvm use 22.14.0 ``` -------------------------------- ### Install Dependencies and Run Integration Tests (Shell) Source: https://docs.metaplay.io/feature-cookbooks/automated-testing/integration-testing Installs project dependencies using pip and then executes the integration tests via a Python script. This is the primary command for running all tests locally or within a CI environment. Ensure Docker is running beforehand. ```shell pip install -r MetaplaySDK/Scripts/requirements.txt python MetaplaySDK/Scripts/integration-tests.py ``` -------------------------------- ### Install dotnet-dump Tool Source: https://docs.metaplay.io/game-server-programming/how-to-guides/troubleshooting-servers Installs the dotnet-dump global tool, which is necessary for collecting and analyzing .NET process memory dumps. ```shell dotnet tool install -g dotnet-dump ``` -------------------------------- ### Terraform Initialization and Provider Installation Source: https://docs.metaplay.io/self-hosting/how-to-guides/deploying-cloud-infrastructure This section details the process of initializing Terraform and installing various providers required for the Metaplay infrastructure. It lists the providers and their versions, including HashiCorp's AWS, Kubernetes, Helm, local, random, cloud-init, and time providers, as well as custom providers like alekc/kubectl and terraform-registry.platform.metaplay.dev/metaplay/hydra. ```terraform Initializing provider plugins... - Finding latest version of hashicorp/local... - Finding hashicorp/random versions matching ">= 2.2.0"... - Finding hashicorp/kubernetes versions matching ">= 2.0.0, >= 2.10.0"... - Finding alekc/kubectl versions matching ">= 2.0.3"... - Finding hashicorp/cloudinit versions matching ">= 2.0.0"... - Finding hashicorp/time versions matching ">= 0.9.0"... - Finding hashicorp/aws versions matching ">= 3.63.0, >= 3.72.0, >= 4.0.0, ~> 4.0, >= 4.22.0, >= 4.30.0, >= 4.47.0, >= 4.64.0"... - Finding terraform-registry.platform.metaplay.dev/metaplay/hydra versions matching ">= 0.5.0"... - Finding hashicorp/helm versions matching ">= 2.9.0"... - Finding hashicorp/tls versions matching ">= 3.0.0"... - Installing hashicorp/local v2.4.1... - Installed hashicorp/local v2.4.1 (signed by HashiCorp) - Installing hashicorp/random v3.6.0... - Installed hashicorp/random v3.6.0 (signed by HashiCorp) - Installing hashicorp/kubernetes v2.26.0... - Installed hashicorp/kubernetes v2.26.0 (signed by HashiCorp) - Installing hashicorp/time v0.10.0... - Installed hashicorp/time v0.10.0 (signed by HashiCorp) - Installing hashicorp/tls v4.0.5... - Installed hashicorp/tls v4.0.5 (signed by HashiCorp) - Installing alekc/kubectl v2.0.4... - Installed alekc/kubectl v2.0.4 (self-signed, key ID 772FB27A86DAFCE7) - Installing hashicorp/cloudinit v2.3.3... - Installed hashicorp/cloudinit v2.3.3 (signed by HashiCorp) - Installing hashicorp/aws v4.67.0... - Installed hashicorp/aws v4.67.0 (signed by HashiCorp) - Installing terraform-registry.platform.metaplay.dev/metaplay/hydra v0.5.2... - Installed terraform-registry.platform.metaplay.dev/metaplay/hydra v0.5.2 (self-signed, key ID 866445064D71A5AE) - Installing hashicorp/helm v2.12.1... - Installed hashicorp/helm v2.12.1 (signed by HashiCorp) Partner and community providers are signed by their developers. If you'd like to know more about provider signing, you can read about it here: https://www.terraform.io/docs/cli/plugins/signing.html Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above. Include this file in your version control repository so that Terraform can guarantee to make the same selections by default when you run "terraform init" in the future. Terraform has been successfully initialized! You may now begin working with Terraform. Try running [terraform plan](https://developer.hashicorp.com/terraform/cli/commands/plan) to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary. ``` -------------------------------- ### GET /api/logTest Source: https://docs.metaplay.io/liveops-dashboard/how-to-guides/working-with-the-liveops-dashboard-http-api An example API endpoint that returns a simple JSON response and logs the request details. ```APIDOC ## GET /api/logTest ### Description This endpoint is an example of a new API endpoint that can be added to the game server. It logs the incoming request and returns a predefined JSON response. ### Method GET ### Endpoint /api/logTest ### Parameters #### Query Parameters None #### Request Body None ### Request Example None ### Response #### Success Response (200) - **DummyValue** (string) - A dummy string value returned by the API. #### Response Example ```json { "dummyValue": "Some Value" } ``` ``` -------------------------------- ### List All Rule Groups Source: https://docs.metaplay.io/cloud-deployments/advanced-topics/configuring-alertmanager-rules This example demonstrates how to retrieve a list of all existing rule groups within a Metaplay environment using a GET request to the stack API. ```APIDOC ## GET /stackapi/tenant/v1/{tenant_name}/observability/loki/loki/api/v1/rules/ ### Description Retrieves a list of all rule groups configured for the specified Metaplay tenant. ### Method GET ### Endpoint `https://{METAPLAY_STACK_DOMAIN}/stackapi/tenant/v1/{METAPLAY_TENANT_NAME}/observability/loki/loki/api/v1/rules/` ### Parameters #### Header Parameters - **Authorization** (string) - Required - Bearer token for authentication (`Bearer $METAPLAY_ACCESS_TOKEN`). ### Request Example ```bash # Choose an environment export METAPLAY_TENANT_NAME= # Obtain an OAuth2 access token export METAPLAY_ACCESS_TOKEN=$(metaplay auth show-tokens | jq -r .access_token) # Obtain the stack domain under which environment is provisioned export METAPLAY_STACK_DOMAIN=$(metaplay get environment-info $METAPLAY_TENANT_NAME --format json | jq -r .observability.loki_endpoint | cut -d "/" -f 3) curl -X GET \ -H "Authorization: Bearer $METAPLAY_ACCESS_TOKEN" \ https://$METAPLAY_STACK_DOMAIN/stackapi/tenant/v1/$METAPLAY_TENANT_NAME/observability/loki/loki/api/v1/rules/ ``` ### Response #### Success Response (200 OK) Returns a JSON array of rule group objects. #### Response Example ```json { "rule_groups": [ { "name": "example-rule-group-1", "rules": [ // ... individual rule definitions ... ] }, { "name": "example-rule-group-2", "rules": [ // ... individual rule definitions ... ] } ] } ``` ``` -------------------------------- ### Game Config Build Source Definition Example (C#) Source: https://docs.metaplay.io/feature-cookbooks/game-configs/deep-dive-custom-game-config-build-pipelines Provides an example of integrating with the LiveOps Dashboard for game config building. This class specifies available build sources, specifically a Google Sheet for development. ```csharp public class MyGameConfigBuildIntegration : GameConfigBuildIntegration { public override IEnumerable GetAvailableBuildSources(string sourceProperty) { if (sourceProperty == nameof(GameConfigBuildParameters.DefaultSource)) { return new[] { new GoogleSheetBuildSource("Development sheet", "1wXKv4SPD7BZBwTRc7YfAU7oVjuWFDZZFbHZ8PKyCatM"), }; } return base.GetAvailableBuildSources(sourceProperty); } } ``` -------------------------------- ### Initialize Terraform Project Dependencies Source: https://docs.metaplay.io/self-hosting/how-to-guides/deploying-cloud-infrastructure This command initializes a Terraform project by downloading the necessary provider plugins and modules. It first checks for the `infra.tf` file to ensure it's in the correct directory. ```bash $ ls infra.tf # ensure we are at the right directory $ terraform init # start initialization of Terraform dependencies ```