Add AIServer, character art pipeline, and Art package boot integration.

Bring in MaleExplorer assets/shaders, model tooling and Hunyuan updates, plus boot/YooAsset Art package support.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
udx
2026-09-21 17:06:59 +08:00
co-authored by Cursor
parent b7a0bbcb89
commit 6550b8df4b
121 changed files with 313260 additions and 113 deletions
@@ -0,0 +1,122 @@
#if UNITY_EDITOR
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
using YooAsset;
using YooAsset.Editor;
namespace AOT.Editor.YooAssetTools
{
/// <summary>
/// 构建 Art 包(角色等)并暂存 CDN / StreamingAssets。
/// </summary>
public static class BuildAndStageArt
{
public const string PackageName = "Art";
public const string AppVersion = "1.0.0";
public const string Channel = "wechat";
static string RepoRoot => Path.GetFullPath(Path.Combine(Application.dataPath, "..", ".."));
static string CdnRoot => Path.Combine(RepoRoot, "Tools", "cdn_root");
static string ProjectBundlesRoot => BundleBuilderHelper.GetDefaultBuildOutputRoot();
[MenuItem("AIGame/YooAsset/Build And Stage Art")]
public static string BuildAndStage()
{
SetupM3Character.SetupAll();
var target = EditorUserBuildSettings.activeBuildTarget;
string packageVersion = DateTime.Now.ToString("yyyyMMdd.HHmmss");
var buildResult = BuildArtPackage(target, packageVersion);
if (!buildResult.Success)
return $"fail: {buildResult.ErrorInfo}";
string packageDir = buildResult.OutputPackageDirectory;
string stageDir = Path.Combine(CdnRoot, Channel, AppVersion, PackageName);
StageDirectory(packageDir, stageDir);
// 合并 version.json packages 列表
MergeVersionPackages(packageVersion);
AssetDatabase.Refresh();
return $"ok ver={packageVersion} dir={packageDir} stage={stageDir}";
}
static BuildResult BuildArtPackage(BuildTarget target, string packageVersion)
{
var uniqueBundleName = BundleCollectorSettingData.Setting.UniqueBundleName;
var packRuleResult = DefaultBundlePackRule.CreateShadersPackRuleResult();
var builtinShaderBundleName = packRuleResult.GetBundleName(PackageName, uniqueBundleName);
var buildParameters = new ScriptableBuildParameters
{
BuildOutputRoot = ProjectBundlesRoot,
BundledFileRoot = BundleBuilderHelper.GetStreamingAssetsRoot(),
BuildPipeline = EBuildPipeline.ScriptableBuildPipeline.ToString(),
BuildBundleType = (int)EBundleType.AssetBundle,
BuildTarget = target,
PackageName = PackageName,
PackageVersion = packageVersion,
EnableSharePackRule = true,
VerifyBuildingResult = true,
FileNameStyle = EFileNameStyle.HashName,
BundledCopyOption = EBundledCopyOption.ClearAndCopyAll,
BundledCopyParams = string.Empty,
CompressOption = ECompressOption.LZ4,
ClearBuildCacheFiles = false,
UseAssetDependencyDB = true,
BuiltinShadersBundleName = builtinShaderBundleName,
};
var pipeline = new ScriptableBuildPipeline();
return pipeline.Run(buildParameters, true);
}
static void StageDirectory(string src, string dst)
{
if (Directory.Exists(dst))
Directory.Delete(dst, true);
Directory.CreateDirectory(dst);
foreach (var file in Directory.GetFiles(src, "*", SearchOption.AllDirectories))
{
var rel = file.Substring(src.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
var destFile = Path.Combine(dst, rel);
Directory.CreateDirectory(Path.GetDirectoryName(destFile) ?? dst);
File.Copy(file, destFile, true);
}
}
static void MergeVersionPackages(string resVersion)
{
var path = Path.Combine(CdnRoot, Channel, "version.json");
Directory.CreateDirectory(Path.GetDirectoryName(path) ?? CdnRoot);
var localCdn = $"http://127.0.0.1:8787/{Channel}/{AppVersion}/{BuildAndStageHotUpdate.PackageName}";
var json =
"{\n" +
$" \"appVersionMin\": \"{AppVersion}\",\n" +
$" \"appVersionMax\": \"{AppVersion}\",\n" +
$" \"resVersion\": \"{resVersion}\",\n" +
$" \"cdnRoot\": \"{localCdn}\",\n" +
" \"packages\": [ \"DefaultPackage\", \"Art\" ],\n" +
" \"forceUpdate\": false,\n" +
" \"notice\": \"\"\n" +
"}\n";
File.WriteAllText(path, json);
File.WriteAllText(Path.Combine(Application.streamingAssetsPath, "cdn", "version.json"), json);
var prod =
"{\n" +
$" \"appVersionMin\": \"{AppVersion}\",\n" +
$" \"appVersionMax\": \"{AppVersion}\",\n" +
$" \"resVersion\": \"{resVersion}\",\n" +
$" \"cdnRoot\": \"https://www.xworld.ren/{Channel}/{AppVersion}/{BuildAndStageHotUpdate.PackageName}\",\n" +
" \"packages\": [ \"DefaultPackage\", \"Art\" ],\n" +
" \"forceUpdate\": false,\n" +
" \"notice\": \"\"\n" +
"}\n";
File.WriteAllText(Path.Combine(CdnRoot, Channel, "version.prod.json"), prod);
}
}
}
#endif
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: db0c7c0cdfa612f47bea89cc1bd02a6e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -3,13 +3,12 @@ using AOT.Boot;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
using YooAsset;
namespace AOT.Editor.YooAssetTools
{
/// <summary>
/// 切换 BootDriver 的 M1 验证模式(Offline / Host / Direct)。
/// 切换 BootDriver 的运行模式 / 服务器地址。
/// </summary>
public static class ConfigureBootPlayMode
{
@@ -30,6 +29,33 @@ namespace AOT.Editor.YooAssetTools
versionUrl: "http://127.0.0.1:8787/wechat/version.json",
defaultCdn: "http://127.0.0.1:8787/wechat/1.0.0/DefaultPackage");
[MenuItem("AIGame/Boot/Server/Production game.xworld.ren")]
public static string SetServerProd() => ApplyServer(
"https://game.xworld.ren",
"wss://game.xworld.ren/ws?pid={pid}&token={token}");
[MenuItem("AIGame/Boot/Server/Local AIServer :8080")]
public static string SetServerLocal() => ApplyServer(
"http://127.0.0.1:8080",
"ws://127.0.0.1:8080/ws?pid={pid}&token={token}");
static string ApplyServer(string httpBase, string wsTemplate)
{
var scene = EditorSceneManager.OpenScene(BootScene, OpenSceneMode.Single);
var boot = Object.FindObjectOfType<BootDriver>();
if (boot == null)
return "fail: BootDriver not found in Boot scene";
var so = new SerializedObject(boot);
var settings = so.FindProperty("settings");
settings.FindPropertyRelative("HttpBase").stringValue = httpBase;
settings.FindPropertyRelative("WsUrlTemplate").stringValue = wsTemplate;
so.ApplyModifiedPropertiesWithoutUndo();
EditorSceneManager.MarkSceneDirty(scene);
EditorSceneManager.SaveScene(scene);
return $"ok http={httpBase} ws={wsTemplate}";
}
static string Apply(EPlayMode playMode, bool editorDirect, string versionUrl = null, string defaultCdn = null)
{
var scene = EditorSceneManager.OpenScene(BootScene, OpenSceneMode.Single);