// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // Standard Library using System; using System.Collections.Generic; using System.IO; using System.Threading; // Unity using UnityEditor; // GameKit using AWS.GameKit.Common; using AWS.GameKit.Common.Models; using AWS.GameKit.Editor.Models; using AWS.GameKit.Runtime.Core; using AWS.GameKit.Runtime.Models; using AWS.GameKit.Runtime.Utils; namespace AWS.GameKit.Editor.Core { public class FeatureResourceManager : IDisposable { private static int _threadSafeCounter = 0; /// /// Provider for all needed GameKit paths. /// public IGameKitPathsProvider Paths; private AccountInfo _accountInfo = new AccountInfo() { AccountId = string.Empty, CompanyName = string.Empty, Environment = string.Empty, GameName = string.Empty }; private AccountCredentials _accountCredentials = new AccountCredentials(); private ICoreWrapperProvider _coreWrapper; private Threader _threader; private bool _disposedValue = false; public FeatureResourceManager(ICoreWrapperProvider coreWrapper, IGameKitPathsProvider gameKitPathsProvider, Threader threader) { _coreWrapper = coreWrapper; Paths = gameKitPathsProvider; _accountInfo.Environment = Constants.EnvironmentCodes.DEVELOPMENT; _threader = threader; } ~FeatureResourceManager() { Dispose(); _coreWrapper.AccountInstanceRelease(); } public void Dispose() { _threader.WaitForThreadedWork(); if (!_disposedValue) { _coreWrapper.SettingsInstanceRelease(); _disposedValue = true; } GC.SuppressFinalize(this); } /// /// Sets Game name and reinitializes settings. /// /// Name of the game. public void SetGameName(string gameName) { _accountInfo.GameName = gameName; InitializeSettings(true); } /// /// Sets environment code and reinitializes settings. /// /// Environment code in use. public void SetEnvironment(string environment) { if (!string.IsNullOrEmpty(_accountInfo.GameName)) { _accountInfo.Environment = environment; InitializeSettings(true); } } /// /// Get the last used deployment environment or returns dev on failure. /// /// Deployment environment code. public string GetLastUsedEnvironment() { return _coreWrapper.SettingsGetLastUsedEnvironment(); } /// /// Get the last used deployment region or returns us-west-2 on failure. /// /// Deployment region. public string GetLastUsedRegion() { return _coreWrapper.SettingsGetLastUsedRegion(); } /// /// Populates accountInfo and accountCredentials. /// /// Account Details. /// Deployment environment. public void SetAccountDetails(AccountDetails accountDetails) { _accountInfo = accountDetails.CreateAccountInfo(); _accountCredentials = accountDetails.CreateAccountCredentials(); InitializeSettings(true); } /// /// Gets the current account info variable stored in FeatureResourceManager. /// /// Account info that is currently stored in FeatureResourceManager. public AccountInfo GetAccountInfo() { return _accountInfo; } /// /// Gets the current account credentials variable stored in FeatureResourceManager. /// /// Account credentials that is currently stored in FeatureResourceManager. public AccountCredentials GetAccountCredentials() { return _accountCredentials; } /// /// Bootstrap account by creating appropriate S3 buckets. /// /// AWSGameKit result. /// GAMEKIT_SUCCESS : Bootstrap Account was successful. /// public uint BootstrapAccount() { _coreWrapper.AccountInstanceRelease(); _coreWrapper.AccountInstanceCreateWithRootPaths(_accountInfo, _accountCredentials, Paths.ASSETS_INSTANCE_FILES_FULL_PATH, Paths.PACKAGES_BASE_TEMPLATES_FULL_PATH, Logging.LogCb); uint result = _coreWrapper.AccountInstanceBootstrap(); if (result != GameKitErrors.GAMEKIT_SUCCESS) { Logging.LogError(L10n.Tr($"Error: FeatureResourceManager::BootstrapAccount() Failed to create bucket. Error Code: { result }")); } return result; } /// /// Checks if the user's current account info is valid, does not permanently set accountDetails as that should only be done on submit. /// /// Account Details of the account we are checking for validity. /// public bool IsAccountInfoValid(AccountDetails accountDetails) { _coreWrapper.AccountInstanceRelease(); _coreWrapper.AccountInstanceCreateWithRootPaths(accountDetails.CreateAccountInfo(), accountDetails.CreateAccountCredentials(), Paths.ASSETS_INSTANCE_FILES_FULL_PATH, Paths.PACKAGES_BASE_TEMPLATES_FULL_PATH, Logging.LogCb); bool isAccountValid = _coreWrapper.AccountHasValidCredentials(); return isAccountValid; } /// /// Creates or Reinitializes settings instance handle. /// /// It determines if the settings instance should be reinitialized or not. public void InitializeSettings(bool reinitialize) { if (reinitialize) { _coreWrapper.SettingsInstanceRelease(); } _coreWrapper.SettingsInstanceCreate(Paths.ASSETS_INSTANCE_FILES_FULL_PATH, GetPluginVersion(), _accountInfo.GameName, _accountInfo.Environment, Logging.LogCb); } /// /// Gets AWS GameKit plugin version. /// /// Plugin version. private string GetPluginVersion() { return "1.1"; } /// /// Add a custom deployment environment to the AWS GameKit settings menu. /// /// /// This custom environment will be available to select from the dropdown menu in the Environment and Credentials section of the settings menu, /// alongside the default environments of "Development", "QA", "Staging", and "Production". /// /// Two to Three letter code for the environment name. This code will be prefixed on all AWS resources that are /// deployed to this environment.Ex: "gam" for "Gamma". /// envDescription The environment name that will be displayed in the Environment and Credentials section of the settings menu. Ex: "Gamma". public void SaveCustomEnvironment(string envCode, string envDescription) { _coreWrapper.SettingsAddCustomEnvironment(envCode, envDescription); uint result = _coreWrapper.SettingsSave(); if (result != GameKitErrors.GAMEKIT_SUCCESS) { Logging.LogError(L10n.Tr($"Error: FeatureResourceManager.SaveCustomEnvironment() Failed to save : { GameKitErrors.ToString(result) }")); } } /// /// Get all of the feature's variables as key-value pairs. /// /// The feature to get the variables for. /// A result object containing a map of variable key value pairs. public Dictionary GetFeatureVariables(FeatureType featureType) { return _coreWrapper.SettingsGetFeatureVariables(featureType); } /// /// Get the value of the specified feature variable. /// /// The feature to get the variable for. /// The key for the variable. /// When this method returns, contains the value of the feature variable, if the key is found; otherwise, an empty string. This parameter is passed in uninitialized. /// true if the feature variable exists; otherwise, false. public bool TryGetFeatureVariable(FeatureType featureType, string varName, out string varValue) { Dictionary featureVars = GetFeatureVariables(featureType); return featureVars.TryGetValue(varName, out varValue); } /// /// Write a variable for a setting to the saveInfo.yml file if that variable is not already present. /// /// This method will NOT overwrite an existing variable if present. /// The feature that is using the variable pair. /// key for the variable. /// value for the variable. /// callback that is executed once this method is complete, it is executed even if the new variables are saved. public void SetFeatureVariableIfUnset(FeatureType featureType, string varName, string varValue, Action callback) { Dictionary featureVars = GetFeatureVariables(featureType); if (!featureVars.ContainsKey(varName)) { SetFeatureVariable(featureType, varName, varValue, callback); return; } // call the callback to signify the completion of this method and return an empty job handle callback(); } /// /// Get AWS Account Id using the access and secret key. /// /// Structure containing both the AWS Access Key and the AWS Secret Key. /// Callback that is executed once this method is complete, returning a string. /// AWSGameKit Account Id. public void GetAccountId(GetAWSAccountIdDescription accountCredentials, Action callback) { _threader.Call(_coreWrapper.GetAWSAccountId, accountCredentials, callback); } /// /// Write a variable for a setting to the saveInfo.yml file. /// /// This method will overwrite an existing variable if present. /// The feature that is using the variable pair. /// key for the variable. /// value for the variable. /// callback that is executed once this method is complete. public void SetFeatureVariable(FeatureType featureType, string varName, string varValue, Action callback) { _coreWrapper.SettingsSetFeatureVariables(featureType, new string[] { varName }, new string[] { varValue }, 1); // Debounce our writes so we don't thresh on IO int currentValue = Interlocked.Increment(ref _threadSafeCounter); _threader.Call(() => { Thread.Sleep(3000); if (currentValue == Volatile.Read(ref _threadSafeCounter)) { _coreWrapper.SettingsSave(); Interlocked.Exchange(ref _threadSafeCounter, 0); } }, callback); } /// /// Write several variable at once to the saveInfo.yml file. /// /// This method will overwrite an existing variable if present. /// IEnumerable of variables in the form Tuple where Item1 is a FeatureType, Item2 is the variable name and Item3 is the variable value. /// callback that is executed once this method is complete. public void SetFeatureVariables(IEnumerable> variables, Action callback) { foreach (var variableTuple in variables) { _coreWrapper.SettingsSetFeatureVariables(variableTuple.Item1, new string[] { variableTuple.Item2 }, new string[] { variableTuple.Item3 }, 1); } _coreWrapper.SettingsSave(); callback(); } /// /// Get all the custom environment key-value pairs (ex: "gam", "Gamma").

/// /// The custom environments are returned through the callback and receiver.
/// The callback is invoked once for each custom environment.
/// The returned keys are 3-letter environment codes(ex: "gam"), and the values are corresponding environment descriptions(ex: "Gamma"). ///
/// A result object containing a map of environment key value pairs. public Dictionary GetCustomEnvironments() { string settingsFile = Path.Combine(Paths.ASSETS_INSTANCE_FILES_FULL_PATH, _accountInfo.GameName, Paths.SAVE_INFO_FILE_NAME); FileInfo fileInfo = new FileInfo(settingsFile); if (fileInfo == null || fileInfo.Exists == false) { return new Dictionary(); } return _coreWrapper.SettingsGetCustomEnvironments(); } /// /// Get the game's full name, example: "Stevie goes to the moon". /// /// A string containing the name of the game. public string GetGameName() { return _accountInfo.GameName.Length > 0 ? _accountInfo.GameName : _coreWrapper.SettingsGetGameName(); } /// /// Set the Game's name, environment, and region, then save settings. /// /// /// Use this method to create the settings file directory, set the game's name, set the games environment, set the games region, and then persist the settings. /// /// The result code of the operation. GAMEKIT_SUCCESS if successful, else a non-zero value in case of error. Consult errors.h file for details. public uint SaveSettings() { uint result = _coreWrapper.SettingsPopulateAndSave(_accountInfo.GameName, _accountInfo.Environment, _accountCredentials.Region); if (result != GameKitErrors.GAMEKIT_SUCCESS) { Logging.LogError(L10n.Tr($"Error: FeatureResourceManager.SaveSettings() Failed to save : { GameKitErrors.ToString(result) }")); } return result; } /// /// See ICoreWrapperProvider.ResourcesCreateEmptyConfigFile() for details. /// public uint CreateEmptyClientConfigFile() { _coreWrapper.ResourcesInstanceCreateWithRootPaths(_accountInfo, _accountCredentials, FeatureType.Main, Paths.ASSETS_INSTANCE_FILES_FULL_PATH, Paths.ASSETS_FULL_PATH, Logging.LogCb); uint result = _coreWrapper.ResourcesCreateEmptyConfigFile(); _coreWrapper.ResourcesInstanceRelease(); return result; } } }