// 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;
}
}
}