using System; using System.Diagnostics; using System.IO; using System.Net; using System.Net.Sockets; using UnityEditor; using UnityEngine; namespace UnityEditor.Build.Pipeline.Tests { class LocalCacheServer : ScriptableSingleton { [SerializeField] public string m_path; [SerializeField] public int m_port; [SerializeField] public int m_pid = -1; public static string Combine(params string[] components) { if (components.Length < 1) throw new ArgumentException("At least one component must be provided!"); var path1 = components[0]; for (var index = 1; index < components.Length; ++index) path1 = Path.Combine(path1, components[index]); return path1; } private void Create(int port, ulong size, string cachePath) { var nodeExecutable = Combine(EditorApplication.applicationContentsPath, "Tools", "nodejs"); nodeExecutable = Application.platform == RuntimePlatform.WindowsEditor ? Combine(nodeExecutable, "node.exe") : Combine(nodeExecutable, "bin", "node"); if (!Directory.Exists(cachePath)) Directory.CreateDirectory(cachePath); m_path = cachePath; var cacheServerJs = Combine(EditorApplication.applicationContentsPath, "Tools", "CacheServer", "main.js"); var processStartInfo = new ProcessStartInfo(nodeExecutable) { Arguments = "\"" + cacheServerJs + "\"" + " --port " + port + " --path \"" + m_path + "\" --nolegacy" + " --monitor-parent-process " + Process.GetCurrentProcess().Id // node.js has issues running on windows with stdout not redirected. // so we silence logging to avoid that. And also to avoid CacheServer // spamming the editor logs on OS X. + " --silent" + " --size " + size, UseShellExecute = false, CreateNoWindow = true }; var p = new Process { StartInfo = processStartInfo }; p.Start(); m_port = port; m_pid = p.Id; Save(true); } public static string CachePath { get { return instance.m_path; } } public static int Port { get { return instance.m_port; } } public static void Setup(ulong size, string cachePath) { Kill(); instance.Create(GetRandomUnusedPort(), size, cachePath); WaitForServerToComeAlive(instance.m_port); } public static void Kill() { if (instance.m_pid == -1) return; try { var p = Process.GetProcessById(instance.m_pid); p.Kill(); instance.m_pid = -1; } catch { // if we could not get a process, there is non alive. continue. } } public static void Clear() { Kill(); if (Directory.Exists(instance.m_path)) Directory.Delete(instance.m_path, true); } private static void WaitForServerToComeAlive(int port) { var start = DateTime.Now; var maximum = start.AddSeconds(5); while (DateTime.Now < maximum) { if (!PingHost("localhost", port, 10)) continue; Console.WriteLine("Server Came alive after {0} ms", (DateTime.Now - start).TotalMilliseconds); break; } } private static int GetRandomUnusedPort() { var listener = new TcpListener(IPAddress.Any, 0); listener.Start(); var port = ((IPEndPoint)listener.LocalEndpoint).Port; listener.Stop(); return port; } private static bool PingHost(string host, int port, int timeout) { try { using (var client = new TcpClient()) { var result = client.BeginConnect(host, port, null, null); result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(timeout)); return client.Connected; } } catch { return false; } } } }