#if UNITY_EDITOR
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;
using Debug = UnityEngine.Debug;
namespace AOT.Editor.YooAssetTools
{
/// Explicit, incremental publication of the current WebGL resource packages.
public static class CdnPublisher
{
static bool _busy;
static string Repo => Path.GetFullPath(Path.Combine(Application.dataPath, "../.."));
static string Config => Path.Combine(Repo, "Tools/cdn_publish.local.json");
static string LogPath => Path.Combine(Repo, "Tools/output/cdn_publish/last-unity.log");
public static string Status { get; private set; } = "idle";
[MenuItem("AIGame/CDN/Check SSH Connection")]
public static async void CheckSshMenu() => await Guard(() => RunPython("check"));
[MenuItem("AIGame/CDN/Preview Upload Changes")]
public static async void PreviewMenu() => await Guard(() => RunPython("plan --compare"));
[MenuItem("AIGame/CDN/Upload Staged Resources")]
public static async void UploadMenu() => await Guard(() => RunPython("upload"));
[MenuItem("AIGame/CDN/Build And Upload (Art + HotUpdate)")]
public static async void BuildAndUploadMenu() => await Guard(async () => {
await RunPython("check");
BuildPackages();
await RunPython("upload");
});
[MenuItem("AIGame/CDN/Prepare SSH Key")]
public static async void PrepareKeyMenu() => await Guard(() => RunPython("init-key"));
[MenuItem("AIGame/CDN/Open Publish Folder")]
public static void OpenPublishFolder()
{
Directory.CreateDirectory(Path.GetDirectoryName(LogPath));
EditorUtility.RevealInFinder(Path.GetDirectoryName(LogPath));
}
public static string BuildPackages()
{
if (EditorApplication.isPlaying || EditorApplication.isCompiling)
throw new InvalidOperationException("Stop Play Mode and finish compiling before building CDN resources.");
if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.WebGL)
throw new InvalidOperationException("CDN packages must be built for WebGL. Select that target first.");
Status = "building Art";
var art = BuildAndStageArt.BuildConfiguredAndStage();
if (!art.StartsWith("ok", StringComparison.Ordinal)) throw new InvalidOperationException(art);
Debug.Log("[CDN] " + art);
Status = "building HotUpdate";
var raw = BuildAndStageHotUpdate.BuildAndStage();
if (!raw.StartsWith("ok", StringComparison.Ordinal)) throw new InvalidOperationException(raw);
Debug.Log("[CDN] " + raw);
Status = "packages staged";
return art + " | " + raw;
}
static async Task Guard(Func action)
{
if (_busy) { Debug.LogWarning("[CDN] A publish command is already running."); return; }
_busy = true;
try { await action(); Status = "completed"; Debug.Log("[CDN] Completed. Log: " + LogPath); }
catch (Exception error) { Status = "failed"; Debug.LogError("[CDN] " + error.Message); }
finally { _busy = false; }
}
static async Task RunPython(string command)
{
if (!File.Exists(Config))
File.Copy(Path.Combine(Repo, "Tools/cdn_publish.example.json"), Config);
Status = command;
var pending = new ConcurrentQueue();
var start = new ProcessStartInfo {
FileName = EditorPrefs.GetString("AIGame.CdnPython", "python"),
Arguments = "\"" + Path.Combine(Repo, "Tools/publish_cdn.py") + "\" " + command + " --config \"" + Config + "\"",
WorkingDirectory = Repo, UseShellExecute = false, CreateNoWindow = true,
RedirectStandardOutput = true, RedirectStandardError = true,
StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8
};
start.EnvironmentVariables["PYTHONIOENCODING"] = "utf-8";
start.EnvironmentVariables["PYTHONUNBUFFERED"] = "1";
Directory.CreateDirectory(Path.GetDirectoryName(LogPath));
using (var log = new StreamWriter(LogPath, true, new UTF8Encoding(false)))
using (var process = new Process { StartInfo = start })
{
log.WriteLine(DateTime.UtcNow.ToString("o") + " " + command);
process.OutputDataReceived += (_, e) => { if (e.Data != null) pending.Enqueue(e.Data); };
process.ErrorDataReceived += (_, e) => { if (e.Data != null) pending.Enqueue(e.Data); };
process.Start(); process.BeginOutputReadLine(); process.BeginErrorReadLine();
while (!process.HasExited)
{
while (pending.TryDequeue(out var line)) { Debug.Log("[CDN] " + line); log.WriteLine(line); }
log.Flush(); await Task.Delay(150);
}
process.WaitForExit();
while (pending.TryDequeue(out var line)) { Debug.Log("[CDN] " + line); log.WriteLine(line); }
if (process.ExitCode != 0) throw new InvalidOperationException("Publish command failed. See " + LogPath);
}
}
}
}
#endif