218 lines
8.3 KiB
C#
218 lines
8.3 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
namespace UnityMcpBridge.Editor.Helpers
|
|
{
|
|
public static class ServerInstaller
|
|
{
|
|
private const string RootFolder = "UnityMCP";
|
|
private const string ServerFolder = "UnityMcpServer";
|
|
/// <summary>
|
|
/// Ensures the unity-mcp-server is installed locally by copying from the embedded package source.
|
|
/// No network calls or Git operations are performed.
|
|
/// </summary>
|
|
public static void EnsureServerInstalled()
|
|
{
|
|
try
|
|
{
|
|
string saveLocation = GetSaveLocation();
|
|
string destRoot = Path.Combine(saveLocation, ServerFolder);
|
|
string destSrc = Path.Combine(destRoot, "src");
|
|
|
|
if (File.Exists(Path.Combine(destSrc, "server.py")))
|
|
{
|
|
return; // Already installed
|
|
}
|
|
|
|
if (!TryGetEmbeddedServerSource(out string embeddedSrc))
|
|
{
|
|
throw new Exception("Could not find embedded UnityMcpServer/src in the package.");
|
|
}
|
|
|
|
// Ensure destination exists
|
|
Directory.CreateDirectory(destRoot);
|
|
|
|
// Copy the entire UnityMcpServer folder (parent of src)
|
|
string embeddedRoot = Path.GetDirectoryName(embeddedSrc) ?? embeddedSrc; // go up from src to UnityMcpServer
|
|
CopyDirectoryRecursive(embeddedRoot, destRoot);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogError($"Failed to ensure server installation: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
public static string GetServerPath()
|
|
{
|
|
return Path.Combine(GetSaveLocation(), ServerFolder, "src");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the platform-specific save location for the server.
|
|
/// </summary>
|
|
private static string GetSaveLocation()
|
|
{
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
{
|
|
return Path.Combine(
|
|
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
|
"AppData",
|
|
"Local",
|
|
"Programs",
|
|
RootFolder
|
|
);
|
|
}
|
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
|
{
|
|
return Path.Combine(
|
|
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
|
"bin",
|
|
RootFolder
|
|
);
|
|
}
|
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
|
{
|
|
string path = "/usr/local/bin";
|
|
return !Directory.Exists(path) || !IsDirectoryWritable(path)
|
|
? Path.Combine(
|
|
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
|
"Applications",
|
|
RootFolder
|
|
)
|
|
: Path.Combine(path, RootFolder);
|
|
}
|
|
throw new Exception("Unsupported operating system.");
|
|
}
|
|
|
|
private static bool IsDirectoryWritable(string path)
|
|
{
|
|
try
|
|
{
|
|
File.Create(Path.Combine(path, "test.txt")).Dispose();
|
|
File.Delete(Path.Combine(path, "test.txt"));
|
|
return true;
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if the server is installed at the specified location.
|
|
/// </summary>
|
|
private static bool IsServerInstalled(string location)
|
|
{
|
|
return Directory.Exists(location)
|
|
&& File.Exists(Path.Combine(location, ServerFolder, "src", "server.py"));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempts to locate the embedded UnityMcpServer/src directory inside the installed package
|
|
/// or common development locations.
|
|
/// </summary>
|
|
private static bool TryGetEmbeddedServerSource(out string srcPath)
|
|
{
|
|
// 1) Development mode: common repo layouts
|
|
try
|
|
{
|
|
string projectRoot = Path.GetDirectoryName(Application.dataPath);
|
|
string[] devCandidates =
|
|
{
|
|
Path.Combine(projectRoot ?? string.Empty, "unity-mcp", "UnityMcpServer", "src"),
|
|
Path.Combine(projectRoot ?? string.Empty, "..", "unity-mcp", "UnityMcpServer", "src"),
|
|
};
|
|
foreach (string candidate in devCandidates)
|
|
{
|
|
string full = Path.GetFullPath(candidate);
|
|
if (Directory.Exists(full) && File.Exists(Path.Combine(full, "server.py")))
|
|
{
|
|
srcPath = full;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
catch { /* ignore */ }
|
|
|
|
// 2) Installed package: resolve via Package Manager
|
|
try
|
|
{
|
|
var list = UnityEditor.PackageManager.Client.List();
|
|
while (!list.IsCompleted) { }
|
|
if (list.Status == UnityEditor.PackageManager.StatusCode.Success)
|
|
{
|
|
foreach (var pkg in list.Result)
|
|
{
|
|
if (pkg.name == "com.justinpbarnett.unity-mcp")
|
|
{
|
|
string packagePath = pkg.resolvedPath; // e.g., Library/PackageCache/... or local path
|
|
|
|
// Preferred: UnityMcpServer embedded alongside Editor/Runtime within the package
|
|
string embedded = Path.Combine(packagePath, "UnityMcpServer", "src");
|
|
if (Directory.Exists(embedded) && File.Exists(Path.Combine(embedded, "server.py")))
|
|
{
|
|
srcPath = embedded;
|
|
return true;
|
|
}
|
|
|
|
// Legacy: sibling of the package folder (dev-linked). Only valid when present on disk.
|
|
string sibling = Path.Combine(Path.GetDirectoryName(packagePath) ?? string.Empty, "UnityMcpServer", "src");
|
|
if (Directory.Exists(sibling) && File.Exists(Path.Combine(sibling, "server.py")))
|
|
{
|
|
srcPath = sibling;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch { /* ignore */ }
|
|
|
|
// 3) Fallback to previous common install locations
|
|
try
|
|
{
|
|
string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
|
string[] candidates =
|
|
{
|
|
Path.Combine(home, "unity-mcp", "UnityMcpServer", "src"),
|
|
Path.Combine(home, "Applications", "UnityMCP", "UnityMcpServer", "src"),
|
|
};
|
|
foreach (string candidate in candidates)
|
|
{
|
|
if (Directory.Exists(candidate) && File.Exists(Path.Combine(candidate, "server.py")))
|
|
{
|
|
srcPath = candidate;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
catch { /* ignore */ }
|
|
|
|
srcPath = null;
|
|
return false;
|
|
}
|
|
|
|
private static void CopyDirectoryRecursive(string sourceDir, string destinationDir)
|
|
{
|
|
Directory.CreateDirectory(destinationDir);
|
|
|
|
foreach (string filePath in Directory.GetFiles(sourceDir))
|
|
{
|
|
string fileName = Path.GetFileName(filePath);
|
|
string destFile = Path.Combine(destinationDir, fileName);
|
|
File.Copy(filePath, destFile, overwrite: true);
|
|
}
|
|
|
|
foreach (string dirPath in Directory.GetDirectories(sourceDir))
|
|
{
|
|
string dirName = Path.GetFileName(dirPath);
|
|
string destSubDir = Path.Combine(destinationDir, dirName);
|
|
CopyDirectoryRecursive(dirPath, destSubDir);
|
|
}
|
|
}
|
|
}
|
|
}
|