using System; using System.IO; using System.Runtime.CompilerServices; using Needle.Engine.Settings; using Needle.Engine.Utils; using UnityEditor; using UnityEditor.Callbacks; using UnityEngine; namespace Needle.Engine.ProjectBundle { public class BundleEditorCallbacks : UnityEditor.AssetModificationProcessor { private static AssetDeleteResult OnWillDeleteAsset(string assetPath, RemoveAssetOptions options) { if (assetPath.EndsWith(Constants.Extension)) { var basePath = Path.GetDirectoryName(assetPath) + "/" + Path.GetFileNameWithoutExtension(assetPath); // TODO: handle linked folders var embeddedPath = basePath + "~"; var codeGenDir = basePath + ".codegen"; if (Directory.Exists(embeddedPath) || Directory.Exists(codeGenDir)) { var res = EditorUtility.DisplayDialogComplex("Deleting NpmDef file", $"You are about to delete a npm definition file which contains assets in other folders. Do you want to delete linked folders to {Path.GetFileName(assetPath)} also? Make sure you have a backup, this action cannot be undone.\n\nLinked folders:\n{embeddedPath}\n{codeGenDir}", "Ok – Delete linked folders", "Cancel", "Only delete NpmDef file"); switch (res) { case 0: // ok if (Directory.Exists(embeddedPath)) { Actions.DeleteRecursive(embeddedPath); } if (Directory.Exists(codeGenDir)) { Directory.Delete(codeGenDir, true); File.Delete(codeGenDir + ".meta"); } return AssetDeleteResult.DidNotDelete; case 1: // cancel: Debug.Log("Cancelled deleting " + assetPath); return AssetDeleteResult.DidDelete; case 2: // alt: break; } } } return AssetDeleteResult.DidNotDelete; } private static AssetMoveResult OnWillMoveAsset(string sourcePath, string destinationPath) { if (sourcePath.EndsWith(".codegen") && Directory.Exists(sourcePath)) { var npmdefPath = sourcePath.Substring(0, sourcePath.Length - ".codegen".Length) + Constants.Extension; if (File.Exists(npmdefPath)) { Debug.LogWarning("Prevented move of codegen directory because it is linked to the " + Path.GetFileName(npmdefPath) + " - please rename or move " + npmdefPath.AsLink() + " instead."); return AssetMoveResult.DidMove; } } // when moving a npmdef file if (sourcePath.EndsWith(Constants.Extension)) { var sourcePathWithoutExtension = Path.GetDirectoryName(sourcePath) + "/" + Path.GetFileNameWithoutExtension(sourcePath); var hiddenDirPath = sourcePathWithoutExtension + "~"; var hiddenFolder = new DirectoryInfo(hiddenDirPath); var targetFile = new FileInfo(destinationPath); if (hiddenFolder.Exists) { var targetPackageDirectory = targetFile.Directory!.FullName + "/" + Path.GetFileNameWithoutExtension(destinationPath) + "~"; Debug.Log("Move directory from " + hiddenFolder.FullName + " to " + targetPackageDirectory); try { Directory.Move(hiddenFolder.FullName, targetPackageDirectory); } catch (Exception ex) { switch (ex) { case IOException _: case UnauthorizedAccessException _: Debug.LogWarning( "Moving hidden directory failed: preventing rename! The directory is probably locked by some process (e.g. an editor has a file opened or your server is running using this package)."); break; default: Debug.LogException(ex); break; } // prevent rename if this fails return AssetMoveResult.DidMove; } } var codeGenPath = sourcePathWithoutExtension + ".codegen"; if (Directory.Exists(codeGenPath)) { var targetCodeGenDirectory = targetFile.Directory!.FullName + "/" + Path.GetFileNameWithoutExtension(destinationPath) + ".codegen"; try { Directory.Move(codeGenPath, targetCodeGenDirectory); var metaSource = codeGenPath + ".meta"; var metaTarget = targetCodeGenDirectory + ".meta"; File.Move(metaSource, metaTarget); } catch (Exception ex) { switch (ex) { case IOException _: case UnauthorizedAccessException _: Debug.LogWarning( "Moving codegen directory failed: preventing rename! The directory is probably locked by some process (e.g. an editor has a file opened or your server is running using this package)"); break; default: Debug.LogException(ex); break; } // prevent rename if this fails return AssetMoveResult.DidMove; } } // to update scripted importer var sel = Selection.activeObject; Selection.activeObject = null; EditorApplication.delayCall += () => Selection.activeObject = sel; AssetDatabase.ImportAsset(sourcePath); // ensure we collect all types again TypesUtils.MarkDirty(); // ensure moved codegen folder is correctly updated in ui AssetDatabase.Refresh(); EditorApplication.delayCall += AssetDatabase.Refresh; } return AssetMoveResult.DidNotMove; } [OnOpenAsset(100)] private static bool OpenAsset(int instanceID, int line) { // if user double clicked on a typescript sub asset if (EditorUtility.InstanceIDToObject(instanceID) is Typescript typescriptSubAsset) { var path = Path.GetFullPath(typescriptSubAsset.Path); Actions.OpenWorkspace(typescriptSubAsset.NpmDefPath, path); return true; } // if user double clicked on npmdef asset (or any other sub asset type...) var filePath = AssetDatabase.GetAssetPath(instanceID); if (filePath.EndsWith(Constants.Extension)) { Actions.OpenWorkspace(filePath, "package.json"); return true; } return false; } } }