74 lines
3.5 KiB
C#
74 lines
3.5 KiB
C#
using System;
|
||
using System.IO;
|
||
|
||
namespace XWorld.PublishTool
|
||
{
|
||
// 把构建产物落成发布目录 + 清单 + (可选)签名。
|
||
// privateKeyPem 传 null = 不签名:files.txt.sig 落空文件(两端验签均为"未配置则跳过")。
|
||
public sealed class PublishLayout
|
||
{
|
||
// 服务器树:outDir 即 <gamesRoot>/<id>/<ver>/ 的内容(普通文件名,不做 md5 命名)
|
||
public void PublishServer(string srcDir, string outDir, GameSpec spec, string privateKeyPem)
|
||
{
|
||
Directory.CreateDirectory(outDir);
|
||
CopyTo(srcDir, spec.ServerAssembly, outDir);
|
||
CopyTo(srcDir, spec.CoreDll, outDir);
|
||
// game.json 先写,确保其 md5 纳入清单
|
||
File.WriteAllText(Path.Combine(outDir, "game.json"), GameJsonWriter.WriteServer(spec));
|
||
|
||
var fm = new FilesManifest();
|
||
foreach (var name in new[] { spec.ServerAssembly, spec.CoreDll, "game.json" })
|
||
{
|
||
byte[] b = File.ReadAllBytes(Path.Combine(outDir, name));
|
||
fm.Add(name, Md5Util.OfBytes(b), b.Length);
|
||
}
|
||
WriteManifestAndSig(outDir, fm, privateKeyPem);
|
||
}
|
||
|
||
// 客户端树:outDir 即 CDN/minigame/<id>/<ver>/<platform>/ 的内容。
|
||
// spec.CodeAb / spec.Assets 均为完整 AB 文件名(code.unity3d / res.unity3d),统一 AB:无散 .bytes。
|
||
public void PublishClient(string srcDir, string outDir, GameSpec spec, string privateKeyPem)
|
||
{
|
||
if (string.IsNullOrEmpty(spec.CodeAb))
|
||
throw new InvalidOperationException("spec.CodeAb 为空:客户端发布需要代码 AB(统一 AB 格式)");
|
||
Directory.CreateDirectory(outDir);
|
||
|
||
var fm = new FilesManifest();
|
||
CopyMd5Named(srcDir, spec.CodeAb, outDir, fm);
|
||
foreach (var ab in spec.Assets) CopyMd5Named(srcDir, ab, outDir, fm);
|
||
|
||
File.WriteAllText(Path.Combine(outDir, "game.json"), GameJsonWriter.WriteClient(spec));
|
||
WriteManifestAndSig(outDir, fm, privateKeyPem);
|
||
}
|
||
|
||
private static void WriteManifestAndSig(string outDir, FilesManifest fm, string privateKeyPem)
|
||
{
|
||
File.WriteAllText(Path.Combine(outDir, "files.txt"), fm.Render());
|
||
byte[] sig = privateKeyPem == null
|
||
? Array.Empty<byte>()
|
||
: ManifestSigner.Sign(fm.Digest(), privateKeyPem);
|
||
File.WriteAllBytes(Path.Combine(outDir, "files.txt.sig"), sig);
|
||
}
|
||
|
||
private static void CopyTo(string srcDir, string name, string dstDir)
|
||
=> File.Copy(Path.Combine(srcDir, name), Path.Combine(dstDir, name), true);
|
||
|
||
// 落盘 md5 命名(name_md5.ext,与 MiniGameDownloader.Md5Name 推导一致),files.txt 登记逻辑名
|
||
private static void CopyMd5Named(string srcDir, string name, string dstDir, FilesManifest fm)
|
||
{
|
||
byte[] data = File.ReadAllBytes(Path.Combine(srcDir, name));
|
||
string md5 = Md5Util.OfBytes(data);
|
||
File.WriteAllBytes(Path.Combine(dstDir, Md5Name(name, md5)), data);
|
||
fm.Add(name, md5, data.Length);
|
||
}
|
||
|
||
// name.ext -> name_md5.ext(无扩展名则 name_md5)
|
||
private static string Md5Name(string filename, string md5)
|
||
{
|
||
int pos = filename.LastIndexOf('.');
|
||
return pos >= 0 ? $"{filename.Substring(0, pos)}_{md5}{filename.Substring(pos)}"
|
||
: $"{filename}_{md5}";
|
||
}
|
||
}
|
||
}
|