// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Standard Library
using System;
using System.Collections.Generic;
// Unity
#if UNITY_EDITOR
using UnityEditor;
#endif
// GameKit
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
{
///
/// This class handles the deployment of all GameKit features (i.e. create, re-deploy, delete).
///
public class FeatureDeploymentOrchestrator : IDisposable
{
private Threader _threader = new Threader();
private ICoreWrapperProvider _coreWrapper;
private bool _disposedValue = false;
private IDictionary _enterPlayModeRestrictions = new Dictionary();
public FeatureDeploymentOrchestrator(ICoreWrapperProvider coreWrapper, string baseTemplatesFolder, string instanceFilesFolder)
{
_coreWrapper = coreWrapper;
_coreWrapper.DeploymentOrchestratorInstanceCreate(baseTemplatesFolder, instanceFilesFolder);
#if UNITY_EDITOR
EditorApplication.playModeStateChanged += this.OnPlayModeStateChanged;
#endif
}
public FeatureDeploymentOrchestrator(string baseTemplatesFolder, string instanceFilesFolder) : this(CoreWrapper.Get(), baseTemplatesFolder, instanceFilesFolder) { }
~FeatureDeploymentOrchestrator() => Dispose();
public void Dispose()
{
if (!_disposedValue)
{
_coreWrapper.DeploymentOrchestratorInstanceRelease();
_disposedValue = true;
}
GC.SuppressFinalize(this);
}
public void Update()
{
_threader.Update();
}
///
/// Set account credentials.
///
///
/// This is a fast method. It does not invoke any networked procedures.
///
/// Object holding the AccountInfo and AccountCredentials.
///
/// The result status code of the operation (from GameKitErrors.cs):
/// - GAMEKIT_SUCCESS: The API call was successful.
/// - GAMEKIT_ERROR_ORCHESTRATION_DEPLOYMENT_IN_PROGRESS: Cannot change credentials while a deployment is in progress.
/// - GAMEKIT_ERROR_REGION_CODE_CONVERSION_FAILED: Could not retrieve the short region code from the mappings file. See the log for details on how to fix this.
///
public uint SetCredentials(SetCredentialsDesc setCredentialsDesc)
{
return _coreWrapper.DeploymentOrchestratorSetCredentials(setCredentialsDesc);
}
///
/// Get the status of a requested feature.
///
///
/// This is a fast method. It does not invoke any networked procedures.
///
/// The GameKit feature to work with.
/// FeatureStatus enum related to the status of the requested feature.
public FeatureStatus GetFeatureStatus(FeatureType feature)
{
return _coreWrapper.DeploymentOrchestratorGetFeatureStatus(feature);
}
///
/// Get an abridged status of a requested feature.
///
///
/// This is a fast method. It does not invoke any networked procedures.
///
/// The GameKit feature to work with.
/// FeatureStatusSummary enum related to the abridged status of the requested feature.
public FeatureStatusSummary GetFeatureStatusSummary(FeatureType feature)
{
return _coreWrapper.DeploymentOrchestratorGetFeatureStatusSummary(feature);
}
///
/// Check if the requested feature is currently being created, redeployed, or deleted.
///
///
/// This is a fast method. It does not invoke any networked procedures.
///
/// Unlike DeploymentOrchestratorIsFeatureUpdating, this method returns true while the Main stack is being deployed (before the feature itself is created or redeployed).
///
/// The GameKit feature to work with.
/// true if the requested feature is being created, redeployed, or deleted.
public bool IsFeatureDeploymentInProgress(FeatureType feature)
{
return _coreWrapper.DeploymentOrchestratorIsFeatureDeploymentInProgress(feature);
}
///
/// Check if the requested feature is currently updating (i.e. it's FeatureStatus is not Deployed, Undeployed, Error, or RollbackComplete).
///
///
/// This is a fast method. It does not invoke any networked procedures.
///
/// The GameKit feature to work with.
/// true if the requested feature is updating.
public bool IsFeatureUpdating(FeatureType feature)
{
return _coreWrapper.DeploymentOrchestratorIsFeatureUpdating(feature);
}
///
/// Check if any GameKit feature is currently updating (i.e. has a FeatureStatus other than Deployed, Undeployed, Error, or RollbackComplete).
///
///
/// This is a fast method. It does not invoke any networked procedures.
///
/// true if any feature is updating.
public bool IsAnyFeatureUpdating()
{
return _coreWrapper.DeploymentOrchestratorIsAnyFeatureUpdating();
}
///
/// Refresh the status of a requested feature.
///
///
/// This is a long running operation.
///
/// Result status codes returned in the callback function (from GameKitErrors.cs):
/// - GAMEKIT_SUCCESS: The API call was successful.
///
/// The GameKit feature to work with.
/// Delegate that is called once this method completes.
/// It contains the of all features and the result status code for the call.
///
public void RefreshFeatureStatus(FeatureType feature, Action callback)
{
_threader.Call(() => _coreWrapper.DeploymentOrchestratorRefreshFeatureStatus(feature), callback);
}
///
/// Refresh the status of all features.
///
///
/// This is a long running operation.
///
/// Result status codes returned in the callback function (from GameKitErrors.cs):
/// - GAMEKIT_SUCCESS: The API call was successful.
///
/// Delegate that is called once this method completes.
/// It contains the of all features and the result status code for the call.
///
public void RefreshFeatureStatuses(Action callback)
{
_threader.Call(() => _coreWrapper.DeploymentOrchestratorRefreshFeatureStatuses(), callback);
}
///
/// Request if a feature is in a state to be created.
///
///
/// This is a fast method. It does not invoke any networked procedures.
///
/// The GameKit feature to work with.
///
/// A object describing whether the feature can be created.
/// A and list of blocking are provided in cases where the feature cannot be created.
///
public CanExecuteDeploymentActionResult CanCreateFeature(FeatureType feature)
{
return _coreWrapper.DeploymentOrchestratorCanCreateFeature(feature);
}
///
/// Request if a feature can be re-deployed.
///
///
/// This is a fast method. It does not invoke any networked procedures.
///
/// The GameKit feature to work with.
///
/// A object describing whether the feature can be redeployed.
/// A and list of blocking are provided in cases where the feature cannot be redeployed.
///
public CanExecuteDeploymentActionResult CanRedeployFeature(FeatureType feature)
{
return _coreWrapper.DeploymentOrchestratorCanRedeployFeature(feature);
}
///
/// Request if a feature is in a state where it can be deleted.
///
///
/// This is a fast method. It does not invoke any networked procedures.
///
/// The GameKit feature to work with.
///
/// A object describing whether the feature can be deleted.
/// A and list of blocking are provided in cases where the feature cannot be deleted.
///
public CanExecuteDeploymentActionResult CanDeleteFeature(FeatureType feature)
{
return _coreWrapper.DeploymentOrchestratorCanDeleteFeature(feature);
}
///
/// Create a requested feature.
///
///
/// This is a long running operation.
///
/// Result status codes returned in the callback function (from GameKitErrors.cs):
/// - GAMEKIT_SUCCESS: The API call was successful.
/// - GAMEKIT_ERROR_ORCHESTRATION_INVALID_FEATURE_STATE: Cannot create feature as it or one of its dependencies are in an invalid state for deployment.
///
/// The GameKit feature to work with.
/// Delegate that is called once this method completes.
/// It contains the of all features and the result status code for the call.
///
public void CreateFeature(FeatureType feature, Action callback)
{
_threader.Call(() =>
{
Guid taskGuid = Guid.NewGuid();
lock (_enterPlayModeRestrictions) _enterPlayModeRestrictions.Add(taskGuid, $"\"{feature.GetDisplayName()}\" feature deployment is in progress.");
DeploymentResponseResult result = _coreWrapper.DeploymentOrchestratorCreateFeature(feature);
lock (_enterPlayModeRestrictions) _enterPlayModeRestrictions.Remove(taskGuid);
return result;
},
callback);
}
///
/// Re-deploy a requested feature.
///
///
/// This is a long running operation.
///
/// Result status codes returned in the callback function (from GameKitErrors.cs):
/// - GAMEKIT_SUCCESS: The API call was successful.
/// - GAMEKIT_ERROR_ORCHESTRATION_INVALID_FEATURE_STATE: Cannot redeploy feature as it or one of its dependencies are in an invalid state for deployment.
///
/// The GameKit feature to work with.
/// Delegate that is called once this method completes.
/// It contains the of all features and the result status code for the call.
///
public void RedeployFeature(FeatureType feature, Action callback)
{
_threader.Call(() =>
{
Guid taskGuid = Guid.NewGuid();
lock (_enterPlayModeRestrictions) _enterPlayModeRestrictions.Add(taskGuid, $"\"{feature.GetDisplayName()}\" feature redeployment is in progress.");
DeploymentResponseResult result = _coreWrapper.DeploymentOrchestratorRedeployFeature(feature);
lock (_enterPlayModeRestrictions) _enterPlayModeRestrictions.Remove(taskGuid);
return result;
},
callback);
}
///
/// Delete a requested feature.
///
///
/// This is a long running operation.
///
/// Result status codes returned in the callback function (from GameKitErrors.cs):
/// - GAMEKIT_SUCCESS: The API call was successful.
/// - GAMEKIT_ERROR_CLOUDFORMATION_STACK_DELETE_FAILED: Failed to delete the stack, check output log for exact reason.
/// - GAMEKIT_ERROR_ORCHESTRATION_INVALID_FEATURE_STATE: Cannot delete feature as it or one of its downstream dependencies are in an invalid state for deletion.
///
/// The GameKit feature to work with.
/// Delegate that is called once this method completes.
/// It contains the of all features and the result status code for the call.
public void DeleteFeature(FeatureType feature, Action callback)
{
_threader.Call(() =>
{
Guid taskGuid = Guid.NewGuid();
lock (_enterPlayModeRestrictions) _enterPlayModeRestrictions.Add(taskGuid, $"\"{feature.GetDisplayName()}\" feature deletion is in progress.");
DeploymentResponseResult result = _coreWrapper.DeploymentOrchestratorDeleteFeature(feature);
lock (_enterPlayModeRestrictions) _enterPlayModeRestrictions.Remove(taskGuid);
return result;
},
callback);
}
#if UNITY_EDITOR
private void OnPlayModeStateChanged(PlayModeStateChange stateChange)
{
// Don't allow transitioning from Edit mode to Play mode when a restrictive operation is running.
if (stateChange == PlayModeStateChange.ExitingEditMode)
{
lock (_enterPlayModeRestrictions)
{
if (_enterPlayModeRestrictions.Count == 0)
{
return;
}
EditorUtility.DisplayDialog("Change to Play Mode", "You can't switch to Play mode while AWS GameKit features are deploying or updating. See logs for more details.", "Ok");
foreach (var restrictionKvp in _enterPlayModeRestrictions)
{
Logging.Log(Logging.Level.WARNING, $"Cannot enter Play mode, {restrictionKvp.Value}");
}
// Revert to Edit mode
EditorApplication.isPlaying = false;
}
}
}
#endif
///
/// Gets the deployment status of each AWS resource within the specified feature.
///
///
/// This is a long running operation.
///
/// Result status codes returned in the callback function (from GameKitErrors.cs):
/// - GAMEKIT_SUCCESS: The API call was successful.
/// - GAMEKIT_ERROR_CLOUDFORMATION_DESCRIBE_RESOURCE_FAILED: If status of the resources could not be determined.
///
/// The GameKit feature to work with.
/// Delegate that is called once this method completes.
/// It contains the resource id, resource type, and resource status of each AWS resource within the specified feature.
/// Additionally it contains the result status code for the call.
public void DescribeFeatureResources(FeatureType feature, Action callback)
{
_threader.Call(() => _coreWrapper.DeploymentOrchestratorDescribeFeatureResources(feature), callback);
}
}
}