{"TotalCount":16,"Files":[{"Ident":"coomzy.libraryimporter","Path":"JiggleBone.cs","FileName":"JiggleBone.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":71858,"Code":"public sealed class JiggleBone : TransformProxyComponent\r\n{\r\n\tJiggleBoneState state = new JiggleBoneState();\r\n\r\n\t[Property]\r\n\tpublic Vector3 StartPoint = new Vector3( 0, 0, 0 );\r\n\r\n\t[Property]\r\n\tpublic Vector3 EndPoint = new Vector3( 32, 0, 0 );\r\n\r\n\t[Property, Range( 0, 2 )]\r\n\tpublic float Speed { get; set; } = 1.0f;\r\n\r\n\t[Property, Range( 0, 2 )]\r\n\tpublic float Stiffness { get; set; } = 1.0f;\r\n\r\n\t[Property, Range( 0, 2 )]\r\n\tpublic float Damping { get; set; } = 1.0f;\r\n\r\n\t[Property, Range( 0, 100 )]\r\n\tpublic float Radius { get; set; } = 40.0f;\r\n\r\n\t[Property, Range( 0, 100 )]\r\n\tpublic float Mass { get; set; } = 1.0f;\r\n\r\n\tTransform LocalJigglePosition;\r\n\r\n\tprotected override void OnEnabled()\r\n\t{\r\n\t\tLocalJigglePosition = Transform.Local;\r\n\r\n\t\tbase.OnEnabled();\r\n\r\n\t\tstate = new JiggleBoneState();\r\n\t}\r\n\r\n\tprotected override void OnUpdate()\r\n\t{\r\n\t\tvar oldPos = LocalJigglePosition;\r\n\r\n\r\n\r\n\t\tusing ( Transform.DisableProxy() )\r\n\t\t{\r\n\t\t\tvar worldTx = Transform.World;\r\n\r\n\t\t\tvar startPoint = worldTx.PointToWorld( StartPoint );\r\n\t\t\tvar endPoint = worldTx.PointToWorld( EndPoint );\r\n\r\n\t\t\t//Gizmo.Draw.LineSphere( startPoint, 1 );\r\n\t\t\t//Gizmo.Draw.LineSphere( endPoint, 1 );\r\n\r\n\t\t\tstate.Extent = (endPoint - startPoint);\r\n\t\t\tstate.Stiffness = Stiffness;\r\n\t\t\tstate.Damping = Damping;\r\n\t\t\tstate.Radius = Radius;\r\n\t\t\tstate.Mass = Mass;\r\n\r\n\t\t\tstate.Update( startPoint, Time.Delta * Speed * 16.0f );\r\n\r\n\t\t\tvar tx = worldTx.RotateAround( startPoint, state.Rotation );\r\n\t\t\tLocalJigglePosition = GameObject.Parent.Transform.World.ToLocal( tx );\r\n\t\t}\r\n\r\n\t\tif ( oldPos != LocalJigglePosition )\r\n\t\t{\r\n\t\t\tMarkTransformChanged();\r\n\t\t}\r\n\t}\r\n\r\n\tprotected override void DrawGizmos()\r\n\t{\r\n\t\tbase.DrawGizmos();\r\n\r\n\t\tif ( !Gizmo.IsSelected )\r\n\t\t\treturn;\r\n\r\n\t\tusing ( Transform.DisableProxy() )\r\n\t\t{\r\n\t\t\tGizmo.Transform = Transform.World;\r\n\t\t\tGizmo.Draw.IgnoreDepth = false;\r\n\t\t\tGizmo.Draw.Color = Gizmo.Colors.Yaw.WithAlpha( 0.5f );\r\n\t\t\tGizmo.Draw.Line( StartPoint, EndPoint );\r\n\t\t\tGizmo.Draw.LineBBox( BBox.FromPositionAndSize( StartPoint, 5 ) );\r\n\t\t\tGizmo.Draw.LineBBox( BBox.FromPositionAndSize( EndPoint, 5 ) );\r\n\t\t\tGizmo.Draw.LineSphere( EndPoint, Radius * 2.0f, 4 );\r\n\t\t}\r\n\t}\r\n\r\n\tpublic override Transform GetLocalTransform()\r\n\t{\r\n\t\treturn LocalJigglePosition;\r\n\t}\r\n}\r\n\r\nclass JiggleBoneState\r\n{\r\n\tpublic Vector3 Extent = new Vector3( 32, 0, 0 );\r\n\r\n\tpublic Vector3 Position { get; set; }\r\n\tpublic Rotation Rotation { get; set; }\r\n\tpublic float Stiffness { get; set; } = 1.0f;\r\n\tpublic float Damping { get; set; } = 1.0f;\r\n\tpublic float Radius { get; set; } = 10.0f;\r\n\tpublic float Gravity { get; set; } = 1.0f;\r\n\tpublic float Mass { get; set; } = 1.0f;\r\n\r\n\r\n\tVector3 basePosition;\r\n\tVector3 velocity;\r\n\r\n\tpublic JiggleBoneState()\r\n\t{\r\n\r\n\t}\r\n\r\n\tinternal void Update( Vector3 position, float timeDelta )\r\n\t{\r\n\t\tbasePosition = position \u002B Extent;\r\n\r\n\t\t// initialization\r\n\t\tif ( Position == default )\r\n\t\t{\r\n\t\t\tPosition = basePosition;\r\n\t\t}\r\n\r\n\t\t// Calculate spring force based on displacement from the cube\r\n\t\tVector3 displacement = Position - basePosition;\r\n\t\tVector3 springForce = -Stiffness * displacement;\r\n\r\n\t\t// Calculate acceleration (Newton\u0027s second law)\r\n\t\tVector3 acceleration = springForce / Mass;\r\n\r\n\t\t// Update velocity (integrate acceleration)\r\n\t\tvelocity \u002B= acceleration * timeDelta;\r\n\r\n\t\t// Apply exponential damping\r\n\t\tvelocity *= (float)Math.Exp( -Damping * timeDelta );\r\n\r\n\t\t// Update position (integrate velocity)\r\n\t\tPosition \u002B= velocity * timeDelta;\r\n\r\n\t\t{\r\n\t\t\tvar diff = Position - basePosition;\r\n\t\t\tvar diffLen = diff.Length;\r\n\t\t\tif ( diffLen \u003E Radius )\r\n\t\t\t{\r\n\t\t\t\tPosition = basePosition \u002B diff.Normal * Radius;\r\n\t\t\t\t//velocity = velocity.AddClamped( -diff * 2.0f, diff.Length );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Store the rotation offset result\r\n\t\tRotation = Rotation.FromToRotation( basePosition - position, Position - position );\r\n\r\n\t\t//Gizmo.Draw.IgnoreDepth = true;\r\n\t\t//Gizmo.Draw.Line( position, Position );\r\n\t\t//Gizmo.Draw.Line( basePosition, Position );\r\n\t}\r\n}\r\n"},{"Ident":"coomzy.libraryimporter","Path":"MyLibraryComponent.cs","FileName":"MyLibraryComponent.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":71858,"Code":"using Sandbox;\r\n\r\n/// \u003Csummary\u003E\r\n/// This is a component - in your library!\r\n/// \u003C/summary\u003E\r\n[Title( \u0022LibraryImporter - My Component\u0022 )]\r\npublic class MyLibraryComponent : Component\r\n{\r\n\r\n}\r\n"},{"Ident":"coomzy.libraryimporter","Path":"UnitTests/LibraryTest.cs","FileName":"LibraryTest.cs","PackageType":"library","CodeKind":"UnitTest","AssetVersionId":71858,"Code":"using Sandbox;\r\n\r\n[TestClass]\r\npublic partial class LibraryTests\r\n{\r\n\t[TestMethod]\r\n\tpublic void SceneTest()\r\n\t{\r\n\t\tvar scene = new Scene();\r\n\t\tusing ( scene.Push() )\r\n\t\t{\r\n\t\t\tvar go = new GameObject();\r\n\r\n\t\t\tAssert.AreEqual( 1, scene.Directory.GameObjectCount );\r\n\t\t}\r\n\t}\r\n\r\n}\r\n"},{"Ident":"coomzy.libraryimporter","Path":"UnitTests/UnitTest.cs","FileName":"UnitTest.cs","PackageType":"library","CodeKind":"UnitTest","AssetVersionId":71858,"Code":"global using Microsoft.VisualStudio.TestTools.UnitTesting;\r\n\r\n[TestClass]\r\npublic class TestInit\r\n{\r\n\t[AssemblyInitialize]\r\n\tpublic static void ClassInitialize( TestContext context )\r\n\t{\r\n\t\tSandbox.Application.InitUnitTest();\r\n\t}\r\n}\r\n"},{"Ident":"coomzy.libraryimporter","Path":"Editor/LibraryListProxy.cs","FileName":"LibraryListProxy.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":71858,"Code":"using System;\r\nusing System.Threading.Tasks;\r\n\r\nclass LibraryListProxy : ListView\r\n{\r\n\tpublic Action\u003CPackage\u003E OnLibrarySelected { get; set; }\r\n\r\n\tpublic bool ShowInstalled { get; set; }\r\n\tpublic bool ShowAvailable { get; set; }\r\n\r\n\tpublic string Filter { get; set; } = \u0022\u0022;\r\n\r\n\tpublic LibraryListProxy( Widget parent ) : base( parent )\r\n\t{\r\n\t\tItemContextMenu = ShowItemContext;\r\n\t\tItemSelected = OnItemClicked;\r\n\r\n\t\tItemSize = new Vector2( -1, 64 );\r\n\t}\r\n\r\n\tpublic void OnItemClicked( object value )\r\n\t{\r\n\t\tif ( value is LibraryProject lib )\r\n\t\t{\r\n\t\t\tOnLibrarySelected?.Invoke( lib.Project.Package );\r\n\t\t}\r\n\r\n\t\tif ( value is Package p )\r\n\t\t{\r\n\t\t\tOnLibrarySelected?.Invoke( p );\r\n\t\t}\r\n\t}\r\n\r\n\tprivate void ShowItemContext( object obj )\r\n\t{\r\n\t\tLibraryProject project = obj as LibraryProject;\r\n\t\tPackage package = obj as Package ?? project?.Project.Package;\r\n\r\n\t\tvar m = new Menu();\r\n\r\n\t\tif ( package is not null \u0026\u0026 package.Org.Ident != \u0022local\u0022 )\r\n\t\t{\r\n\t\t\tm.AddOption( \u0022View in Browser\u0022, \u0022public\u0022, () =\u003E EditorUtility.OpenFolder( package.Url ) );\r\n\t\t\tm.AddSeparator();\r\n\t\t}\r\n\r\n\t\tif ( project is not null )\r\n\t\t{\r\n\t\t\tm.AddOption( \u0022Project Properties\u0022, \u0022tune\u0022, () =\u003E _ = ProjectSettingsWindow.OpenForProject( project.Project ) );\r\n\t\t\tm.AddSeparator();\r\n\t\t\tm.AddOption( \u0022Publish Project\u0022, \u0022upload_file\u0022, () =\u003E _ = ProjectSettingsWindow.OpenForProject( project.Project, \u0022upload\u0022 ) );\r\n\t\t\tm.AddSeparator();\r\n\t\t\tm.AddOption( \u0022Show in Explorer\u0022, \u0022folder\u0022, () =\u003E EditorUtility.OpenFolder( project.Project.RootDirectory.FullName ) );\r\n\t\t}\r\n\r\n\t\tm.OpenAt( Editor.Application.CursorPosition );\r\n\t}\r\n\r\n\tprotected override void PaintItem( VirtualWidget item )\r\n\t{\r\n\t\tif ( item.Object is LibraryProject c )\r\n\t\t\tPaintItem( item, c.Project.Package );\r\n\r\n\t\tif ( item.Object is Package p )\r\n\t\t\tPaintItem( item, p );\r\n\t}\r\n\r\n\tprivate void PaintItem( VirtualWidget item, Package c )\r\n\t{\r\n\t\tvar rect = item.Rect;\r\n\r\n\t\tif ( Paint.HasPressed )\r\n\t\t\trect = rect.Shrink( 2, 2, 0, 0 );\r\n\r\n\t\tvar library = LibrarySystem.All.Where( x =\u003E x.Project.Package.Ident == c.Ident \u0026\u0026 x.Project.Package.Org.Ident == c.Org.Ident \u0026\u0026 x.Project.Package.Ident != \u0022libraryimporter\u0022).FirstOrDefault();\r\n\r\n\t\tvar pen = Color.White.WithAlpha( 0.6f );\r\n\r\n\t\tif ( Paint.HasMouseOver || Paint.HasSelected )\r\n\t\t{\r\n\t\t\tPaint.SetBrush( Theme.Primary.WithAlpha( Paint.HasMouseOver ? 0.8f : 0.5f ) );\r\n\t\t\tPaint.ClearPen();\r\n\t\t\tPaint.DrawRect( rect, 4 );\r\n\r\n\t\t\tpen = Color.White.WithAlpha( Paint.HasMouseOver ? 1 : 0.9f );\r\n\t\t\tPaint.ClearBrush();\r\n\t\t}\r\n\r\n\t\trect = rect.Shrink( 4 );\r\n\r\n\t\t// Icon\r\n\t\t{\r\n\t\t\tvar iconRect = rect;\r\n\t\t\ticonRect.Width = iconRect.Height;\r\n\t\t\ticonRect = iconRect.Shrink( 8 );\r\n\r\n\t\t\tPaint.SetBrushAndPen( Theme.Grey.WithAlpha( 0.4f ) );\r\n\r\n\t\t\tif ( !string.IsNullOrWhiteSpace( c.Thumb ) \u0026\u0026 !c.Thumb.StartsWith( \u0022/\u0022 ) )\r\n\t\t\t{\r\n\t\t\t\tPaint.Draw( iconRect, c.Thumb );\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tPaint.DrawRect( iconRect, 4 );\r\n\t\t\t}\r\n\r\n\t\t\trect.Left = iconRect.Right \u002B 8;\r\n\t\t}\r\n\r\n\t\t//header\r\n\t\t{\r\n\t\t\trect.Top \u002B= 8;\r\n\r\n\t\t\tPaint.SetFont( \u0022Poppins\u0022, 11, 450 );\r\n\t\t\tPaint.Pen = pen;\r\n\t\t\tvar r = Paint.DrawText( rect, c.Title, TextFlag.LeftTop );\r\n\r\n\t\t\tr.Left = r.Right \u002B 8;\r\n\t\t\tr.Right = rect.Right;\r\n\t\t\tr.Bottom -= 3;\r\n\r\n\t\t\trect.Top = r.Bottom \u002B 4;\r\n\r\n\t\t\tvar installedVersion = library is not null ? $\u0022v{library?.Version}\u0022 : \u0022\u0022;\r\n\r\n\t\t\tPaint.Pen = pen.WithAlphaMultiplied( 0.6f );\r\n\t\t\tPaint.SetDefaultFont();\r\n\t\t\tPaint.DrawText( r, $\u0022{c.Org.Title} {installedVersion}\u0022, TextFlag.LeftBottom );\r\n\t\t}\r\n\r\n\t\t// body text\r\n\t\t{\r\n\t\t\tPaint.Pen = pen.WithAlphaMultiplied( 0.6f );\r\n\t\t\tPaint.SetDefaultFont();\r\n\t\t\tPaint.DrawText( rect, c.Summary, TextFlag.LeftTop );\r\n\t\t}\r\n\t}\r\n\r\n\tprotected override void OnPaint()\r\n\t{\r\n\t\tPaint.ClearPen();\r\n\t\tPaint.SetBrush( Theme.ControlBackground );\r\n\t\tPaint.DrawRect( LocalRect );\r\n\r\n\t\tPaint.Antialiasing = true;\r\n\t\tPaint.TextAntialiasing = true;\r\n\r\n\t\tbase.OnPaint();\r\n\t}\r\n\r\n\t[EditorEvent.Frame]\r\n\tpublic void Frame()\r\n\t{\r\n\t\tif ( !Visible ) return;\r\n\r\n\t\tif ( ShowInstalled )\r\n\t\t{\r\n\t\t\tif ( SetContentHash( GetContentHash(), 0.5f ) )\r\n\t\t\t{\r\n\t\t\t\tSetItems(LibrarySystem.All.Where(x =\u003E x.Project.Package.Ident != \u0022libraryimporter\u0022));\r\n\t\t\t\tSelectItem(LibrarySystem.All.FirstOrDefault(x =\u003E x.Project.Package.Ident != \u0022libraryimporter\u0022) );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif ( ShowAvailable )\r\n\t\t{\r\n\t\t\tif ( SetContentHash( GetContentHash(), 0.5f ) )\r\n\t\t\t{\r\n\t\t\t\t_ = UpdateAsync();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tasync Task UpdateAsync()\r\n\t{\r\n\t\tvar search = \u0022\u0022;\r\n\t\tif ( !string.IsNullOrEmpty( Filter ) ) search = Filter \u002B \u0022 \u0022;\r\n\t\tvar found = await Package.FindAsync( $\u0022{search}type:library sort:updated\u0022, 200, 0 );\r\n\t\tSetItems( found.Packages );\r\n\r\n\t\tSelectItem( found.Packages.FirstOrDefault() );\r\n\t}\r\n\r\n\tint GetContentHash()\r\n\t{\r\n\t\tif ( ShowInstalled )\r\n\t\t\treturn LibrarySystem.All.GetHashCode();\r\n\r\n\t\treturn HashCode.Combine( Filter );\r\n\t}\r\n\r\n}\r\n"},{"Ident":"coomzy.libraryimporter","Path":"BouncyBone.cs","FileName":"BouncyBone.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":71858,"Code":"public sealed class BouncyBone : TransformProxyComponent\r\n{\r\n\tJiggleBoneState state = new JiggleBoneState();\r\n\r\n\t[Property]\r\n\tpublic Vector3 Influence { get; set; } = new Vector3( 1, 1, 1 );\r\n\r\n\t[Property, Range( 0, 50.0f )]\r\n\tpublic float Stiffness { get; set; } = 1;\r\n\r\n\t[Property, Range( 0, 50.0f )]\r\n\tpublic float Damping { get; set; } = 1;\r\n\r\n\tTransform LocalJigglePosition;\r\n\tTransformSpring springer;\r\n\r\n\tprotected override void OnEnabled()\r\n\t{\r\n\t\tspringer = new TransformSpring();\r\n\t\tspringer.Transform = Transform.World;\r\n\t\tLocalJigglePosition = springer.Transform;\r\n\r\n\t\tbase.OnEnabled();\r\n\r\n\r\n\t}\r\n\r\n\tprotected override void OnUpdate()\r\n\t{\r\n\t\tvar oldPos = LocalJigglePosition;\r\n\r\n\t\tusing ( Transform.DisableProxy() )\r\n\t\t{\r\n\t\t\tvar worldTx = Transform.World;\r\n\r\n\t\t\tspringer.Stiffness = Stiffness;\r\n\t\t\tspringer.Damping = Damping;\r\n\t\t\tspringer.UpdateSpring( Transform.World, Time.Delta );\r\n\r\n\t\t\tvar tx = GameObject.Parent.Transform.World.ToLocal( springer.Transform );\r\n\t\t\tLocalJigglePosition = tx;\r\n\t\t}\r\n\r\n\t\tif ( oldPos != LocalJigglePosition )\r\n\t\t{\r\n\t\t\tMarkTransformChanged();\r\n\t\t}\r\n\t}\r\n\r\n\tpublic override Transform GetLocalTransform()\r\n\t{\r\n\t\treturn LocalJigglePosition;\r\n\t}\r\n}\r\n\r\n\r\npublic struct TransformSpring\r\n{\r\n\tpublic Transform Transform;\r\n\r\n\tprivate Vector3 velocityPosition;\r\n\tprivate Vector3 velocityScale;\r\n\tprivate Rotation velocityRotation = Rotation.Identity;\r\n\r\n\tpublic float Stiffness = 1.5f;  // Spring stiffness, higher is stiffer\r\n\tpublic float Damping = 1.0f;      // Damping, higher is less oscillation\r\n\r\n\tpublic TransformSpring()\r\n\t{\r\n\t\tTransform = global::Transform.Zero;\r\n\t}\r\n\r\n\tpublic void UpdateSpring( Transform target, float deltaTime )\r\n\t{\r\n\t\tTransform.Position = SpringLerp( Transform.Position, target.Position, ref velocityPosition, deltaTime );\r\n\t\tTransform.Scale = SpringLerp( Transform.Scale, target.Scale, ref velocityScale, deltaTime );\r\n\t\tTransform.Rotation = target.Rotation;\r\n\t}\r\n\r\n\tprivate Vector3 SpringLerp( Vector3 current, Vector3 target, ref Vector3 velocity, float deltaTime )\r\n\t{\r\n\t\tfloat omega = 2f * MathF.PI * Stiffness;\r\n\t\tfloat damper = MathF.Exp( -Damping * deltaTime * omega );\r\n\r\n\t\tVector3 displacement = current - target;\r\n\t\tVector3 springForce = -omega * omega * displacement;\r\n\t\tVector3 dampingForce = -2f * omega * Damping * velocity;\r\n\r\n\t\tVector3 acceleration = springForce \u002B dampingForce;\r\n\t\tvelocity = (velocity \u002B acceleration * deltaTime) * damper;\r\n\t\treturn target \u002B displacement \u002B velocity * deltaTime;\r\n\t}\r\n\r\n\r\n}\r\n"},{"Ident":"coomzy.libraryimporter","Path":"Code/MyLibraryComponent.cs","FileName":"MyLibraryComponent.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":71858,"Code":"using Sandbox;\r\n\r\n/// \u003Csummary\u003E\r\n/// This is a component - in your library!\r\n/// \u003C/summary\u003E\r\n[Title( \u0022LibraryImporter - My Component\u0022 )]\r\npublic class MyLibraryComponent : Component\r\n{\r\n\r\n}\r\n"},{"Ident":"coomzy.libraryimporter","Path":".obj/__compiler_extra.cs","FileName":"__compiler_extra.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":71858,"Code":"global using static Sandbox.Internal.GlobalGameNamespace;\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022AddonTitle\u0022, \u0022LibraryImporter\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022AddonIdent\u0022, \u0022libraryimporter\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022OrgIdent\u0022, \u0022coomzy\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022Ident\u0022, \u0022coomzy.libraryimporter\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022CompileTime\u0022, \u00229/2/2024 10:47:22 PM\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022EngineVersion\u0022, \u002217\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022EngineMinorVersion\u0022, \u00221\u0022 )]\r\n\r\n[assembly: System.Runtime.Versioning.TargetFramework( \u0022.NETCoreApp,Version=v7.0\u0022, FrameworkDisplayName = \u0022.NET 7.0\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyVersion(\u00220.0.126.0\u0022)]\r\n[assembly: global::System.Reflection.AssemblyFileVersion(\u00220.0.126.0\u0022)]"},{"Ident":"coomzy.libraryimporter","Path":"Editor/LibraryImporter.cs","FileName":"LibraryImporter.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":71858,"Code":"global using Editor;\r\nglobal using System.Collections.Generic;\r\nglobal using System.Linq;\r\nglobal using Sandbox;\r\nusing System;\r\nusing System.IO;\r\nusing Sandbox.Internal;\r\n\r\npublic static class LibraryImporter\r\n{\r\n\t//[Event(\u0022localaddons.changed\u0022)]\r\n\tpublic static void CheckCookiePrompt()\r\n\t{\r\n\t\tif (ProjectCookie == null)\r\n\t\t{\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tvar hasOfferedLibraryImport = ProjectCookie.Get(\u0022easysaving.hasOfferedLibraryImport\u0022, false);\r\n\t\tif (!hasOfferedLibraryImport)\r\n\t\t{\r\n\t\t\tProjectCookie.Set(\u0022easysaving.hasOfferedLibraryImport\u0022, true);\r\n\r\n\t\t\tSystem.Reflection.MethodInfo saveMethod = ProjectCookie.GetType().GetMethod(\u0022Save\u0022, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);\r\n\r\n\t\t\tif (saveMethod != null)\r\n\t\t\t{\r\n\t\t\t\tsaveMethod.Invoke(ProjectCookie, null);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tLog.Error(\u0022CookieContainer Method \u0027Save\u0027 not found\u0022);\r\n\t\t\t}\r\n\r\n\t\t\tOpenMyMenu();\r\n\t\t}\r\n\t}\r\n\r\n\tpublic static void OpenMyMenu()\r\n\t{\r\n\t\tvar confirm = new PopupWindow(\r\n\t\t\t\u0022Import UI from Library? (Easy Saving)\u0022,\r\n\t\t\t\u0022UI doesn\u0027t work in libraries, so if you want the options screens that comes with this library you need to import it into your project\u0022,\r\n\t\t\t\u0022No\u0022,\r\n\t\t\tnew Dictionary\u003Cstring, Action\u003E()\r\n\t\t\t{\r\n\t\t\t\t{ \u0022Yes\u0022, () =\u003E ImportDisabledLibraryFiles() }\r\n\t\t\t}\r\n\t\t);\r\n\r\n\t\tconfirm.Show();\r\n\t}\r\n\r\n\t[Menu(\u0022Editor\u0022, \u0022Library/Easy Saving/Import Disabled Library Files\u0022)]\r\n\tpublic static void ImportDisabledLibraryFiles()\r\n\t{\r\n\t\tstring libraryFolder = new System.IO.DirectoryInfo(Editor.FileSystem.Content.GetFullPath(\u0022/\u0022)).FullName.Replace(\u0022Assets\u0022, \u0022\u0022);\r\n\t\tstring projectFolder = new System.IO.DirectoryInfo(Editor.FileSystem.ProjectSettings.GetFullPath(\u0022/\u0022)).FullName.Replace(\u0022ProjectSettings\u0022, \u0022\u0022);\r\n\r\n\t\tstring source = $\u0022{libraryFolder}code\u0022;\r\n\t\tstring target = @$\u0022{projectFolder}code\\libraries\u0022;\r\n\r\n\t\tstring[] files = Directory.GetFiles(source, \u0022*.DISABLED\u0022, SearchOption.AllDirectories);\r\n\r\n\t\tLog.Info($\u0022Moving .DISABLED files from source: {source}\u0022);\r\n\r\n\t\tforeach (string file in files)\r\n\t\t{\r\n\t\t\tstring relativePath = Path.GetRelativePath(source, file);\r\n\t\t\tstring targetPath = Path.Combine(target, relativePath);\r\n\r\n\t\t\ttargetPath = RemoveDisabledExtension(targetPath);\r\n\r\n\t\t\tDirectory.CreateDirectory(Path.GetDirectoryName(targetPath));\r\n\r\n\t\t\tif (File.Exists(targetPath))\r\n\t\t\t{\r\n\t\t\t\tif ((File.GetAttributes(targetPath) \u0026 FileAttributes.ReadOnly) != 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tFile.SetAttributes(targetPath, FileAttributes.Normal);\r\n\t\t\t\t}\r\n\t\t\t\tFile.Delete(targetPath);\r\n\t\t\t}\r\n\t\t\tFile.Copy(file, targetPath);\r\n\r\n\t\t\tLog.Info($\u0022Moved: {file} -\u003E {targetPath}\u0022);\r\n\t\t}\r\n\r\n\t\tLog.Info(\u0022All files have been moved successfully.\u0022);\r\n\t}\r\n\r\n\tstatic string RemoveDisabledExtension(string filePath)\r\n\t{\r\n\t\tif (filePath.EndsWith(\u0022.DISABLED\u0022, StringComparison.OrdinalIgnoreCase))\r\n\t\t{\r\n\t\t\treturn filePath.Substring(0, filePath.Length - \u0022.DISABLED\u0022.Length);\r\n\t\t}\r\n\t\treturn filePath;\r\n\t}\r\n}\r\n"},{"Ident":"coomzy.libraryimporter","Path":"Assembly.cs","FileName":"Assembly.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":71858,"Code":"global using Sandbox;\r\nglobal using System;\r\n\r\n"},{"Ident":"coomzy.libraryimporter","Path":"libraries/libevents/GameEvents/GameEvent.cs","FileName":"GameEvent.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":71858,"Code":"using System;\r\nusing System.Collections.Generic;\r\nusing System.Collections.Immutable;\r\nusing System.Linq;\r\n\r\nnamespace Sandbox.Events;\r\n\r\n/// \u003Csummary\u003E\r\n/// Interface for event payloads that can be listened for by \u003Csee cref=\u0022IGameEventHandler{T}\u0022/\u003Es.\r\n/// \u003C/summary\u003E\r\npublic interface IGameEvent { }\r\n\r\n/// \u003Csummary\u003E\r\n/// Interface for components that handle game events with a payload of type \u003Csee cref=\u0022T\u0022/\u003E.\r\n/// \u003C/summary\u003E\r\n/// \u003Ctypeparam name=\u0022T\u0022\u003EEvent payload type.\u003C/typeparam\u003E\r\npublic interface IGameEventHandler\u003Cin T\u003E\r\n\twhere T : IGameEvent\r\n{\r\n\t/// \u003Csummary\u003E\r\n\t/// Called when an event with payload of type \u003Csee cref=\u0022T\u0022/\u003E is dispatched on a \u003Csee cref=\u0022GameObject\u0022/\u003E\r\n\t/// that contains this component, including on a descendant.\r\n\t/// \u003C/summary\u003E\r\n\t/// \u003Cparam name=\u0022eventArgs\u0022\u003EEvent payload.\u003C/param\u003E\r\n\tvoid OnGameEvent( T eventArgs );\r\n}\r\n\r\n/// \u003Csummary\u003E\r\n/// Helper for dispatching game events in a scene.\r\n/// \u003C/summary\u003E\r\npublic static class GameEvent\r\n{\r\n\tprivate static Dictionary\u003CType, IReadOnlyDictionary\u003CType, int\u003E\u003E HandlerOrderingCache { get; } = new();\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// Notifies all \u003Csee cref=\u0022IGameEventHandler{T}\u0022/\u003E components that are within \u003Cparamref name=\u0022root\u0022/\u003E,\r\n\t/// with a payload of type \u003Ctypeparamref name=\u0022T\u0022/\u003E.\r\n\t/// \u003C/summary\u003E\r\n\tpublic static void Dispatch\u003CT\u003E( this GameObject root, T eventArgs )\r\n\t\twhere T : IGameEvent\r\n\t{\r\n\t\tvar handlers = (root is Scene scene\r\n\t\t\t? scene.GetAllComponents\u003CIGameEventHandler\u003CT\u003E\u003E() // I think this is more efficient?\r\n\t\t\t: root.Components.GetAll\u003CIGameEventHandler\u003CT\u003E\u003E())\r\n\t\t\t.ToArray();\r\n\r\n\t\tif ( !HandlerOrderingCache.TryGetValue( typeof(T), out var ordering ) || handlers.Any( x =\u003E !ordering.ContainsKey( x.GetType() ) ) )\r\n\t\t{\r\n\t\t\tordering = HandlerOrderingCache[typeof(T)] = GetHandlerOrdering\u003CT\u003E();\r\n\t\t}\r\n\r\n\t\tList\u003CException\u003E? exceptions = null;\r\n\r\n\t\tforeach ( var handler in handlers.OrderBy( x =\u003E ordering[x.GetType()] ) )\r\n\t\t{\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\thandler.OnGameEvent( eventArgs );\r\n\t\t\t}\r\n\t\t\tcatch ( Exception e )\r\n\t\t\t{\r\n\t\t\t\texceptions ??= new();\r\n\t\t\t\texceptions.Add( e );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tswitch ( exceptions?.Count )\r\n\t\t{\r\n\t\t\tcase 1:\r\n\t\t\t\tLog.Error( exceptions[0] );\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase \u003E 1:\r\n\t\t\t\tLog.Error( new AggregateException( exceptions ) );\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\r\n\tprivate static bool IsImplementingMethodName( string methodName )\r\n\t{\r\n\t\tif ( methodName == nameof(IGameEventHandler\u003CIGameEvent\u003E.OnGameEvent) )\r\n\t\t{\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\treturn methodName.StartsWith( \u0022Sandbox.Events.IGameEventHandler\u003C\u0022 ) \u0026\u0026 methodName.EndsWith( \u0022\u003E.OnGameEvent\u0022 );\r\n\t}\r\n\r\n\tprivate static MethodDescription? GetImplementation\u003CT\u003E( TypeDescription type )\r\n\t{\r\n\t\tforeach ( var method in type.Methods )\r\n\t\t{\r\n\t\t\tif ( method.IsStatic ) continue;\r\n\t\t\tif ( method.Parameters.Length != 1 ) continue;\r\n\t\t\tif ( method.Parameters[0].ParameterType != typeof( T ) ) continue;\r\n\r\n\t\t\tif ( !IsImplementingMethodName( method.Name ) ) continue;\r\n\r\n\t\t\treturn method;\r\n\t\t}\r\n\r\n\t\treturn null;\r\n\t}\r\n\r\n\tprivate static IReadOnlyDictionary\u003CType, int\u003E GetHandlerOrdering\u003CT\u003E()\r\n\t\twhere T : IGameEvent\r\n\t{\r\n\t\tvar types = TypeLibrary.GetTypes\u003CIGameEventHandler\u003CT\u003E\u003E().ToArray();\r\n\t\tvar helper = new SortingHelper( types.Length );\r\n\r\n\t\tfor ( var i = 0; i \u003C types.Length; \u002B\u002Bi )\r\n\t\t{\r\n\t\t\tvar type = types[i];\r\n\t\t\tvar method = GetImplementation\u003CT\u003E( type );\r\n\r\n\t\t\tif ( method is null )\r\n\t\t\t{\r\n\t\t\t\tLog.Warning( $\u0022Can\u0027t find {nameof( IGameEventHandler\u003CT\u003E )}\u003C{typeof( T ).Name}\u003E implementation in {type.Name}!\u0022 );\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\tforeach ( var attrib in method.Attributes )\r\n\t\t\t{\r\n\t\t\t\tswitch ( attrib )\r\n\t\t\t\t{\r\n\t\t\t\t\tcase EarlyAttribute:\r\n\t\t\t\t\t\thelper.AddFirst( i );\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\tcase LateAttribute:\r\n\t\t\t\t\t\thelper.AddLast( i );\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\tcase IBeforeAttribute before:\r\n\t\t\t\t\t\tfor ( var j = 0; j \u003C types.Length; \u002B\u002Bj )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif ( i == j ) continue;\r\n\r\n\t\t\t\t\t\t\tvar other = types[j];\r\n\r\n\t\t\t\t\t\t\tif ( before.Type.IsAssignableFrom( other.TargetType ) )\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\thelper.AddConstraint( i, j );\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\tcase IAfterAttribute after:\r\n\t\t\t\t\t\tfor ( var j = 0; j \u003C types.Length; \u002B\u002Bj )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif ( i == j ) continue;\r\n\r\n\t\t\t\t\t\t\tvar other = types[j];\r\n\r\n\t\t\t\t\t\t\tif ( after.Type.IsAssignableFrom( other.TargetType ) )\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\thelper.AddConstraint( j, i );\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tvar ordering = new List\u003Cint\u003E();\r\n\r\n\t\tif ( !helper.Sort( ordering, out var invalid ) )\r\n\t\t{\r\n\t\t\tLog.Error( $\u0022Invalid event ordering constraint between {types[invalid.EarlierIndex].Name} and {types[invalid.LaterIndex].Name}!\u0022 );\r\n\t\t\treturn ImmutableDictionary\u003CType, int\u003E.Empty;\r\n\t\t}\r\n\r\n\t\treturn Enumerable.Range( 0, ordering.Count )\r\n\t\t\t.ToImmutableDictionary( i =\u003E types[ordering[i]].TargetType, i =\u003E i );\r\n\t}\r\n}\r\n\r\npublic delegate void GameEventAction\u003Cin T\u003E( T eventArgs )\r\n\twhere T : IGameEvent;\r\n\r\n/// \u003Csummary\u003E\r\n/// Base class for components that expose game events to Action Graph.\r\n/// \u003C/summary\u003E\r\npublic abstract class GameEventComponent\u003CT\u003E : Component, IGameEventHandler\u003CT\u003E\r\n\twhere T : IGameEvent\r\n{\r\n\t/// \u003Csummary\u003E\r\n\t/// Action invoked when the \u003Ctypeparamref name=\u0022T\u0022/\u003E event is dispatched.\r\n\t/// \u003C/summary\u003E\r\n\t[Property]\r\n\tpublic GameEventAction\u003CT\u003E? OnEvent { get; set; }\r\n\r\n\tvoid IGameEventHandler\u003CT\u003E.OnGameEvent( T eventArgs )\r\n\t{\r\n\t\tOnEvent?.Invoke( eventArgs );\r\n\t}\r\n}\r\n"},{"Ident":"coomzy.libraryimporter","Path":"__gen_RazorNamespace.cs","FileName":"__gen_RazorNamespace.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":71858,"Code":"global using Microsoft.AspNetCore.Components; \nglobal using Microsoft.AspNetCore.Components.Rendering;\n"},{"Ident":"coomzy.libraryimporter","Path":"libraries/libevents/GameEvents/Attributes.cs","FileName":"Attributes.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":71858,"Code":"\r\nusing System;\r\n\r\nnamespace Sandbox.Events;\r\n\r\n/// \u003Csummary\u003E\r\n/// Only valid on \u003Csee cref=\u0022IGameEventHandler{T}.OnGameEvent\u0022/\u003E implementations. Forces this\r\n/// event handler to be invoked before any handlers not marked as early, except if more specific\r\n/// constraints are given (i.e., \u003Csee cref=\u0022BeforeAttribute{T}\u0022/\u003E, \u003Csee cref=\u0022AfterAttribute{T}\u0022/\u003E).\r\n/// \u003C/summary\u003E\r\n[AttributeUsage( AttributeTargets.Method )]\r\npublic sealed class EarlyAttribute : Attribute\r\n{\r\n\r\n}\r\n\r\n/// \u003Csummary\u003E\r\n/// Only valid on \u003Csee cref=\u0022IGameEventHandler{T}.OnGameEvent\u0022/\u003E implementations. Forces this\r\n/// event handler to be invoked after any handlers not marked as late, except if more specific\r\n/// constraints are given (i.e., \u003Csee cref=\u0022BeforeAttribute{T}\u0022/\u003E, \u003Csee cref=\u0022AfterAttribute{T}\u0022/\u003E).\r\n/// \u003C/summary\u003E\r\n[AttributeUsage( AttributeTargets.Method )]\r\npublic sealed class LateAttribute : Attribute\r\n{\r\n\r\n}\r\n\r\ninternal interface IBeforeAttribute\r\n{\r\n\tType Type { get; }\r\n}\r\n\r\ninternal interface IAfterAttribute\r\n{\r\n\tType Type { get; }\r\n}\r\n\r\n/// \u003Csummary\u003E\r\n/// Only valid on \u003Csee cref=\u0022IGameEventHandler{T}.OnGameEvent\u0022/\u003E implementations. Forces this\r\n/// event handler to be invoked before any handlers in the specified type.\r\n/// \u003C/summary\u003E\r\n[AttributeUsage( AttributeTargets.Method, AllowMultiple = true )]\r\npublic sealed class BeforeAttribute\u003CT\u003E : Attribute, IBeforeAttribute\r\n{\r\n\tType IBeforeAttribute.Type =\u003E typeof(T);\r\n}\r\n\r\n/// \u003Csummary\u003E\r\n/// Only valid on \u003Csee cref=\u0022IGameEventHandler{T}.OnGameEvent\u0022/\u003E implementations. Forces this\r\n/// event handler to be invoked after any handlers in the specified type.\r\n/// \u003C/summary\u003E\r\n[AttributeUsage( AttributeTargets.Method, AllowMultiple = true )]\r\npublic sealed class AfterAttribute\u003CT\u003E : Attribute, IAfterAttribute\r\n{\r\n\tType IAfterAttribute.Type =\u003E typeof( T );\r\n}\r\n"},{"Ident":"coomzy.libraryimporter","Path":"Editor/LibraryImporterDock.cs","FileName":"LibraryImporterDock.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":71858,"Code":"using Editor;\r\nusing System;\r\nusing System.IO;\r\n\r\nnamespace Editor.LibraryManager;\r\n\r\n[Dock( \u0022Editor\u0022, \u0022Library Importer\u0022, \u0022extension\u0022 )]\r\npublic class LibraryImporterDock : Widget\r\n{\r\n\tinternal Action\u003Cstring\u003E OnValueChanged;\r\n\r\n\tpublic LibraryImporterDock( Widget parent ) : base( parent )\r\n\t{\r\n\t\tLayout = Layout.Row();\r\n\t\tLayout.Margin = 4;\r\n\t\tLayout.Spacing = 4;\r\n\r\n\t\tFocusMode = FocusMode.TabOrClickOrWheel;\r\n\t}\r\n\r\n\t[EditorEvent.Hotload]\r\n\tvoid Rebuild()\r\n\t{\r\n\t\tLayout.Clear( true );\r\n\t\tLayout.Add( new InstalledLibrariesWidgetProxy( this ) );\r\n\t}\r\n\r\n\tstatic void CopyFolder( string sourceDir, string targetDir, string libraryName, string libraryFolder )\r\n\t{\r\n\t\tif ( sourceDir.Contains( \u0022\\\\.\u0022, StringComparison.OrdinalIgnoreCase ) )\r\n\t\t{\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tSystem.IO.Directory.CreateDirectory( targetDir );\r\n\r\n\t\tforeach ( var file in Directory.GetFiles( sourceDir ) )\r\n\t\t{\r\n\t\t\tCopyAndProcessFile( file, targetDir, libraryName, libraryFolder );\r\n\t\t}\r\n\r\n\t\tforeach ( var directory in Directory.GetDirectories( sourceDir ) )\r\n\t\t{\r\n\t\t\tCopyFolder( directory, Path.Combine( targetDir, Path.GetFileName( directory ) ), libraryName, libraryFolder );\r\n\t\t}\r\n\t}\r\n\r\n\tstatic void CopyAndProcessFile( string file, string targetDir, string libraryName, string libraryFolder )\r\n\t{\r\n\t\tvar targetname = Path.Combine( targetDir, Path.GetFileName( file ) );\r\n\r\n\t\t// Replace $ident with our ident in file name\r\n\t\ttargetname = targetname.Replace( \u0022$title\u0022, libraryName );\r\n\t\ttargetname = targetname.Replace( \u0022$ident\u0022, libraryFolder );\r\n\r\n\t\tif ( file.EndsWith( \u0022.cs\u0022 ) || file.EndsWith( \u0022.json\u0022 ) || file.EndsWith( \u0022.sbproj\u0022 ) )\r\n\t\t{\r\n\t\t\tvar txt = System.IO.File.ReadAllText( file );\r\n\t\t\ttxt = txt.Replace( \u0022$title\u0022, libraryName );\r\n\t\t\ttxt = txt.Replace( \u0022$ident\u0022, libraryFolder );\r\n\t\t\tSystem.IO.File.WriteAllText( targetname, txt );\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tFile.Copy( file, targetname );\r\n\t\t}\r\n\t}\r\n\r\n\t[EditorEvent.Frame]\r\n\tpublic void Frame()\r\n\t{\r\n\t\tif ( SetContentHash( ContentHash, 0.5f ) )\r\n\t\t{\r\n\t\t\tRebuild();\r\n\t\t}\r\n\t}\r\n\r\n\tint ContentHash() =\u003E HashCode.Combine( 0 );\r\n\r\n}\r\n\r\npublic class InstalledLibrariesWidgetProxy : Widget\r\n{\r\n\tLayout ContentLayout;\r\n\r\n\tpublic InstalledLibrariesWidgetProxy( LibraryImporterDock manager )\r\n\t{\r\n\t\tLayout = Layout.Row();\r\n\r\n\t\tvar left = new LibraryListProxy( null )\r\n\t\t{\r\n\t\t\tShowInstalled = true\r\n\t\t};\r\n\r\n\t\tLayout.Add( left, 1 );\r\n\t\tContentLayout = Layout.AddColumn();\r\n\r\n\t\tleft.OnLibrarySelected = ( library ) =\u003E\r\n\t\t{\r\n\t\t\tContentLayout.Clear( true );\r\n\t\t\tContentLayout.Add( new LibraryDetailProxy( library ) );\r\n\t\t};\r\n\r\n\t\tmanager.OnValueChanged = ( txt ) =\u003E\r\n\t\t{\r\n\t\t\tleft.Filter = txt;\r\n\t\t};\r\n\t}\r\n}\r\n"},{"Ident":"coomzy.libraryimporter","Path":"Editor/LibraryDetailProxy.cs","FileName":"LibraryDetailProxy.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":71858,"Code":"using System;\r\nusing System.Text.Json;\r\nusing System.Threading.Tasks;\r\nusing System.IO;\r\nusing static Editor.Label;\r\nusing System.Diagnostics;\r\nusing Editor;\r\n\r\npublic class LibraryDetailProxy : Widget\r\n{\r\n\tPackage Package;\r\n\tLibraryProject SelectedLibrary;\r\n\r\n\tButton InstallButton;\r\n\tButton UninstallButton;\r\n\r\n\tComboBox LibraryList;\r\n\tLibraryProject ComboLibrarySelection;\r\n\tLabel ComboLibraryLabel;\r\n\r\n\tButton InstallLibraryButton;\r\n\tButton UninstallLibraryButton;\r\n\r\n\tImportedLibraries importedLibraries = new ImportedLibraries();\r\n\r\n\tpublic string libraryStagingFolder =\u003E $@\u0022{Project.Current.RootDirectory}\\staging\\libraryimporter\u0022;\r\n\r\n\tpublic string projectAssetsFolder =\u003E GetAssetsFolder(Project.Current, Package);\r\n\tpublic string projectCodeFolder =\u003E GetCodeFolder(Project.Current, Package);\r\n\tpublic string projectEditorFolder =\u003E GetEditorFolder(Project.Current, Package);\r\n\r\n\tpublic string selectedLibraryAssetsFolder =\u003E GetAssetsFolder(ComboLibrarySelection?.Project, SelectedLibrary?.Project?.Package);\r\n\tpublic string selectedLibraryCodeFolder =\u003E GetCodeFolder(ComboLibrarySelection?.Project, SelectedLibrary?.Project?.Package);\r\n\tpublic string selectedLibraryEditorFolder =\u003E GetEditorFolder(ComboLibrarySelection?.Project, SelectedLibrary?.Project?.Package);\r\n\r\n\tpublic List\u003Cstring\u003E blacklistedFolders = new List\u003Cstring\u003E { \u0022bin\u0022, \u0022obj\u0022, \u0022Properties\u0022 };\r\n\tpublic List\u003Cstring\u003E blacklistedFileTypes = new List\u003Cstring\u003E { \u0022.csproj\u0022 };\r\n\r\n\tpublic string importingLibraryToLibraryID =\u003E $\u0022{SelectedLibrary.Project.Package.Ident}-{ComboLibrarySelection.Project.Package.Ident}\u0022;\r\n\r\n\tpublic LibraryDetailProxy( Package package ) : base()\r\n\t{\r\n\t\tPackage = package;\r\n\r\n\t\tLayout = Layout.Column();\r\n\t\tLayout.Margin = 8;\r\n\t\tLayout.Spacing = 4;\r\n\r\n\t\timportedLibraries = ImportedLibraries.Load();\r\n\r\n\t\t_ = FetchAndBuild();\r\n\t}\r\n\r\n\tasync Task FetchAndBuild()\r\n\t{\r\n\t\tif ( !this.IsValid() ) return;\r\n\r\n\t\tLayout.Clear( true );\r\n\r\n\t\tif ( !Sandbox.Package.TryParseIdent( Package.FullIdent, out var ident ) )\r\n\t\t\treturn;\r\n\r\n\t\tSelectedLibrary = LibrarySystem.All.FirstOrDefault(x =\u003E x.Project.Package.Ident == Package.Ident \u0026\u0026 x.Project.Package.Org == Package.Org);\r\n\r\n\t\tbool hasStagedLibrary = importedLibraries.packageIndentToStagedVersion.ContainsKey(Package.Ident);\r\n\t\tstring stagedVersion = \u0022\u0022;\r\n\t\tif (hasStagedLibrary)\r\n\t\t{\r\n\t\t\tstagedVersion = importedLibraries.packageIndentToStagedVersion[Package.Ident];\r\n\t\t}\r\n\t\tbool stagedVersionDifferentFromInstalled = stagedVersion != SelectedLibrary.Version.ToString();\r\n\r\n\t\tbool hasImportedLibraryToProject = importedLibraries.packageIndentToProjectImportedVersion.ContainsKey(Package.Ident);\r\n\t\tstring importedVersionToProject = \u0022\u0022;\r\n\t\tif (hasImportedLibraryToProject)\r\n\t\t{\r\n\t\t\timportedVersionToProject = importedLibraries.packageIndentToProjectImportedVersion[Package.Ident];\r\n\t\t}\r\n\t\tbool importedVersionDifferentFromInstalled = importedVersionToProject != SelectedLibrary.Version.ToString();\r\n\r\n\t\t// do we have this package installed? what is the version?\r\n\t\tif ( ident.org != \u0022local\u0022 )\r\n\t\t{\r\n\t\t\tPackage = await Package.FetchAsync( $\u0022{ident.org}.{ident.package}\u0022, false ) ?? Package;\r\n\t\t\tif ( !this.IsValid() ) return;\r\n\t\t}\r\n\r\n\t\t// Header, todo, icon and title\r\n\t\tLayout.Add( new Label( $\u0022{Package.Title} from {Package.Org.Title}\u0022 ) );\r\n\t\tLayout.Add( new Label( $\u0022Installed Version: {SelectedLibrary.Version}\u0022 ) );\r\n\r\n\t\tif (!hasStagedLibrary)\r\n\t\t{\r\n\t\t\tLayout.Add(new Label($\u0022Library has not been staged.\u0022));\r\n\r\n\t\t\tInstallButton = new Button(\u0022Stage Library (move contents into staging folder)\u0022, this) { Pressed = () =\u003E MoveLibraryFolderToStaging(SelectedLibrary) };\r\n\t\t\tLayout.Add(InstallButton);\r\n\t\t\tLayout.AddStretchCell();\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tLayout.Add(new Label(\u0022\u0022));\r\n\t\tLayout.Add(new Label(\u0022Staging\u0022));\r\n\t\tLayout.Add(new Label($\u0022Staged Version: {stagedVersion}\u0022));\r\n\t\tif (stagedVersionDifferentFromInstalled)\r\n\t\t{\r\n\t\t\tInstallButton = new Button(\u0022Restage Library (move contents into staging folder)\u0022, this) { Pressed = () =\u003E MoveLibraryFolderToStaging(SelectedLibrary) };\r\n\t\t\tLayout.Add(InstallButton);\r\n\t\t}\r\n\r\n\t\tLayout.Add(new Label(\u0022\u0022));\r\n\t\tLayout.Add(new Label($\u0022Project\u0022));\r\n\r\n\t\tif (hasImportedLibraryToProject)\r\n\t\t{\r\n\t\t\tLayout.Add(new Label($\u0022Imported Version: {importedVersionToProject}\u0022));\r\n\t\t}\r\n\r\n\t\tif (hasImportedLibraryToProject \u0026\u0026 importedVersionDifferentFromInstalled)\r\n\t\t{\r\n\t\t\tInstallButton = new Button(\u0022Reimport to Project\u0022, this) { Pressed = () =\u003E CopyStagedLibraryToProject(SelectedLibrary) };\r\n\t\t\tLayout.Add(InstallButton);\r\n\t\t}\r\n\t\telse if (!hasImportedLibraryToProject)\r\n\t\t{\r\n\t\t\tInstallButton = new Button(\u0022Import to Project\u0022, this) { Pressed = () =\u003E CopyStagedLibraryToProject(SelectedLibrary) };\r\n\t\t\tLayout.Add(InstallButton);\r\n\t\t}\r\n\r\n\t\tif (hasImportedLibraryToProject)\r\n\t\t{\r\n\t\t\tUninstallButton = new Button(\u0022Remove from Project\u0022, this) { Pressed = () =\u003E DeleteImportedLibraryFolderFromProject(SelectedLibrary) };\r\n\t\t\tLayout.Add(UninstallButton);\r\n\t\t}\r\n\r\n\t\tvar otherLibraries = LibrarySystem.All.Where(x =\u003E x.Project.Package.Ident != \u0022libraryimporter\u0022 \u0026\u0026 x.Project.Package.Ident != SelectedLibrary.Project.Package.Ident);\r\n\t\tComboLibrarySelection = otherLibraries.FirstOrDefault();\r\n\r\n\t\tif (ComboLibrarySelection == null)\r\n\t\t{\r\n\t\t\tLayout.AddStretchCell();\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tLayout.Add(new Label(\u0022\u0022));\r\n\t\tLayout.Add(new Label($\u0022Library\u0022));\r\n\r\n\t\tbool hasImportedLibraryToLibrary = importedLibraries.packageIndentToLibraryImportedVersion.ContainsKey(importingLibraryToLibraryID);\r\n\t\tstring importedVersionToLibrary = \u0022\u0022;\r\n\t\tif (hasImportedLibraryToLibrary)\r\n\t\t{\r\n\t\t\timportedVersionToLibrary = importedLibraries.packageIndentToLibraryImportedVersion[importingLibraryToLibraryID];\r\n\t\t}\r\n\t\tbool importedVersionDifferentFromInstalledLibrary = importedVersionToLibrary != SelectedLibrary.Version.ToString();\r\n\r\n\t\tComboLibraryLabel = new Label($\u0022\u0022);\r\n\t\tLayout.Add(ComboLibraryLabel);\r\n\r\n\t\tif (hasImportedLibraryToLibrary)\r\n\t\t{\r\n\t\t\tComboLibraryLabel.Text = $\u0022Imported Version: {importedVersionToLibrary}\u0022;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tComboLibraryLabel.Hide();\r\n\t\t}\r\n\r\n\t\tLibraryList = new ComboBox(this);\r\n\t\tLayout.Add(LibraryList);\r\n\r\n\t\tforeach (var otherLibrary in otherLibraries)\r\n\t\t{\r\n\t\t\tLibraryList.AddItem($\u0022{otherLibrary.Project.Package.Title}\u0022, null, () =\u003E OnComboBoxLibrarySelected(otherLibrary), null, ComboLibrarySelection == otherLibrary);\r\n\t\t}\r\n\r\n\t\tInstallLibraryButton = new Button(\u0022Reimport to Library\u0022, this) { Pressed = () =\u003E CopyStagedLibraryToLibrary(SelectedLibrary, ComboLibrarySelection) };\r\n\t\tLayout.Add(InstallLibraryButton);\r\n\r\n\t\tif (!hasImportedLibraryToLibrary)\r\n\t\t{\r\n\t\t\tInstallLibraryButton.Text = \u0022Import to Library\u0022;\r\n\t\t}\r\n\r\n\t\tif (hasImportedLibraryToLibrary \u0026\u0026 !importedVersionDifferentFromInstalledLibrary)\r\n\t\t{\r\n\t\t\tInstallLibraryButton.Hide();\r\n\t\t}\r\n\r\n\t\tUninstallLibraryButton = new Button(\u0022Remove from Library\u0022, this) { Pressed = () =\u003E DeleteImportedLibraryFolderFromLibrary() };\r\n\t\tLayout.Add(UninstallLibraryButton);\r\n\r\n\t\tif (!hasImportedLibraryToLibrary)\r\n\t\t{\r\n\t\t\tUninstallLibraryButton.Hide();\r\n\t\t}\r\n\r\n\t\t// information about this\r\n\t\tLayout.AddStretchCell();\r\n\t}\r\n\r\n\tvoid OnComboBoxLibrarySelected(LibraryProject libraryProject)\r\n\t{\r\n\t\tComboLibrarySelection = libraryProject;\r\n\t\t// TODO: Check the state of export\r\n\r\n\t\t// Duplicated logic, but fuck it I\u0027m bored of this\r\n\t\tbool hasImportedLibraryToLibrary = importedLibraries.packageIndentToLibraryImportedVersion.ContainsKey(importingLibraryToLibraryID);\r\n\t\tstring importedVersionToLibrary = \u0022\u0022;\r\n\t\tif (hasImportedLibraryToLibrary)\r\n\t\t{\r\n\t\t\timportedVersionToLibrary = importedLibraries.packageIndentToLibraryImportedVersion[importingLibraryToLibraryID];\r\n\t\t}\r\n\t\tbool importedVersionDifferentFromInstalledLibrary = importedVersionToLibrary != SelectedLibrary.Version.ToString();\r\n\r\n\r\n\t\tif (hasImportedLibraryToLibrary)\r\n\t\t{\r\n\t\t\tif (importedVersionDifferentFromInstalledLibrary)\r\n\t\t\t{\r\n\t\t\t\tInstallLibraryButton.Text = \u0022Reimport to Library\u0022;\r\n\t\t\t\tInstallLibraryButton.Show();\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tInstallLibraryButton.Hide();\r\n\t\t\t}\r\n\r\n\t\t\tUninstallLibraryButton.Show();\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tInstallLibraryButton.Text = \u0022Import to Library\u0022;\r\n\t\t\tInstallLibraryButton.Show();\r\n\t\t\tUninstallLibraryButton.Hide();\r\n\t\t}\r\n\t}\r\n\r\n\tvoid CopyStagedLibraryToProject(LibraryProject libraryProject)\r\n\t{\r\n\t\tif (libraryProject?.Project?.Package == null)\r\n\t\t{\r\n\t\t\tLog.Error($\u0022CopyStagedLibraryToProject() error null libraryProject!!!\u0022);\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tstring libraryRootFolder = @$\u0022{libraryStagingFolder}\\{libraryProject.Project.Package.Ident}\u0022;\r\n\r\n\t\tstring libraryCodeFolder = @$\u0022{libraryRootFolder}\\code\u0022;\r\n\t\tstring libraryEditorFolder = @$\u0022{libraryRootFolder}\\Editor\u0022;\r\n\t\tstring libraryAssetsFolder = @$\u0022{libraryRootFolder}\\Assets\u0022;\r\n\r\n\t\tCopyFiles(libraryCodeFolder, projectCodeFolder);\r\n\t\tCopyFiles(libraryEditorFolder, projectEditorFolder);\r\n\t\tCopyFiles(libraryCodeFolder, projectAssetsFolder);\r\n\r\n\t\timportedLibraries.packageIndentToProjectImportedVersion[Package.Ident] = libraryProject.Version.ToString();\r\n\t\timportedLibraries.Save();\r\n\t\t_ = FetchAndBuild();\r\n\t}\r\n\r\n\tvoid CopyStagedLibraryToLibrary(LibraryProject libraryProject, LibraryProject targetLibraryProject)\r\n\t{\r\n\t\tif (libraryProject?.Project?.Package == null || targetLibraryProject?.Project?.Package == null)\r\n\t\t{\r\n\t\t\tLog.Error($\u0022CopyStagedLibraryToLibrary() error null libraryProject or targetLibraryProject!!!\u0022);\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tstring libraryRootFolder = @$\u0022{libraryStagingFolder}\\{libraryProject.Project.Package.Ident}\u0022;\r\n\r\n\t\tstring libraryCodeFolder = @$\u0022{libraryRootFolder}\\code\u0022;\r\n\t\tstring libraryEditorFolder = @$\u0022{libraryRootFolder}\\Editor\u0022;\r\n\t\tstring libraryAssetsFolder = @$\u0022{libraryRootFolder}\\Assets\u0022;\r\n\r\n\t\tCopyFiles(libraryCodeFolder, selectedLibraryCodeFolder);\r\n\t\tCopyFiles(libraryEditorFolder, selectedLibraryCodeFolder);\r\n\t\tCopyFiles(libraryCodeFolder, selectedLibraryAssetsFolder);\r\n\r\n\t\timportedLibraries.packageIndentToLibraryImportedVersion[importingLibraryToLibraryID] = libraryProject.Version.ToString();\r\n\t\timportedLibraries.Save();\r\n\t\t_ = FetchAndBuild();\r\n\t}\r\n\r\n\tvoid MoveLibraryFolderToStaging(LibraryProject libraryProject)\r\n\t{\r\n\t\tif (libraryProject?.Project?.Package == null)\r\n\t\t{\r\n\t\t\tLog.Error($\u0022MoveLibraryFolderToStaging() error null libraryProject!!!\u0022);\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tstring stageFolderRoot = @$\u0022{libraryStagingFolder}\\{libraryProject.Project.Package.Ident}\\\u0022;\r\n\t\tstring libraryRootFolder = $\u0022{libraryProject.Project.RootDirectory.FullName}\u0022;\r\n\r\n\t\tstring libraryCodeFolder = @$\u0022{libraryRootFolder}\\code\u0022;\r\n\t\tstring libraryEditorFolder = @$\u0022{libraryRootFolder}\\Editor\u0022;\r\n\t\tstring libraryAssetsFolder = @$\u0022{libraryRootFolder}\\Assets\u0022;\r\n\t\tstring libraryUnitTestFolder = @$\u0022{libraryRootFolder}\\UnitTests\u0022;\r\n\r\n\t\tstring targetCodeFolder = @$\u0022{libraryStagingFolder}\\{libraryProject.Project.Package.Ident}\\code\u0022;\r\n\t\tstring targetEditorFolder = @$\u0022{libraryStagingFolder}\\{libraryProject.Project.Package.Ident}\\Editor\u0022;\r\n\t\tstring targetAssetsFolder = @$\u0022{libraryStagingFolder}\\{libraryProject.Project.Package.Ident}\\Assets\u0022;\r\n\r\n\t\tif (Directory.Exists(stageFolderRoot))\r\n\t\t{\r\n\t\t\tDirectory.Delete(stageFolderRoot, true);\r\n\t\t}\r\n\r\n\t\tCopyFiles(libraryCodeFolder, targetCodeFolder, true);\r\n\t\tCopyFiles(libraryEditorFolder, targetEditorFolder, true);\r\n\t\tCopyFiles(libraryAssetsFolder, targetAssetsFolder, true);\r\n\r\n\t\ttry\r\n\t\t{\r\n\t\t\t// I don\u0027t care about unit tests, so fuck em\r\n\t\t\tif (Directory.Exists(libraryUnitTestFolder))\r\n\t\t\t{\r\n\t\t\t\tDirectory.Delete(libraryUnitTestFolder, true);\r\n\t\t\t}\r\n\t\t}\r\n\t\tcatch\r\n\t\t{\r\n\t\t\tLog.Error(\u0022Could not delete libraryUnitTestFolder, you\u0027ll have to do it manually.\u0022);\r\n\t\t}\r\n\r\n\t\timportedLibraries.packageIndentToStagedVersion[libraryProject.Project.Package.Ident] = libraryProject.Version.ToString();\r\n\t\timportedLibraries.Save();\r\n\t\t_ = FetchAndBuild();\r\n\t}\r\n\r\n\tvoid CopyFiles(string sourceFolder, string targetFolder, bool moveNotCopy = false)\r\n\t{\r\n\t\tvar relativePaths = GetRelativeFilePaths(sourceFolder, blacklistedFolders, blacklistedFileTypes);\r\n\r\n\t\t//Log.Info($\u0022CopyFiles() relativePaths: {relativePaths.Count()}\u0022);\r\n\r\n\t\tforeach (var path in relativePaths)\r\n\t\t{\r\n\t\t\tvar sourcePath = @$\u0022{sourceFolder}\\{path}\u0022;\r\n\t\t\tvar targetPath = @$\u0022{targetFolder}\\{path}\u0022;\r\n\r\n\t\t\tvar targetDirectory = Path.GetDirectoryName(targetPath);\r\n\t\t\tif (!Directory.Exists(targetDirectory))\r\n\t\t\t{\r\n\t\t\t\tDirectory.CreateDirectory(targetDirectory);\r\n\t\t\t}\r\n\r\n\t\t\tif (moveNotCopy)\r\n\t\t\t{\r\n\t\t\t\tFile.Move(sourcePath, targetPath, true);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tFile.Copy(sourcePath, targetPath, true);\r\n\t\t\t}\r\n\r\n\t\t\t//Log.Info($\u0022{(moveNotCopy ? \u0022Move\u0022 : \u0022Copy\u0022)} sourcePath: {sourcePath} to targetPath: {targetPath}\u0022);\r\n\t\t}\r\n\t}\r\n\r\n\tvoid DeleteImportedLibraryFolderFromProject(LibraryProject libraryProject)\r\n\t{\r\n\t\tif (Directory.Exists(projectCodeFolder))\r\n\t\t{\r\n\t\t\tDirectory.Delete(projectCodeFolder, true);\r\n\t\t}\r\n\t\tif (Directory.Exists(projectEditorFolder))\r\n\t\t{\r\n\t\t\tDirectory.Delete(projectEditorFolder, true);\r\n\t\t}\r\n\t\tif (Directory.Exists(projectAssetsFolder))\r\n\t\t{\r\n\t\t\tDirectory.Delete(projectAssetsFolder, true);\r\n\t\t}\r\n\r\n\t\tstring packageIdent = libraryProject.Project.Package.Ident;\r\n\t\tif (importedLibraries.packageIndentToProjectImportedVersion.ContainsKey(packageIdent))\r\n\t\t{\r\n\t\t\timportedLibraries.packageIndentToProjectImportedVersion.Remove(packageIdent);\r\n\t\t\timportedLibraries.Save();\r\n\t\t}\r\n\t\t_ = FetchAndBuild();\r\n\t}\r\n\r\n\tvoid DeleteImportedLibraryFolderFromLibrary()\r\n\t{\r\n\t\tif (Directory.Exists(selectedLibraryCodeFolder))\r\n\t\t{\r\n\t\t\tDirectory.Delete(selectedLibraryCodeFolder, true);\r\n\t\t}\r\n\t\tif (Directory.Exists(selectedLibraryEditorFolder))\r\n\t\t{\r\n\t\t\tDirectory.Delete(selectedLibraryEditorFolder, true);\r\n\t\t}\r\n\t\tif (Directory.Exists(selectedLibraryAssetsFolder))\r\n\t\t{\r\n\t\t\tDirectory.Delete(selectedLibraryAssetsFolder, true);\r\n\t\t}\r\n\r\n\t\tif (importedLibraries.packageIndentToLibraryImportedVersion.ContainsKey(importingLibraryToLibraryID))\r\n\t\t{\r\n\t\t\timportedLibraries.packageIndentToLibraryImportedVersion.Remove(importingLibraryToLibraryID);\r\n\t\t\timportedLibraries.Save();\r\n\t\t}\r\n\t\t_ = FetchAndBuild();\r\n\t}\r\n\r\n\tvoid DeleteImportedLibraryFolderInLibrary(string packageIndent)\r\n\t{\r\n\t\tstring folder = @$\u0022{selectedLibraryCodeFolder}\u0022;\r\n\t\tstring folderEditor = @$\u0022{selectedLibraryEditorFolder}\u0022;\r\n\r\n\t\tif (Directory.Exists(folder))\r\n\t\t{\r\n\t\t\tDirectory.Delete(folder, true);\r\n\t\t}\r\n\t\tif (Directory.Exists(folderEditor))\r\n\t\t{\r\n\t\t\tDirectory.Delete(folderEditor, true);\r\n\t\t}\r\n\r\n\t\tstring id = $\u0022{ComboLibrarySelection.Project.Package.Ident}-{Package.Ident}\u0022;\r\n\t\tif (importedLibraries.packageIndentToLibraryImportedVersion.ContainsKey(id))\r\n\t\t{\r\n\t\t\timportedLibraries.packageIndentToLibraryImportedVersion.Remove(id);\r\n\t\t\timportedLibraries.Save();\r\n\t\t}\r\n\t}\r\n\r\n\tstatic IEnumerable\u003Cstring\u003E GetRelativeFilePaths(string rootDirectory, List\u003Cstring\u003E blacklistedFolders, List\u003Cstring\u003E blacklistedFileTypes)\r\n\t{\r\n\t\tvar filePaths = new List\u003Cstring\u003E();\r\n\r\n\t\tif (!Directory.Exists(rootDirectory))\r\n\t\t{\r\n\t\t\treturn filePaths;\r\n\t\t}\r\n\r\n\t\tforeach (var filePath in Directory.EnumerateFiles(rootDirectory, \u0022*.*\u0022, SearchOption.AllDirectories))\r\n\t\t{\r\n\t\t\t// Check if the file is in a blacklisted folder\r\n\t\t\tvar directoryName = Path.GetDirectoryName(filePath);\r\n\t\t\tif (blacklistedFolders.Any(folder =\u003E directoryName.Contains(folder, StringComparison.OrdinalIgnoreCase)))\r\n\t\t\t{\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\t// Check if the file has a blacklisted extension\r\n\t\t\tvar fileExtension = Path.GetExtension(filePath);\r\n\t\t\tif (blacklistedFileTypes.Contains(fileExtension, StringComparer.OrdinalIgnoreCase))\r\n\t\t\t{\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\t// Get the relative path\r\n\t\t\tvar relativePath = Path.GetRelativePath(rootDirectory, filePath);\r\n\t\t\tfilePaths.Add(relativePath);\r\n\t\t}\r\n\r\n\t\treturn filePaths;\r\n\t}\r\n\r\n\tpublic string GetAssetsFolder(Project inProject, Package inPackage)\r\n\t{\r\n\t\tif (inProject == null || inPackage == null)\r\n\t\t\treturn null;\r\n\r\n\t\tstring folder = @$\u0022{inProject.GetAssetsPath()}\\libraries\\{inPackage.Ident}\u0022;\r\n\t\treturn folder;\r\n\t}\r\n\r\n\tpublic string GetCodeFolder(Project inProject, Package inPackage)\r\n\t{\r\n\t\tif (inProject == null || inPackage == null)\r\n\t\t\treturn null;\r\n\r\n\t\tstring folder = @$\u0022{inProject.GetCodePath()}\\libraries\\{inPackage.Ident}\u0022;\r\n\t\treturn folder;\r\n\t}\r\n\r\n\tpublic string GetEditorFolder(Project inProject, Package inPackage)\r\n\t{\r\n\t\tif (inProject == null || inPackage == null)\r\n\t\t\treturn null;\r\n\r\n\t\tstring folder = @$\u0022{inProject.GetRootPath()}\\Editor\\libraries\\{inPackage.Ident}\u0022;\r\n\t\treturn folder;\r\n\t}\r\n\r\n\tprotected override Vector2 SizeHint()\r\n\t{\r\n\t\treturn new Vector2( 300, 100 );\r\n\t}\r\n}\r\n\r\npublic class ImportedLibraries\r\n{\r\n\tpublic Dictionary\u003Cstring, string\u003E packageIndentToProjectImportedVersion { get; set; } = new Dictionary\u003Cstring, string\u003E();\r\n\r\n\tpublic Dictionary\u003Cstring, string\u003E packageIndentToLibraryImportedVersion { get; set; } = new Dictionary\u003Cstring, string\u003E();\r\n\r\n\tpublic Dictionary\u003Cstring, string\u003E packageIndentToStagedVersion { get; set; } = new Dictionary\u003Cstring, string\u003E();\r\n\r\n\tstatic string libraryStagingFolder =\u003E $@\u0022{Project.Current.RootDirectory}\\staging\\libraryimporter\u0022;\r\n\tstatic string saveFilePath =\u003E @$\u0022{libraryStagingFolder}\\imported_libraries.json\u0022;\r\n\r\n\tpublic void Save()\r\n\t{\t\t\r\n\t\tvar json = JsonSerializer.Serialize(this, GetSerializerOptions());\r\n\t\tSystem.IO.File.WriteAllText(saveFilePath, json);\r\n\t}\r\n\r\n\tpublic static ImportedLibraries Load()\r\n\t{\r\n\t\tif (!System.IO.File.Exists(saveFilePath))\r\n\t\t{\r\n\t\t\treturn new ImportedLibraries();\r\n\t\t}\r\n\t\tvar fileContents = System.IO.File.ReadAllText(saveFilePath);\r\n\t\tvar inst = JsonSerializer.Deserialize\u003CImportedLibraries\u003E(fileContents, GetSerializerOptions());\r\n\t\treturn inst;\r\n\t}\r\n\r\n\tpublic static JsonSerializerOptions GetSerializerOptions()\r\n\t{\r\n\t\tvar serializerOptions = new JsonSerializerOptions();\r\n\t\tserializerOptions.WriteIndented = true;\r\n\t\treturn serializerOptions;\r\n\t}\r\n}\r\n"},{"Ident":"coomzy.libraryimporter","Path":"libraries/libevents/SortingHelper.cs","FileName":"SortingHelper.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":71858,"Code":"using System.Collections.Generic;\r\nusing System.Linq;\r\n\r\nnamespace Sandbox.Events;\r\n\r\n/// \u003Csummary\u003E\r\n/// Generate an ordering based on a set of first-most and last-most items, and\r\n/// individual constraints between pairs of items. All first-most items will be\r\n/// ordered before all last-most items, and any other items will be put in the\r\n/// middle unless forced to be elsewhere by a constraint.\r\n/// \u003C/summary\u003E\r\ninternal class SortingHelper\r\n{\r\n\tpublic record struct SortConstraint( int EarlierIndex, int LaterIndex )\r\n\t{\r\n\t\tpublic SortConstraint Complement =\u003E new ( LaterIndex, EarlierIndex );\r\n\t}\r\n\r\n\tprivate readonly int _itemCount;\r\n\r\n\tprivate readonly HashSet\u003CSortConstraint\u003E _initialConstraints = new HashSet\u003CSortConstraint\u003E();\r\n\r\n\tprivate readonly HashSet\u003Cint\u003E _first = new HashSet\u003Cint\u003E();\r\n\tprivate readonly HashSet\u003Cint\u003E _last = new HashSet\u003Cint\u003E();\r\n\r\n\tpublic SortingHelper( int itemCount )\r\n\t{\r\n\t\t_itemCount = itemCount;\r\n\t}\r\n\r\n\tpublic void AddConstraint( int earlierIndex, int laterIndex )\r\n\t{\r\n\t\t_initialConstraints.Add( new SortConstraint( earlierIndex, laterIndex ) );\r\n\t}\r\n\r\n\tpublic void AddFirst( int earlierIndex )\r\n\t{\r\n\t\t_first.Add( earlierIndex );\r\n\t}\r\n\r\n\tpublic void AddLast( int laterIndex )\r\n\t{\r\n\t\t_last.Add( laterIndex );\r\n\t}\r\n\r\n\tpublic bool Sort( List\u003Cint\u003E result, out SortConstraint invalidConstraint )\r\n\t{\r\n\t\tvar middle = new HashSet\u003Cint\u003E();\r\n\r\n\t\tfor ( var index = 0; index \u003C _itemCount; \u002B\u002Bindex )\r\n\t\t{\r\n\t\t\tif ( !_first.Contains( index ) \u0026\u0026 !_last.Contains( index ) )\r\n\t\t\t\tmiddle.Add( index );\r\n\t\t}\r\n\r\n\t\tvar allConstraints = new HashSet\u003CSortConstraint\u003E();\r\n\t\tvar newConstraints = new Queue\u003CSortConstraint\u003E();\r\n\t\tvar beforeDict = new Dictionary\u003Cint, HashSet\u003Cint\u003E\u003E();\r\n\t\tvar afterDict = new Dictionary\u003Cint, HashSet\u003Cint\u003E\u003E();\r\n\r\n\t\tbool AddWorkingConstraint( int earlierIndex, int laterIndex, out SortConstraint constraint )\r\n\t\t{\r\n\t\t\tconstraint = new SortConstraint( earlierIndex, laterIndex );\r\n\r\n\t\t\tif ( allConstraints.Contains( constraint.Complement ) )\r\n\t\t\t\treturn false;\r\n\r\n\t\t\tif ( !allConstraints.Add( constraint ) )\r\n\t\t\t\treturn true;\r\n\r\n\t\t\tnewConstraints.Enqueue( constraint );\r\n\r\n\t\t\tif ( !beforeDict.TryGetValue( earlierIndex, out var before ) )\r\n\t\t\t\tbeforeDict.Add( earlierIndex, before = new HashSet\u003Cint\u003E() );\r\n\r\n\t\t\tif ( !afterDict.TryGetValue( laterIndex, out var after ) )\r\n\t\t\t\tafterDict.Add( laterIndex, after = new HashSet\u003Cint\u003E() );\r\n\r\n\t\t\tbefore.Add( laterIndex );\r\n\t\t\tafter.Add( earlierIndex );\r\n\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\t// Add initial constraints\r\n\r\n\t\tforeach ( var initialConstraint in _initialConstraints )\r\n\t\t{\r\n\t\t\tif ( !AddWorkingConstraint( initialConstraint.EarlierIndex, initialConstraint.LaterIndex, out invalidConstraint ) )\r\n\t\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\t// Everything in _first should be before everything in _last\r\n\r\n\t\tforeach ( var earlierIndex in _first )\r\n\t\t{\r\n\t\t\tforeach ( var laterIndex in _last )\r\n\t\t\t{\r\n\t\t\t\tif ( !AddWorkingConstraint( earlierIndex, laterIndex, out invalidConstraint ) )\r\n\t\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Keep propagating constraints until nothing changes\r\n\r\n\t\twhile ( newConstraints.TryDequeue( out var nextConstraint ) )\r\n\t\t{\r\n\t\t\t// if a \u003C b, and b \u003C c, then a \u003C c etc\r\n\r\n\t\t\tif ( beforeDict.TryGetValue( nextConstraint.LaterIndex, out var before ) )\r\n\t\t\t{\r\n\t\t\t\tforeach ( var laterIndex in before )\r\n\t\t\t\t{\r\n\t\t\t\t\tif ( !AddWorkingConstraint( nextConstraint.EarlierIndex, laterIndex, out invalidConstraint ) )\r\n\t\t\t\t\t\treturn false;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif ( afterDict.TryGetValue( nextConstraint.EarlierIndex, out var after ) )\r\n\t\t\t{\r\n\t\t\t\tforeach ( var earlierIndex in after )\r\n\t\t\t\t{\r\n\t\t\t\t\tif ( !AddWorkingConstraint( earlierIndex, nextConstraint.LaterIndex, out invalidConstraint ) )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\treturn false;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Now if we have any items that aren\u0027t using GroupOrder.First, and haven\u0027t\r\n\t\t// determined that they are ordered before another item with GroupOrder.First,\r\n\t\t// we can safely order them after all GroupOrder.First items. And vice versa.\r\n\r\n\t\tforeach ( var middleIndex in middle )\r\n\t\t{\r\n\t\t\tvar isBeforeAnyFirst = beforeDict.TryGetValue( middleIndex, out var before )\r\n\t\t\t\t\u0026\u0026 before.Any( x =\u003E _first.Contains( x ) );\r\n\r\n\t\t\tvar isAfterAnyLast = afterDict.TryGetValue( middleIndex, out var after )\r\n\t\t\t\t\u0026\u0026 after.Any( x =\u003E _last.Contains( x ) );\r\n\r\n\t\t\tif ( !isBeforeAnyFirst )\r\n\t\t\t{\r\n\t\t\t\tforeach ( var earlierIndex in _first )\r\n\t\t\t\t\tAddWorkingConstraint( earlierIndex, middleIndex, out invalidConstraint );\r\n\t\t\t}\r\n\r\n\t\t\tif ( !isAfterAnyLast )\r\n\t\t\t{\r\n\t\t\t\tforeach ( var laterIndex in _last )\r\n\t\t\t\t\tAddWorkingConstraint( middleIndex, laterIndex, out invalidConstraint );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Now lets add items to the final ordering if all items that should be sorted\r\n\t\t// before them are already added to that ordering. We\u0027ll implement this by choosing\r\n\t\t// items that have an empty list / don\u0027t appear in afterDict, and update that\r\n\t\t// dictionary as we go.\r\n\r\n\t\tvar earliestRemaining = new Queue\u003Cint\u003E();\r\n\r\n\t\t// First, seed the queue with everything that\u0027s already not ordered after anything\r\n\r\n\t\tfor ( var index = 0; index \u003C _itemCount; \u002B\u002Bindex )\r\n\t\t{\r\n\t\t\tif ( !afterDict.ContainsKey( index ) )\r\n\t\t\t{\r\n\t\t\t\tearliestRemaining.Enqueue( index );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tresult.Clear();\r\n\r\n\t\twhile ( earliestRemaining.TryDequeue( out var nextIndex ) )\r\n\t\t{\r\n\t\t\tresult.Add( nextIndex );\r\n\r\n\t\t\tforeach ( var laterIndex in beforeDict.TryGetValue( nextIndex, out var laterIndices )\r\n\t\t\t\t? laterIndices : Enumerable.Empty\u003Cint\u003E() )\r\n\t\t\t{\r\n\t\t\t\tvar beforeLater = afterDict[laterIndex];\r\n\t\t\t\tbeforeLater.Remove( nextIndex );\r\n\r\n\t\t\t\tif ( beforeLater.Count == 0 )\r\n\t\t\t\t\tearliestRemaining.Enqueue( laterIndex );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tinvalidConstraint = default;\r\n\t\treturn result.Count == _itemCount;\r\n\t}\r\n}\r\n"}]}