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:
@@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.RegularExpressions;
|
||||
using AOT.Editor.WeChat;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
public static class VerifyYooAssetStripping
|
||||
{
|
||||
static readonly string[] TypeNames = {
|
||||
"YooAsset.WebNetworkFileSystem", "YooAsset.BuiltinFileSystem", "YooAsset.SandboxFileSystem"
|
||||
};
|
||||
static string Project => Path.GetFullPath(Path.Combine(Application.dataPath, ".."));
|
||||
static string Output => Path.GetFullPath(Path.Combine(Project, "../Tools/output/yooasset_stripping"));
|
||||
|
||||
public static string Before()
|
||||
{
|
||||
return Save("before", new {
|
||||
full = Inspect(Path.Combine(Project, "Library/ScriptAssemblies/YooAsset.dll")),
|
||||
stripped = Inspect(Path.Combine(Project, "Library/Bee/artifacts/WebGL/ManagedStripped/YooAsset.dll"))
|
||||
});
|
||||
}
|
||||
|
||||
public static string BuildAndVerify()
|
||||
{
|
||||
if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.WebGL)
|
||||
throw new Exception("Select WebGL before running this verification");
|
||||
string gamePath = Path.Combine(Project, "Build/WeChat/Base/minigame/game.js");
|
||||
string oldDataPath = StartupDataPath(gamePath);
|
||||
string oldDataHash = Hash(oldDataPath);
|
||||
string[] business = SnapshotBusinessBundles();
|
||||
DateTime started = DateTime.UtcNow;
|
||||
string result = ExportWeChatBasePackage.Export();
|
||||
if (!result.StartsWith("ok minigame=", StringComparison.Ordinal)) throw new Exception(result);
|
||||
string strippedPath = Path.Combine(Project, "Library/Bee/artifacts/WebGL/ManagedStripped/YooAsset.dll");
|
||||
var stripped = Inspect(strippedPath);
|
||||
if (File.GetLastWriteTimeUtc(strippedPath) < started)
|
||||
throw new Exception("Stripped YooAsset DLL is stale");
|
||||
if (stripped.Any(t => !t.defaultConstructor || !t.constructorBody))
|
||||
throw new Exception("A reflected file system constructor was stripped");
|
||||
string cpp = Path.Combine(Project, "Library/Bee/artifacts/WebGL/il2cppOutput/cpp");
|
||||
bool nativeConstructor = Directory.GetFiles(cpp, "YooAsset*.cpp")
|
||||
.Any(p => File.ReadAllText(p).Contains("WebNetworkFileSystem__ctor"));
|
||||
if (!nativeConstructor) throw new Exception("Native WebNetworkFileSystem constructor was not emitted");
|
||||
if (!business.SequenceEqual(SnapshotBusinessBundles()))
|
||||
throw new Exception("Business bundles changed unexpectedly");
|
||||
string newDataPath = StartupDataPath(gamePath);
|
||||
return Save("after", new {
|
||||
passed = true, export = result, startedUtc = started, completedUtc = DateTime.UtcNow,
|
||||
stripped, nativeConstructor,
|
||||
oldStartupData = oldDataPath, startupData = newDataPath,
|
||||
oldStartupHash = oldDataHash, startupHash = Hash(newDataPath),
|
||||
startupDataUnchanged = oldDataHash == Hash(newDataPath),
|
||||
businessBundlesUnchanged = true,
|
||||
wechatRuntimeRetested = false
|
||||
});
|
||||
}
|
||||
|
||||
public sealed class TypeCheck
|
||||
{
|
||||
public string type;
|
||||
public bool typePresent;
|
||||
public bool defaultConstructor;
|
||||
public bool constructorBody;
|
||||
}
|
||||
|
||||
static TypeCheck[] Inspect(string path)
|
||||
{
|
||||
// Burst embeds a second Cecil copy; select Unity's loaded Mono.Cecil explicitly.
|
||||
var cecil = AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "Mono.Cecil");
|
||||
var definition = cecil.GetType("Mono.Cecil.AssemblyDefinition");
|
||||
object assembly = definition.GetMethod("ReadAssembly", new[] { typeof(string) }).Invoke(null, new object[] { path });
|
||||
using ((IDisposable)assembly)
|
||||
{
|
||||
object module = Property(assembly, "MainModule");
|
||||
return TypeNames.Select(name => {
|
||||
object type = module.GetType().GetMethod("GetType", new[] { typeof(string) }).Invoke(module, new object[] { name });
|
||||
var check = new TypeCheck { type = name, typePresent = type != null };
|
||||
if (type != null)
|
||||
foreach (object method in (IEnumerable)Property(type, "Methods"))
|
||||
if ((string)Property(method, "Name") == ".ctor" && !(bool)Property(method, "HasParameters"))
|
||||
{
|
||||
check.defaultConstructor = true;
|
||||
check.constructorBody = (bool)Property(method, "HasBody");
|
||||
}
|
||||
return check;
|
||||
}).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
static object Property(object instance, string name) => instance.GetType().GetProperty(name).GetValue(instance);
|
||||
|
||||
static string StartupDataPath(string gamePath)
|
||||
{
|
||||
var match = Regex.Match(File.ReadAllText(gamePath), @"DATA_FILE_MD5:\s*'([0-9a-f]+)'");
|
||||
if (!match.Success) throw new Exception("Cannot read DATA_FILE_MD5");
|
||||
return Path.Combine(Project, "Build/WeChat/Base/webgl", match.Groups[1].Value + ".webgl.data.unityweb.bin.txt");
|
||||
}
|
||||
|
||||
static string Hash(string path)
|
||||
{
|
||||
using (var sha = SHA256.Create())
|
||||
using (var stream = File.OpenRead(path))
|
||||
return BitConverter.ToString(sha.ComputeHash(stream)).Replace("-", "").ToLowerInvariant();
|
||||
}
|
||||
|
||||
static string[] SnapshotBusinessBundles() => Directory.GetFiles(Path.Combine(Application.dataPath, "StreamingAssets/yoo"), "*.bundle", SearchOption.AllDirectories)
|
||||
.OrderBy(p => p, StringComparer.Ordinal).Select(p => p + ":" + Hash(p)).ToArray();
|
||||
|
||||
static string Save(string name, object report)
|
||||
{
|
||||
Directory.CreateDirectory(Output);
|
||||
string json = JsonConvert.SerializeObject(report, Formatting.Indented);
|
||||
File.WriteAllText(Path.Combine(Output, name + ".json"), json);
|
||||
return json;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user