Skip to content

Commit

Permalink
Merge pull request #166 from Algoryx/feature/npm-support
Browse files Browse the repository at this point in the history
Add support for installing example dependencies from NPMJS
  • Loading branch information
FilipAlg authored Oct 1, 2024
2 parents b0fd43b + 3c6b479 commit 7832000
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 18 deletions.
2 changes: 2 additions & 0 deletions Editor/AGXUnityEditor/AssetPostprocessorHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ private void UpdatePrefab()

private void OnPostprocessPrefab( GameObject gameObject )
{
if ( context.assetPath.StartsWith( "Packages/" ) )
return;
// Since AGX materials are stored in the scene (in memory) these material instances cannot be added
// to prefabs. This causes the prefabs to have 'None' materials.
// One solution to this is to add the material as a subasset to the prefab.
Expand Down
11 changes: 7 additions & 4 deletions Editor/AGXUnityEditor/IO/AGXFileInfo.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
using System;
using System.IO;
using System.Linq;
using UnityEngine;
using UnityEditor;
using UnityEngine;

namespace AGXUnityEditor.IO
{
Expand Down Expand Up @@ -81,7 +80,7 @@ public FileType FindType( FileInfo info )
FileType.AGXPrefab :
FileType.Unknown;
}

/// <summary>
/// True if valid path was given.
/// </summary>
Expand Down Expand Up @@ -250,7 +249,11 @@ private void Construct( string path )
}
}

RootDirectory = Utils.MakeRelative( m_fileInfo.Directory.FullName, Application.dataPath ).Replace( '\\', '/' );
if ( path.StartsWith( "Packages/" ) )
RootDirectory = Path.GetDirectoryName( path ).Replace( '\\', '/' );
else
RootDirectory = Utils.MakeRelative( m_fileInfo.Directory.FullName, Application.dataPath ).Replace( '\\', '/' );

// If the file is located in the root Assets folder the relative directory
// is the empty string and Unity requires the relative path to include "Assets".
if ( RootDirectory == string.Empty )
Expand Down
5 changes: 3 additions & 2 deletions Editor/AGXUnityEditor/PackageManifest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ public class PackageManifest

private static PackageManifest s_instance = null;

public static string Raw => File.ReadAllText(s_manifestPath);

public static PackageManifest Instance
{
get
{
if ( s_instance == null ) {
var manifestRaw = File.ReadAllText(s_manifestPath);
try {
s_instance = JsonUtility.FromJson<PackageManifest>( manifestRaw );
s_instance = JsonUtility.FromJson<PackageManifest>( Raw );
}
catch {
Debug.LogError( "Failed to load package manifest. AGXUnity installation might be corrupt." );
Expand Down
55 changes: 55 additions & 0 deletions Editor/AGXUnityEditor/Utils/ScopedRegistryManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System.Linq;
using System.Reflection;
using UnityEditor.PackageManager;
using UnityEditor.PackageManager.Requests;

public static class ScopedRegistryManager
{
private static Request<RegistryInfo[]> s_request = null;
public static void RequestRegistryListRefresh()
{
var getMethod = typeof( Client ).GetMethod( "GetRegistries", BindingFlags.Static | BindingFlags.NonPublic, null, new System.Type[]{}, null );
s_request = (Request<RegistryInfo[]>)getMethod.Invoke( null, null );
}

public static RegistryInfo[] GetRegistryInfos()
{
if ( s_request == null )
RequestRegistryListRefresh();

var resultAccessor = typeof(Request<RegistryInfo[]>).GetProperty("Result",BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty);
return (RegistryInfo[])resultAccessor.GetValue( s_request );
}

public static void AddOrUpdateScopedRegistry( string name, string url, string[] scopes )
{
var assembly = typeof(UnityEditor.AssetDatabase).Assembly;
var type = assembly.GetType( "UnityEditor.PackageManager.UI.Internal.ServicesContainer" );
var instanceAccessor = type.GetProperty( "instance", BindingFlags.Public | BindingFlags.Static | BindingFlags.GetProperty | BindingFlags.FlattenHierarchy );
var container = instanceAccessor.GetValue(null);

var clientType = assembly.GetType("UnityEditor.PackageManager.UI.Internal.UpmRegistryClient");

var client = container.GetType().GetMethod( "Resolve", BindingFlags.Public | BindingFlags.Instance ).MakeGenericMethod( new System.Type[] { clientType } ).Invoke( container, new object[] {} );

var infos = GetRegistryInfos();
if(infos.Any(info => info.name == name ) ) {
var updateMethod = clientType.GetMethod(
"UpdateRegistry",
BindingFlags.Instance | BindingFlags.Public,
null,
new System.Type[] { typeof( string ), typeof( string ), typeof( string ), typeof( string[] ) },
null );
updateMethod.Invoke( client, new object[] { (object)name, (object)name, (object)url, (object)scopes } );
} else {
var addMethod = clientType.GetMethod(
"AddRegistry",
BindingFlags.Instance | BindingFlags.Public,
null,
new System.Type[] { typeof( string ), typeof( string ), typeof( string[] ) },
null );
addMethod.Invoke( client, new object[] { (object)name, (object)url, (object)scopes } );
}
RequestRegistryListRefresh();
}
}
11 changes: 11 additions & 0 deletions Editor/AGXUnityEditor/Utils/ScopedRegistryManager.cs.meta

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

31 changes: 30 additions & 1 deletion Editor/AGXUnityEditor/Windows/ExamplesManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,22 @@ public static bool LegacyInputManagerEnabled
}
}

private static bool HasAGXUnityScope( RegistryInfo reg )
{
var scopesAccessor = typeof(RegistryInfo).GetProperty("scopes", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetProperty);
var scopes = (string[])scopesAccessor.GetValue(reg);
return scopes.Contains( "com.algoryx.agxunity.machines" );
}

public static bool AGXScopedRegistryAdded
{
get
{
var infos = ScopedRegistryManager.GetRegistryInfos();
return infos != null && infos.Any( HasAGXUnityScope );
}
}

/// <summary>
/// Scraps old data and fetch new information about examples
/// and dependencies.
Expand Down Expand Up @@ -364,6 +380,11 @@ public static void Uninitialize()
s_dependencyData.Clear();
}

public static void AddAGXUnityScopedRegistry()
{
ScopedRegistryManager.AddOrUpdateScopedRegistry( "AGXUnity Registry", "https://registry.npmjs.org", new string[] { "com.algoryx.agxunity.machines" } );
}

/// <summary>
/// Resolve input settings to work with all examples. If any setting
/// has been changed, the user will be asked to restart the editor.
Expand Down Expand Up @@ -492,6 +513,7 @@ public static bool HasUnresolvedIssues( ExampleData example )
{
return HasUnresolvedDependencies( example ) ||
example == null ||
( RequiresAGXRegistry(example) && !AGXScopedRegistryAdded ) ||
( example.RequiresLegacyInputManager && !LegacyInputManagerEnabled ) ||
( example.Dependencies.Contains( "com.unity.inputsystem" ) && !InputSystemEnabled );
}
Expand All @@ -509,6 +531,13 @@ public static bool HasUnresolvedDependencies( ExampleData example )
example.Dependencies.Any( dependency => GetDependencyState( dependency ) != DependencyState.Installed );
}

public static bool RequiresAGXRegistry( ExampleData example )
{
return example != null &&
example.Dependencies.Length > 0 &&
example.Dependencies.Any( dependency => dependency.StartsWith("com.algoryx.agxunity.machines") );
}

/// <summary>
/// Install the given dependency. Does nothing of the dependency
/// is already available.
Expand Down Expand Up @@ -841,7 +870,7 @@ private static void DeleteTemporaryPackage( ExampleData data )

public static DependencyState GetDependencyState( string packageName )
{
return s_dependencyData.ContainsKey(packageName) ? s_dependencyData[packageName] : DependencyState.Unknown;
return s_dependencyData.ContainsKey( packageName ) ? s_dependencyData[ packageName ] : DependencyState.Unknown;
}

private static string s_metadataURL = @"https://us.download.algoryx.se/AGXUnity/examples/current/ExampleMetadata.json";
Expand Down
31 changes: 20 additions & 11 deletions Editor/AGXUnityEditor/Windows/ExamplesWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ public static ExamplesWindow Open()

private void OnEnable()
{
ScopedRegistryManager.RequestRegistryListRefresh();
var thumbnailDirectory = FindThumbnailDirectory();
ExamplesManager.Initialize();
EditorApplication.update += OnUpdate;
EditorApplication.update += OnUpdate;
}

private void OnDisable()
Expand All @@ -34,7 +35,7 @@ private void OnDisable()

private void OnGUI()
{
minSize = new Vector2(400, 0);
minSize = new Vector2( 400, 0 );
if ( m_exampleNameStyle == null ) {
m_exampleNameStyle = new GUIStyle( InspectorEditor.Skin.Label );
m_exampleNameStyle.alignment = TextAnchor.MiddleLeft;
Expand Down Expand Up @@ -76,11 +77,11 @@ private void OnGUI()

using ( new EditorGUILayout.HorizontalScope() ) {
var boxStyle = new GUIStyle();
boxStyle.margin = new RectOffset(5,5,0,0 );
boxStyle.margin = new RectOffset( 5, 5, 0, 0 );
GUILayout.Box( data.Thumbnail,
boxStyle,
boxStyle,
GUILayout.Width( ExampleRowSize ),
GUILayout.Height( ExampleRowSize ));
GUILayout.Height( ExampleRowSize ) );
var exampleNameLabel = GUI.MakeLabel( $"{data.Name}", true );
if ( data != null ) {
if ( Link( exampleNameLabel, GUILayout.Height( ExampleRowSize ) ) )
Expand All @@ -90,7 +91,7 @@ private void OnGUI()
GUILayout.Label( exampleNameLabel,
m_exampleNameStyle,
GUILayout.Height( ExampleRowSize ) );

GUILayout.FlexibleSpace();

var hasUnresolvedIssues = ExamplesManager.HasUnresolvedIssues( data );
Expand Down Expand Up @@ -126,6 +127,7 @@ private void OnGUI()
if ( hasUnresolvedIssues ) {
var dependencyContextButtonWidth = (float)ButtonSize;

var requiresAGXRegistryAdd = ( ExamplesManager.RequiresAGXRegistry( data ) && !ExamplesManager.AGXScopedRegistryAdded );
var hasUnresolvedDependencies = ExamplesManager.HasUnresolvedDependencies( data );
var hasUnresolvedInputSettings = ( data.RequiresLegacyInputManager &&
!ExamplesManager.LegacyInputManagerEnabled ) ||
Expand All @@ -145,12 +147,19 @@ private void OnGUI()
GUILayout.Width(dependencyContextButtonWidth));
if ( contextButton ) {
var dependenciesMenu = new GenericMenu();
if ( hasUnresolvedDependencies ) {
if ( requiresAGXRegistryAdd ) {
dependenciesMenu.AddDisabledItem( GUI.MakeLabel( "Add AGXUnity Scoped registry..." ) );
dependenciesMenu.AddSeparator( string.Empty );
dependenciesMenu.AddItem( GUI.MakeLabel( "Add AGXUnity Scoped registry" ),
false,
() => ExamplesManager.AddAGXUnityScopedRegistry() );
}
else if ( hasUnresolvedDependencies ) {
dependenciesMenu.AddDisabledItem( GUI.MakeLabel( "Install dependency..." ) );
dependenciesMenu.AddSeparator( string.Empty );
foreach ( var dependency in data.Dependencies )
foreach ( var dependency in data.Dependencies )
dependenciesMenu.AddItem( GUI.MakeLabel( dependency.ToString() ),
false,
ExamplesManager.GetDependencyState( dependency ) == ExamplesManager.DependencyState.Installed,
() => ExamplesManager.InstallDependency( dependency ) );
}
else {
Expand Down Expand Up @@ -187,14 +196,14 @@ private void OnGUI()
var progressRect = GUILayoutUtility.GetLastRect();
EditorGUI.ProgressBar( progressRect,
data.DownloadProgress,
$"Downloading: { (int)( 100.0f * data.DownloadProgress + 0.5f ) }%" );
$"Downloading: {(int)( 100.0f * data.DownloadProgress + 0.5f )}%" );
}
}
}
}
}
}
InspectorGUI.Separator(1,ExampleSpacing);
InspectorGUI.Separator( 1, ExampleSpacing );
}

EditorGUILayout.EndScrollView();
Expand Down

0 comments on commit 7832000

Please sign in to comment.