Initial commit: Client Doc docs Server Tools
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user