using UnityEngine; using UnityEditor; using UnityEditor.PackageManager; using UnityEditor.PackageManager.Requests; using UnityEngine.Networking; using System.Collections.Generic; using System.IO; using System; using System.Linq; using ShiroginSDK.Runtime.Core.SDK; using ShiroginSDK.Editor.Core; namespace ShiroginSDK.Editor.Bitbucket { // --- DATA STRUCTURES --- [Serializable] public class VersionData { public string version; public string packageName; // NEW: Specific ID for this version (e.g., shirogin.sdk.appsflyer.6.17.72) public string url; public string docsWebUrl; public string docsDownloadUrl; public string releaseNotes; public string fileSize; public bool isExternal; public bool isUpm; public string upmId; } [Serializable] public class PackageData { public string uniqueId; public string displayName; public string category; public VersionData[] versions; } [Serializable] public class PackageManifest { public PackageData[] packages; } // --- CONFIG EDITOR --- [CustomEditor(typeof(ShiroginConfig))] public class ShiroginConfigEditor : UnityEditor.Editor { public override void OnInspectorGUI() { base.OnInspectorGUI(); GUILayout.Space(20); GUILayout.Label("SDK Compilation Control", EditorStyles.boldLabel); GUI.backgroundColor = new Color(0.2f, 0.8f, 0.2f); if (GUILayout.Button("✅ Apply Changes & Compile", GUILayout.Height(40))) { bool confirm = EditorUtility.DisplayDialog("Apply SDK Defines?", "This will trigger a script recompilation.\nEnsure required packages are installed.", "Yes, Compile", "Cancel"); if (confirm) ShiroginDefinesManager.UpdateDefines(); } GUI.backgroundColor = Color.white; GUILayout.Space(10); GUI.backgroundColor = new Color(0.2f, 0.6f, 1f); if (GUILayout.Button("OPEN ASSET MANAGER", GUILayout.Height(40))) { AssetRegistryEditor.ShowWindow(); } GUI.backgroundColor = Color.white; } } // --- EDITOR WINDOW --- [InitializeOnLoad] public class AssetRegistryEditor : EditorWindow { private ShiroginConfig _config; private static AddRequest _upmAddRequest; private static bool _isInstallingUpm; // Essential Packages (Uses EXACT packageName from VersionData) private readonly List _essentialPackages = new List { "shirogin.sdk.googleexternaldependencymanager.1.2.186.Shirogin", "shirogin.sdk.dotweenpro.1.0.380.Shirogin", "com.unity.purchasing.4.13.0", "shirogin.sdk.applovinmax.8.5.1.Shirogin", "shirogin.sdk.firebaseanalytics.13.6.0.Shirogin", "shirogin.sdk.appsflyer.6.17.72.Shirogin", "shirogin.sdk.facebooksdk.18.0.0.Shirogin", "shirogin.sdk.firebaseremoteconfig.13.6.0.Shirogin" }; private readonly List _rootPackages = new List { "Firebase", "Facebook", "AppLovin", "AppsFlyer", "External" }; private Queue _downloadQueue = new Queue(); private PackageData _currentQueueItem; private VersionData _currentQueueVersion; private bool _isQueueRunning = false; private List allPackages = new List(); private List filteredPackages = new List(); private HashSet existingFolderNames = new HashSet(); private bool _isSetupMode = false; private string searchString = ""; private string selectedCategory = "All"; private string[] categories = new string[] { "All" }; private Vector2 scrollPos; private bool showSettings = false; private string jsonListUrl = ""; private string repoToken = ""; private Dictionary packageVersionSelection = new Dictionary(); private bool isWorking = false; private string statusMessage = "Waiting for download..."; private float downloadProgress = 0f; private EditorApplication.CallbackFunction downloadDelegate; private UnityWebRequest currentRequest; static AssetRegistryEditor() { EditorApplication.delayCall += CheckFirstTimeInstall; EditorApplication.update += UpmProgressUpdate; } private static void CheckFirstTimeInstall() { if (!EditorPrefs.GetBool("ShiroginSDK_WelcomeShown_V2", false)) { EditorPrefs.SetBool("ShiroginSDK_WelcomeShown_V2", true); ShowWindow(true); } } private static void UpmProgressUpdate() { if (_isInstallingUpm && _upmAddRequest != null) { if (_upmAddRequest.IsCompleted) { _isInstallingUpm = false; var wnd = GetWindow(); if (_upmAddRequest.Status == StatusCode.Success) { Debug.Log($"[ShiroginSDK] UPM Package Installed: {_upmAddRequest.Result.packageId}"); if(wnd) wnd.RefreshFolderCache(); } else { Debug.LogError($"[ShiroginSDK] UPM Install Failed: {_upmAddRequest.Error.message}"); } if(wnd) { wnd.ProcessNextQueueItem(); wnd.Repaint(); } _upmAddRequest = null; } } } [MenuItem("ShiroginSDK/Shirogin Asset Manager")] public static void ShowWindow() => ShowWindow(false); public static void ShowWindow(bool forceSetupMode) { AssetRegistryEditor wnd = GetWindow("Asset Manager"); wnd.minSize = new Vector2(650, 750); if (forceSetupMode) wnd.SetSetupMode(true); wnd.Show(); } public void SetSetupMode(bool active) { _isSetupMode = active; ApplyFilters(); } private void OnEnable() { _config = ShiroginConfig.Load(); jsonListUrl = EditorPrefs.GetString("Shirogin_RepoUrl", "https://api.bitbucket.org/2.0/repositories/batud/studio-asset-registry/src/main/packages.json"); repoToken = EditorPrefs.GetString("Shirogin_RepoToken", _config != null ? _config.bitbucketAccessToken : ""); RefreshFolderCache(); if (!string.IsNullOrEmpty(jsonListUrl) && !string.IsNullOrEmpty(repoToken)) FetchPackageList(); else showSettings = true; } private void OnDisable() { AbortDownload(); _downloadQueue.Clear(); _isQueueRunning = false; } private void OnFocus() => RefreshFolderCache(); private void RefreshFolderCache() { existingFolderNames.Clear(); string internalSdkPath = Path.Combine(Application.dataPath, "ShiroginSDK", "ThirdPartySDKs"); if (Directory.Exists(internalSdkPath)) { try { string[] dirs = Directory.GetDirectories(internalSdkPath, "*", SearchOption.AllDirectories); foreach (var d in dirs) existingFolderNames.Add(new DirectoryInfo(d).Name.ToLower()); } catch {} } try { string[] rootDirs = Directory.GetDirectories(Application.dataPath, "*", SearchOption.TopDirectoryOnly); foreach (var d in rootDirs) existingFolderNames.Add(new DirectoryInfo(d).Name.ToLower()); } catch {} } // --- GUI --- private void OnGUI() { EditorGUI.DrawRect(new Rect(0, 0, position.width, position.height), new Color(0.22f, 0.22f, 0.22f)); DrawModernHeader(); GUILayout.BeginVertical(); if (_isSetupMode) DrawSetupWizardUI(); else { DrawToolbar(); if (showSettings) DrawSettings(); GUILayout.Space(5); DrawCategories(); GUILayout.Space(5); DrawPackageList(); } GUILayout.EndVertical(); GUILayout.FlexibleSpace(); DrawStaticStatusBar(); } private void DrawModernHeader() { Rect headerRect = new Rect(0, 0, position.width, 60); Color headerColor = _isSetupMode ? new Color(0.1f, 0.3f, 0.5f) : new Color(0.15f, 0.15f, 0.15f); EditorGUI.DrawRect(headerRect, headerColor); GUILayout.BeginArea(headerRect); GUILayout.BeginVertical(); GUILayout.FlexibleSpace(); GUIStyle titleStyle = new GUIStyle(EditorStyles.boldLabel) { fontSize = 18, normal = { textColor = new Color(0.95f, 0.95f, 0.95f) }, alignment = TextAnchor.MiddleCenter }; GUIStyle subStyle = new GUIStyle(EditorStyles.label) { fontSize = 11, normal = { textColor = new Color(0.8f, 0.8f, 0.8f) }, alignment = TextAnchor.MiddleCenter }; string title = _isSetupMode ? "WELCOME TO SHIROGIN SDK" : "SHIROGIN ASSET MANAGER"; string sub = _isSetupMode ? "Setup Wizard & Essential Packages" : "Package Management System"; GUILayout.Label(title, titleStyle); GUILayout.Label(sub, subStyle); GUILayout.FlexibleSpace(); GUILayout.EndVertical(); GUILayout.EndArea(); GUILayout.Space(65); } private void DrawSetupWizardUI() { GUILayout.Space(10); // --- CORE MISSING CHECK --- int missingCount = 0; if(filteredPackages != null) { foreach(var p in filteredPackages) { // Find the REQUIRED version data VersionData requiredVer = p.versions.FirstOrDefault(v => _essentialPackages.Contains(v.packageName)); if(requiredVer != null && !IsPackageInstalled(p, requiredVer)) missingCount++; } } if (missingCount > 0) { GUI.backgroundColor = new Color(1f, 0.6f, 0.6f); EditorGUILayout.HelpBox($"⚠ SDK CORE MISSING ({missingCount} packages). Please install essential packages below.", MessageType.Error); GUI.backgroundColor = Color.white; } else { GUI.backgroundColor = Color.green; EditorGUILayout.HelpBox("✅ SDK Core is fully installed and ready!", MessageType.Info); GUI.backgroundColor = Color.white; } if (string.IsNullOrEmpty(repoToken)) { if(GUILayout.Button("Open Settings to Enter Token")) showSettings = !showSettings; if(showSettings) DrawSettings(); } GUILayout.Space(10); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); bool canDownloadAll = !isWorking && !_isInstallingUpm && missingCount > 0; EditorGUI.BeginDisabledGroup(!canDownloadAll); GUI.backgroundColor = missingCount > 0 ? new Color(0.2f, 0.6f, 1f) : Color.gray; string btnLabel = missingCount > 0 ? $"⬇ Download & Install All Core ({missingCount})" : "All Core Installed ✓"; if (GUILayout.Button(btnLabel, GUILayout.Height(35), GUILayout.Width(300))) { StartDownloadAllQueue(); } GUI.backgroundColor = Color.white; EditorGUI.EndDisabledGroup(); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.Space(10); DrawPackageList(); GUILayout.Space(15); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); if (GUILayout.Button("Continue to Full Dashboard ➔", GUILayout.Height(40), GUILayout.Width(250))) SetSetupMode(false); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } private void DrawToolbar() { GUILayout.BeginHorizontal(EditorStyles.toolbar); if (GUILayout.Button("⚙ Settings", EditorStyles.toolbarButton, GUILayout.Width(80))) showSettings = !showSettings; if (GUILayout.Button("★ Setup Wizard", EditorStyles.toolbarButton, GUILayout.Width(100))) SetSetupMode(true); if (GUILayout.Button("Refresh", EditorStyles.toolbarButton, GUILayout.Width(60))) { RefreshFolderCache(); FetchPackageList(); } if (GUILayout.Button("Clear Cache", EditorStyles.toolbarButton, GUILayout.Width(80))) { ClearCache(); Repaint(); } GUILayout.FlexibleSpace(); GUILayout.Label("Search:", GUILayout.Width(50)); string newSearch = EditorGUILayout.TextField(searchString, EditorStyles.toolbarTextField, GUILayout.Width(150)); if (newSearch != searchString) { searchString = newSearch; ApplyFilters(); } if (GUILayout.Button("×", EditorStyles.toolbarButton, GUILayout.Width(25))) { searchString = ""; GUI.FocusControl(null); ApplyFilters(); } GUILayout.EndHorizontal(); } private void DrawCategories() { if (categories == null || categories.Length == 0) return; GUILayout.BeginHorizontal(); GUILayout.Space(10); foreach (var cat in categories) { bool isSelected = selectedCategory == cat; GUIStyle btnStyle = new GUIStyle(EditorStyles.miniButton); if (isSelected) GUI.backgroundColor = new Color(0.3f, 0.6f, 1f); else if(cat.Contains("Core")) GUI.backgroundColor = new Color(1f, 0.8f, 0.8f); if (GUILayout.Button(cat, btnStyle, GUILayout.MinWidth(80))) { selectedCategory = cat; searchString = ""; GUI.FocusControl(null); ApplyFilters(); } GUI.backgroundColor = Color.white; } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } private void DrawSettings() { GUILayout.BeginVertical(EditorStyles.helpBox); GUILayout.Label("Connection Settings", EditorStyles.boldLabel); EditorGUI.BeginChangeCheck(); jsonListUrl = EditorGUILayout.TextField("JSON List URL", jsonListUrl); repoToken = EditorGUILayout.PasswordField("Access Token", repoToken); if (EditorGUI.EndChangeCheck()) { EditorPrefs.SetString("Shirogin_RepoUrl", jsonListUrl); EditorPrefs.SetString("Shirogin_RepoToken", repoToken); } if (GUILayout.Button("Save Token to Config File")) { if (_config == null) _config = ShiroginConfig.Load(); if (_config != null) { _config.bitbucketAccessToken = repoToken; EditorUtility.SetDirty(_config); AssetDatabase.SaveAssets(); } } GUILayout.EndVertical(); } private void DrawStaticStatusBar() { Rect bar = GUILayoutUtility.GetRect(position.width, 30); EditorGUI.DrawRect(bar, new Color(0.1f, 0.2f, 0.1f)); if (_isQueueRunning) { EditorGUI.DrawRect(bar, new Color(0.5f, 0.2f, 0.8f)); string qText = _currentQueueItem != null ? $"Batch Installing: {_currentQueueItem.displayName} ({_downloadQueue.Count} left)" : "Batch Processing..."; GUI.Label(bar, qText, new GUIStyle(EditorStyles.boldLabel) { alignment = TextAnchor.MiddleCenter, normal = { textColor = Color.white } }); } else if (_isInstallingUpm) { EditorGUI.DrawRect(bar, new Color(0.2f, 0.4f, 0.8f)); GUI.Label(bar, "Installing Package via Unity Package Manager...", new GUIStyle(EditorStyles.boldLabel) { alignment = TextAnchor.MiddleCenter, normal = { textColor = Color.white } }); } else if (isWorking && downloadProgress > 0) { float w = bar.width * downloadProgress; EditorGUI.DrawRect(new Rect(bar.x, bar.y, w, bar.height), new Color(0.2f, 0.8f, 0.2f)); GUI.Label(bar, $"{statusMessage} {(downloadProgress * 100):F0}%", new GUIStyle(EditorStyles.boldLabel) { alignment = TextAnchor.MiddleCenter, normal = { textColor = Color.white } }); } else { GUI.Label(bar, "Ready", new GUIStyle(EditorStyles.label) { alignment = TextAnchor.MiddleCenter, normal = { textColor = Color.gray } }); } if (isWorking || _isQueueRunning) { Rect btnRect = new Rect(bar.width - 85, bar.y + 2, 80, 26); GUI.backgroundColor = new Color(0.9f, 0.3f, 0.3f); if (GUI.Button(btnRect, "CANCEL")) { AbortDownload(); _downloadQueue.Clear(); _isQueueRunning = false; } GUI.backgroundColor = Color.white; } } private void DrawPackageList() { if (filteredPackages == null || filteredPackages.Count == 0) { GUILayout.Label("No packages found.", EditorStyles.centeredGreyMiniLabel); return; } scrollPos = GUILayout.BeginScrollView(scrollPos); foreach (var pkg in filteredPackages) DrawPackageItem(pkg); GUILayout.EndScrollView(); } private void DrawPackageItem(PackageData pkg) { if (pkg.versions == null || pkg.versions.Length == 0) return; // SETUP MODE LOGIC: Force specific version int selectedIndex = 0; bool forceVersion = false; if (_isSetupMode) { for (int i = 0; i < pkg.versions.Length; i++) { if (_essentialPackages.Contains(pkg.versions[i].packageName)) { selectedIndex = i; forceVersion = true; break; } } } else { if (!packageVersionSelection.ContainsKey(pkg.uniqueId)) packageVersionSelection[pkg.uniqueId] = 0; selectedIndex = packageVersionSelection[pkg.uniqueId]; if (selectedIndex >= pkg.versions.Length) selectedIndex = 0; } VersionData vData = pkg.versions[selectedIndex]; // Status Logic bool isInstalled = IsPackageInstalled(pkg, vData); string installedVer = EditorPrefs.GetString("RepoManager_Version_" + pkg.uniqueId, ""); bool isVersionMatch = isInstalled && (installedVer == vData.version); // UPM Hack: Assume version match if folder exists because checking UPM version is slow if (vData.isUpm && isInstalled) isVersionMatch = true; string cachePath = Path.Combine(Application.temporaryCachePath, $"{pkg.uniqueId}_{vData.version}.unitypackage"); bool isCached = !vData.isUpm && File.Exists(cachePath); // COLOR LOGIC Color boxColor = Color.gray; if (isInstalled) { if (isVersionMatch) boxColor = new Color(0.2f, 0.6f, 0.2f); // Green (Correct Version) else boxColor = new Color(0.2f, 0.5f, 0.9f); // Blue (Wrong Version) } else if (isCached) { boxColor = new Color(0.2f, 0.8f, 0.8f); // Cyan (Cached) } else if (_isSetupMode) { boxColor = new Color(0.8f, 0.3f, 0.3f); // Red (Missing in Setup) } else { boxColor = new Color(0.3f, 0.3f, 0.3f); // Gray (Missing) } GUI.backgroundColor = boxColor; GUILayout.BeginVertical(EditorStyles.helpBox); GUI.backgroundColor = Color.white; GUILayout.BeginHorizontal(); string icon = vData.isUpm ? "📦" : (vData.isExternal ? "🌍" : "📦"); GUIStyle labelStyle = new GUIStyle(EditorStyles.boldLabel); labelStyle.normal.textColor = Color.white; GUILayout.Label(new GUIContent($" {pkg.displayName}", icon), labelStyle, GUILayout.Width(200)); // Version Selection if (forceVersion) { GUIStyle staticVerStyle = new GUIStyle(EditorStyles.label); staticVerStyle.normal.textColor = new Color(0.9f, 0.9f, 0.9f); GUILayout.Label($"v{vData.version} (Required)", staticVerStyle, GUILayout.Width(200)); } else { string[] options = pkg.versions.Select(v => v.version).ToArray(); int newIndex = EditorGUILayout.Popup(selectedIndex, options, GUILayout.Width(80)); if (newIndex != selectedIndex) packageVersionSelection[pkg.uniqueId] = newIndex; } // Docs if (!string.IsNullOrEmpty(vData.docsWebUrl)) { if (GUILayout.Button("🌐", EditorStyles.miniButton, GUILayout.Width(30))) Application.OpenURL(vData.docsWebUrl); } if (!string.IsNullOrEmpty(vData.docsDownloadUrl)) { if (GUILayout.Button("⬇", EditorStyles.miniButton, GUILayout.Width(30))) StartDownloadDocs(vData.docsDownloadUrl, pkg.category, pkg.displayName, vData.version); } GUILayout.FlexibleSpace(); // Status Label GUIStyle statusStyle = new GUIStyle(EditorStyles.label) { alignment = TextAnchor.MiddleRight, normal = { textColor = Color.white } }; if (isInstalled) { string statusText = isVersionMatch ? "✔ Installed" : $"⚠ v{installedVer}"; GUILayout.Label(statusText, statusStyle, GUILayout.Width(80)); } else if (isCached) { GUILayout.Label("⬇ Cached", statusStyle, GUILayout.Width(80)); } // ACTION BUTTON EditorGUI.BeginDisabledGroup(isWorking || _isInstallingUpm); string btnText = "Install"; if (isInstalled) { if (isVersionMatch) btnText = "Re-Install"; else btnText = "Update"; } else if (isCached) { btnText = "Import (Cached)"; } else { btnText = "Download"; } if (GUILayout.Button(btnText, GUILayout.Width(110))) { _downloadQueue.Clear(); _isQueueRunning = false; InstallSpecificPackage(pkg, vData); } EditorGUI.EndDisabledGroup(); // Delete / Verify if (!vData.isUpm) { if (isInstalled || isCached) { if (GUILayout.Button("🗑", GUILayout.Width(25))) { if (isCached) try { File.Delete(cachePath); } catch {} if (isInstalled) EditorPrefs.DeleteKey("RepoManager_Version_" + pkg.uniqueId); RefreshFolderCache(); } } } GUILayout.EndHorizontal(); if(!string.IsNullOrEmpty(vData.releaseNotes)) { GUIStyle noteStyle = new GUIStyle(EditorStyles.miniLabel) { normal = { textColor = new Color(0.9f,0.9f,0.9f) } }; GUILayout.Label(vData.releaseNotes, noteStyle); } GUILayout.EndVertical(); } // --- INSTALL LOGIC --- private void InstallSpecificPackage(PackageData pkg, VersionData vData) { if (vData.isUpm) { InstallUpmPackage(vData.upmId, vData.version); } else { string cachePath = Path.Combine(Application.temporaryCachePath, $"{pkg.uniqueId}_{vData.version}.unitypackage"); if (File.Exists(cachePath)) InstallPackageLocal(cachePath, pkg.uniqueId, vData.version); else StartDownloadPackage(vData.url, pkg.uniqueId, vData.version); } } // --- QUEUE --- private void StartDownloadAllQueue() { if (filteredPackages == null || filteredPackages.Count == 0) return; _downloadQueue.Clear(); foreach (var pkg in filteredPackages) { VersionData targetVer = null; // Setup Mode: Find exact version if (_isSetupMode) { targetVer = pkg.versions.FirstOrDefault(v => _essentialPackages.Contains(v.packageName)); } else { // Normal Mode: Selected version int index = 0; if(packageVersionSelection.ContainsKey(pkg.uniqueId)) index = packageVersionSelection[pkg.uniqueId]; if(index < pkg.versions.Length) targetVer = pkg.versions[index]; } if (targetVer != null && !IsPackageInstalled(pkg, targetVer)) { _downloadQueue.Enqueue(pkg); } } if (_downloadQueue.Count > 0) { _isQueueRunning = true; ProcessNextQueueItem(); } else { EditorUtility.DisplayDialog("Setup Wizard", "All required packages are already installed!", "OK"); } } private void ProcessNextQueueItem() { if (!_isQueueRunning) return; if (_downloadQueue.Count > 0) { _currentQueueItem = _downloadQueue.Dequeue(); // Determine Version again VersionData vData = null; if (_isSetupMode) { vData = _currentQueueItem.versions.FirstOrDefault(v => _essentialPackages.Contains(v.packageName)); } else { int index = 0; if(packageVersionSelection.ContainsKey(_currentQueueItem.uniqueId)) index = packageVersionSelection[_currentQueueItem.uniqueId]; vData = _currentQueueItem.versions[index]; } if (vData != null) { Debug.Log($"[Queue] Processing: {_currentQueueItem.displayName} ({vData.version})"); InstallSpecificPackage(_currentQueueItem, vData); } else { ProcessNextQueueItem(); // Skip if invalid } } else { _isQueueRunning = false; _currentQueueItem = null; EditorUtility.DisplayDialog("Setup Wizard", "All packages have been installed!", "OK"); } } // --- HELPERS --- private bool IsPackageInstalled(PackageData pkg, VersionData vData) { if (vData.isUpm && !string.IsNullOrEmpty(vData.upmId)) { string upmPath = Path.Combine("Packages", vData.upmId); if (AssetDatabase.IsValidFolder(upmPath)) return true; string manifestPath = Path.Combine(Application.dataPath, "..", "Packages", "manifest.json"); if (File.Exists(manifestPath)) { string content = File.ReadAllText(manifestPath); if (content.Contains($"\"{vData.upmId}\"")) return true; } return false; } bool folderFound = IsPackageFolderFound(pkg); string storedVer = EditorPrefs.GetString("RepoManager_Version_" + pkg.uniqueId, ""); return folderFound || !string.IsNullOrEmpty(storedVer); } private void InstallUpmPackage(string packageId, string version) { _isInstallingUpm = true; string id = string.IsNullOrEmpty(version) ? packageId : $"{packageId}@{version}"; Debug.Log($"[Asset Manager] Requesting UPM Install: {id}"); _upmAddRequest = Client.Add(id); } private bool IsPackageFolderFound(PackageData pkg) { string simpleName = pkg.displayName.Replace(" SDK", "").Replace(" ", "").ToLower(); if (existingFolderNames.Contains(simpleName)) return true; if (existingFolderNames.Contains(pkg.displayName.ToLower())) return true; if (_rootPackages.Contains(pkg.displayName) && existingFolderNames.Contains(pkg.displayName.ToLower())) return true; return false; } private void FetchPackageList() { if (isWorking) return; StartWebOperation(jsonListUrl, OnJsonListDownloaded, null); } private void StartDownloadPackage(string url, string pkgId, string version) { if (isWorking) return; string fileName = $"{pkgId}_{version}.unitypackage"; string tempPath = Path.Combine(Application.temporaryCachePath, fileName); StartWebOperation(url, (data) => InstallPackageLocal(tempPath, pkgId, version), tempPath); } private void StartDownloadDocs(string url, string category, string pkgName, string version) { if (isWorking) return; string safeName = string.Join("_", pkgName.Split(Path.GetInvalidFileNameChars())); string docsRoot = Path.Combine(Application.dataPath, "ShiroginSDK", "ThirdPartySDKs"); string targetFolder = Path.Combine(docsRoot, category, safeName); if (!Directory.Exists(targetFolder)) Directory.CreateDirectory(targetFolder); string targetPath = Path.Combine(targetFolder, $"{safeName}_{version}.md"); StartWebOperation(url, (data) => { if (data != null && data.Length > 0) { File.WriteAllBytes(targetPath, data); EditorUtility.DisplayDialog("Download Complete", $"Documentation saved to:\n{targetPath}", "OK"); string relPath = "Assets" + targetPath.Substring(Application.dataPath.Length); AssetDatabase.ImportAsset(relPath); var obj = AssetDatabase.LoadAssetAtPath(relPath); EditorGUIUtility.PingObject(obj); } }, targetPath); } private void InstallPackageLocal(string path, string pkgId, string version) { if (!File.Exists(path)) return; AssetDatabase.ImportPackage(path, true); EditorPrefs.SetString("RepoManager_Version_" + pkgId, version); RefreshFolderCache(); Repaint(); if (_isQueueRunning) ProcessNextQueueItem(); } private void AbortDownload() { if (currentRequest != null) { currentRequest.Abort(); currentRequest.Dispose(); currentRequest = null; } if (downloadDelegate != null) { EditorApplication.update -= downloadDelegate; downloadDelegate = null; } isWorking = false; statusMessage = "Waiting..."; downloadProgress = 0f; Repaint(); } private void StartWebOperation(string url, Action onComplete, string savePath = null) { AbortDownload(); isWorking = true; statusMessage = "Connecting..."; downloadProgress = 0f; downloadDelegate = () => WebRequestRoutine(url, savePath, onComplete); EditorApplication.update += downloadDelegate; } private void WebRequestRoutine(string url, string savePath, Action onComplete) { if (currentRequest == null) { currentRequest = UnityWebRequest.Get(url); if (url.Contains("bitbucket.org") && !string.IsNullOrEmpty(repoToken)) currentRequest.SetRequestHeader("Authorization", "Bearer " + repoToken); if (!string.IsNullOrEmpty(savePath)) currentRequest.downloadHandler = new DownloadHandlerFile(savePath); else currentRequest.downloadHandler = new DownloadHandlerBuffer(); currentRequest.SendWebRequest(); } if (!currentRequest.isDone) { downloadProgress = currentRequest.downloadProgress; statusMessage = "Downloading..."; Repaint(); return; } bool success = currentRequest.result == UnityWebRequest.Result.Success; byte[] data = null; if (success) { if (string.IsNullOrEmpty(savePath) && currentRequest.downloadHandler is DownloadHandlerBuffer) { data = currentRequest.downloadHandler.data; } if(!string.IsNullOrEmpty(savePath)) data = new byte[1]; } else if (currentRequest.error != "Request aborted") { Debug.LogError($"Network Error: {currentRequest.error}"); EditorUtility.DisplayDialog("Download Failed", currentRequest.error, "OK"); if (!string.IsNullOrEmpty(savePath) && File.Exists(savePath)) try { File.Delete(savePath); } catch {} } currentRequest.Dispose(); currentRequest = null; EditorApplication.update -= downloadDelegate; downloadDelegate = null; isWorking = false; Repaint(); if(success) onComplete?.Invoke(data); } private void OnJsonListDownloaded(byte[] data) { if (data == null) return; try { PackageManifest manifest = JsonUtility.FromJson(System.Text.Encoding.UTF8.GetString(data)); if (manifest != null && manifest.packages != null) { allPackages = new List(manifest.packages); var catList = new HashSet { "All" }; foreach (var p in allPackages) if (!string.IsNullOrEmpty(p.category)) catList.Add(p.category); categories = catList.OrderBy(c => c).ToArray(); ApplyFilters(); Repaint(); } } catch (Exception e) { Debug.LogError("JSON Error: " + e.Message); } } private void ApplyFilters() { if (allPackages == null) return; if (_isSetupMode) { // Check against packageName instead of uniqueId filteredPackages = allPackages.Where(p => p.versions.Any(v => _essentialPackages.Contains(v.packageName)) ).ToList(); } else { filteredPackages = allPackages.Where(p => { bool catMatch = selectedCategory == "All" || p.category == selectedCategory; bool searchMatch = string.IsNullOrEmpty(searchString) || p.displayName.ToLower().Contains(searchString.ToLower()); return catMatch && searchMatch; }).ToList(); } } private void ClearCache() { string path = Application.temporaryCachePath; var info = new DirectoryInfo(path); foreach (var f in info.GetFiles("*.unitypackage")) f.Delete(); Debug.Log("Cache cleared."); } } }