diff --git a/Assets/00.StaryEvoTools/Editor/Build/BuildApkWindow.cs b/Assets/00.StaryEvoTools/Editor/Build/BuildApkWindow.cs
index 401a27b..0073c02 100644
--- a/Assets/00.StaryEvoTools/Editor/Build/BuildApkWindow.cs
+++ b/Assets/00.StaryEvoTools/Editor/Build/BuildApkWindow.cs
@@ -1,202 +1,523 @@
+using System;
using System.Collections.Generic;
+using System.IO;
+using System.Linq;
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using UnityEditor;
using UnityEditor.Build.Reporting;
+using UnityEditor.SceneManagement;
using UnityEngine;
+using UnityEngine.SceneManagement;
namespace Stary.Evo.Editor
{
+ ///
+ /// Android APK打包工具窗口
+ ///
public class BuildApkWindow : OdinEditorWindow
{
- public static OdinEditorWindow window;
+ #region 字段与属性
+
+ private static OdinEditorWindow window;
+ private bool _isBuilding;
+ private UnityEditor.Build.Reporting.BuildReport _lastBuildReport;
+ private string[] _scenePaths;
+
+ [Title("设备类型选择", titleAlignment: TitleAlignments.Centered)] [EnumToggleButtons, HideLabel]
+ public DeviceType deviceType = DeviceType.Xreal;
+
+ [Title("包裹列表", titleAlignment: TitleAlignments.Centered)]
+ [LabelText("Domain子包")]
+ [ValueDropdown("GetAvailablePackageNames")]
+ [OnValueChanged("OnPackageNameChanged")]
+ public string selectedPackageName;
+
+ [Title("打包配置", titleAlignment: TitleAlignments.Centered)] [ToggleLeft] [LabelText("自动递增版本号")]
+ public bool autoIncrementVersion = true;
+
+ [Title("打包配置", titleAlignment: TitleAlignments.Centered)] [ToggleLeft] [LabelText("是否添加水印")]
+ public bool isWatermark = false;
+
+ [ToggleLeft] [LabelText("开发构建")] public bool developmentBuild = false;
+
+ [ToggleLeft] [LabelText("构建完成后打开文件夹")] public bool openFolderOnComplete = true;
+
+ [BoxGroup("缓存管理", centerLabel: true)]
+ [Button("清空打包缓存", ButtonSizes.Large)]
+ private void ClearBuildCache()
+ {
+ ClearCache();
+ }
+
+ [ButtonGroup]
+ [Button("本地普通包", ButtonSizes.Large, ButtonStyle.FoldoutButton)]
+ private void BuildNormalPackage()
+ {
+ StartBuild(PLayerMode.LOCAL_PLAYMODE);
+ }
+ [Button("服务器热更包", ButtonSizes.Large, ButtonStyle.FoldoutButton)]
+ private void BuildServerPackage()
+ {
+ StartBuild(PLayerMode.HOST_PLAYMODE);
+ }
+
+ [Title("打包状态", titleAlignment: TitleAlignments.Centered)] [ReadOnly] [LabelText("当前状态")]
+ public string buildStatus = "就绪";
+
+ [ProgressBar(0, 100)] [ReadOnly] [LabelText("打包进度")]
+ public int buildProgress;
+
+ #endregion
+
+ #region 菜单与初始化
[MenuItem("Evo/Dev/Apk打包工具", false, 4)]
- static void ShowWindows()
+ private static void ShowWindow()
{
- window = (BuildApkWindow)EditorWindow.GetWindow(typeof(BuildApkWindow));
- window.maxSize = new Vector2(400, 500);
+ window = GetWindow(false, "APK打包工具", true);
+ window.maxSize = new Vector2(500, 700);
+ window.minSize = new Vector2(400, 600);
window.Show();
}
protected override void Initialize()
{
base.Initialize();
- buildApkLocalEntity = new BuildApkEntity(() => { BuildAndroid(false); }, null);
- buildApkServerEntity = new BuildApkEntity(null, null);
- buildApkWatermarkEntity = new BuildApkEntity(() => { BuildAndroid(true); }, null);
+ FindScenesWithLoadDll();
+ UpdateAvailablePackages();
}
- [EnumToggleButtons, HideLabel] public DeviceType deviceType;
+ #endregion
+ #region 核心功能
- [HideLabel] [Title("包裹列表", titleAlignment: TitleAlignments.Centered)] [ValueDropdown("GetBuildPackageNames")]
- public string selectedPackageNames;
-
-
- [BoxGroup("Build(清空打包缓存)", centerLabel: true, order: 1)]
- [Button("清空打包缓存")]
- void OnClearCache()
+ ///
+ /// 开始打包流程
+ ///
+ /// 是否为水印包
+ private void StartBuild(PLayerMode playMode)
{
- // 清理Library缓存目录
- // 使用System.IO删除
- // try {
- // System.IO.Directory.Delete(Application.dataPath + "/../Library/AssetImportState", true);
- // Debug.Log("成功删除AssetImportState");
- // } catch (System.Exception e) {
- // Debug.LogError($"删除失败: {e.Message}");
- // }
- // 使用System.IO删除
- try
+ if (_isBuilding)
{
- System.IO.Directory.Delete(Application.dataPath + "/../Library/BuildCache", true);
- Debug.Log("成功删除BuildCache");
- }
- catch (System.Exception e)
- {
- Debug.LogError($"删除失败: {e.Message}");
- }
-
- // 使用System.IO删除
- try
- {
- System.IO.Directory.Delete(Application.dataPath + "/../Library/ScriptAssemblies", true);
- Debug.Log("成功删除ScriptAssemblies");
- }
- catch (System.Exception e)
- {
- Debug.LogError($"删除失败: {e.Message}");
- }
-
- AssetDatabase.Refresh();
- EditorUtility.DisplayDialog("缓存清理", "已成功清除所有打包缓存文件", "确定");
- }
-
- [BoxGroup("Build(普通包\\服务器本地包)", centerLabel: true, order: 2), HideLabel]
- public BuildApkEntity buildApkLocalEntity;
-
- [BoxGroup("Build(服务器热更包)", centerLabel: true, order: 3), HideLabel]
- public BuildApkEntity buildApkServerEntity;
-
- [BoxGroup("Build(水印包)", centerLabel: true, order: 4), HideLabel]
- public BuildApkEntity buildApkWatermarkEntity;
-
- public void BuildAndroid(bool isWatermark)
- {
- HotfixMainResDomain hotfixMainResDomain = Resources.Load("HotfixMainResDomain");
- if (hotfixMainResDomain == null)
- {
- Debug.LogError("HotfixMainResDomain 资源在Resources下不存在,请检查!");
+ Debug.LogWarning("打包正在进行中,请稍候...");
return;
}
+
+ _isBuilding = true;
+ buildStatus = "准备打包...";
+ buildProgress = 0;
+ Repaint();
+
+ try
+ {
+ // 执行打包
+ BuildAndroid(playMode);
+ }
+ catch (Exception e)
+ {
+ buildStatus = $"打包失败: {e.Message}";
+ Debug.LogError($"打包过程中发生错误: {e}");
+ ShowNotification(new GUIContent("APK打包失败!"), 3f);
+ }
+ finally
+ {
+ _isBuilding = false;
+ buildProgress = 100;
+ Repaint();
+ }
+ }
+
+ ///
+ /// Android打包核心逻辑
+ ///
+ private void BuildAndroid(PLayerMode pLayerMode)
+ {
+ buildStatus = "加载配置文件...";
+ Repaint();
+
+
+ buildStatus = "设置打包模式...";
+ Repaint();
ChangePlayerSchema.SetPlayerMode(PLayerMode.HOST_PLAYMODE);
- string packageName = "";
- // 检测包名格式
- if (!IsValidPackageName(selectedPackageNames))
- {
- // 不是有效包名格式,添加前缀 com.xosmo.
- packageName = "com.xosmo." + selectedPackageNames;
- // 设置包名和项目名
- PlayerSettings.productName = selectedPackageNames;
- PlayerSettings.applicationIdentifier = packageName;
- }
- else
- {
- // 分割包名字符串
- string[] segments = selectedPackageNames.Split('.');
- // 返回最后一个部分
- packageName = segments[segments.Length - 1];
- PlayerSettings.productName = packageName;
- PlayerSettings.applicationIdentifier = selectedPackageNames;
- }
+ buildStatus = "配置包名...";
+ Repaint();
+ ConfigurePackageInfo();
- string[] scenelist = default;
+ buildStatus = "配置场景列表...";
+ Repaint();
+ string[] sceneList = ConfigureScenes();
+
+ buildStatus = "配置水印信息...";
+ Repaint();
if (isWatermark)
{
- scenelist = new[]
- {
- hotfixMainResDomain.projectInfo.loadingScenePath,
- $"Assets/Main/main_{deviceType.ToString()}.unity"
- };
- PlayerSettings.productName += "_watermark";
- PlayerSettings.applicationIdentifier += "_watermark";
- Sprite xosmo = Resources.Load("logo");
- PlayerSettings.virtualRealitySplashScreen = xosmo.texture;
- PlayerSettings.SplashScreen.showUnityLogo = false;
- PlayerSettings.SplashScreen.logos = new[]
- {
- PlayerSettings.SplashScreenLogo.Create(3f, xosmo),
- };
+ ConfigureWatermark();
}
- else
+
+ buildStatus = "执行打包...";
+ Repaint();
+
+ // 打包前检查
+ if (!PreBuildCheck())
{
- scenelist = new[]
- {
- $"Assets/Main/main_{deviceType.ToString()}.unity"
- };
- PlayerSettings.virtualRealitySplashScreen = null;
- PlayerSettings.SplashScreen.showUnityLogo = false;
- PlayerSettings.SplashScreen.logos = null;
+ return;
}
// 配置构建选项
- // BuildPlayerOptions buildOptions = new BuildPlayerOptions
- // {
- // scenes = scenelist,
- // locationPathName = $"Builds/Android/{PlayerSettings.productName}.apk",
- // target = BuildTarget.Android,
- // options = BuildOptions.None
- // };
- //
- // // 执行打包
- // var report = BuildPipeline.BuildPlayer(buildOptions);
- //
- // BuildSummary summary = report.summary;
- // if (summary.result == BuildResult.Succeeded)
- // {
- // Debug.Log("Build succeeded: " + summary.totalSize + " bytes");
- // }
- //
- // if (summary.result == BuildResult.Failed)
- // {
- // Debug.LogError("Build failed");
- // }
+ BuildPlayerOptions buildOptions = ConfigureBuildOptions(sceneList, isWatermark);
+
+ // 执行打包
+ var report = BuildPipeline.BuildPlayer(buildOptions);
+ _lastBuildReport = report;
+
+ // 处理打包结果
+ HandleBuildResult(report, buildOptions.locationPathName);
}
-
- public enum DeviceType
- {
- Xreal,
- Rokid
- }
-
-
///
- /// 检测当前所有包裹
+ /// 配置包名和产品名称
///
- ///
- private List GetBuildPackageNames()
+ private void ConfigurePackageInfo()
{
- List result = new List();
- foreach (var name in CreatAssetWindow.GetCreatDomainAllName())
+ string productName;
+ string packageName;
+
+ if (!IsValidPackageName(selectedPackageName))
{
- result.Add(name);
+ // 不是有效包名格式,添加前缀 com.xosmo.
+ productName = selectedPackageName;
+ packageName = "com.xosmo." + selectedPackageName;
+ }
+ else
+ {
+ // 分割包名字符串,使用最后一个部分作为产品名称
+ string[] segments = selectedPackageName.Split('.');
+ productName = segments.Last();
+ packageName = selectedPackageName;
}
- if (selectedPackageNames.IsNullOrEmpty())
+ PlayerSettings.productName = productName;
+ PlayerSettings.applicationIdentifier = packageName;
+
+ // 自动递增版本号
+ if (autoIncrementVersion)
{
- if (result.Count > 0)
+ IncrementBuildVersion();
+ }
+ }
+
+ ///
+ /// 配置场景列表
+ ///
+ /// 是否为水印包
+ /// 资源配置
+ /// 场景路径数组
+ private string[] ConfigureScenes()
+ {
+ List scenes = new List();
+ string mainScenePath = $"Assets/Main/main_{deviceType.ToString()}.unity";
+
+ // 验证主场景是否存在
+ if (!File.Exists(mainScenePath))
+ {
+ throw new Exception($"主场景文件不存在: {mainScenePath}");
+ }
+
+ if (deviceType == DeviceType.Xreal && isWatermark)
+ {
+ // 加载资源配置
+ HotfixMainResDomain hotfixMainResDomain = Resources.Load("HotfixMainResDomain");
+ if (hotfixMainResDomain == null)
{
- selectedPackageNames = result[0];
+ throw new Exception("HotfixMainResDomain资源在Resources下不存在,请检查!");
+ }
+
+ // 水印包包含水印场景
+ string loadingScenePath = hotfixMainResDomain.projectInfo.loadingScenePath;
+ if (!string.IsNullOrEmpty(loadingScenePath) && File.Exists(loadingScenePath))
+ {
+ scenes.Add(loadingScenePath);
}
else
{
- selectedPackageNames = "";
+ Debug.LogWarning($"加载场景不存在或未配置,仅打包主场景: {loadingScenePath}");
}
}
- return result;
+ foreach (var scenePath in _scenePaths)
+ {
+ if (mainScenePath == scenePath)
+ {
+ LoadSceneForEditing(mainScenePath);
+ }
+ }
+
+ scenes.Add(mainScenePath);
+ return scenes.ToArray();
+ }
+
+ ///
+ /// 配置水印信息
+ ///
+ private void ConfigureWatermark()
+ {
+ PlayerSettings.productName += "_watermark";
+ PlayerSettings.applicationIdentifier += "_watermark";
+
+ Sprite logoSprite = Resources.Load("logo");
+ if (logoSprite != null)
+ {
+ PlayerSettings.virtualRealitySplashScreen = logoSprite.texture;
+ PlayerSettings.SplashScreen.showUnityLogo = false;
+ PlayerSettings.SplashScreen.logos = new[]
+ {
+ PlayerSettings.SplashScreenLogo.Create(3f, logoSprite)
+ };
+ }
+ else
+ {
+ Debug.LogWarning("Resources下未找到logo图片,水印包将不显示自定义logo");
+ }
+ }
+
+ ///
+ /// 配置构建选项
+ ///
+ /// 场景列表
+ /// 是否为水印包
+ /// 构建选项
+ private BuildPlayerOptions ConfigureBuildOptions(string[] scenes, bool isWatermark)
+ {
+ string buildPath = GetBuildPath(isWatermark);
+
+ // 确保构建目录存在
+ string buildDir = Path.GetDirectoryName(buildPath);
+ if (!Directory.Exists(buildDir))
+ {
+ Directory.CreateDirectory(buildDir);
+ }
+
+ BuildOptions buildOptions = BuildOptions.None;
+ if (developmentBuild)
+ {
+ buildOptions |= BuildOptions.Development;
+ buildOptions |= BuildOptions.AllowDebugging;
+ buildOptions |= BuildOptions.ConnectWithProfiler;
+ }
+
+ return new BuildPlayerOptions
+ {
+ scenes = scenes,
+ locationPathName = buildPath,
+ target = BuildTarget.Android,
+ options = buildOptions
+ };
+ }
+
+ ///
+ /// 获取构建路径
+ ///
+ /// 是否为水印包
+ /// 构建路径
+ private string GetBuildPath(bool isWatermark)
+ {
+ string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
+ string packageType = isWatermark ? "watermark" : "normal";
+ string buildFileName = $"{PlayerSettings.productName}_{packageType}_{timestamp}.apk";
+ return Path.Combine("Builds", "Android", buildFileName);
+ }
+
+ ///
+ /// 处理打包结果
+ ///
+ /// 构建报告
+ /// 构建路径
+ private void HandleBuildResult(UnityEditor.Build.Reporting.BuildReport report, string buildPath)
+ {
+ BuildSummary summary = report.summary;
+ if (summary.result == BuildResult.Succeeded)
+ {
+ buildStatus = $"打包成功!大小: {FormatBytes(summary.totalSize)}";
+ Debug.Log($"构建成功: {buildPath},大小: {FormatBytes(summary.totalSize)}");
+ ShowNotification(new GUIContent("APK打包成功!"), 3f);
+
+ // 构建完成后打开文件夹
+ if (openFolderOnComplete)
+ {
+ OpenBuildFolder(Path.GetDirectoryName(buildPath));
+ }
+ }
+ else
+ {
+ buildStatus = "打包失败!";
+ Debug.LogError("构建失败");
+ ShowNotification(new GUIContent("APK打包失败!"), 3f);
+
+ // 显示详细错误信息
+ foreach (var step in report.steps)
+ {
+ foreach (var message in step.messages)
+ {
+ if (message.type == LogType.Error)
+ {
+ Debug.LogError($"构建错误: {message.content}");
+ }
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ #region 辅助功能
+
+ ///
+ /// 打包前检查
+ ///
+ /// 检查是否通过
+ private bool PreBuildCheck()
+ {
+ // 检查是否选择了包裹
+ if (string.IsNullOrEmpty(selectedPackageName))
+ {
+ ShowNotification(new GUIContent("请先选择一个包裹!"), 2f);
+ return false;
+ }
+
+ // 检查当前平台
+ if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.Android)
+ {
+ if (EditorUtility.DisplayDialog("平台提示", "当前平台不是Android,是否切换到Android平台?", "是", "否"))
+ {
+ EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Android, BuildTarget.Android);
+ return false; // 切换平台后需要重新检查
+ }
+
+ return false;
+ }
+
+ // 检查场景是否保存
+ if (!EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo())
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// 清空打包缓存
+ ///
+ private void ClearCache()
+ {
+ try
+ {
+ buildStatus = "正在清理缓存...";
+ Repaint();
+
+ string projectPath = Application.dataPath;
+ string libraryPath = Path.GetFullPath(Path.Combine(projectPath, "../Library"));
+
+ // 要清理的缓存目录
+ string[] cacheDirectories =
+ {
+ Path.Combine(libraryPath, "BuildCache"),
+ Path.Combine(libraryPath, "ScriptAssemblies"),
+ Path.Combine(libraryPath, "ArtifactDB")
+ };
+
+ int clearedCount = 0;
+ foreach (string cacheDir in cacheDirectories)
+ {
+ if (Directory.Exists(cacheDir))
+ {
+ Directory.Delete(cacheDir, true);
+ clearedCount++;
+ Debug.Log($"成功删除缓存目录: {cacheDir}");
+ }
+ }
+
+ // 清理构建输出目录
+ string buildOutputPath = Path.Combine(Application.dataPath, "../Builds/Android");
+ if (Directory.Exists(buildOutputPath))
+ {
+ // 只清理旧的APK文件,保留目录结构
+ string[] oldApks = Directory.GetFiles(buildOutputPath, "*.apk", SearchOption.AllDirectories);
+ foreach (string apkFile in oldApks)
+ {
+ File.Delete(apkFile);
+ }
+
+ Debug.Log("成功清理旧的APK文件");
+ }
+
+ AssetDatabase.Refresh();
+ buildStatus = "缓存清理完成!";
+ ShowNotification(new GUIContent("缓存清理完成!"), 2f);
+
+ Debug.Log($"缓存清理完成,共清理 {clearedCount} 个缓存目录");
+ }
+ catch (Exception e)
+ {
+ buildStatus = "缓存清理失败!";
+ Debug.LogError($"缓存清理失败: {e.Message}");
+ ShowNotification(new GUIContent("缓存清理失败!"), 2f);
+ }
+ }
+
+ ///
+ /// 获取可用的包裹名称列表
+ ///
+ /// 包裹名称列表
+ private List GetAvailablePackageNames()
+ {
+ UpdateAvailablePackages();
+ return CreatAssetWindow.GetCreatDomainAllName().ToList();
+ }
+
+ ///
+ /// 更新可用包裹列表
+ ///
+ private void UpdateAvailablePackages()
+ {
+ var availablePackages = CreatAssetWindow.GetCreatDomainAllName().ToList();
+ if (availablePackages.Any() && string.IsNullOrEmpty(selectedPackageName))
+ {
+ selectedPackageName = availablePackages.First();
+ }
+ }
+
+ ///
+ /// 当包裹名称改变时的回调
+ ///
+ private void OnPackageNameChanged()
+ {
+ buildStatus = "就绪";
+ }
+
+ ///
+ /// 自动递增版本号
+ ///
+ private void IncrementBuildVersion()
+ {
+ Version version = new Version(PlayerSettings.bundleVersion);
+ Version newVersion = new Version(version.Major, version.Minor, version.Build + 1);
+ PlayerSettings.bundleVersion = newVersion.ToString();
+ Debug.Log($"版本号已递增: {newVersion}");
+ }
+
+ ///
+ /// 打开构建文件夹
+ ///
+ /// 文件夹路径
+ private void OpenBuildFolder(string folderPath)
+ {
+ if (Directory.Exists(folderPath))
+ {
+ EditorUtility.RevealInFinder(folderPath);
+ }
}
///
@@ -230,5 +551,115 @@ namespace Stary.Evo.Editor
return true;
}
+
+ ///
+ /// 格式化字节大小
+ ///
+ /// 字节数
+ /// 格式化后的字符串
+ private string FormatBytes(ulong bytes)
+ {
+ string[] suffixes = { "B", "KB", "MB", "GB" };
+ int index = 0;
+ double size = bytes;
+
+ while (size >= 1024 && index < suffixes.Length - 1)
+ {
+ size /= 1024;
+ index++;
+ }
+
+ return $"{size:F2} {suffixes[index]}";
+ }
+
+ #endregion
+
+ #region 场景处理
+
+ ///
+ /// 查找所有包含LoadDll的场景文件
+ ///
+ private void FindScenesWithLoadDll()
+ {
+ List sceneList = new List();
+
+ // 搜索所有unity场景文件
+ string[] allScenes = Directory.GetFiles(Application.dataPath, "*.unity", SearchOption.AllDirectories);
+
+ foreach (string scenePath in allScenes)
+ {
+ // 读取场景文本内容,检查是否包含LoadDll
+ try
+ {
+ string sceneContent = File.ReadAllText(scenePath);
+ if (sceneContent.Contains("m_Name: LoadDll"))
+ {
+ // 转换为Assets相对路径
+ string assetsPath = scenePath.Replace(Application.dataPath, "Assets");
+ sceneList.Add(assetsPath);
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"读取场景文件失败: {scenePath}, 错误: {e.Message}");
+ }
+ }
+
+ _scenePaths = sceneList.ToArray();
+ }
+
+ ///
+ /// 加载场景进行编辑
+ ///
+ private void LoadSceneForEditing(string scenePath)
+ {
+ try
+ {
+ // 加载场景
+ EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single);
+
+ // 查找所有LoadDll物体
+ GameObject[] loadDllObjects = GameObject.FindGameObjectsWithTag("LoadDll");
+
+ // 如果没有找到,尝试按名称查找
+ if (loadDllObjects.Length == 0)
+ {
+ GameObject loadDllObj = GameObject.Find("LoadDll");
+ if (loadDllObj != null)
+ {
+ loadDllObjects = new GameObject[] { loadDllObj };
+ }
+ }
+
+ // 收集HybridClREntrance组件
+ HybridClREntrance entrance = null;
+ foreach (GameObject obj in loadDllObjects)
+ {
+ entrance = obj.GetComponent();
+ entrance.loadDomain = selectedPackageName;
+ }
+
+ Debug.Log($"成功加载场景: {scenePath}");
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"加载场景失败: {scenePath}, 错误: {e.Message}");
+ }
+ }
+
+ #endregion
+
+ #region 枚举
+
+ ///
+ /// 设备类型
+ ///
+ public enum DeviceType
+ {
+ Xreal,
+ Rokid
+ }
+
+ #endregion
}
}
\ No newline at end of file
diff --git a/Assets/00.StaryEvoTools/package.json b/Assets/00.StaryEvoTools/package.json
index ab0c26d..f99e5dc 100644
--- a/Assets/00.StaryEvoTools/package.json
+++ b/Assets/00.StaryEvoTools/package.json
@@ -1,6 +1,6 @@
{
"name": "com.staryevo.tools",
- "version": "1.3.16",
+ "version": "1.3.17",
"displayName": "00.StaryEvo.Tools",
"description": "This is an Framework package(后台服务器版本,端口9527)",
"unity": "2021.3",