Skip to content
13 changes: 6 additions & 7 deletions AGXUnity/LicenseInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEditor;
using Math = System.Math;

namespace AGXUnity
Expand Down Expand Up @@ -89,10 +88,8 @@ public static LicenseInfo Create()
{
var info = new LicenseInfo();

#if UNITY_EDITOR
if ( AssetDatabase.IsAssetImportWorkerProcess() )
if ( !LicenseManager.CanAccessRuntime )
return info;
#endif

try {
info.Version = agx.agxSWIG.agxGetVersion( false );
Expand Down Expand Up @@ -130,9 +127,10 @@ public static LicenseInfo Create()
if ( string.IsNullOrEmpty( info.TypeDescription ) )
info.TypeDescription = "Unknown";

if ( agx.Runtime.instance().hasKey( "InstallationID" ) ) {
if ( info.IsFloating || agx.Runtime.instance().hasKey( "InstallationID" ) ) {
info.Type = LicenseType.Service;
info.UniqueId = agx.Runtime.instance().readValue( "InstallationID" );
if ( agx.Runtime.instance().hasKey( "InstallationID" ) )
info.UniqueId = agx.Runtime.instance().readValue( "InstallationID" );
}
else if ( agx.Runtime.instance().hasKey( "License" ) ) {
info.Type = LicenseType.Legacy;
Expand All @@ -158,7 +156,8 @@ public static LicenseInfo FromNative( agx.LicenseInfo native )
info.IsValid = native.licenseType != -1;
LicenseInfo.ParseDate( ref info, native.endDate );

info.Type = LicenseInfo.LicenseType.Service;
info.Type = info.IsValid ? LicenseType.Service : LicenseType.Unknown;
info.TypeDescription = "Unknown";
var subscriptionType = native.product.Split("-");
if ( subscriptionType.Length == 2 )
info.TypeDescription = subscriptionType[ 1 ].Trim();
Expand Down
835 changes: 602 additions & 233 deletions AGXUnity/LicenseManager.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -405,8 +405,7 @@ private static void VerifyBuild( BuildTarget target, string targetDataPath )

private static void CheckGenerateEncryptedRuntime( FileInfo targetExecutableFileInfo )
{
if ( AGXUnity.LicenseManager.LicenseInfo.Type == AGXUnity.LicenseInfo.LicenseType.Service &&
!Application.isBatchMode &&
if ( !Application.isBatchMode &&
AskGenerateRuntimeLicense ) {
var generateEncryptedRuntimeActivation = EditorUtility.DisplayDialogComplex( "AGX Dynamics for Unity - Runtime license",
"Open activation window to generate Runtime license for this build?",
Expand Down
95 changes: 95 additions & 0 deletions Editor/AGXUnityEditor/LicenseWarnings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using AGXUnity;
using System;
using System.Linq;
using UnityEditor;
using UnityEngine;

namespace AGXUnityEditor
{
// Keep the result of loading separate from file queries, which may change
// the native status without changing the currently loaded license.
internal static class LicenseWarnings
{
internal static LicenseInfo CurrentLicense { get; private set; }

internal static void Capture( LicenseInfo info )
{
CurrentLicense = info;
}

internal static bool IsFloating( LicenseInfo info )
{
return info.IsFloating ||
( info.Status?.IndexOf( "floating", StringComparison.OrdinalIgnoreCase ) ?? -1 ) >= 0;
}

internal static string GetWarningMessage( LicenseInfo info, bool hasFloatingLicense = false )
{
if ( info.IsValid || !LicenseManager.AutomaticFloatingCheckoutEnabled )
return string.Empty;

var message = "No valid AGX Dynamics license is available.";
if ( !string.IsNullOrWhiteSpace( info.Status ) )
message += "\n\n" + info.Status.Trim();

return message + "\n\n" + ( IsFloating( info ) || hasFloatingLicense ?
"Check the connection to your floating license server and that a license seat is available." :
"Activate a license or import an existing license file in the License Manager." );
}

internal static string GetActivationError( LicenseInfo info )
{
// A failed request can leave a previously valid license loaded. Its
// status must not be presented as the reason activation failed.
return "License activation failed.\n\n" +
( !info.IsValid && !string.IsNullOrWhiteSpace( info.Status ) ?
info.Status.Trim() :
"Verify your license ID, activation password, and connection before retrying." );
}

internal static void ScheduleStartupWarning()
{
if ( Application.isBatchMode || CurrentLicense.IsValid || !LicenseManager.AutomaticFloatingCheckoutEnabled ||
SessionState.GetBool( s_startupWarningShownKey, false ) )
return;

EditorApplication.update -= ShowStartupWarning;
EditorApplication.update += ShowStartupWarning;
}

private static void ShowStartupWarning()
{
if ( Application.isBatchMode || !LicenseManager.AutomaticFloatingCheckoutEnabled ||
SessionState.GetBool( s_startupWarningShownKey, false ) ) {
EditorApplication.update -= ShowStartupWarning;
return;
}

if ( EditorApplication.isCompiling || EditorApplication.isUpdating ||
EditorApplication.isPlayingOrWillChangePlaymode || LicenseManager.IsBusy ||
Resources.FindObjectsOfTypeAll<Windows.LicenseManagerWindow>()
.Any( window => window.IsUpdatingLicenseInformation ) )
return;

EditorApplication.update -= ShowStartupWarning;
if ( !NativeHandler.HasInstance || !NativeHandler.Instance.Initialized )
return;

// Observe validity only: checking a warning must never acquire or return
// a floating seat. Retain the captured failure status if still invalid.
if ( agx.Runtime.instance().isValid() ) {
Capture( LicenseInfo.Create() );
return;
}

SessionState.SetBool( s_startupWarningShownKey, true );
if ( EditorUtility.DisplayDialog( "AGX Dynamics license",
GetWarningMessage( CurrentLicense ),
"Open License Manager",
"Dismiss" ) )
Windows.LicenseManagerWindow.Open();
}

private const string s_startupWarningShownKey = "AGXUnity.LicenseWarningShown";
}
}
11 changes: 11 additions & 0 deletions Editor/AGXUnityEditor/LicenseWarnings.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 10 additions & 3 deletions Editor/AGXUnityEditor/Manager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -788,12 +788,19 @@ internal static EnvironmentState ConfigureEnvironment()
return EnvironmentState.Uninitialized;
}

// This validate is only for "license status" window so
// the user will be noticed when something is wrong.
// Complete license loading (including floating checkout) before deciding
// whether to notify the user. Native initialization failures are separate.
try {
AGXUnity.LicenseManager.LoadFile();
// Native initialization may find a license in the AGX installation.
// Prefer a floating file in this project unless the seat was returned.
AGXUnity.LicenseManager.LoadFile( allowFloating: AGXUnity.LicenseManager.AutomaticFloatingCheckoutEnabled,
preferFloating: true );

AGXUnity.NativeHandler.Instance.ValidateLicense();
if ( AGXUnity.NativeHandler.Instance.Initialized ) {
LicenseWarnings.Capture( AGXUnity.LicenseInfo.Create() );
LicenseWarnings.ScheduleStartupWarning();
}
}
catch ( Exception ) {
return EnvironmentState.Uninitialized;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using AGXUnity.Utils;
using AGXUnity.Utils;
using System.IO;
using System.Linq;
using UnityEditor;
Expand Down Expand Up @@ -26,15 +26,15 @@ public string BuildDirectory
set
{
if ( string.IsNullOrEmpty( value ) ) {
Debug.LogWarning( "Invalid build directory - directory is null or empty." );
m_generationError = "Invalid build directory - directory is null or empty.";
return;
}

if ( !Path.IsPathRooted( value ) )
value = $"{Directory.GetCurrentDirectory()}/{value}";

if ( !Directory.Exists( value ) ) {
Debug.LogWarning( $"Invalid build directory - directory \"{value}\" doesn't exist." );
m_generationError = $"Invalid build directory - directory \"{value}\" doesn't exist.";
return;
}

Expand All @@ -55,8 +55,8 @@ public string ReferenceFileInBuild
if ( tmp.StartsWith( BuildDirectory ) )
tmp = tmp.MakeRelative( BuildDirectory, false );
else if ( Path.IsPathRooted( tmp ) ) {
Debug.LogWarning( $"Reference file \"{tmp}\" doesn't seems to be in the build directory \"{BuildDirectory}\". " +
"Make sure the build directory is correct before reference file is set." );
m_generationError = $"Reference file \"{tmp}\" is not in the build directory \"{BuildDirectory}\". " +
"Check the build directory before selecting the reference file.";
return;
}

Expand All @@ -77,6 +77,8 @@ public void Initialize( string buildDirectory, string referenceFile )

private void OnGUI()
{
using var feedback = AGXUnity.LicenseManager.SuppressConsoleLogging();
m_scroll = EditorGUILayout.BeginScrollView( m_scroll );
using ( GUI.AlignBlock.Center )
GUILayout.Box( IconManager.GetAGXUnityLogo(),
GUI.Skin.customStyles[ 3 ],
Expand Down Expand Up @@ -104,7 +106,7 @@ private void OnGUI()
"Build directory",
newBuildDirectory => {
if ( !Directory.Exists( newBuildDirectory ) ) {
Debug.LogWarning( $"Ignoring given build path {newBuildDirectory} doesn't exist." );
m_generationError = $"The build directory does not exist: {newBuildDirectory}";
return;
}
BuildDirectory = newBuildDirectory;
Expand All @@ -121,15 +123,19 @@ private void OnGUI()
m_idPassword.Id = EditorGUILayout.TextField( GUI.MakeLabel( "Runtime License Id" ),
m_idPassword.Id,
InspectorEditor.Skin.TextField );
if ( m_idPassword.Id.Any( c => !char.IsDigit( c ) ) )
m_idPassword.Id = new string( m_idPassword.Id.Where( c => char.IsDigit( c ) ).ToArray() );
m_idPassword.Password = EditorGUILayout.PasswordField( GUI.MakeLabel( "Runtime Activation Code" ),
m_idPassword.Password );

var generateToolTip = string.Empty;
if ( !string.IsNullOrEmpty( m_idPassword.Id ) &&
( !int.TryParse( m_idPassword.Id, out var enteredId ) || enteredId <= 0 ) )
EditorGUILayout.HelpBox( "Enter a license ID between 1 and 2147483647.", MessageType.Error, true );
if ( !string.IsNullOrEmpty( m_generationError ) )
EditorGUILayout.HelpBox( m_generationError, MessageType.Error, true );
using ( new GUI.EnabledBlock( ValidateGenerate( ref generateToolTip ) ) ) {
GUILayout.Space( 3 );
if ( GUILayout.Button( GUI.MakeLabel( "Generate", false, generateToolTip ) ) ) {
m_generationError = null;
var generatedFilename = string.Empty;
if ( AGXUnity.LicenseManager.GenerateEncryptedRuntime( System.Convert.ToInt32( m_idPassword.Id ),
m_idPassword.Password,
Expand All @@ -141,18 +147,24 @@ private void OnGUI()
"Ok" );
Close();
}
else
m_generationError = AGXUnity.LicenseManager.LastOperationError ?? "Runtime activation file generation failed. Check the license details and that the build directory is writable.";
}
}
EditorGUILayout.EndScrollView();
}

private bool ValidateGenerate( ref string toolTip )
{
var idGiven = !string.IsNullOrEmpty( m_idPassword.Id ) && m_idPassword.Id.Length > 1;
var passwordGiven = !string.IsNullOrEmpty( m_idPassword.Password ) && m_idPassword.Password.Length > 1;
var idGiven = int.TryParse( m_idPassword.Id, out var id ) && id > 0;
var passwordGiven = !string.IsNullOrWhiteSpace( m_idPassword.Password );
var directoryValid = Directory.Exists( BuildDirectory );
var fileExist = File.Exists( ReferenceFileInBuildFull );
var canGenerate = !AGXUnity.LicenseManager.IsBusy && !EditorApplication.isPlayingOrWillChangePlaymode;
if ( !canGenerate )
toolTip += "Wait for license operations to finish and exit play mode before generating.\n";
if ( !idGiven )
toolTip += "Missing license id.\n";
toolTip += "Enter a license ID between 1 and 2147483647.\n";
if ( !passwordGiven )
toolTip += "Missing license password.\n";
if ( !directoryValid )
Expand All @@ -165,7 +177,7 @@ private bool ValidateGenerate( ref string toolTip )
else if ( string.IsNullOrEmpty( toolTip ) )
toolTip = "Generate encrypted runtime license.";

return idGiven &&
return canGenerate && idGiven &&
passwordGiven &&
directoryValid &&
fileExist;
Expand All @@ -174,5 +186,7 @@ private bool ValidateGenerate( ref string toolTip )
private LicenseManagerWindow.IdPassword m_idPassword = LicenseManagerWindow.IdPassword.Empty();
private string m_buildDirectory = string.Empty;
private string m_referenceFileInBuild = string.Empty;
private string m_generationError;
private Vector2 m_scroll;
}
}
Loading
Loading