using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Codely.Newtonsoft.Json.Linq;
using UnityEditor;
using UnityEditor.PackageManager;
using UnityEngine;
using UnityTcp.Editor.Helpers;
namespace UnityTcp.Editor.Tools
{
///
/// [EXPERIMENTAL] Handles baking operations (NavMesh, Lighting, etc.).
/// Compatible with Unity 2022.3 LTS.
///
public static class ManageBake
{
// Store callbacks for proper unsubscription
private static readonly Dictionary _updateCallbacks = new Dictionary();
private static readonly object _callbackLock = new object();
// Store async operations for NavMesh baking
private static readonly Dictionary> _navMeshBakeOperations = new Dictionary>();
// Runtime check for AI Navigation package availability
private static bool? _hasAINavigation = null;
private static Type _navMeshSurfaceType = null;
private static MethodInfo _buildNavMeshMethod = null;
private static MethodInfo _updateNavMeshMethod = null;
private static PropertyInfo _activeSurfacesProperty = null;
private static Type _navMeshType = null;
private static MethodInfo _calculateTriangulationMethod = null;
private static MethodInfo _removeAllNavMeshDataMethod = null;
///
/// Reset the AI Navigation package cache. Call this after installing the package
/// to force re-checking for available types.
///
private static void ResetAINavigationCache()
{
_hasAINavigation = null;
_navMeshSurfaceType = null;
_buildNavMeshMethod = null;
_updateNavMeshMethod = null;
_activeSurfacesProperty = null;
_navMeshType = null;
_calculateTriangulationMethod = null;
_removeAllNavMeshDataMethod = null;
}
private static bool HasAINavigation()
{
if (_hasAINavigation.HasValue)
return _hasAINavigation.Value;
try
{
// First, check if the package is installed via PackageManager
bool packageInstalled = false;
try
{
#if UNITY_2021_2_OR_NEWER
// Use GetAllRegisteredPackages for Unity 2021.2+
var packages = UnityEditor.PackageManager.PackageInfo.GetAllRegisteredPackages();
packageInstalled = packages.Any(p => p.name == "com.unity.ai.navigation");
#else
// Fallback for older Unity versions
var listRequest = Client.List(true, false);
while (!listRequest.IsCompleted)
{
System.Threading.Thread.Sleep(50);
}
if (listRequest.Status == StatusCode.Success)
{
packageInstalled = listRequest.Result.Any(p => p.name == "com.unity.ai.navigation");
}
#endif
}
catch (Exception ex)
{
Debug.LogWarning($"[ManageBake] Error checking package installation: {ex.Message}");
// Continue with type checking as fallback
}
// Try to find NavMeshSurface type (Unity.AI.Navigation namespace from com.unity.ai.navigation package)
// Try multiple methods to find the type
_navMeshSurfaceType = Type.GetType("Unity.AI.Navigation.NavMeshSurface, Unity.AI.Navigation");
if (_navMeshSurfaceType == null)
{
// Try with full assembly qualified name variations
_navMeshSurfaceType = Type.GetType("Unity.AI.Navigation.NavMeshSurface, Unity.AI.Navigation, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
}
if (_navMeshSurfaceType == null)
{
// Fallback: search in loaded assemblies by name first
System.Reflection.Assembly targetAssembly = null;
foreach (var assembly in System.AppDomain.CurrentDomain.GetAssemblies())
{
var assemblyName = assembly.GetName().Name;
if (assemblyName == "Unity.AI.Navigation" || assemblyName.Contains("Unity.AI.Navigation"))
{
targetAssembly = assembly;
break;
}
}
if (targetAssembly != null)
{
_navMeshSurfaceType = targetAssembly.GetType("Unity.AI.Navigation.NavMeshSurface");
}
}
if (_navMeshSurfaceType == null)
{
// Last resort: search all assemblies
foreach (var assembly in System.AppDomain.CurrentDomain.GetAssemblies())
{
_navMeshSurfaceType = assembly.GetType("Unity.AI.Navigation.NavMeshSurface");
if (_navMeshSurfaceType != null) break;
}
}
if (_navMeshSurfaceType != null)
{
_buildNavMeshMethod = _navMeshSurfaceType.GetMethod("BuildNavMesh", BindingFlags.Public | BindingFlags.Instance);
_updateNavMeshMethod = _navMeshSurfaceType.GetMethod("UpdateNavMesh", BindingFlags.Public | BindingFlags.Instance);
_activeSurfacesProperty = _navMeshSurfaceType.GetProperty("activeSurfaces", BindingFlags.Public | BindingFlags.Static);
}
// Try to find NavMesh type (UnityEngine.AI namespace - still used by the package)
_navMeshType = Type.GetType("UnityEngine.AI.NavMesh, UnityEngine.AIModule");
if (_navMeshType == null)
{
foreach (var assembly in System.AppDomain.CurrentDomain.GetAssemblies())
{
_navMeshType = assembly.GetType("UnityEngine.AI.NavMesh");
if (_navMeshType != null) break;
}
}
if (_navMeshType != null)
{
_calculateTriangulationMethod = _navMeshType.GetMethod("CalculateTriangulation", BindingFlags.Public | BindingFlags.Static);
_removeAllNavMeshDataMethod = _navMeshType.GetMethod("RemoveAllNavMeshData", BindingFlags.Public | BindingFlags.Static);
}
// Check both package installation and required types/methods
bool hasRequiredTypes = _navMeshSurfaceType != null && _buildNavMeshMethod != null && _navMeshType != null;
// If package is installed but types are missing, check compilation status
if (packageInstalled && !hasRequiredTypes)
{
bool isCompiling = EditorApplication.isCompiling;
string compilationStatus = isCompiling ? "compiling" : "idle";
// Collect diagnostic information
var loadedAssemblies = System.AppDomain.CurrentDomain.GetAssemblies()
.Where(a => a.GetName().Name.Contains("AI") || a.GetName().Name.Contains("Navigation"))
.Select(a => a.GetName().Name)
.ToList();
string diagnosticInfo = "";
if (loadedAssemblies.Count > 0)
{
diagnosticInfo = $" Found related assemblies: {string.Join(", ", loadedAssemblies)}.";
}
else
{
diagnosticInfo = " No AI/Navigation assemblies found in loaded assemblies.";
}
string typeStatus = "";
if (_navMeshSurfaceType == null)
{
typeStatus += " NavMeshSurface type not found.";
}
else
{
typeStatus += $" NavMeshSurface found, but methods missing: BuildNavMesh={_buildNavMeshMethod != null}, UpdateNavMesh={_updateNavMeshMethod != null}, activeSurfaces={_activeSurfacesProperty != null}.";
}
if (_navMeshType == null)
{
typeStatus += " NavMesh type not found.";
}
Debug.LogWarning(
$"[ManageBake] com.unity.ai.navigation package is installed but required types/methods are not available. " +
$"Editor is currently {compilationStatus}.{diagnosticInfo}{typeStatus} " +
(isCompiling
? "Please wait for compilation to complete, then call 'unity_editor { \"action\": \"wait_for_idle\" }' before retrying."
: "The package may need to be reloaded. Try restarting Unity or wait a moment and retry.")
);
}
// Package installation check is primary, but we also need the types to be available
// If package is installed but types are missing and we're not compiling, return false
// If we're compiling, also return false (types won't be available until compilation completes)
_hasAINavigation = packageInstalled && hasRequiredTypes && !EditorApplication.isCompiling;
}
catch (Exception ex)
{
Debug.LogWarning($"[ManageBake] Error checking for AI Navigation package: {ex.Message}");
_hasAINavigation = false;
}
return _hasAINavigation.Value;
}
public static object HandleCommand(JObject @params)
{
string action = @params["action"]?.ToString().ToLower();
if (string.IsNullOrEmpty(action))
{
return Response.Error("Action parameter is required.");
}
try
{
switch (action)
{
case "bake_navmesh":
return BakeNavMesh(@params);
case "bake_lighting":
return BakeLighting(@params);
case "wait_for_bake":
return WaitForBake(@params);
case "clear_navmesh":
return ClearNavMesh();
case "clear_baked_data":
return ClearBakedData();
default:
return Response.Error(
$"Unknown action: '{action}'. Valid actions: bake_navmesh, bake_lighting, wait_for_bake, clear_navmesh, clear_baked_data."
);
}
}
catch (Exception e)
{
Debug.LogError($"[ManageBake] Action '{action}' failed: {e}");
return Response.Error($"[EXPERIMENTAL] Bake operation failed: {e.Message}");
}
}
private static object BakeNavMesh(JObject @params)
{
try
{
// Reset cache and re-check if first check fails (in case package was just installed)
if (!HasAINavigation())
{
ResetAINavigationCache();
if (!HasAINavigation())
{
// Check if package is installed but types are not available
bool packageInstalled = false;
try
{
#if UNITY_2021_2_OR_NEWER
var packages = UnityEditor.PackageManager.PackageInfo.GetAllRegisteredPackages();
packageInstalled = packages.Any(p => p.name == "com.unity.ai.navigation");
#else
var listRequest = Client.List(true, false);
while (!listRequest.IsCompleted)
{
System.Threading.Thread.Sleep(50);
}
if (listRequest.Status == StatusCode.Success)
{
packageInstalled = listRequest.Result.Any(p => p.name == "com.unity.ai.navigation");
}
#endif
}
catch { }
bool isCompiling = EditorApplication.isCompiling;
string errorMessage;
if (packageInstalled && isCompiling)
{
errorMessage =
"[EXPERIMENTAL] NavMesh baking requires AI Navigation package types to be loaded. " +
"The package is installed but Unity is currently compiling. " +
"Please wait for compilation to complete by calling 'unity_editor { \"action\": \"wait_for_idle\" }', then retry.";
}
else if (packageInstalled)
{
errorMessage =
"[EXPERIMENTAL] NavMesh baking requires AI Navigation package types to be loaded. " +
"The package 'com.unity.ai.navigation' is installed but required types are not available. " +
"This may happen if: (1) compilation is in progress, (2) the package needs to be reloaded, or (3) Unity needs to be restarted. " +
"Try: (1) Call 'unity_editor { \"action\": \"wait_for_idle\" }' to ensure compilation is complete, " +
"(2) Wait a few seconds and retry, or (3) Restart Unity.";
}
else
{
errorMessage =
"[EXPERIMENTAL] NavMesh baking requires AI Navigation package. " +
"Install 'com.unity.ai.navigation' via Package Manager using: " +
"'unity_package { \"action\": \"install_package\", \"id_or_url\": \"com.unity.ai.navigation\" }', " +
"then wait for installation and compilation to complete using 'unity_editor { \"action\": \"wait_for_idle\" }'.";
}
return Response.Error(errorMessage);
}
}
var writeCheck = WriteGuard.CheckWriteAllowed("bake_navmesh");
if (writeCheck != null) return writeCheck;
var job = AsyncOperationTracker.CreateJob(
AsyncOperationTracker.JobType.NavMeshBake,
"Baking NavMesh..."
);
// Get all active NavMeshSurface components in the scene
List