Ship the WeChat hot-update catalogs and the camp, TideWhale T2, UI atlas, and animation-lab work.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
udx
2026-10-24 18:04:41 +08:00
co-authored by Cursor
parent 6ebc9401ce
commit 45e62132c8
1432 changed files with 2971856 additions and 2798 deletions
@@ -1,8 +1,12 @@
#if UNITY_EDITOR
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using HybridCLR.Editor.Installer;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEditor;
using UnityEngine;
using WeChatWASM;
@@ -16,7 +20,7 @@ namespace AOT.Editor.WeChat
{
const string BootScene = "Assets/Scenes/Boot.unity";
const string RelativeDst = "Build/WeChat/Base";
const string DataCdn = "https://www.xworld.ren/wechat/1.0.0/minigame/";
const string DataCdn = "https://www.xworld.ren/game/Friends/webgl/";
const int ShellHeapMb = 496;
const string ResumeKey = "AIGame.WeChat.ExportBase.Resume";
@@ -92,6 +96,9 @@ namespace AOT.Editor.WeChat
return Fail("微信转换失败 " + error + ",详见 Console");
string dst = Path.GetFullPath(Path.Combine(Application.dataPath, "..", RelativeDst));
string projectError = PrepareDevToolsProject(dst);
if (projectError != null)
return Fail(projectError);
string summary = WriteReport(dst);
string minigame = Path.Combine(dst, "minigame");
string result = "ok minigame=" + minigame + " " + summary;
@@ -99,6 +106,94 @@ namespace AOT.Editor.WeChat
return result;
}
[MenuItem("AIGame/WeChat/Repair Exported DevTools Project")]
public static string RepairExportedProject()
{
string dst = Path.GetFullPath(Path.Combine(Application.dataPath, "..", RelativeDst));
string error = PrepareDevToolsProject(dst);
if (error != null) return Fail(error);
string result = "ok devtools=" + dst + " source=minigame/ " + WriteReport(dst);
Debug.Log("[AIGame] " + result);
return result;
}
[MenuItem("AIGame/WeChat/Refresh Exported Config (No Build)")]
public static string RefreshExportedConfig()
{
string dst = Path.GetFullPath(Path.Combine(Application.dataPath, "..", RelativeDst));
string gamePath = Path.Combine(dst, "minigame", "game.js");
if (!File.Exists(gamePath)) return Fail("尚未导出小游戏,请先执行 Export Base Package");
string game = File.ReadAllText(gamePath);
var cdnPattern = new Regex(@"\bDATA_CDN\s*:\s*(['""])[^'""]*\1");
if (cdnPattern.Matches(game).Count != 1)
return Fail("无法识别导出 game.js 的 DATA_CDN,未修改配置");
string error = ApplyBaseConfig();
if (error != null) return Fail(error);
error = PrepareDevToolsProject(dst);
if (error != null) return Fail(error);
string updated = cdnPattern.Replace(game, _ => "DATA_CDN: " + JsonConvert.SerializeObject(DataCdn), 1);
if (updated != game) File.WriteAllText(gamePath, updated, new UTF8Encoding(false));
string result = "ok config refreshed (no WebGL build) " + WriteReport(dst);
Debug.Log("[AIGame] " + result);
return result;
}
[MenuItem("AIGame/WeChat/Open Exported MiniGame Folder")]
public static void OpenExportedMiniGameFolder()
{
string game = Path.GetFullPath(Path.Combine(Application.dataPath, "..", RelativeDst, "minigame", "game.json"));
if (!File.Exists(game))
{
Fail("尚未导出小游戏,请先执行 Export Base Package");
return;
}
EditorUtility.RevealInFinder(game);
}
/// <summary>Allow importing either the export container or its minigame directory.</summary>
public static string PrepareDevToolsProject(string dst)
{
string mini = Path.Combine(dst, "minigame");
foreach (string required in new[] { "game.json", "game.js", "project.config.json" })
if (!File.Exists(Path.Combine(mini, required)))
return "小游戏导出不完整,缺少 " + Path.Combine(mini, required);
try
{
JObject.Parse(File.ReadAllText(Path.Combine(mini, "game.json")));
string miniConfigPath = Path.Combine(mini, "project.config.json");
string rootConfigPath = Path.Combine(dst, "project.config.json");
JObject miniConfig = JObject.Parse(File.ReadAllText(miniConfigPath));
JObject rootConfig = File.Exists(rootConfigPath)
? JObject.Parse(File.ReadAllText(rootConfigPath)) : new JObject();
string appid = (string)miniConfig["appid"];
if (string.IsNullOrWhiteSpace(appid)) appid = (string)rootConfig["appid"];
// Carry SDK compiler options and symbol exclusions to the parent entry.
// Keep other existing user fields and combine package ignore rules.
rootConfig.Merge(miniConfig, new JsonMergeSettings { MergeArrayHandling = MergeArrayHandling.Union });
rootConfig["compileType"] = "game";
rootConfig["miniprogramRoot"] = "minigame/";
if (!string.IsNullOrWhiteSpace(appid))
{
rootConfig["appid"] = appid;
if (string.IsNullOrWhiteSpace((string)miniConfig["appid"]))
{
miniConfig["appid"] = appid;
File.WriteAllText(miniConfigPath, miniConfig.ToString(Formatting.Indented), new UTF8Encoding(false));
}
}
string rootJson = rootConfig.ToString(Formatting.Indented);
if (!File.Exists(rootConfigPath) || File.ReadAllText(rootConfigPath) != rootJson)
File.WriteAllText(rootConfigPath, rootJson, new UTF8Encoding(false));
return null;
}
catch (Exception exception)
{
return "小游戏项目配置失败: " + exception.Message;
}
}
static string ValidateBootOnly()
{
if (!File.Exists(BootScene))
@@ -167,6 +262,14 @@ namespace AOT.Editor.WeChat
text.AppendLine("minigameBytes: " + miniBytes);
text.AppendLine("dataBytes: " + dataBytes);
text.AppendLine("devtools: " + mini);
text.AppendLine("devtoolsContainer: " + dst + " (miniprogramRoot=minigame/)");
text.AppendLine("uploadSource: " + Path.Combine(dst, "webgl"));
text.AppendLine("uploadContentsTo: " + DataCdn);
text.AppendLine("uploadNote: copy the CONTENTS of webgl, preserving relative paths; export does not upload to the server.");
string webgl = Path.Combine(dst, "webgl");
if (Directory.Exists(webgl))
foreach (string resource in Directory.GetFiles(webgl, "*.webgl.data.unityweb.bin.*"))
text.AppendLine("startupData: " + Path.GetFileName(resource) + " -> " + DataCdn + Path.GetFileName(resource));
if (Directory.Exists(mini))
{
text.AppendLine("largest:");