using System; using MCPForUnity.Editor.Constants; using UnityEditor; namespace MCPForUnity.Editor.Services { /// /// Centralized cache for frequently-read EditorPrefs values. /// Reduces scattered EditorPrefs.Get* calls and provides change notification. /// /// Usage: /// var config = EditorConfigurationCache.Instance; /// if (config.UseHttpTransport) { ... } /// config.OnConfigurationChanged += (key) => { /* refresh UI */ }; /// public class EditorConfigurationCache { private static EditorConfigurationCache _instance; private static readonly object _lock = new object(); /// /// Singleton instance. Thread-safe lazy initialization. /// public static EditorConfigurationCache Instance { get { if (_instance == null) { lock (_lock) { if (_instance == null) { _instance = new EditorConfigurationCache(); } } } return _instance; } } /// /// Event fired when any cached configuration value changes. /// The string parameter is the EditorPrefKeys constant name that changed. /// public event Action OnConfigurationChanged; // Cached values - most frequently read private bool _useHttpTransport; private bool _debugLogs; private bool _useBetaServer; private bool _devModeForceServerRefresh; private string _uvxPathOverride; private string _gitUrlOverride; private string _httpBaseUrl; private string _httpRemoteBaseUrl; private string _claudeCliPathOverride; private string _httpTransportScope; private int _unitySocketPort; /// /// Whether to use HTTP transport (true) or Stdio transport (false). /// Default: true /// public bool UseHttpTransport => _useHttpTransport; /// /// Whether debug logging is enabled. /// Default: false /// public bool DebugLogs => _debugLogs; /// /// Whether to use the beta server channel. /// Default: true /// public bool UseBetaServer => _useBetaServer; /// /// Whether to force server refresh in dev mode (--no-cache --refresh). /// Default: false /// public bool DevModeForceServerRefresh => _devModeForceServerRefresh; /// /// Custom path override for uvx executable. /// Default: empty string (auto-detect) /// public string UvxPathOverride => _uvxPathOverride; /// /// Custom Git URL override for server installation. /// Default: empty string (use default) /// public string GitUrlOverride => _gitUrlOverride; /// /// HTTP base URL for the local MCP server. /// Default: empty string /// public string HttpBaseUrl => _httpBaseUrl; /// /// HTTP base URL for the remote-hosted MCP server. /// Default: empty string /// public string HttpRemoteBaseUrl => _httpRemoteBaseUrl; /// /// Custom path override for Claude CLI executable. /// Default: empty string (auto-detect) /// public string ClaudeCliPathOverride => _claudeCliPathOverride; /// /// HTTP transport scope: "local" or "remote". /// Default: empty string /// public string HttpTransportScope => _httpTransportScope; /// /// Unity socket port for Stdio transport. /// Default: 0 (auto-assign) /// public int UnitySocketPort => _unitySocketPort; /// /// Gets UseBetaServer value with dynamic default based on package version. /// If the pref hasn't been explicitly set, defaults to true for prerelease packages /// (beta, alpha, rc, etc.) and false for stable releases. /// private static bool GetUseBetaServerWithDynamicDefault() { // If user has explicitly set the pref, use that value if (EditorPrefs.HasKey(EditorPrefKeys.UseBetaServer)) { return EditorPrefs.GetBool(EditorPrefKeys.UseBetaServer, false); } // Otherwise, default based on whether this is a prerelease package return Helpers.AssetPathUtility.IsPreReleaseVersion(); } private EditorConfigurationCache() { Refresh(); } /// /// Refresh all cached values from EditorPrefs. /// Call this after bulk EditorPrefs changes or domain reload. /// public void Refresh() { _useHttpTransport = EditorPrefs.GetBool(EditorPrefKeys.UseHttpTransport, true); _debugLogs = EditorPrefs.GetBool(EditorPrefKeys.DebugLogs, false); _useBetaServer = GetUseBetaServerWithDynamicDefault(); _devModeForceServerRefresh = EditorPrefs.GetBool(EditorPrefKeys.DevModeForceServerRefresh, false); _uvxPathOverride = EditorPrefs.GetString(EditorPrefKeys.UvxPathOverride, string.Empty); _gitUrlOverride = EditorPrefs.GetString(EditorPrefKeys.GitUrlOverride, string.Empty); _httpBaseUrl = EditorPrefs.GetString(EditorPrefKeys.HttpBaseUrl, string.Empty); _httpRemoteBaseUrl = EditorPrefs.GetString(EditorPrefKeys.HttpRemoteBaseUrl, string.Empty); _claudeCliPathOverride = EditorPrefs.GetString(EditorPrefKeys.ClaudeCliPathOverride, string.Empty); _httpTransportScope = EditorPrefs.GetString(EditorPrefKeys.HttpTransportScope, string.Empty); _unitySocketPort = EditorPrefs.GetInt(EditorPrefKeys.UnitySocketPort, 0); } /// /// Set UseHttpTransport and update cache + EditorPrefs atomically. /// public void SetUseHttpTransport(bool value) { if (_useHttpTransport != value) { _useHttpTransport = value; EditorPrefs.SetBool(EditorPrefKeys.UseHttpTransport, value); OnConfigurationChanged?.Invoke(nameof(UseHttpTransport)); } } /// /// Set DebugLogs and update cache + EditorPrefs atomically. /// public void SetDebugLogs(bool value) { if (_debugLogs != value) { _debugLogs = value; EditorPrefs.SetBool(EditorPrefKeys.DebugLogs, value); OnConfigurationChanged?.Invoke(nameof(DebugLogs)); } } /// /// Set UseBetaServer and update cache + EditorPrefs atomically. /// public void SetUseBetaServer(bool value) { if (_useBetaServer != value) { _useBetaServer = value; EditorPrefs.SetBool(EditorPrefKeys.UseBetaServer, value); OnConfigurationChanged?.Invoke(nameof(UseBetaServer)); } } /// /// Set DevModeForceServerRefresh and update cache + EditorPrefs atomically. /// public void SetDevModeForceServerRefresh(bool value) { if (_devModeForceServerRefresh != value) { _devModeForceServerRefresh = value; EditorPrefs.SetBool(EditorPrefKeys.DevModeForceServerRefresh, value); OnConfigurationChanged?.Invoke(nameof(DevModeForceServerRefresh)); } } /// /// Set UvxPathOverride and update cache + EditorPrefs atomically. /// public void SetUvxPathOverride(string value) { value = value ?? string.Empty; if (_uvxPathOverride != value) { _uvxPathOverride = value; EditorPrefs.SetString(EditorPrefKeys.UvxPathOverride, value); OnConfigurationChanged?.Invoke(nameof(UvxPathOverride)); } } /// /// Set GitUrlOverride and update cache + EditorPrefs atomically. /// public void SetGitUrlOverride(string value) { value = value ?? string.Empty; if (_gitUrlOverride != value) { _gitUrlOverride = value; EditorPrefs.SetString(EditorPrefKeys.GitUrlOverride, value); OnConfigurationChanged?.Invoke(nameof(GitUrlOverride)); } } /// /// Set HttpBaseUrl and update cache + EditorPrefs atomically. /// public void SetHttpBaseUrl(string value) { value = value ?? string.Empty; if (_httpBaseUrl != value) { _httpBaseUrl = value; EditorPrefs.SetString(EditorPrefKeys.HttpBaseUrl, value); OnConfigurationChanged?.Invoke(nameof(HttpBaseUrl)); } } /// /// Set HttpRemoteBaseUrl and update cache + EditorPrefs atomically. /// public void SetHttpRemoteBaseUrl(string value) { value = value ?? string.Empty; if (_httpRemoteBaseUrl != value) { _httpRemoteBaseUrl = value; EditorPrefs.SetString(EditorPrefKeys.HttpRemoteBaseUrl, value); OnConfigurationChanged?.Invoke(nameof(HttpRemoteBaseUrl)); } } /// /// Set ClaudeCliPathOverride and update cache + EditorPrefs atomically. /// public void SetClaudeCliPathOverride(string value) { value = value ?? string.Empty; if (_claudeCliPathOverride != value) { _claudeCliPathOverride = value; EditorPrefs.SetString(EditorPrefKeys.ClaudeCliPathOverride, value); OnConfigurationChanged?.Invoke(nameof(ClaudeCliPathOverride)); } } /// /// Set HttpTransportScope and update cache + EditorPrefs atomically. /// public void SetHttpTransportScope(string value) { value = value ?? string.Empty; if (_httpTransportScope != value) { _httpTransportScope = value; EditorPrefs.SetString(EditorPrefKeys.HttpTransportScope, value); OnConfigurationChanged?.Invoke(nameof(HttpTransportScope)); } } /// /// Set UnitySocketPort and update cache + EditorPrefs atomically. /// public void SetUnitySocketPort(int value) { if (_unitySocketPort != value) { _unitySocketPort = value; EditorPrefs.SetInt(EditorPrefKeys.UnitySocketPort, value); OnConfigurationChanged?.Invoke(nameof(UnitySocketPort)); } } /// /// Force refresh of a single cached value from EditorPrefs. /// Useful when external code modifies EditorPrefs directly. /// public void InvalidateKey(string keyName) { switch (keyName) { case nameof(UseHttpTransport): _useHttpTransport = EditorPrefs.GetBool(EditorPrefKeys.UseHttpTransport, true); break; case nameof(DebugLogs): _debugLogs = EditorPrefs.GetBool(EditorPrefKeys.DebugLogs, false); break; case nameof(UseBetaServer): _useBetaServer = GetUseBetaServerWithDynamicDefault(); break; case nameof(DevModeForceServerRefresh): _devModeForceServerRefresh = EditorPrefs.GetBool(EditorPrefKeys.DevModeForceServerRefresh, false); break; case nameof(UvxPathOverride): _uvxPathOverride = EditorPrefs.GetString(EditorPrefKeys.UvxPathOverride, string.Empty); break; case nameof(GitUrlOverride): _gitUrlOverride = EditorPrefs.GetString(EditorPrefKeys.GitUrlOverride, string.Empty); break; case nameof(HttpBaseUrl): _httpBaseUrl = EditorPrefs.GetString(EditorPrefKeys.HttpBaseUrl, string.Empty); break; case nameof(HttpRemoteBaseUrl): _httpRemoteBaseUrl = EditorPrefs.GetString(EditorPrefKeys.HttpRemoteBaseUrl, string.Empty); break; case nameof(ClaudeCliPathOverride): _claudeCliPathOverride = EditorPrefs.GetString(EditorPrefKeys.ClaudeCliPathOverride, string.Empty); break; case nameof(HttpTransportScope): _httpTransportScope = EditorPrefs.GetString(EditorPrefKeys.HttpTransportScope, string.Empty); break; case nameof(UnitySocketPort): _unitySocketPort = EditorPrefs.GetInt(EditorPrefKeys.UnitySocketPort, 0); break; } OnConfigurationChanged?.Invoke(keyName); } } }