first commit

This commit is contained in:
Chris
2025-03-12 14:22:16 -04:00
commit 0ad0c01249
1999 changed files with 189708 additions and 0 deletions

View File

@@ -0,0 +1,128 @@
using System.Diagnostics;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using SingularityGroup.HotReload.Newtonsoft.Json;
using UnityEngine;
using System;
namespace SingularityGroup.HotReload.Editor.Cli {
internal static class CliUtils {
static readonly string projectIdentifier = GetProjectIdentifier();
class Config {
public bool singleInstance;
}
public static string GetProjectIdentifier() {
if (File.Exists(PackageConst.ConfigFileName)) {
var config = JsonConvert.DeserializeObject<Config>(File.ReadAllText(PackageConst.ConfigFileName));
if (config.singleInstance) {
return null;
}
}
var path = Path.GetDirectoryName(UnityHelper.DataPath);
var name = new DirectoryInfo(path).Name;
using (SHA256 sha256 = SHA256.Create()) {
byte[] inputBytes = Encoding.UTF8.GetBytes(path);
byte[] hashBytes = sha256.ComputeHash(inputBytes);
var hash = BitConverter.ToString(hashBytes).Replace("-", "").Substring(0, 6).ToUpper();
return $"{name}-{hash}";
}
}
public static string GetTempDownloadFilePath(string osxFileName) {
if (UnityHelper.Platform == RuntimePlatform.OSXEditor) {
// project specific temp directory that is writeable on MacOS (Path.GetTempPath() wasn't when run through HotReload.app)
return Path.GetFullPath(PackageConst.LibraryCachePath + $"/HotReloadServerTemp/{osxFileName}");
} else {
return Path.GetTempFileName();
}
}
public static string GetHotReloadTempDir() {
if (UnityHelper.Platform == RuntimePlatform.OSXEditor) {
// project specific temp directory that is writeable on MacOS (Path.GetTempPath() wasn't when run through HotReload.app)
return Path.GetFullPath(PackageConst.LibraryCachePath + "/HotReloadServerTemp");
} else {
if (projectIdentifier != null) {
return Path.Combine(Path.GetTempPath(), "HotReloadTemp", projectIdentifier);
} else {
return Path.Combine(Path.GetTempPath(), "HotReloadTemp");
}
}
}
public static string GetAppDataPath() {
# if (UNITY_EDITOR_OSX)
var baseDir = "/Users/Shared";
# elif (UNITY_EDITOR_LINUX)
var baseDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
# else
var baseDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
#endif
return Path.Combine(baseDir, "singularitygroup-hotreload");
}
public static string GetExecutableTargetDir() {
if (PackageConst.IsAssetStoreBuild) {
return Path.Combine(GetAppDataPath(), "asset-store", $"executables_{PackageConst.ServerVersion.Replace('.', '-')}");
}
return Path.Combine(GetAppDataPath(), $"executables_{PackageConst.ServerVersion.Replace('.', '-')}");
}
public static string GetCliTempDir() {
return Path.Combine(GetHotReloadTempDir(), "MethodPatches");
}
public static void Chmod(string targetFile, string flags = "+x") {
// ReSharper disable once PossibleNullReferenceException
Process.Start(new ProcessStartInfo("chmod", $"{flags} \"{targetFile}\"") {
UseShellExecute = false,
}).WaitForExit(2000);
}
public static bool TryFindServerDir(out string path) {
const string serverBasePath = "Packages/com.singularitygroup.hotreload/Server";
if(Directory.Exists(serverBasePath)) {
path = Path.GetFullPath(serverBasePath);
return true;
}
//Not found in packages. Try to find in assets folder.
//fast path - this is the expected folder
const string alternativeExecutablePath = "Assets/HotReload/Server";
if(Directory.Exists(alternativeExecutablePath)) {
path = Path.GetFullPath(alternativeExecutablePath);
return true;
}
//slow path - try to find the server directory somewhere in the assets folder
var candidates = Directory.GetDirectories("Assets", "HotReload", SearchOption.AllDirectories);
foreach(var candidate in candidates) {
var serverDir = Path.Combine(candidate, "Server");
if(Directory.Exists(serverDir)) {
path = Path.GetFullPath(serverDir);
return true;
}
}
path = null;
return false;
}
public static string GetPidFilePath(string hotreloadTempDir) {
return Path.GetFullPath(Path.Combine(hotreloadTempDir, "server.pid"));
}
public static void KillLastKnownHotReloadProcess() {
var pidPath = GetPidFilePath(GetHotReloadTempDir());
try {
var pid = int.Parse(File.ReadAllText(pidPath));
Process.GetProcessById(pid).Kill();
}
catch {
//ignore
}
File.Delete(pidPath);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b0243b348dec4a308dc7b98e09842d2c
timeCreated: 1673820875

View File

@@ -0,0 +1,13 @@
using System.Threading.Tasks;
namespace SingularityGroup.HotReload.Editor.Cli {
class FallbackCliController : ICliController {
public string BinaryFileName => "";
public string PlatformName => "";
public bool CanOpenInBackground => false;
public Task Start(StartArgs args) => Task.CompletedTask;
public Task Stop() => Task.CompletedTask;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 090ed5d45f294f0d8799879206139bd6
timeCreated: 1673824275

View File

@@ -0,0 +1,239 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading.Tasks;
using SingularityGroup.HotReload.Newtonsoft.Json;
using UnityEditor;
namespace SingularityGroup.HotReload.Editor.Cli {
[InitializeOnLoad]
public static class HotReloadCli {
internal static readonly ICliController controller;
//InitializeOnLoad ensures controller gets initialized on unity thread
static HotReloadCli() {
controller =
#if UNITY_EDITOR_OSX
new OsxCliController();
#elif UNITY_EDITOR_LINUX
new LinuxCliController();
#elif UNITY_EDITOR_WIN
new WindowsCliController();
#else
new FallbackCliController();
#endif
}
public static bool CanOpenInBackground => controller.CanOpenInBackground;
/// <summary>
/// Public API: Starts the Hot Reload server. Must be on the main thread
/// </summary>
public static Task StartAsync() {
return StartAsync(
exposeServerToNetwork: HotReloadPrefs.ExposeServerToLocalNetwork,
allAssetChanges: HotReloadPrefs.AllAssetChanges,
createNoWindow: HotReloadPrefs.DisableConsoleWindow
);
}
internal static async Task StartAsync(bool exposeServerToNetwork, bool allAssetChanges, bool createNoWindow, LoginData loginData = null) {
var port = await Prepare().ConfigureAwait(false);
await ThreadUtility.SwitchToThreadPool();
StartArgs args;
if (TryGetStartArgs(UnityHelper.DataPath, exposeServerToNetwork, allAssetChanges, createNoWindow, loginData, port, out args)) {
await controller.Start(args);
}
}
/// <summary>
/// Public API: Stops the Hot Reload server
/// </summary>
/// <remarks>
/// This is a no-op in case the server is not running
/// </remarks>
public static Task StopAsync() {
return controller.Stop();
}
class Config {
#pragma warning disable CS0649
public bool useBuiltInProjectGeneration;
#pragma warning restore CS0649
}
static bool TryGetStartArgs(string dataPath, bool exposeServerToNetwork, bool allAssetChanges, bool createNoWindow, LoginData loginData, int port, out StartArgs args) {
string serverDir;
if(!CliUtils.TryFindServerDir(out serverDir)) {
Log.Warning($"Failed to start the Hot Reload Server. " +
$"Unable to locate the 'Server' directory. " +
$"Make sure the 'Server' directory is " +
$"somewhere in the Assets folder inside a 'HotReload' folder or in the HotReload package");
args = null;
return false;
}
Config config;
if (File.Exists(PackageConst.ConfigFileName)) {
config = JsonConvert.DeserializeObject<Config>(File.ReadAllText(PackageConst.ConfigFileName));
} else {
config = new Config();
}
var hotReloadTmpDir = CliUtils.GetHotReloadTempDir();
var cliTempDir = CliUtils.GetCliTempDir();
// Versioned path so that we only need to extract the binary once. User can have multiple projects
// on their machine using different HotReload versions.
var executableTargetDir = CliUtils.GetExecutableTargetDir();
Directory.CreateDirectory(executableTargetDir); // ensure exists
var executableSourceDir = Path.Combine(serverDir, controller.PlatformName);
var unityProjDir = Path.GetDirectoryName(dataPath);
string slnPath;
if (config.useBuiltInProjectGeneration) {
var info = new DirectoryInfo(Path.GetFullPath("."));
slnPath = Path.Combine(Path.GetFullPath("."), info.Name + ".sln");
if (!File.Exists(slnPath)) {
Log.Warning($"Failed to start the Hot Reload Server. Cannot find solution file. Please disable \"useBuiltInProjectGeneration\" in settings to enable custom project generation.");
args = null;
return false;
}
Log.Info("Using default project generation. If you encounter any problem with Unity's default project generation consider disabling it to use custom project generation.");
try {
Directory.Delete(ProjectGeneration.ProjectGeneration.tempDir, true);
} catch(Exception ex) {
Log.Exception(ex);
}
} else {
slnPath = ProjectGeneration.ProjectGeneration.GetSolutionFilePath(dataPath);
}
if (!File.Exists(slnPath)) {
Log.Warning($"No .sln file found. Open any c# file to generate it so Hot Reload can work properly");
}
var searchAssemblies = string.Join(";", CodePatcher.I.GetAssemblySearchPaths());
var cliArguments = $@"-u ""{unityProjDir}"" -s ""{slnPath}"" -t ""{cliTempDir}"" -a ""{searchAssemblies}"" -ver ""{PackageConst.Version}"" -proc ""{Process.GetCurrentProcess().Id}"" -assets ""{allAssetChanges}"" -p ""{port}""";
if (loginData != null) {
cliArguments += $@" -email ""{loginData.email}"" -pass ""{loginData.password}""";
}
if (exposeServerToNetwork) {
// server will listen on local network interface (default is localhost only)
cliArguments += " -e true";
}
args = new StartArgs {
hotreloadTempDir = hotReloadTmpDir,
cliTempDir = cliTempDir,
executableTargetDir = executableTargetDir,
executableSourceDir = executableSourceDir,
cliArguments = cliArguments,
unityProjDir = unityProjDir,
createNoWindow = createNoWindow,
};
return true;
}
private static int DiscoverFreePort() {
var maxAttempts = 10;
for (int attempt = 0; attempt < maxAttempts; attempt++) {
var port = RequestHelper.defaultPort + attempt;
if (IsPortInUse(port)) {
continue;
}
return port;
}
// we give up at this point
return RequestHelper.defaultPort + maxAttempts;
}
public static bool IsPortInUse(int port) {
// Note that there is a racecondition that a port gets occupied after checking.
// However, it will very rare someone will run into this.
#if UNITY_EDITOR_WIN
IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
IPEndPoint[] activeTcpListeners = ipGlobalProperties.GetActiveTcpListeners();
foreach (IPEndPoint endPoint in activeTcpListeners) {
if (endPoint.Port == port) {
return true;
}
}
return false;
#else
try {
using (TcpClient tcpClient = new TcpClient()) {
tcpClient.Connect(IPAddress.Loopback, port); // Try to connect to the specified port
return true;
}
} catch (SocketException) {
return false;
} catch (Exception e) {
Log.Exception(e);
// act as if the port is allocated
return true;
}
#endif
}
static async Task<int> Prepare() {
await ThreadUtility.SwitchToMainThread();
var dataPath = UnityHelper.DataPath;
await ProjectGeneration.ProjectGeneration.EnsureSlnAndCsprojFiles(dataPath);
await PrepareBuildInfoAsync();
PrepareSystemPathsFile();
var port = DiscoverFreePort();
HotReloadState.ServerPort = port;
RequestHelper.SetServerPort(port);
return port;
}
static bool didLogWarning;
internal static async Task PrepareBuildInfoAsync() {
await ThreadUtility.SwitchToMainThread();
var buildInfoInput = await BuildInfoHelper.GetGenerateBuildInfoInput();
await Task.Run(() => {
try {
var buildInfo = BuildInfoHelper.GenerateBuildInfoThreaded(buildInfoInput);
PrepareBuildInfo(buildInfo);
} catch (Exception e) {
if (!didLogWarning) {
Log.Warning($"Preparing build info failed! On-device functionality might not work. Exception: {e}");
didLogWarning = true;
} else {
Log.Debug($"Preparing build info failed! On-device functionality might not work. Exception: {e}");
}
}
});
}
internal static void PrepareBuildInfo(BuildInfo buildInfo) {
// When starting server make sure it starts with correct player data state.
// (this fixes issue where Unity is in background and not sending files state).
// Always write player data because you can be on any build target and want to connect with a downloaded android build.
var json = buildInfo.ToJson();
var cliTempDir = CliUtils.GetCliTempDir();
Directory.CreateDirectory(cliTempDir);
File.WriteAllText(Path.Combine(cliTempDir, "playerdata.json"), json);
}
static void PrepareSystemPathsFile() {
#pragma warning disable CS0618 // obsolete since 2023
var lvl = PlayerSettings.GetApiCompatibilityLevel(EditorUserBuildSettings.selectedBuildTargetGroup);
#pragma warning restore CS0618
#if UNITY_2020_3_OR_NEWER
var dirs = UnityEditor.Compilation.CompilationPipeline.GetSystemAssemblyDirectories(lvl);
#else
var t = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.Scripting.ScriptCompilation.MonoLibraryHelpers");
var m = t.GetMethod("GetSystemReferenceDirectories");
var dirs = m.Invoke(null, new object[] { lvl });
#endif
Directory.CreateDirectory(PackageConst.LibraryCachePath);
File.WriteAllText(PackageConst.LibraryCachePath + "/systemAssemblies.json", JsonConvert.SerializeObject(dirs));
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9f756ed6b78d428b8b9f83a6544317fe
timeCreated: 1673820326

View File

@@ -0,0 +1,13 @@
using System.Threading.Tasks;
namespace SingularityGroup.HotReload.Editor.Cli {
interface ICliController {
string BinaryFileName {get;}
string PlatformName {get;}
bool CanOpenInBackground {get;}
Task Start(StartArgs args);
Task Stop();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8cba48e21f76483da3ba615915e731fd
timeCreated: 1673820542

View File

@@ -0,0 +1,73 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using Debug = UnityEngine.Debug;
namespace SingularityGroup.HotReload.Editor.Cli {
class LinuxCliController : ICliController {
Process process;
public string BinaryFileName => "CodePatcherCLI";
public string PlatformName => "linux-x64";
public bool CanOpenInBackground => true;
public Task Start(StartArgs args) {
var startScript = Path.Combine(args.executableSourceDir, "hotreload-start-script.sh");
if (!File.Exists(startScript)) {
throw new FileNotFoundException(startScript);
}
File.WriteAllText(startScript, File.ReadAllText(startScript).Replace("\r\n", "\n"));
CliUtils.Chmod(startScript);
var title = CodePatcher.TAG + "Server " + new DirectoryInfo(args.unityProjDir).Name;
title = title.Replace(" ", "-");
title = title.Replace("'", "");
var cliargsfile = Path.GetTempFileName();
File.WriteAllText(cliargsfile,args.cliArguments);
var codePatcherProc = Process.Start(new ProcessStartInfo {
FileName = startScript,
Arguments =
$"--title \"{title}\""
+ $" --executables-source-dir \"{args.executableSourceDir}\" "
+ $" --executable-taget-dir \"{args.executableTargetDir}\""
+ $" --pidfile \"{CliUtils.GetPidFilePath(args.hotreloadTempDir)}\""
+ $" --cli-arguments-file \"{cliargsfile}\""
+ $" --method-patch-dir \"{args.cliTempDir}\""
+ $" --create-no-window \"{args.createNoWindow}\"",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
});
if (codePatcherProc == null) {
if (File.Exists(cliargsfile)) {
File.Delete(cliargsfile);
}
throw new Exception("Could not start code patcher process.");
}
codePatcherProc.BeginErrorReadLine();
codePatcherProc.BeginOutputReadLine();
codePatcherProc.OutputDataReceived += (_, a) => {
};
// error data can also mean we kill the proc beningly
codePatcherProc.ErrorDataReceived += (_, a) => {
};
process = codePatcherProc;
return Task.CompletedTask;
}
public async Task Stop() {
await RequestHelper.KillServer();
try {
// process.CloseMainWindow throws if proc already exited.
// also we just rely on the pid file it is fine
CliUtils.KillLastKnownHotReloadProcess();
} catch {
//ignored
}
process = null;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c894a69d595d4ada8cfa4afe23c68ab9
timeCreated: 1673820131

View File

@@ -0,0 +1,189 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using SingularityGroup.HotReload.Editor.Semver;
using Debug = UnityEngine.Debug;
namespace SingularityGroup.HotReload.Editor.Cli {
class OsxCliController : ICliController {
Process process;
public string BinaryFileName => "HotReload.app.zip";
public string PlatformName => "osx-x64";
public bool CanOpenInBackground => false;
/// In MacOS 13 Ventura, our app cannot launch a terminal window.
/// We use a custom app that launches HotReload server and shows it's output (just like a terminal would).
// Including MacOS 12 Monterey as well so I can dogfood it -Troy
private static bool UseCustomConsoleApp() => MacOSVersion.Value.Major >= 12;
// dont use static because null comparison on SemVersion is broken
private static readonly Lazy<SemVersion> MacOSVersion = new Lazy<SemVersion>(() => {
//UnityHelper.OperatingSystem; // in Unity 2018 it returns 10.16 on monterey (no idea why)
//Environment.OSVersion returns unix version like 21.x
var startinfo = new ProcessStartInfo {
FileName = "/usr/bin/sw_vers",
Arguments = "-productVersion",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true,
};
var process = Process.Start(startinfo);
string osVersion = process.StandardOutput.ReadToEnd().Trim();
SemVersion macosVersion;
if (SemVersion.TryParse(osVersion, out macosVersion)) {
return macosVersion;
}
// should never happen
Log.Warning("Failed to detect MacOS version, if Hot Reload fails to start, please contact support.");
return SemVersion.None;
});
public async Task Start(StartArgs args) {
// Unzip the .app.zip to temp folder .app
var appExecutablePath = $"{args.executableTargetDir}/HotReload.app/Contents/MacOS/HotReload";
var cliExecutablePath = $"{args.executableTargetDir}/HotReload.app/Contents/Resources/CodePatcherCLI";
// ensure running on threadpool
await ThreadUtility.SwitchToThreadPool();
// executableTargetDir is versioned, so only need to extract once.
if (!File.Exists(appExecutablePath)) {
try {
// delete only the extracted app folder (must not delete downloaded zip which is in same folder)
Directory.Delete(args.executableTargetDir + "/HotReload.app", true);
} catch (IOException) {
// ignore directory not found
}
Directory.CreateDirectory(args.executableTargetDir);
UnzipMacOsPackage($"{args.executableTargetDir}/{BinaryFileName}", args.executableTargetDir + "/");
}
try {
// Always stop first because rarely it has happened that the server process was still running after custom console closed.
// Note: this will also stop Hot Reload started by other Unity projects.
await Stop();
} catch {
// ignored
}
if (UseCustomConsoleApp()) {
await StartCustomConsole(args, appExecutablePath);
} else {
await StartTerminal(args, cliExecutablePath);
}
}
public Task StartCustomConsole(StartArgs args, string executablePath) {
process = Process.Start(new ProcessStartInfo {
// Path to the HotReload.app
FileName = executablePath,
Arguments = args.cliArguments,
UseShellExecute = false,
});
return Task.CompletedTask;
}
public Task StartTerminal(StartArgs args, string executablePath) {
var pidFilePath = CliUtils.GetPidFilePath(args.hotreloadTempDir);
// To run in a Terminal window (so you can see compiler logs), we must put the arguments into a script file
// and run the script in Terminal. Terminal.app does not forward the arguments passed to it via `open --args`.
// *.command files are opened with the user's default terminal app.
var executableScriptPath = Path.Combine(Path.GetTempPath(), "Start_HotReloadServer.command");
// You don't need to copy the cli executable on mac
// omit hashbang line, let shell use the default interpreter (easier than detecting your default shell beforehand)
File.WriteAllText(executableScriptPath, $"echo $$ > \"{pidFilePath}\"" +
$"\ncd \"{Environment.CurrentDirectory}\"" + // set cwd because 'open' launches script with $HOME as cwd.
$"\n\"{executablePath}\" {args.cliArguments} || read");
CliUtils.Chmod(executableScriptPath); // make it executable
CliUtils.Chmod(executablePath); // make it executable
Directory.CreateDirectory(args.hotreloadTempDir);
Directory.CreateDirectory(args.executableTargetDir);
Directory.CreateDirectory(args.cliTempDir);
process = Process.Start(new ProcessStartInfo {
FileName = "open",
Arguments = $"{(args.createNoWindow ? "-gj" : "")} '{executableScriptPath}'",
UseShellExecute = true,
});
if (process.WaitForExit(1000)) {
if (process.ExitCode != 0) {
Log.Warning("Failed to the run the start server command. ExitCode={0}\nFilepath: {1}", process.ExitCode, executableScriptPath);
}
}
else {
process.EnableRaisingEvents = true;
process.Exited += (_, __) => {
if (process.ExitCode != 0) {
Log.Warning("Failed to the run the start server command. ExitCode={0}\nFilepath: {1}", process.ExitCode, executableScriptPath);
}
};
}
return Task.CompletedTask;
}
public async Task Stop() {
// kill HotReload server process (on mac it has different pid to the window which started it)
await RequestHelper.KillServer();
// process.CloseMainWindow throws if proc already exited.
// We rely on the pid file for killing the trampoline script (in-case script is just starting and HotReload server not running yet)
process = null;
CliUtils.KillLastKnownHotReloadProcess();
}
static void UnzipMacOsPackage(string zipPath, string unzippedFolderPath) {
//Log.Info("UnzipMacOsPackage called with {0}\n workingDirectory = {1}", zipPath, unzippedFolderPath);
if (!zipPath.EndsWith(".zip")) {
throw new ArgumentException($"Expected to end with .zip, but it was: {zipPath}", nameof(zipPath));
}
if (!File.Exists(zipPath)) {
throw new ArgumentException($"zip file not found {zipPath}", nameof(zipPath));
}
var processStartInfo = new ProcessStartInfo {
FileName = "unzip",
Arguments = $"-o \"{zipPath}\"",
WorkingDirectory = unzippedFolderPath, // unzip extracts to working directory by default
UseShellExecute = true,
CreateNoWindow = true
};
Process process = Process.Start(processStartInfo);
process.WaitForExit();
if (process.ExitCode != 0) {
throw new Exception($"unzip failed with ExitCode {process.ExitCode}");
}
//Log.Info($"did unzip to {unzippedFolderPath}");
// Move the .app folder to unzippedFolderPath
// find the .app directory which is now inside unzippedFolderPath directory
var foundDirs = Directory.GetDirectories(unzippedFolderPath, "*.app", SearchOption.AllDirectories);
var done = false;
var destDir = unzippedFolderPath + "HotReload.app";
foreach (var dir in foundDirs) {
if (dir.EndsWith(".app")) {
done = true;
if (dir == destDir) {
// already in the right place
break;
}
Directory.Move(dir, destDir);
//Log.Info("Moved to " + destDir);
break;
}
}
if (!done) {
throw new Exception("Failed to find .app directory and move it to " + destDir);
}
//Log.Info($"did unzip to {unzippedFolderPath}");
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5ebeed1c29454bc78e5a9ee64f2c9def
timeCreated: 1673821666

View File

@@ -0,0 +1,12 @@
namespace SingularityGroup.HotReload.Editor.Cli {
class StartArgs {
public string hotreloadTempDir;
// aka method patch temp dir
public string cliTempDir;
public string executableTargetDir;
public string executableSourceDir;
public string cliArguments;
public string unityProjDir;
public bool createNoWindow;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 43d69eb7ae8aef4428da83562105bfaa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
namespace SingularityGroup.HotReload.Editor.Cli {
class WindowsCliController : ICliController {
Process process;
public string BinaryFileName => "CodePatcherCLI.exe";
public string PlatformName => "win-x64";
public bool CanOpenInBackground => true;
public Task Start(StartArgs args) {
process = Process.Start(new ProcessStartInfo {
FileName = Path.GetFullPath(Path.Combine(args.executableTargetDir, "CodePatcherCLI.exe")),
Arguments = args.cliArguments,
UseShellExecute = !args.createNoWindow,
CreateNoWindow = args.createNoWindow,
});
return Task.CompletedTask;
}
public async Task Stop() {
await RequestHelper.KillServer();
try {
process?.CloseMainWindow();
} catch {
//ignored
}
process = null;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e5644af69ec7404a8039ff2833610d48
timeCreated: 1673822169