From c0de38e1e7329bef9adffaa6efd65839a998153e Mon Sep 17 00:00:00 2001 From: David Sarno Date: Sat, 9 Aug 2025 12:05:47 -0700 Subject: [PATCH] Merge upstream/main: CoplayDev rebrand with bridge stability improvements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This merge combines upstream's organizational rebrand and updates with our comprehensive bridge stability improvements: **From Upstream:** - CoplayDev organizational rebrand (README, LICENSE, documentation) - Updated logo and deployment scripts - Python version pinning (.python-version file) **From Our Branch (Preserved):** - Comprehensive bridge stability improvements (threading, heartbeat, retries) - Enhanced debugging and diagnostic features - Embedded server installation approach (more reliable than git-based) - Broader Python compatibility (>=3.10 vs >=3.12) - Advanced port management with per-project persistence - Auto-setup and connection reliability features - Robust error handling and recovery mechanisms **Key Technical Decisions:** - Used our comprehensive UnityMcpBridge.cs (625 lines vs 473) with all stability features - Maintained embedded server approach over upstream's git-based installer - Preserved broader Python compatibility (>=3.10) for better accessibility - Used our optimized connection settings and retry logic - Kept our user-centric server installation approach (on-demand vs automatic) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- LICENSE | 2 +- README-DEV.md | 2 +- README.md | 89 ++++++------- .../Editor/Helpers/PackageInstaller.cs | 8 +- UnityMcpBridge/Editor/Helpers/PortManager.cs | 21 ++-- .../Editor/Helpers/ServerInstaller.cs | 118 +++++++++++++----- .../Editor/Windows/UnityMcpEditorWindow.cs | 110 +++++++++++----- UnityMcpBridge/package.json | 26 ++++ UnityMcpServer/src/.python-version | 1 + deploy-dev.bat | 2 +- logo.png | Bin 0 -> 40503 bytes restore-dev.bat | 2 +- 12 files changed, 258 insertions(+), 123 deletions(-) create mode 100644 UnityMcpServer/src/.python-version create mode 100644 logo.png diff --git a/LICENSE b/LICENSE index ebeecf5..e7f878d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2025 Justin P Barnett +Copyright (c) 2025 CoplayDev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README-DEV.md b/README-DEV.md index 954348b..398bdab 100644 --- a/README-DEV.md +++ b/README-DEV.md @@ -48,7 +48,7 @@ Restores original files from backup. Unity package cache is typically located at: ``` -X:\UnityProject\Library\PackageCache\com.justinpbarnett.unity-mcp@1.0.0 +X:\UnityProject\Library\PackageCache\com.coplaydev.unity-mcp@1.0.0 ``` To find it: diff --git a/README.md b/README.md index fd097f1..ec23c4f 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,15 @@ # Unity MCP ✨ +#### Proudly sponsored and maintained by [Coplay](https://www.coplay.dev/?ref=unity-mcp), the AI assistant for Unity. [Read the backstory here.](https://www.coplay.dev/blog/coplay-and-open-source-unity-mcp-join-forces) + +[![Discord](https://img.shields.io/badge/discord-join-red.svg?logo=discord&logoColor=white)](https://discord.gg/y4p8KfzrN4) [![](https://img.shields.io/badge/Unity-000000?style=flat&logo=unity&logoColor=blue 'Unity')](https://unity.com/releases/editor/archive) [![python](https://img.shields.io/badge/Python-3.12-3776AB.svg?style=flat&logo=python&logoColor=white)](https://www.python.org) [![](https://badge.mcpx.dev?status=on 'MCP Enabled')](https://modelcontextprotocol.io/introduction) -![GitHub commit activity](https://img.shields.io/github/commit-activity/w/justinpbarnett/unity-mcp) -![GitHub Issues or Pull Requests](https://img.shields.io/github/issues/justinpbarnett/unity-mcp) +![GitHub commit activity](https://img.shields.io/github/commit-activity/w/CoplayDev/unity-mcp) +![GitHub Issues or Pull Requests](https://img.shields.io/github/issues/CoplayDev/unity-mcp) [![](https://img.shields.io/badge/License-MIT-red.svg 'MIT License')](https://opensource.org/licenses/MIT) +[![](https://img.shields.io/badge/Sponsor-Coplay-red.svg 'Coplay')](https://www.coplay.dev/?ref=unity-mcp) **Create your Unity apps with LLMs!** @@ -13,14 +17,12 @@ Unity MCP acts as a bridge, allowing AI assistants (like Claude, Cursor) to inte ## 💬 Join Our Community -### [Discord](https://discord.gg/vhTUxXaqYr) +### [Discord](https://discord.gg/y4p8KfzrN4) **Get help, share ideas, and collaborate with other Unity MCP developers!** - --- - ## Key Features 🚀 * **🗣️ Natural Language Control:** Instruct your LLM to perform Unity tasks. @@ -35,7 +37,7 @@ Unity MCP acts as a bridge, allowing AI assistants (like Claude, Cursor) to inte * `read_console`: Gets messages from or clears the console. * `manage_script`: Manages C# scripts (create, read, update, delete). - * `manage_editor`: Controls and queries the editor's state and settings. + * `manage_editor`: Controls and queries the editor\'s state and settings. * `manage_scene`: Manages scenes (load, save, create, get hierarchy, etc.). * `manage_asset`: Performs asset operations (import, create, modify, delete, etc.). * `manage_shader`: Performs shader CRUD operations (create, read, modify, delete). @@ -62,7 +64,6 @@ Unity MCP connects your tools using two components: ### Prerequisites - * **Git CLI:** For cloning the server code. [Download Git](https://git-scm.com/downloads) * **Python:** Version 3.12 or newer. [Download Python](https://www.python.org/downloads/) * **Unity Hub & Editor:** Version 2020.3 LTS or newer. [Download Unity](https://unity.com/download) @@ -76,6 +77,7 @@ Unity MCP connects your tools using two components: * [Claude Code](https://github.com/anthropics/claude-code) * [Cursor](https://www.cursor.com/en/downloads) * [Visual Studio Code Copilot](https://code.visualstudio.com/docs/copilot/overview) + * [Windsurf](https://windsurf.com) * *(Others may work with manual config)* *
[Optional] Roslyn for Advanced Script Validation @@ -95,9 +97,8 @@ Unity MCP connects your tools using two components: 3. Ensure .NET compatibility settings are correct 4. Add `USE_ROSLYN` to Scripting Define Symbols 5. Restart Unity - - **Note:** Without Roslyn, script validation falls back to basic structural checks. Roslyn enables full C# compiler diagnostics with precise error reporting.
+ **Note:** Without Roslyn, script validation falls back to basic structural checks. Roslyn enables full C# compiler diagnostics with precise error reporting. ### Step 1: Install the Unity Package (Bridge) @@ -106,11 +107,13 @@ Unity MCP connects your tools using two components: 3. Click `+` -> `Add package from git URL...`. 4. Enter: ``` - https://github.com/justinpbarnett/unity-mcp.git?path=/UnityMcpBridge + https://github.com/CoplayDev/unity-mcp.git?path=/UnityMcpBridge ``` 5. Click `Add`. 6. The MCP Server should automatically be installed onto your machine as a result of this process. +**Note:** If you installed the MCP Server before Coplay's maintenance, you will need to uninstall the old package before re-installing the new one. + ### Step 2: Configure Your MCP Client Connect your MCP Client (Claude, Cursor, etc.) to the Python server you installed in Step 1. @@ -121,13 +124,13 @@ Connect your MCP Client (Claude, Cursor, etc.) to the Python server you installe 1. In Unity, go to `Window > Unity MCP`. 2. Click `Auto Configure` on the IDE you uses. -3. Look for a green status indicator 🟢 and "Connected". *(This attempts to modify the MCP Client's config file automatically)*. +3. Look for a green status indicator 🟢 and "Connected". *(This attempts to modify the MCP Client\'s config file automatically)*. **Option B: Manual Configuration** If Auto-Configure fails or you use a different client: -1. **Find your MCP Client's configuration file.** (Check client documentation). +1. **Find your MCP Client\'s configuration file.** (Check client documentation). * *Claude Example (macOS):* `~/Library/Application Support/Claude/claude_desktop_config.json` * *Claude Example (Windows):* `%APPDATA%\Claude\claude_desktop_config.json` 2. **Edit the file** to add/update the `mcpServers` section, using the *exact* paths from Step 1. @@ -174,6 +177,7 @@ If Auto-Configure fails or you use a different client: } } ``` + (Replace YOUR_USERNAME if using ~/bin) **Linux:** @@ -197,18 +201,18 @@ If Auto-Configure fails or you use a different client: (Replace YOUR_USERNAME) - - **For Claude Code** -If you're using Claude Code, you can register the MCP server using these commands: +If you\'re using Claude Code, you can register the MCP server using these commands: **macOS:** + ```bash claude mcp add UnityMCP -- uv --directory /[PATH_TO]/UnityMCP/UnityMcpServer/src run server.py ``` **Windows:** + ```bash claude mcp add UnityMCP -- "C:/Users/USERNAME/AppData/Roaming/Python/Python313/Scripts/uv.exe" --directory "C:/Users/USERNAME/AppData/Local/Programs/UnityMCP/UnityMcpServer/src" run server.py ``` @@ -225,12 +229,13 @@ claude mcp add UnityMCP -- "C:/Users/USERNAME/AppData/Roaming/Python/Python313/S 3. **Interact!** Unity tools should now be available in your MCP Client. Example Prompt: `Create a 3D player controller`, `Create a yellow and bridge sun`, `Create a cool shader and apply it on a cube`. - + --- ## Future Dev Plans (Besides PR) 📝 ### 🔴 High Priority + - [ ] **Asset Generation Improvements** - Enhanced server request handling and asset pipeline optimization - [ ] **Code Generation Enhancements** - Improved generated code quality and error handling - [ ] **Robust Error Handling** - Comprehensive error messages, recovery mechanisms, and graceful degradation @@ -238,10 +243,12 @@ claude mcp add UnityMCP -- "C:/Users/USERNAME/AppData/Roaming/Python/Python313/S - [ ] **Documentation Expansion** - Complete tutorials for custom tool creation and API reference ### 🟡 Medium Priority + - [ ] **Custom Tool Creation GUI** - Visual interface for users to create and configure their own MCP tools - [ ] **Advanced Logging System** - Logging with filtering, export, and debugging capabilities ### 🟢 Low Priority + - [ ] **Mobile Platform Support** - Extended toolset for mobile development workflows and platform-specific features - [ ] **Easier Tool Setup** - [ ] **Plugin Marketplace** - Community-driven tool sharing and distribution platform @@ -254,6 +261,7 @@ claude mcp add UnityMCP -- "C:/Users/USERNAME/AppData/Roaming/Python/Python313/S ### 🔬 Research & Exploration + - [ ] **AI-Powered Asset Generation** - Integration with AI tools for automatic 3D models, textures, and animations - [ ] **Real-time Collaboration** - Live editing sessions between multiple developers *(Currently in progress)* - [ ] **Analytics Dashboard** - Usage analytics, project insights, and performance metrics @@ -266,7 +274,7 @@ claude mcp add UnityMCP -- "C:/Users/USERNAME/AppData/Roaming/Python/Python313/S ### Development Tools -If you're contributing to Unity MCP or want to test core changes, we have development tools to streamline your workflow: +If you\'re contributing to Unity MCP or want to test core changes, we have development tools to streamline your workflow: - **Development Deployment Scripts**: Quickly deploy and test your changes to Unity MCP Bridge and Python Server - **Automatic Backup System**: Safe testing with easy rollback capabilities @@ -289,8 +297,7 @@ Help make Unity MCP better! 5. **Push** your branch. -6. **Open a Pull Request** against the master branch. - +6. **Open a Pull Request** against the main branch. --- @@ -300,53 +307,37 @@ Help make Unity MCP better! Click to view common issues and fixes... - **Unity Bridge Not Running/Connecting:** - - Ensure Unity Editor is open. - - Check the status window: Window > Unity MCP. - - Restart Unity. - - **MCP Client Not Connecting / Server Not Starting:** - - - **Verify Server Path:** Double-check the --directory path in your MCP Client's JSON config. It must exactly match the location where you cloned the UnityMCP repository in Installation Step 1 (e.g., .../Programs/UnityMCP/UnityMcpServer/src). - - - **Verify uv:** Make sure uv is installed and working (pip show uv). - + - **Verify Server Path:** Double-check the --directory path in your MCP Client\'s JSON config. It must exactly match the location where you cloned the UnityMCP repository in Installation Step 1 (e.g., .../Programs/UnityMCP/UnityMcpServer/src). + - **Verify uv:** Make sure `uv` is installed and working (pip show uv). - **Run Manually:** Try running the server directly from the terminal to see errors: `# Navigate to the src directory first! cd /path/to/your/UnityMCP/UnityMcpServer/src uv run server.py` - - **Permissions (macOS/Linux):** If you installed the server in a system location like /usr/local/bin, ensure the user running the MCP client has permission to execute uv and access files there. Installing in ~/bin might be easier. - - **Auto-Configure Failed:** - - - Use the Manual Configuration steps. Auto-configure might lack permissions to write to the MCP client's config file. - + - Use the Manual Configuration steps. Auto-configure might lack permissions to write to the MCP client\'s config file. -Still stuck? [Open an Issue](https://www.google.com/url?sa=E&q=https%3A%2F%2Fgithub.com%2Fjustinpbarnett%2Funity-mcp%2Fissues) or [Join the Discord](https://discord.gg/vhTUxXaqYr)! - ---- - -## Contact 👋 - -- **justinpbarnett:** [X/Twitter](https://www.google.com/url?sa=E&q=https%3A%2F%2Fx.com%2Fjustinpbarnett) -- **scriptwonder**: [Email](mailto:swu85@ur.rochester.edu), [LinkedIn](https://www.linkedin.com/in/shutong-wu-214043172/) - +Still stuck? [Open an Issue](https://github.com/CoplayDev/unity-mcp/issues) or [Join the Discord](https://discord.gg/y4p8KfzrN4)! --- ## License 📜 -MIT License. See [LICENSE](https://www.google.com/url?sa=E&q=https%3A%2F%2Fgithub.com%2Fjustinpbarnett%2Funity-mcp%2Fblob%2Fmaster%2FLICENSE) file. +MIT License. See [LICENSE](LICENSE) file. --- -## Acknowledgments 🙏 - -Thanks to the contributors and the Unity team. - - ## Star History -[![Star History Chart](https://api.star-history.com/svg?repos=justinpbarnett/unity-mcp&type=Date)](https://www.star-history.com/#justinpbarnett/unity-mcp&Date) +[![Star History Chart](https://api.star-history.com/svg?repos=CoplayDev/unity-mcp&type=Date)](https://www.star-history.com/#CoplayDev/unity-mcp&Date) + +## Sponsor + +

+ + Coplay Logo + +

diff --git a/UnityMcpBridge/Editor/Helpers/PackageInstaller.cs b/UnityMcpBridge/Editor/Helpers/PackageInstaller.cs index 75cdb3b..ae420a2 100644 --- a/UnityMcpBridge/Editor/Helpers/PackageInstaller.cs +++ b/UnityMcpBridge/Editor/Helpers/PackageInstaller.cs @@ -25,18 +25,18 @@ namespace UnityMcpBridge.Editor.Helpers { try { - Debug.Log("Unity MCP: Installing Python server..."); + Debug.Log("UNITY-MCP: Installing Python server..."); ServerInstaller.EnsureServerInstalled(); // Mark as installed EditorPrefs.SetBool(InstallationFlagKey, true); - Debug.Log("Unity MCP: Python server installation completed successfully."); + Debug.Log("UNITY-MCP: Python server installation completed successfully."); } catch (System.Exception ex) { - Debug.LogError($"Unity MCP: Failed to install Python server: {ex.Message}"); - Debug.LogWarning("Unity MCP: You may need to manually install the Python server. Check the Unity MCP Editor Window for instructions."); + Debug.LogError($"UNITY-MCP: Failed to install Python server: {ex.Message}"); + Debug.LogWarning("UNITY-MCP: You may need to manually install the Python server. Check the Unity MCP Editor Window for instructions."); } } } diff --git a/UnityMcpBridge/Editor/Helpers/PortManager.cs b/UnityMcpBridge/Editor/Helpers/PortManager.cs index 9caeccc..376f916 100644 --- a/UnityMcpBridge/Editor/Helpers/PortManager.cs +++ b/UnityMcpBridge/Editor/Helpers/PortManager.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using UnityEditor; using System.Net; using System.Net.Sockets; using System.Security.Cryptography; @@ -15,6 +16,12 @@ namespace UnityMcpBridge.Editor.Helpers /// public static class PortManager { + private static bool IsDebugEnabled() + { + try { return EditorPrefs.GetBool("UnityMCP.DebugLogs", false); } + catch { return false; } + } + private const int DefaultPort = 6400; private const int MaxPortAttempts = 100; private const string RegistryFileName = "unity-mcp-port.json"; @@ -41,7 +48,7 @@ namespace UnityMcpBridge.Editor.Helpers string.Equals(storedConfig.project_path ?? string.Empty, Application.dataPath ?? string.Empty, StringComparison.OrdinalIgnoreCase) && IsPortAvailable(storedConfig.unity_port)) { - Debug.Log($"Using stored port {storedConfig.unity_port} for current project"); + if (IsDebugEnabled()) Debug.Log($"UNITY-MCP: Using stored port {storedConfig.unity_port} for current project"); return storedConfig.unity_port; } @@ -50,7 +57,7 @@ namespace UnityMcpBridge.Editor.Helpers { if (WaitForPortRelease(storedConfig.unity_port, 1500)) { - Debug.Log($"Stored port {storedConfig.unity_port} became available after short wait"); + if (IsDebugEnabled()) Debug.Log($"UNITY-MCP: Stored port {storedConfig.unity_port} became available after short wait"); return storedConfig.unity_port; } // Prefer sticking to the same port; let the caller handle bind retries/fallbacks @@ -71,7 +78,7 @@ namespace UnityMcpBridge.Editor.Helpers { int newPort = FindAvailablePort(); SavePort(newPort); - Debug.Log($"Discovered and saved new port: {newPort}"); + if (IsDebugEnabled()) Debug.Log($"UNITY-MCP: Discovered and saved new port: {newPort}"); return newPort; } @@ -84,18 +91,18 @@ namespace UnityMcpBridge.Editor.Helpers // Always try default port first if (IsPortAvailable(DefaultPort)) { - Debug.Log($"Using default port {DefaultPort}"); + if (IsDebugEnabled()) Debug.Log($"UNITY-MCP: Using default port {DefaultPort}"); return DefaultPort; } - Debug.Log($"Default port {DefaultPort} is in use, searching for alternative..."); + if (IsDebugEnabled()) Debug.Log($"UNITY-MCP: Default port {DefaultPort} is in use, searching for alternative..."); // Search for alternatives for (int port = DefaultPort + 1; port < DefaultPort + MaxPortAttempts; port++) { if (IsPortAvailable(port)) { - Debug.Log($"Found available port {port}"); + if (IsDebugEnabled()) Debug.Log($"UNITY-MCP: Found available port {port}"); return port; } } @@ -204,7 +211,7 @@ namespace UnityMcpBridge.Editor.Helpers string legacy = Path.Combine(GetRegistryDirectory(), RegistryFileName); File.WriteAllText(legacy, json); - Debug.Log($"Saved port {port} to storage"); + if (IsDebugEnabled()) Debug.Log($"UNITY-MCP: Saved port {port} to storage"); } catch (Exception ex) { diff --git a/UnityMcpBridge/Editor/Helpers/ServerInstaller.cs b/UnityMcpBridge/Editor/Helpers/ServerInstaller.cs index ed92786..03b753f 100644 --- a/UnityMcpBridge/Editor/Helpers/ServerInstaller.cs +++ b/UnityMcpBridge/Editor/Helpers/ServerInstaller.cs @@ -281,7 +281,7 @@ namespace UnityMcpBridge.Editor.Helpers return false; } - Debug.Log("Unity MCP: Python environment repaired successfully."); + Debug.Log("UNITY-MCP: Python environment repaired successfully."); return true; } catch (Exception ex) @@ -305,47 +305,100 @@ namespace UnityMcpBridge.Editor.Helpers catch { } string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) ?? string.Empty; - string[] candidates = + + // Platform-specific candidate lists + string[] candidates; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - "/opt/homebrew/bin/uv", - "/usr/local/bin/uv", - "/usr/bin/uv", - "/opt/local/bin/uv", - Path.Combine(home, ".local", "bin", "uv"), - "/opt/homebrew/opt/uv/bin/uv", - // Framework Python installs - "/Library/Frameworks/Python.framework/Versions/3.13/bin/uv", - "/Library/Frameworks/Python.framework/Versions/3.12/bin/uv", - // Fallback to PATH resolution by name - "uv" - }; + candidates = new[] + { + // Common per-user installs + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty, @"Programs\Python\Python313\Scripts\uv.exe"), + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty, @"Programs\Python\Python312\Scripts\uv.exe"), + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty, @"Programs\Python\Python311\Scripts\uv.exe"), + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty, @"Programs\Python\Python310\Scripts\uv.exe"), + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? string.Empty, @"Python\Python313\Scripts\uv.exe"), + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? string.Empty, @"Python\Python312\Scripts\uv.exe"), + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? string.Empty, @"Python\Python311\Scripts\uv.exe"), + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? string.Empty, @"Python\Python310\Scripts\uv.exe"), + // Program Files style installs (if a native installer was used) + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) ?? string.Empty, @"uv\uv.exe"), + // Try simple name resolution later via PATH + "uv.exe", + "uv" + }; + } + else + { + candidates = new[] + { + "/opt/homebrew/bin/uv", + "/usr/local/bin/uv", + "/usr/bin/uv", + "/opt/local/bin/uv", + Path.Combine(home, ".local", "bin", "uv"), + "/opt/homebrew/opt/uv/bin/uv", + // Framework Python installs + "/Library/Frameworks/Python.framework/Versions/3.13/bin/uv", + "/Library/Frameworks/Python.framework/Versions/3.12/bin/uv", + // Fallback to PATH resolution by name + "uv" + }; + } + foreach (string c in candidates) { try { - if (ValidateUvBinary(c)) return c; + if (File.Exists(c) && ValidateUvBinary(c)) return c; } catch { /* ignore */ } } - // Try which uv (explicit path) + // Use platform-appropriate which/where to resolve from PATH try { - var whichPsi = new System.Diagnostics.ProcessStartInfo + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - FileName = "/usr/bin/which", - Arguments = "uv", - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - }; - using var wp = System.Diagnostics.Process.Start(whichPsi); - string output = wp.StandardOutput.ReadToEnd().Trim(); - wp.WaitForExit(3000); - if (wp.ExitCode == 0 && !string.IsNullOrEmpty(output) && File.Exists(output)) + var wherePsi = new System.Diagnostics.ProcessStartInfo + { + FileName = "where", + Arguments = "uv.exe", + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true + }; + using var wp = System.Diagnostics.Process.Start(wherePsi); + string output = wp.StandardOutput.ReadToEnd().Trim(); + wp.WaitForExit(3000); + if (wp.ExitCode == 0 && !string.IsNullOrEmpty(output)) + { + foreach (var line in output.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)) + { + string path = line.Trim(); + if (File.Exists(path) && ValidateUvBinary(path)) return path; + } + } + } + else { - if (ValidateUvBinary(output)) return output; + var whichPsi = new System.Diagnostics.ProcessStartInfo + { + FileName = "/usr/bin/which", + Arguments = "uv", + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true + }; + using var wp = System.Diagnostics.Process.Start(whichPsi); + string output = wp.StandardOutput.ReadToEnd().Trim(); + wp.WaitForExit(3000); + if (wp.ExitCode == 0 && !string.IsNullOrEmpty(output) && File.Exists(output)) + { + if (ValidateUvBinary(output)) return output; + } } } catch { } @@ -359,8 +412,11 @@ namespace UnityMcpBridge.Editor.Helpers { try { - string candidate = Path.Combine(part, "uv"); - if (File.Exists(candidate) && ValidateUvBinary(candidate)) return candidate; + // Check both uv and uv.exe + string candidateUv = Path.Combine(part, "uv"); + string candidateUvExe = Path.Combine(part, "uv.exe"); + if (File.Exists(candidateUv) && ValidateUvBinary(candidateUv)) return candidateUv; + if (File.Exists(candidateUvExe) && ValidateUvBinary(candidateUvExe)) return candidateUvExe; } catch { } } diff --git a/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs b/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs index 408a63b..f85aa6f 100644 --- a/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs +++ b/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs @@ -1990,37 +1990,91 @@ namespace UnityMcpBridge.Editor.Windows { try { - // Common absolute paths - string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) ?? string.Empty; - string[] candidates = + // Windows-specific Python detection + if (Application.platform == RuntimePlatform.WindowsEditor) { - "/opt/homebrew/bin/python3", - "/usr/local/bin/python3", - "/usr/bin/python3", - "/opt/local/bin/python3", - Path.Combine(home, ".local", "bin", "python3"), - "/Library/Frameworks/Python.framework/Versions/3.13/bin/python3", - "/Library/Frameworks/Python.framework/Versions/3.12/bin/python3", - }; - foreach (string c in candidates) - { - if (File.Exists(c)) return true; - } + // Common Windows Python installation paths + string[] windowsCandidates = + { + @"C:\Python313\python.exe", + @"C:\Python312\python.exe", + @"C:\Python311\python.exe", + @"C:\Python310\python.exe", + @"C:\Python39\python.exe", + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Programs\Python\Python313\python.exe"), + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Programs\Python\Python312\python.exe"), + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Programs\Python\Python311\python.exe"), + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Programs\Python\Python310\python.exe"), + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Programs\Python\Python39\python.exe"), + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Python313\python.exe"), + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Python312\python.exe"), + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Python311\python.exe"), + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Python310\python.exe"), + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Python39\python.exe"), + }; + + foreach (string c in windowsCandidates) + { + if (File.Exists(c)) return true; + } - // Try 'which python3' - var psi = new ProcessStartInfo + // Try 'where python' command (Windows equivalent of 'which') + var psi = new ProcessStartInfo + { + FileName = "where", + Arguments = "python", + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true + }; + using var p = Process.Start(psi); + string outp = p.StandardOutput.ReadToEnd().Trim(); + p.WaitForExit(2000); + if (p.ExitCode == 0 && !string.IsNullOrEmpty(outp)) + { + string[] lines = outp.Split('\n'); + foreach (string line in lines) + { + string trimmed = line.Trim(); + if (File.Exists(trimmed)) return true; + } + } + } + else { - FileName = "/usr/bin/which", - Arguments = "python3", - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - }; - using var p = Process.Start(psi); - string outp = p.StandardOutput.ReadToEnd().Trim(); - p.WaitForExit(2000); - if (p.ExitCode == 0 && !string.IsNullOrEmpty(outp) && File.Exists(outp)) return true; + // macOS/Linux detection (existing code) + string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) ?? string.Empty; + string[] candidates = + { + "/opt/homebrew/bin/python3", + "/usr/local/bin/python3", + "/usr/bin/python3", + "/opt/local/bin/python3", + Path.Combine(home, ".local", "bin", "python3"), + "/Library/Frameworks/Python.framework/Versions/3.13/bin/python3", + "/Library/Frameworks/Python.framework/Versions/3.12/bin/python3", + }; + foreach (string c in candidates) + { + if (File.Exists(c)) return true; + } + + // Try 'which python3' + var psi = new ProcessStartInfo + { + FileName = "/usr/bin/which", + Arguments = "python3", + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true + }; + using var p = Process.Start(psi); + string outp = p.StandardOutput.ReadToEnd().Trim(); + p.WaitForExit(2000); + if (p.ExitCode == 0 && !string.IsNullOrEmpty(outp) && File.Exists(outp)) return true; + } } catch { } return false; diff --git a/UnityMcpBridge/package.json b/UnityMcpBridge/package.json index d1ee008..8471710 100644 --- a/UnityMcpBridge/package.json +++ b/UnityMcpBridge/package.json @@ -1,4 +1,5 @@ { +<<<<<<< HEAD "name": "com.justinpbarnett.unity-mcp", "version": "2.0.0", "displayName": "Unity MCP Bridge", @@ -6,5 +7,30 @@ "unity": "2020.3", "dependencies": { "com.unity.nuget.newtonsoft-json": "3.0.2" +======= + "name": "com.coplaydev.unity-mcp", + "version": "1.0.0", + "displayName": "Unity MCP Bridge", + "description": "A bridge that manages and communicates with the sister application, Unity MCP Server, which allows for communications with MCP Clients like Claude Desktop or Cursor.", + "unity": "2020.3", + "documentationUrl": "https://github.com/CoplayDev/unity-mcp", + "licensesUrl": "https://github.com/CoplayDev/unity-mcp/blob/main/LICENSE", + "dependencies": { + "com.unity.nuget.newtonsoft-json": "3.0.2" + }, + "keywords": [ + "unity", + "ai", + "llm", + "mcp", + "model-context-protocol", + "mcp-server", + "mcp-client" + ], + "author": { + "name": "CoplayDev", + "email": "support@coplay.dev", + "url": "https://coplay.dev" +>>>>>>> upstream/main } } diff --git a/UnityMcpServer/src/.python-version b/UnityMcpServer/src/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/UnityMcpServer/src/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/deploy-dev.bat b/deploy-dev.bat index 4cc61de..6a83fcf 100644 --- a/deploy-dev.bat +++ b/deploy-dev.bat @@ -19,7 +19,7 @@ echo. :: Package cache location echo Unity Package Cache Location: -echo Example: X:\UnityProject\Library\PackageCache\com.justinpbarnett.unity-mcp@1.0.0 +echo Example: X:\UnityProject\Library\PackageCache\com.coplaydev.unity-mcp@1.0.0 set /p "PACKAGE_CACHE_PATH=Enter Unity package cache path: " if "%PACKAGE_CACHE_PATH%"=="" ( diff --git a/logo.png b/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..9472a36068e9c721e58811c30f65dfd2a9fc6693 GIT binary patch literal 40503 zcmeEugfD;1%fF~6Iz;VcIRpR?| zBGpt=%1llUK>LS>2f#pl1VH`aApU#+5cq(c4mh zi+`9>yI%i85ga77oB;qNjK6jWKzb(bpDZw0s%pAu%E|H=+uJf4{?!blhpoe3C;*=a z&mY#-)Wwk0!`8;mna6|w(?2wL{_uZ^fuBhKq2glA|4CC$kyO;)$&{4iFXui9Ad-@j z@;RB9@hE>0{}=qv5&tI(7Z(Q}Akf|2ozb0@(cZ}%$jr^n4P;^gvam4x(O_`)v~w}^ zV6by0|EH1vvh&5%+1Sa_!NtCMD@?c`2M7ZN7TvG(8bGaI!T06YgI@2{7{k|3BIP*4I)tb+)&0{f9m$%fA4B7yfU6=KlrwyYOEC zKHy(D{acRz*@6F%{^>yh#6Pk8*Juzx+yhjj0{}t*sV~B+9uTL#aQ?+=uJ>sh-pgsr z8^TMb%P@vz%JlH~NZ8tJwV%cP2=Fo9soyBm!^o7_)Ia|WiwGwDmj4YCCRr~fSU361 z)~)2>UT~`A0krw-J8N#9lI1&<1>SJ_-7>}huyK>U%JRhoUXV4!pUeOr7xG_(S(ucS z65L>e|6hWC$UUHC0c`3_(8v&?-RS=yq-OA`0Ni@WrqlnS37LEKKh24DLwBcQHJdK} z6ZTIu1|SOV-=@0J!|)&}0fUv@3~hgRCKX`8_czZ%gvQaCAehWx?3UU8=14cR(C&YG z^_O|05Pt-JW;%zls=voY3BX7D?;!uQg3kIUr0-e*U)@mtj)s`__*a1cAOWdl29O5h zh--D$|4z|g_6MQXnyBFhuQ!?EL) zJ|m-}Ntu~GBi%W}v3ia&-o$U&+kDLMy%qqk>x(xC&O!X0-Ga`WfMk4i{oDAkA&C@^ z2R$84oM?3#$D+q=q?>rMK+(V%{yE?}i~qAf`M`5y#y=;l?*0!3UBawvdj!YfaO^oD z-7?<*+#L67>2YZc%Pj-wk4_EfUOX9!;m`WQ~aO3sh18s->x@tqP z;pX?2yG#5@V4G(@!`S%xjxZLMh{6^j^}cMg^?!!**n^0(Su5}ef-NE;GDNfZ-S>K_ z%<1}>wyur>3H(cdFTIRLA`M$=<+tW9Si;%BX%GPV9u+me3iaeh^GY`5tDadh=HcG= zU$n8xiY1RIQZhhk{eVnH#OO5->KCQUvC+}^_??fIw$@3)iBqziU@_D9tO*vX#Uf`T ztMfTHha=IJ2e+TaJj6Rq?eWqhw=R@rvI22GT@vEbXncumXExVSsixYFv!kycJPuZH ztjyd3_OKJB?KLux@7;?r?_R{HWW>a>RMj{n?KlR=Ehb>Iu$iE+d>_&1F&j(X`@Q=a z409S(q6k+fT;4Z;c8sq0e`A;YMd7^O=L$ew%igY1b$1cuhRL~Huj-Hd&vYZCI(cBit?Y2D#VYrBLl z%W6c|cfe9k)c>?>aqC11ZR!a@i-BT587$-iYByI-?PU$FD9rFy=e@|jtT620<*_!8 zx$Mz<<@J^eyl_$JV$34_%)^r^@bc_%B91Qw>^0fa3hqOpC`5b8AsGumUvNwO9}y z3#O8}xX>ga0OsM1Av)#Y%p`ZLEZ&RY>rnmg?TJ&|%=i zd|86^3;pVhS7cmxxHzHvNesQ5i(iBV>TQxrhRZ@kaqT5pnxVSXU7#<^=Yxo~zhQmV zHu2RC|88=+dRU;;{q4h8K$Sdr_9sqOV^^{$R6G)jYQbW==SSri*?kIlKe`jMv04;o z6aQ}pUILit60D7em``sFh8?HDA;I`+R`fCI2`CWlBxgrEFp>L2DX&Rb1LyE&V= z%RkCX<53gU+}#(ykAh%NZqQE-rOeyKrAKKqIWYFQDsiDMKDi&2e~NIeix_ut2k3j)Z86!=_cO)gsNP-H1;P_Zoj3>`OuH!n$Z- zn7Bg^kLC*nH3Qjm7=5Q8C1qE|_HZjpbC!l#T3;b!Rc^iiO@krUIEGvP&EfE3?_6-R zNUJo6bZKt9OpMi7ldG%dOY2I;esAQvw$<;$vV4E!Q|{#}md%lRwq~>~3M5jpSayJy(ApCID=4FQ7bn6b-?GunTpJUK>u1(N=e}07W;_4qWU-x$D2o1KCS|`$jP+(l;@q0)FgT#h>E41VG%InI zCA38#6v_?_>!i(IhnUXB){55&VfHNJW;A=6{fo(b3on*ikeshTX?#vT0Fv;P8Few2 zs`nG0kEKqu={6iFOV>hM7_N_%;c3z1rM^jp^MdLF+H0`GFLRUvHBsJlJ;cyXjLFc; z`i5NCE?Vv;qwq@mO$N?Wkm}o9hc05Qmy~$bDG`rLpB(8~OOn`$$J{5KNVmEwqW7aF6FW w^iJ^xPyNp%hgdFxR()nQtbiHHmxrB_ss7fxl#2`Uc13(NYP0Q zN9e=?HD9Y%ufLxXI{2uf2|EB!NCX*0_!1`iR9{*7({0cvuMO6fvZ%7z1wG8)ZqxRI zq9-kV@K~0&YkV3ygizUVN-uH0Kv94HkmpDsQqu2ru;Y}<+=n1XmW@ZptDX#Vb=YRD zf7uF;9L8ub;Fb2K z%u`6Tzm3%fcwGEB{#Fxj@_jc95epXSfFYKS(YK`iWlv>4h4Wp9!*+vu4imcowXuOi z6kRso?en+9z!=^W`iu& z^ay#IF0D5Q(HHQIUNn1w*OO@c8;9VXydaHR+(YTt9d7&$!HNtwjC4Kp&eWf8A}`zg z!lCX8OC=rFTwKuy*=AGIWL;zzv?jTq>RE6^a5bSBmb6TfOdailUw)p!(DUHNF5`h` zqD?=sPB2xo5e^q!Ho@Vup=N+;e2RP;+@dn|e+prX%!-kv_QDGw+)kR4<`c1!4rbU- z`bgkzgf5ZI$e-!XVyscqay^@P9EQ-r7uefo`;_4K9upxC30k>veO^RptzLhsS%crU zcQa$WX7=&G9+4YH$0$|`Y3&VWd`5T2gC19)=h71RX%SWV!^)p&JH&I@Q7M+3Nau4u zpQS}nj)I=x>E=Aii%Q=h-$7na6_7eP39EkZ4kgk=OR0r#y+Pq#2%TPZH!D=UjQ}Jy zyUjwFZz3T5ORK}~sy03)D_p0!juK_=o-B9q{5{Ogvnjb8F|73nH$TDNJJQ z2A1FhgZ;)8Qi$*f)$N4&;`Ya{Bs6xIomzt2gfg~F!Btk{ykCDYNlfasf5HE{x|jC) zTv(M0&|V6xD4E-8GzM)SN2ZL2U(TXd*wQp-JuQh92*I&gI zK?+?uyR4U?m}(^^CXQ;AWKp!Vc+S9$)1WUbvJa=LSr_Jke)8QO1 zdBY#t5YzY4FGAc#P4RWgFI4htAV$hCklN|c^mx^D0^;t(ja{lo$^M!tHI%H^gQBT{uLn4x~x2itkE2g!yFj`S45vw zD3tfA!<_gFzn70AjPwZt7ChW*L=w)4i2l*P>X;VM5?%|t3Y7HRCik5tdEdlNaH{5j zzsXcP3#SiLgKZrmNC(}SVpb$BOsHHVAJISUjA3_f;PqPM+X z5_nv&j-^KxgWK!JggeDJ*x28nQ13m=n}Z{*^_ouW^L zs)TEzLxC%xr>;#=on0?^aw!Hf-<8)TWg&>H(nd25%)|uLlK~ht0!#sE(>^TL^cn|| zc)R@9Rq7|ndpHc7E5U*nlcl(NyFw;3j*uS?_fClxD(@uqz!9}%)P0uFjQp;FM6Y)c ztcdaT(lVwD2L-jK=OD*=HX)3qL9}(6^g=+hZ1Oy(M*xOpJZZX6!VHYe%MI(+TPlKo z5jtRs>&ylVKoeRc`$Xojx4%F3;)%3>t1^|C-}}k)rA$KYv7)?EU5*r{f_y?Tj4mK{ zJ_PKlt4ri`I;xU85!eguSFS~V(;G_yuI=ST(qo_ZD8z2CBW4Tjc)dOqb(gYqg~ASQ zhGD^XpvB2C)9MqkT&-8zxZIu^b1ccKu81zv(hwEY7Su&6EYgtb7@0I){ds?o}U*WnCno4~?gIYuN;VTS39aHXvJ<1i5G!krU z9pm;06sEZiS;rH1c7HzFW!2M6()28~EFEP`RFOFt?#*;+X|XM^nxZ9BbQMlDY9?PF zAG~D8@tE5JxvpznKSc~Er)5=t-88rqA3*~O&_6qXCXDT2hx_8sh*82+xb=w|fmThy>s~_QqvmR7c*sOQ> zbT>jH#_n{-Y(Anjf5UXhEa63}s7OjFXQ5)K`IV5Ea2~qxshIO{yy8Hvqm9#i|I>6c zE(r>Id4D@R#!yT}X_B+q#H<*Uf;*cG(aZ&OmmIr(dN(vs3W7<3QOD`~ULbJ_%h6s} ziAKS0J-HxW+cj*ZFL41|NsuZ0S*d^7y$0i_ju51abXcP+Hwf<`n5|!1@-FB~k};NX zu#;V5V1_+r4PO zx__q_q7dNGY6YkbgO{uUp;IA>zF_tFF30cJdCg0e%dK9%cy*|tpZ4g;KWMS#ed0#W zuE|CIri68}=d3xcq%=!3Uw_CsgdKUky_lEqb0k+Nq_2vc z=Z`%p``~B0l=xQgOmG?X3B=ouMFSp>7t49-=MtA>tJkFky~h~Aa#2LPG2F9vH{AT+>JYIwZj+{}*OA)jc<0_T_$GR`>QXK7RP|8Gz z20ityqSOC^wdxafM>)T+Ia)Vy2r zeT8K{G=+YzNsk?o5FIhR)YS~S;zSu5IgI9)NygF;x$LXq3KGPk7D{ltF9h~W^U9_5 zhBc{@4TIs;5iZcBkMaeL`l-wa1Vu*9#Jn!QDr?hdWO*dF`;E{pM}hs5rg@yPuD96= z*Iym>p&03b*aFp)lN~qnzPQldPx(qY5gAXnkonF#FWa$*x*yt#3##xpCRCi@z0*EM zN3$j_GA%L%IXAS_AW{;Swag{8hCN47RmOMmo*^$ssgAL9kB9OiE3702v1I`n)3kBx0oXn>ALOtxPJ>fVLO zUku>B)k#kgNQfK|m7ET(HP4J{UGLh14gSIU5q-mJ86ftdJ~4;|kP@U#~bEyOQVc0iB%OD#2Yry;Qp%1VK24M#tY99fus zB!8^L(LjF`Lw#0$^!5WIf*sb!ia1K(Jq^qUia4t9Ee?553q@0xrUZ@9LBfiv#KwaY9 z$X632Or|0uVuPD9o4OSjM*|h~xV~<9IOBc{KK!ync2HQ+hkf zp}o=IRCIcJ8jFuC(V8milhDFy`-U6%T-%3YkDqWbvA+yfT3yl^{OFKLAdvt3G;Lxw zGKy8PS3vR<)c%}x(FfTNVQI(h&oEpwT|RwA_Baz55)Y&AZb(Ox4)i@)cgl54*)x#0 zaAIeb$r*HnSPe3ue!Lf{=f+9z8xwS657Bv$);c3Rl!eVI&C`Fmu6YJbv~@j_msMs2 zg0dn7xZdOM=)NR-NGh@fxd{;T?%dzSjL2#DeL=K7kX)_8(FFuW)l2=(HeG z3VlMb?3KeIdUQ8+dV27CB6gH%hjU+BezAVBoC@b^=cT*&QUUR*#3^iRKou0f4>N!L zI~WtPUKPI5U)(a^F)HZUWi_sc=EX7Tf~rD3S*9X-Hi53S#UCa*;=_xgXo)V*#beoE z^!c0~c$0Jk15yq@FWsC?d5`tgxM09^^FtqJRK}OQ?QatAHxxU(r|3bZqe*31=%bw> zhMPHM2S5c>^&5#F_)nZFO`m--?9*WX+4_h zEdU2p6G2`n!?eWka=n)WUrosPt#F={)DJnv6RZ_@G&q|ML_Q)`hOSZ5jPthiA~EY} z%D6GPn0p2gD>SWi=iKc6n&IOiOtNdg*(D2dbi70q?Lw}$pF^KX?yW;o@Sc>Ke`MDq ziKcsiO5Lb#1pt2%x#Uwa;fUN}l~g9d+NidpCF~>Bav55|vc*b#44H~PF~Fi$e};bT zxjX9O{X&fyb4yN3jd{tU^!xlQNrZlld-t|}A1UX(uKg{aa~C?!?D)tgMH(3i#{jev zXYf=be&V1kgiUpYz!^c_z zE9Xm&(P!`Z>T_(d7U1dk{s}2YpS)ws80!sFY5|)v^*gX9$zrjthPtHDvFT;6{$jTI zxvj+B)Q7f~_2P`Fp*$2WQI`Jk$lkvH%e)WwVW57JGS*x7=M~4#c*ZVPnIuDV78}2W zooB2$D9g=_cc@baexqT`IU5{;8g@N<;z-s!oFp$3e!pI4kJSf*&;QgW^AlfN0_bvH zW2O{S4#B*yd)rxV+c>_5&K^^JK8&xwP(>|H1P3PDDs+F^ZOIr$Cw*Vv@Su$N+{i(g ztdprh#yR1a0BieAyi9T+^2&&Z@7xvlzUb0j?M}O;)Q6^Dz()By39rW4f#c#2Sw8M6 z2slC;+1nxWhnpYBK6NWxhB2U};SWe}-;^Le0$X`dzHs>obD>#UWePwd`stj> zO#D?lX@XYTN37r0Kvugcs=1%K6!>BpWPH^hUJp{`>yM4b-U0Im(=STont?x5r~#mpcaWYu?;Sn2zu`6`JS`?P zhBsqzzbltMOQGw#U0Eu-#`$v}aKER`ZvIRoAJZdxZf8*;H&$(}jBER)&3WT7MY+i! zt>JXN>KW<@A3e(Kk(E~WL(tSg7H~8jX@jz2vPCk~7mwfb`62H__PTmJRjSk&yF|x8@99{NA^U1axniTmpB0( z6V2CZ8+hln;WT}OGflINC~~u>OTM6O8r@6b;$EQFM~NDR<okYYu#5?_wa_6L#+@@(eBD|n#7q(zV-89%UfZ8a^k$V zXegpqD=KK>41I>pjAtItp`ZHO%{4a19taNzE>WURlbb`d3&R!CwV&it2mx@24#O1( zDqmw_zuK3yP@PZevDYF(uNj^N@h3+(mnaCZu%Vnaeo%F9w+zLbxqRd6`jz|^SlWZD zdILv+!d|Jr`BwT;xeqfA=Jl8$v8)gY9Om}Z0P>5ion2p>vD(e!bLK>g^K zyV*Tb7mC-qf$we;-@A0k(6y#V4!RWfrS8!KN9*!^G1Dbl2Rxm{ zg5>IhcCt;zS|~?O1@lOj^Q44%eRzQ>p`{njV1HR<8q+OI?bO>b1o17Q581teLB9Ko z38n{xF!~ak*VX|OECK{}QAm|643hKZFsDu1Z~k8e-o&>sshF+YK~aVh@BwsYoWpe3 z49S9>H3)PywT8gUtjwzwshp5#o>*-C1!=~)!E_RZ-Mt+_L>~;KS27@RNv#vsXgF2) zrRT3+3YJ6QCWG~2Hci*l{M3F+)KvM;AMNeR+S<)=EI_YwA8)}PV3Y}Ng z%BxKJ$;5tjw+2d~R!~V=T2%2NKGJXsHFyuV)Kv zlU=tLOfQ)+O8mD2(?<1nl(RSCWF3V>R}H0ev0R-L3geHI;^4894YJk&@aU=c|;c8RiLe%P~+mlwB6 z-q-V&4Or()>)P*OBWl9$#^gD6Ry~UFdU}Z`x_eZ;Yik!M6CD>>lg7^OXTzlWI9n(> z*_)N@Q|zYL=4&gZ`Gon{JYtD`u=7hsab-G6aQ=K-z7KaPwyiPE<3>b{2j>?FFZ{-F za@;m{evQ=bhajTV-3Jt#>v7+=1L4Qj2pAH!Fyc!zd z5Y%A=iU8~)c}skfNz?L3Bs*X8KAgb`Ed);Gg^%p6-|FE5T+_A zK>1opINp+5pkS+;^Wj{Ni|VSOb@@6?&3d0TW}WJ8I{sqEi1Dr{yW}k2QxWnKdIb~1 zWHc$hZ5C}T!gDK@eJldfaF{dP(NI^9u+znzLc;GQn2(kCE=DyBPq$&WFjlr^-kznZ z+xm87fYqkUg@mUlJdYi!nH6Se2(!r*m!DIDQL{w^rrXE@f{FnUkNr^X@*xtluTZKU ze_JzvOhiGTRe~k9d^NmCx z`Hjy;IULoxZ@#uGgP87VrR zjcX1Qn=+p3K9&a}$RsSsg5bnZaIEhV*W0@O3q(kC9QFa`iIv{IU68CXYv&0Xmt$8K zgM#39Pd^PKIeyA&v2M&iQ(?Ay@VM5EbZO|TSyFh2g=RJewyK=D za78Qv!|(>tlwr%0{od8HU$(=csGS|nix3@r)xrV2QiU)3!v)QzKuu&PRUh}%Q&kyh z^cSCoux6@LQRjS!+k8i=HGbWb_y*a+4?SdtB{V1}3se(Jl!prkcTdKcDe76&IP-*5 zV@ypk^Qi=_bz{U+Om=R!IO_BXR6GyE3M-tF3_&%0zDDpLwmduAas&7%O2ip|2*?Lx zPC7cvC5|Dya`VM2Oa`x0xPm4Hr(TL;IBZnc!fxWf`fFf-7+(2X4L+51NW}~d9AI1l z;|M;wAKyeor$c@~Hr~#ToZ3NN|GN6~7@Gm}?I_Ytg&BG1#+~M+ivH`K7RC zOcD+7(cCbJ?=W4#RK#A2p(?eqVd$K4_ctovL!%G~Zu?2dWq6cfG)_S}S9C~sQTR+d zQMurXrd)_Lwe2XyZfM~&tmIxBMtS_D~KE;f7NN13gib}pVdzhQlcu*Iw&ZiQ}gOgHclv077HQ&oUx z&g>9hm=p%@v^Dql_3N<=Q2v#w`hp}C_N_}7wDYo8+iDT+Gt^IGzi$1_OBB!5O%UV? zAGJavT+(WypiT(??iZUUC3}r*kiaAosATPC>_Xad!p3r7+9ram03xXF2|QD09r-c< z_pWu&Wz%>I_Vl~`t&Q)|m!n=3i@E0V3r@fx{oyqB;=NDbdaff?L&}@A41U}Kf(a^1 z+YjmfDnRQk-Tj0zMpRYG8&-P4>N-lc4`|MRUTm6E>37M|^4_$miq^t)_f4N}jm0dt zi$I_Atn|AC)vjWN8oVJ5+9zU-bf?D$iD#1e%^`xx!L;t7ql00-jbV2cDfHRwr6os& zCLas5O(o}Vi2mzgF?ZJvt}wEZo--;`moIOv?5UU86?Dn#diKJp;&$zT-gr|k+sdn4 zT}ChK$(+-`oI|hly|;xdC;i`Up7~vP0%$p9Fv)~fMclZ^El0*^J7q&JOLPZB)rWWw zBBj#sgmDA3ml6Gnf**gBV|ZwmG^gg`5zVq{THKKyK)*U}s$1+1E?iH3s~LQKl^?`; z9AnBU%PjdUgkC5fkuHkQDJg|&%;+1=wnpXP zB`1{T{x*!)T*n{q%T4rRixpnUn`dP|J#?XLRmQQ!WFNulDx6AMPAc3E@~jv?h|hxz zKj?!NquI`o4j%M%q=F_5r@#}gx{k4RUO2pe`1l8V4QT|r_buqJozuG32?E}Snrk}0 zceF9679pzpT)ImjT&FJqc?|h-dsw=rG#tnGVm)d*^|B$ ze}r`n=+zfn=kwe<|7zNEjpg(NgHtk%&c*a)s#E<8J(YDE)S+rAaa-4JUE+Xhhg@Xg4>VbJ* z)$@Vp?J{#`^B*jFAwI zUF7bvw6@gQ=H6aK?`Y~sNl=iWNjM49&>7k(14Z2f6bMx=v%CqfU4o<5<(zm!^xsSF zz|h=D-B?dX1O8l0E@5Qbmfz3{wuc|NDy6qWzBUB8P~x72ZIpgE6YaVnoXz|NnQ z(8^C&9Fs~os!}_0)+~kc2xS#<&U4)9^!M9nQ|r9xO}N%^canHV?9YNT9L~$nyY1~6 zA}(x!BT*LIs8#Zdtch7{OUWWFnd2xGkKI&zstrN!7uXu%Q#o+DsQP-=r}~{1`Pk<+ zNM#@D;hp$Ky}q#Ck4u4yCiMhEMQrCYsi7o+ApX&T#eoWk0H$TKk*h7Qx(lqZ5<}9H zB%#l(67R!Rm4MA>#1IF}t?0Uz?eHPT`wMaK)9MBzmYCSgnCIxJ$u=;t%)107NXQ^H zv_(V4${H`p%K6T)7bgXw*k8EgGXuPWm(Cke590uWgwLEuVR6c70p;53Zxee6g-Nu} zf=-f;5o4PSCD~3PYqwp3Npcw+-8?FzV-+ti-J(h)Go|9(3D|b0I?8ImuE%>Ehn*oK zt=65-ghK~EO&yVO{Ux2g!HRul?gsns&tI!o9(CM{;#d=>qD2qKDb(LYx^@W;UXG3l zk}f}5oJzdzv~;;BK=Qp{#g%r2`gs@dd9B72VLrb$o_i@-7H6cXLYq|rQnfV8diCcK z4o53@6+#Cm*+JpzH0(KV6U^kHzD)wp{x46fBjUaG{fewTbsy6i5kY6T zBDayl63YZ~yl59S>(IDaGUyBg3I#w$5DMoAJ4&%LV9FrLY$Dx94&%5T8t;E@>~;tSp< zmzF2RBR)+J&fwB2bqxglP@LubGKF%QT3B>BIvp-j5pHk4O9s9Tw@Pr{B^3IL+pW8> zBwnmB^Y_U|~Br5msf_{7&QFq|p$)gaWPacf~nl z31@AtcIh0~_)dFBHyd#HR`rp4ee*yK1yfMqQJko34ub~Ctd3rzvUHrpGedH*# zgIUiXBzV~?h;DnBcGQ61-oK#I+ystEm>QQuaCn=xNsOd zVuH7A80kWGyDT_2!FGMekoGOlW}ZIp#91;D_-he8h3L}w4Vkgote1g;GcMb`MOIiL zPaUC_Ei^j0L&uTJQM_;PaS1WhW@Un#Wgc;K9^5t$6F+{WFT7WJYoZ!x@)6v843B5w z)a7!=2x5U_+w)A15NYZn9P?(O4069}sf!T*z^48^2^9D~QpimH`t-g0UV)WKZ*q2h zc2hzGf&uRYvi9ukcpe;Jt>7K(vyG&m!r6KC(bC>tX*;H`7aVH&E_7b|^}C9Qh286~ z#iAypNZ^$c_M;p(aMZ|SfdlRhti%cUvEgVvmB3mnNE$4N`kZXg{1aoSzGFKuCkbY# zX!3bBsF&g+kOYodL|D!c1rI9jJ)I<4UAI5|f zaG!eDxHH+;=36#>@5Eo6i!8|0zjBDC;*O|_GFb+Zy48e;cBpCgv=Q83T5d!n5KeaQ zy@6r&mE&~%qw!Cikip1Csg*ETu4t$`Am~(I%R^C3*NHJGLrQuBX8(#xwu+m>=avmUF9JD*v|JAyM_!J;p<;wr5!97VpB>{-gDIi%0z=GD{N<#)GuNqunH}(_$!J&j)EQrB|eX3-g9tM2kQri&qX&fY!Sb* z@rvOz?fb424S7?#bRT%ZQ|#trW9IX8ww1AY?U1_iYa$l#Wta_KOh7Bt5S@3ck=pA` zf<+mW`{6aXv-bwvYcUf6lT_U*O)`j?{+7sqpG+4F?FvXJ31LByI#}xW=R@t|gVCsE z^xq6hDgr(r)XedeLki?j988FZcoO$ zbG?k@pU?%WC0zKF8S15JlNy2#+tscey4f_|LI!O=r)wd2pc>%Z-?sf8%FzIt;8ML@ zu`^j)Sy;WY+{hGFo8knG>y_y?OP}Cm=DzXr;$ID9#r#=PLm5wYL;4t3whG(2cNaMo zKUMik2OdqkNT|-dW(4omRwopJ*=b+q%??T^*+!a_T~6t_5dBhs+7z7G-oY=g<<9$S zPZV{fg@_nuH+fnku>8xZM_J^~HB$E@Sk8OvH}c863e6${yze&rFyDGf8Ux0Cy>2kj z^DZ@@$YKwiSI=W9E$}>fVCW3IaPNYU&bWVdAuhF7Tz_dih}aUfL}c=jaW(|A(1>$4 z8`mreb;n0R6W@lqV>!*CYBM5A4bKYNhUNy!IzOk{;=-bV7bVTNf|pT~p4ZaBt=mm> z%!{NksT9!piapyaC3)b2OAY9?QEDA$P#PK=mW>GRU{A-3G*hA)qQKK|pZf>~+&xW~ za)HEz@#Pq#q3@2_@XC>Onj?&3aVEQWr*c80)W^iA)Qa%0@m$m0w?th4w47sTISA-S z1ghdMm6X{FY+z(aU%`;=tYJ8-eCT86+>P}Dc!dyTr|55iTq?T;?KMghWiib0c8QrI zg`B%DABl~i1m-y;asspJ-Oqba!%%N9)_Q$S1bLKjzmeh`T^W17eWP$%#=3}$(eX^8 z*Ycw0h~Fp?QSuaHmqcLbaUnl>3@`CcTwoiXSkxz<;%||6@>t8QYIZ$mR2w1>fLw5~ z`VPnBnvdIiQbh8ekLc$j_|9_rkfM^SBGWr87Z&a_6c>Dnn#d+SiRQC+pEI`Gv{`!d zrut~ior;GMfL8)B-pw>8^cysxx_`}XY{qzeglpLM+7(|+r0EK6SlVXEn`xP2QdH>I zEc93~J+zrF^Dc!=uNyd_azhd0?3-)1d9~K(G{&GmL+Xi(O?>jZ_l35JeV}CW)xWY_ zn^f$m(Wz$H%8q1mf+2_8T!SGwr{mGz$NSk=gv{Y0cXmT;mgu4dIXxBo-Sjqzb%VNO zG9BnCs@W4+&B#CG3P>o@rsmVX-aJ`L5&@NC%x&Gr%`B;bHXd!k&N!Zyq@i;T&0w*E7u;?xD zR;8pUxkaIto@S9qP^Y3nF1e!vHlr&fgikYK5|yow_ab>+OBX|}!2sMZbk;kOs3QT3 z_XX_5;TI8~e#IJL5RD}FMDmnYVcZ@L2fJSzv&Kb-!de+|^OP=9!{KbO6!j}dzt*T2 zW{t<@t6wZZZJ!^}!^=!`pwujtC8}x3Uha4e6ATIC9Avhx3ef#*m0~}v1n){N<(E93 zWr@2$VW>#*0ffsVRgrV+OtuZ*nZ;L&(pRLV>+{F;yi#Hxh}Lb;KBVqxp&dqN4U;A% zYG|TCgF<2YPJAxi{QZ%)%6MasW3xArjkdVx2=wpU2LqChm~*O23zDZIdY@aWdo-U@ zvX(dAMQLZ10!-ugZnB1VqEH9PhKe6z2ygdl7YPame%&Wt+4GTaC})aWk%}oJG^9h0 zNX0I0pUV1;$RF|f(Q|XfW-9-Co2=aH> zD#z(KZunzMoO*Qv8U$bc+7(JRtm;Ph?-72B|MF1 zu~>d{o$9#$6e8p5*B%G?h|KH?O5@l|9R9PgM2g@G8X`q#uc{Gs#`-%&(b7*?XQ1mN zj^{!M?EM^V?KXVIQQg%xZ&OH1g=ys4LW|ge2bjVh=x8V9yrFxVX$VRr{5{Fkc=g+Gv{`oF|nVl?}rcdWQ7NwSU z9&a?dmhwOF8=<5Dd=xwZ@IU4dvG>i!mYokRHLd7N`l#^{B*WnSG9^y3v6QDJkfr2t zA+w_!NacI1B2&8?G6a0su#NnL()In`>1G0nxIGXhwnD#r8zpAEs5}k|5(CI32^yLU7)tm36G&XuML2hLne`m3qVJ5B42u zf5lL4+Ci38KO4;UN2kkG!s}O*5rl5-?6gZ0^ls4sN53aJA!A%zSS7}^pNKlizB(QE z@KIfp5NO)QIvzT6_F@@zE@L?>EVxjU%JtW@Dr$~A1bQvz_=OY3kbCkdSyHbxhpefU zU+p;iLaH~4So~QLa8ux{kT84w+vUy%3%V>0xq@)<9oy@HH=W}|aa)sSCaTXF2G@wc zrBt|J!2e0|7fC}F0XmMBnr6(Iea!^GLXHh1k4zGM?3^&;SOj-{c9HD;r+wC>LLqD- z<%jd!GA~PVQvGTA0T%>^uw)!|&W3rQ)7Ex&g6Dx7a7w7{ZFfDxgoACXt-CttOwpj1 z3-7^hd@~~otwG<$Pbs?>TvABJ_GIA6-hOQCp%{v+6|q}uF>F3-h=~U8E`Jm`2;Wcr z=-J2f1N8db1OIJYnXG{nwV10|kP&eQ~yl139C1xwazChHf z=|duUXlQL2A0IM7ZW*$C$BnS`)EU-@E0vU(zL%wZOS@QXWPo133WXEf>>yypb6j+~ z#xu8U`6zQ&FeyNLIMmYSk#D#a{z`N(a_zHSexlv7)yp|rQq?Lh&-B2rY)b-ONJ~wf_+$RSMuRW8O**CVZ{arQ+=alUeZW-GU z{ch!QZ2RRlvFxsgKkFuYqux+CefZXz>bKK+MbU9B&3<^ncp#t)E6#di8eEf3Uv=Po z(-S@O0k1asXm326pp8U;>6oed>?JTfm_$B>5|;VyX0{`ZCL2JA=VWJ8ghx3UvVG4- zb^esjqyt`V#2HR+ZJCLRR)HBGVQDJ~g*4@a)}EYw9^y8)ImK=_V6M(tj1so+dao7 zaT2#sEg&s@;C-{tWy0Fl?a7_TlB1psdwp?}1U?T7TT}T^P|x!W$COChSoz3Wrm^i9 zhY*MjU{ z6=H#sPAIfSDCG_PQQqx{)?GD0HW5A_bIVY7zE*e5fa=HJq9i@n*-Zw_2XJpuK1T%_ zBStnIP6qFK;?Z&R<7h!$mdtOvq7QI?9^-D@JE^AxuSh4)2iiS|{78LYwqtsUHy@|O zxqBPKhv1dU)&jqK^?AO;ti2(5o=j#iU)XoL9Oa|6mW05+oe2;I#Hk7Ny%K%kz-tj> z2^+vE8mdb2p%*+{06t;yN_*DNlGmSY9NoK&brXg=qCo%XsiGXosMm3lrn{~9LZ`)+ zs}N&(Go1Yt0js5`6ImT&*M&)T!VM<3 z72-3c@9{l@Y9ON8<$FB%*Qf7Gzb~1ggH4y1!1`ucH17?E+5LRYVNHocAK_(wpraC7 zZ&KXQjw!Fr=%?xs5pb!V=xjuYOam~j6SyEu6}mlcv2wS%B2Nxv_P6kJbCPI}3N zBfeAeWX(^?kCv(9`YOuwRO5EB^EU^Qrj8#3U`4e8KFz`K8^Sd3M!SEAy{PYoI$P&= z+N7k~b2r|&G?8_RgENkVM_mCwr@~_}ySyiaza3o?JW|CGKz?N3_AP%v*MQG?%Wgn> z$-jZRs;x2Fx6<-wNX+&jyn(8Ij-N!M?|lo{Ym2^LK>sy&u?Lkpnfz*D@3i+d2bA-| ze-Bq#l~Li1`;B>9y3^(DmO-j2gy<^&CAhWPulcWXN}Ut9^*L^}^InUlEF;v@@2ig@ z&O_K$>OK=RCFuv0KmR+{GBHBk5CW<$4P^$sJ|i;WFv0tiWAgB#xL9>8kyt;^`l+YG zanrc!>~bs9U3;vJ$U}-oYrgcXuhb9SoX-|3t^^EFun|@fPGJf;*oK&0*#dc`BAB5! zf_-y~ojHi;7U-l8Wl%yyxiPiBFSR({NERdQKu_@h0FHX@BQeuwMOCHZ7jK84E35SJ z|33grK(xR4&di@JH+Q~(y$~~@u^Tz=;W3PF6HYu?jMGXutn2oMQ_&yguU)F6&BM&j zoE_wD@X_vpZtV4kZ8V@k9KRICr{zfT5%zeTUsR^2J@6C_*L(sG3N_4ZqQLmVN!c0l zWHU7bbaIY|1CvA=o}Qo{>84 z_m_{klYMaK!mm(X^ySJj2!Tw4agt!ng+~3@(s)2N?AtH3*f4jmp9`qLCUQY~oS(_| zD-%BzFas8NC(ZQ1G#x;Ej)P&oeU_Q`l%G1iq!*S8hvj>*?_ObHk*vjV@Y3bS$piP@ zB{%)zhe}%o<_C7+yxi$HUw8l`V?`CU($Tb6Zu#D|^7jAp8OesWp2xWbp_GX#Uux=( zm&jL2t79IZ=x|lPuw$<=hz~<<_IzN+7Wv*MuF%k3%`^M-F3rSCbeW!3^Dq&g;U|n2 zR?o)bBFL^_n6V6ZP48;kFT0+(N4R(3VBg4l^QFWAKbct2(X;JY>1%63qsx_wW2M7p z5P#bErNlJsg*pQcz4EkgV;Qdc-GzPpRwK`gv0T0nUq2H_YOE+z+(6|P-?tur_C=={L|0IE;ki`r&R=hQ={O>3l;KHnd`PF(oUPXR?QzE zh%gYbU?afk08h1Gxi*Y0qS2?rcj4kBn#UXWNEhbYW~l}XIRUMXUeZr)rExjra|wo+ zXmtLYJ{|mp>1jI3E2;76!U|Z?JpyB@r!n3r{PiHO-4M#Jef?`>^_+TLjhhF1p0%6{ zO^x39&L8Ic(JoK#bf?~4qzm(Zd3Lw1==SBxWCKk}gdD$fq>pXK-5Z`$>bPKJ(ndjX zd1q%GH3^>5Ig~6nxO)Fle@F8(vrAOSr+QDkd;7$Dx_jDt@E5(07bpD=^OKBM3j^&i z_kQdJ?{FUAYc9H2KK1{;B&VPGN@>#hft^w@E%O62RQM~y{(#Ti_j`HZcfZu>N+8kK zTs{R8rIGIZgV9Uy!;vHkvE0rrTY7K?_wT;@AL>|ukwBgrVrFUx%qRhnn?%y&__#TU&W<)Z*G}E|=2*-< zjeVu?M?sZa@?87+SIO$x_0og9a@dw7K~4G~o>@NZsNFm4@vbGsJAO1eqz@WcSM|FF z)e2B0e#-aor-%U?3X>c6?viD9C`vc-+k6-^c_6L0IKEFSpb94L=u1QV_pCy5V5-A71 zM?N0B^{4X8-M8wzzb$caAyLMR7ATS=A~JA!r`D?>wV z;6%+p^DfCMsF3yl^Euh`_WXKKCn#L71pNH2#pZ zTj49R0Q)0lq2jWcoo>UXgLCI*VXhwO;&v!wX~wd=5VsGjXi3dPn)wo(T0%3CA#e3K z$xAMVx0DbzbRdfRUMX?xsYswFlFrm{ZHE9((|r(Ssu3Jv7nU2(>7^-FjX6bxAksTx zS!Pc7E@f^=k_@kDS`MdUYPWbDFrql5yV({s> zn|s%^kp_i5{<&>eTuOsJv!s!M87_rZyfYURBDt4idry!2@XzZsaB{CjeVzCPiSVQp zSEW3`J{_i{TMQ+La2QTJ@}tD3GJ}=|GL*!~kV4>DxJQKFKDDzs$@%&HKomH|36L;6AivxWENP*z1lx;1D@FG1N={uuxAZ1>%io3gfKwUG zFF;h1Z1u;&;2vfeXqAzUP?qyCY?aWvnKYsfi{dxpQ$ ze{dhoPrCa=!c4>uh(*83`rZSLa6v(loO;?ha`l(KFBiZ4UD%Ad6RUX96C{{%^2OJW zT|4ry5jD-N-~F=AGaSA@AQ{Usm%FNWz3^T4`-AY;xcx=>(Wl;n>0de0fpf&wOtca@ z-*!zyz_qw(mPh6*SOIfbXeOl11JhDq>}hjDeS`R_nT#2tfB>N{tDsWu{`!aH`TK9t zFC&d3TEoYQlnp-@Cv5L*l21JP6ZKt%MG@^|to)5e8TJa~r0>X(>#lSK5)tp>Rp#1i*=e7%7(FcA+SWv8N#kNyN-XE77rn%~frI&Jdkx zFBX057PRo!M1SR^@jKP6*ggD_p;KE>e^zq|KlCE0aF{v+^Iy4<^>4p(jk@HQ;j|}e&&?b(0_9pgo*8rKrR>-i^x`vT>Z&JwN;m4!+){>g)-B z`uoJ#z;|K(;ieuZjOK*Ke*(mWJSIDU2?RO_FG1r7ct{doJ}0?9WCCOX3u=Orklg<$U6#hcv->@ z9kgmI2ml)6fWvUVhYtak4Gs4JBz>-fP%FJ?e}*R|*;|J&f&QfL$vTLa;*2V1XXp)I3UMTI{m1j8%KgT~*SHd}2RF3vIX z?ENY&>gebq&vQ7I3r8NHId{PN0Uzl zv~j`GgSY)up2r5$9Qy6jXJW;Lu7gj&7rw&>ny( zk)g4}erSSW;Qsb~^6J0)3cg|%xGxV@un8JA^u5sbEh(8LKRfk(GB>vr+mCcf9gZF& zvyT@Ow{D>6;H2&GJgd*CU#M+t25mH9NN}Xsg1C?md;&?~K9Yah_4ut{##=NpTKLiI zL%Q5nhj(lhnx#$&wV%Wfkd1l@Y&LHQE z7&j;MxU^{L;ekW2qo2`{qVPxj+oAdU@`dNi8Vvh#-@TyaB%J~CD9uqj7kfyLLLCEz zcNZ$^pYH5DbCEXp{JV4gbMo<@-GVdBvF!}g(4JfWlzjVZSIf8m>)#anQY-@D5b(4ZlRsh~y;=w%d;Jk)qeT`vo<{7^#KwIxNskuiNcQZ2TS3c1{f)w< zARS|Lr}Rrxg5hFzXw({xM^}g6?uI&%@YOdG9aFXARyNx6u;EA}CfP|+NT2|iDV5mp zTl$;bP&ep5S`77_xzKTTK>x>+Jt*NupmEs@0y4^uIp@R*3Jo-E@SF$x{=*Cj_t0$i zQ#^{WwF{GXybs>OL$VB&Og-n8^F4WXF%4<%IM{L|P}E+578jKRA(1xuinM?_(v}Vp zooN>|e_ar`DEyr?Z?+%yr4Jh1eHldK;IN^KgY^kdbPQ$1M?5?KygNT^6Zk}exWRcA zwuxzM?~t1xe_H=4kt@nmA%C z3oQ=>N#Zr}@Q2!`YS)lRwl1E^kexfX$-RI4t^DD)ztp5Fs%A-R^L}Z?B9Q4d=qIp0 zU|uN%OPtqB;j%<45jZAM7syY}D4ia_-VXG;FoR*E_ahNlNA`@n&Kv;{jZ4tt4MLbg zi#I0N+7O{Z%nKSoZLG!&5^=<)sb%%j-?B?qzw@7E?wa#upsN#G9(&H40s~S|;>7O; z90T{y$6hE`J#&lP(Y8g(p-O@p8d=w}55iPtLSuWlazPCqw}Lql=ld36=+Yfu2O2?SLI8^p<2sE&Y3g#*Lh_;Q zZw(zp#NT8dg8-%+!*mfiq)oh{M&BdC8Qo~dg)E%!NAn-AJx5m0t$7Q<4Q`mkJ`O$TQzuJ=zK8~t7+RXZX3&{=I5h;TcITe{7m|evGSuFJMtFSYi!$|S zv{4A`#VW@I*k|z6!Unm&bLW^^Kn~+I@)+pT>(;d1ntMs{J`G@H5) z=t&1$am@yUt9@zoszsM9KMrr!LlaBGW8kpCPhu{aTF&rG&>HW;F^GEP7RhcxuTU7Yyh_=X*80xNySTd=@;5YD10G7miAW9{rZ+11uAzj$iBI%}t=`D5J7 z10zayc`Sp}_JwW~5`BV98b1wzgjrs_z~{xrW_fP+K3U(`r0MWdSC}WQJ^iu|XZ}vV z(UJf$F8>}W5|$t}nI`Y!v-Af{ptc&S(0nP{qdKA;+pmoyDaaANVzpIFR-g1jqt>qga}FAge}(LP>W>iS%5fgy*vrQ=vh!r1-8HC% zwR6$9QNSQ;j%HjXXJePeMI#V*T#Goee^yU6;M^B*Sz(O|fO?MU=-H5ce)aP|{^?d( zT{{owctQ)Po8^v0*U0HdZJ8wJ!ab(Zk_np$k!7n*l#2EDtrJ9z>}C$20LOhzocB2H@Zh8kZcTwHy3AAA4OcE-ypf!44WAFNzS;!g31LgG{VoaUl^BJ+aB$%yNB zh#mE8|DZ?n^98;yl=;D{RR6&eJ{(&l0podw?OdQ-FYYkzeo<0 zZ}{$XeXLUnh+rGa%+0Q#WtmnALO=(#G38)u?>Qsy4cvh+<`3Qp}|GvaX)p@3Egfn_(grMcmm z2j%^%&eFMex<}IJgpKLqsK!2F5L)lTf|PGJJt?y@^7IrC(l;7O1(*p*A@_{Z1yYE; z{iw}!16sqABLp07ZNtjqyL)%2hjuTV4EH@X8xH$cVa_5od5tKyS`4obLWAix;9#Sh z95kblP9cH<1&7{WQL$JBKz3zElj#D+JXK>|>t1QbHV$)eTpG6`;gN5_IAPc|w1FQ> zbMoZk`NzoT)~^E}=g7{(wAWTG_Nv=~RqmG^`!cD(hOa#o=FswU^)>~H^wMEDW<#iL z?Cg}gcJ0yAOFAHY9kdAvjZgs+bYKoCJ0wz|#q#dKPpW9B&PV@+nJ3TU?jQ+hgsi8( z(>~oU{e2>STo`VB#(92On0C;7V81-_0O$FAT@$Cqucy1~5YF?{WX8NC_!r=W8pP3> z0XE7$+v65h}dfC?*}S%zPL!K7Ki$4p-m~R44L~7^W@Kd439Ce%pVh?;WBPNxorZB2 zhJRatYaIl&-G@Ze=0M)qwNI9VKiGC?9<-P#K=>Tto%}#+&efU&jqnv!E93|JpOjfx zk-cY}71<$AGvQu{4-#%cvJLZP=M)wv@=e;EU_y+77>H-gyP+2F8iAruUp5{vFTn=&_9L(&~lEJDWE`+Xw-kxU&OiFxR6)VnCtc znRX5-T=D*jswK)s6#j~U!O>#Eac~ds->v1uj}GRW;vDyc<9>P8LmhjbxJL$haZ(35d)Ff)`n8U!E`&(secT);& zq{DaJnwopZBZQk?SV4Xnb zSZ5MgXoCx_8{B&F0dyR;g!tGot7Q-PcOH2BP?`BSePAavcOPAOf}Bu;Z74XM05p)_ zg7Bx>Hl>f$I=5`7{~Eix+_AT;@8?KWteg0$e^yBM< z`}t*|U!c~HqeMrd(Yl>piI6A#<6Wz~tM^AD{YL{$Gp@HA?82}t79Z@q^T*QFxD&gH zL#T3`GGcjfIHDiJ#8tDG$(q-FQ2N^TNPhWj_$6b7%9<7w0#b(W`0l^%AEj~g6PQ<# zCE3tEa%XaWZLxCg-M?Lae*1N@apyBqiZg>-py}EVA*4E|RIY#WX8Df?Zje9id|EcO z?v~9sP2|D-o8`;T-yt8`@V`iNr`%E!fv* z7Dx;9NB>lJmdwwq(7AuJJTnZn`UUv+IpT1X#4$)Pgg44p^FxpM_x^EC>0L}EH}AA3h0p=M$ z<|zyTt0OLKaD$=S06L8GXD^Vy#J-bT@xgT{gurV4TNKcu zz>_BOOVGw8Yb*Kg@jY%hYZivKrfabZJJett&p009kTiep+ratZ0vg9f|G&L6jk4o7 z&vOB@W0@K3009yNNpTS^%G7E)4z8EDy z?e6NT>Z+%{u6}QIb+z-=j921M$7SPuZ>)$(5qVXCvn^FT3YM~*j!;_46vfOZn7C*q zJ_-P`nx5t9?ei~{Uk;s{qfsK- z%R$tbJ#;9~OV+C%x_vJ)x%4Sg8MC*=WuJO7Z{Gh*daLrdO7(kG;?6x^cD|suUYxvM z@^8WThQ15Zhfe-nx_;AL5q`1iXy=Lidb#kJf?vC;3&3ZeeKcLOc!k~y(G$JDZcu%l z38!Ua-5%O!v1o4ZzW9oG*y1mqeIhNCm~@qvG$ZaAe9hN|%RR#zB>t|u?6UN(%dd!c z|N1qAaSOJQ86V9b)nAoYZ?(0}NW1g^*ykVpNgRFGOPu^U5~;py5h3Oz_R7&)FGx8* znOE8Gs9vAWu2CfoDphG+@US19O$C(f$9;jh&Z>%=&Tv}F=F;ZA;Z~a%$zbHR6By|? zayy6d-7JqS_p~kpx&}S+z&+`I|JkQ?i+3P(^~{Oax=p5We$MsjoX7u#eTChO2wh>A z?&sHtQlHb!DJK9;=lFqL0cqXsUry&;{ni-dddjDP71F&Lo;Idu&b)N(yFZft_S63& z4M{k)cg;;hM|00vBslC#aV!G6dX>P;^yqiKkRBbj-`7_Q(4o|; z+sNGtb5ugYw`Kj%#c>Ihof>cUQjOZ@#`8l*l%{&|Bv!Tz4Wt{^J@|rrjPzJ0;peA{ zx18pI2dD2m`qt|6m2mr=^WPk08jL4@v!EKSsd7BOjbfMb^yhmXiY#oe<_?UFv+&g_ zroXT8l`EF_N5c|NlDdJ7?ypR}^yist0PI|tMA_C=`dvwgNZqCL~J}!GPj#d`-8@cVU#cy~VnOivDdGhpMPe1W+ zy5(D6Nsm8#U-|6yqJ7@-bl};C((Y#-PRp-;OZ7DHskp1b(gn!xB3yXhE7Cjv?Pt@?fBNs!3Ed(+ zbx&Zn#1lvNN1`3NO*qrz4J9O_S4kJrBLjOh6aT3Mq~CsVL=R0g-GS<@9!;M!Un}Ry zAzg?MozRpxI)Ua%V0P)2uEmT$ZaCCo5>uY|;<;C2__m%#_>~+v{l)6}+g^#k8!vfR zx~O}B#x)*`Tc^#&s>Enrdc0vW<$;N~x&1;v^R>(G?M*Sx@2=Z#N_Xg1uidCM z?qWZo_gswEoj-GS+IS+*5dXzX{z>|&*-O(A3D`XO_Ni0zN$a<9Y*!EHAMLz90`5`o zUsVIAjyOSk%9J{v-*RvIZ$0mfTjB1%i))chK*uSy=k;)2(Z{_SMO`uPs4YgTbpiuUJv$+qZjb!mo7`cuxeF$rQY=6NsIl! zhh2X1Rx8!d{UXdGbIylFh4NxCVQKcwr=E>?3pDn3=LFvVjYOloUbBUmvPOw!2$i1} zk*91cja~Mh#;%+%(GfF@k*h}IZHowuA14kVD^;BxKAPK|ZTnx&H%**b#kk&!NEvGi z!O6w9VU*8UBe#U}asBZ)^E{v5&h+4Yccia<>2ndvJ$^Bt-_AU|rX}i>Siq$%-Y$2453EVCo7$y{fH!{))8wu{+bYpWKyJzVRIrbDjl1 z8`S7*yw6j^=;_AGUZ39eAHJAw`|>B!uB}g}F7^EQf_6-|eNR>wr_s_FZ=*0;8fV+j zr%8DzVm!y{P{ZwBM4x~be{bwwo<6wpE$I?H-Rujt1yb{5eDRcT-M=oaA3D&OT>8aB zPu$)kx(LlvZ{0QtwwrgXPXBb-Rgt(G6Sps=331b7S8hG}^+RDD`f~57?PR6i34Ot= z{`9uRm!>U8_NOQHBVhBu-gtW0Z=vv0#CC;>m)?k_R?D&Mot=H$8>xjGwh|#i=Hutp z95aGNAPb1k9+sf!&f!>{#orR$3jX@mwef`SwaYG0ededW8+nXOIYp=3prbsh ziPlst7J+SAzGl(Vbmja7={<)pPa6&#&}V$w=v!L^qL93_3NXB zi;In^XTtebwD&Y9*&vAN(~V+#OXD|BYTnbvcK-2j-h%%0$`wme-|SiO zU{UPWS6uXMS32j)dPb~NTWp{lCumpxaoQ+Y_$e))HD31^-@N9d75I&NPTTxF`bQX^qA_Y z$@(&&SKKL`HAi-(|GMMuc(3r5v-seL^EsN^qg&m+ef^C+kEc}Rm49Wlkq50u-v;#UWId3i+rTbg5q~*CCNPpi-e<<@q1ThX&+OilK6=lO(i~-Y z*971CEywcGqAyCWGUl3S?hP+d$y}QLby>?LSLq9yVIZ=9Sgb0|_5Uyb!8Kx)Yc@~q zZKO(N7~5uICuJP68U>2SNL(3#Hqy{OAKwQY9_M!~t$Fg% zbjwX&PLDryPsHt?uW^2Rcj^ws^tc{**ho~L7SXd@5sEg|>uN7t`?;R79aKSBOdx?N zEdbq9*WSA{4QZN{r*8g>boIM`J0<}>=?l$^KyoAH6MslOhTXIK((B&*o^;_WUaQ&D zzmp!h?`!gJ4yNtW6TWTgJ;~{1bF6GvN2tuR@*Zt_SUwc4x~w*GH?lWj^y4^qb*x5A zZAW?yk7}hfxvl>4%h6jr^+I`FxP5!pV*~xMd+(yxrMEA*JiVev6J|?bxaZFo{CxNq zB+^LFjdQyi)B&Y)!_G&--|sknxdiksm20*xCjWi&-MXbaD_yQ0-strz72;*9SaWZL z{OoNl<8e+ARGveEH(2#rS=88m!da|2j_6Pawj<@} z&7C^7F9zLu=w$POgX#UZ-C6C+@2+kJoVc}IZf8bibD49+YFoMVx)$CW4}d64=~da4SDYASX6E90_WAA*u1vonNdr-27s0HmV4q{Tw@yJKl(FV8C&Eq?*Z5`d zaR`TrlfvhL#iPP&Ux-LON#KVGVicl`E}u&|JzMATIEUqLgLIaBY$f0pGUv&5DX}P9 z?wX}(iS$~*Kl!V_Qb31OiNeNXbDUq!xj(0OMfmx>y>3G{ZFn}_b=&_B zY1qZy-JZQ+Y-yhUs4strIJHy>{(&dhDw^w53vbKCEnxqU2z$zYQEAmGi-{pHr3Ii* z2#ddtc`MTT@7ja@)J1J9E>3=jm60u3aamflcx8I^>)w&JXMGEG5 z|Gu&N$@JBOPp1`n@MF_Cc<|#?*|GSraC4*a(e<~a>o5LA^#;yUFI08=QTs}<;v!bH zJia)%74ouq3DQyAKxyF%j;~O@eU+U9|FV zD{Lf{_eVzcz=e+MH_O3Ijl}zlY%H!RNThg`o>(cG6-2+F` zC+@y49nyHeMH)M}L%n~FmXD^D33NHloZC1^tmb?>UtWv)_evFP0uMh0l9CN)!&Y81 zm%J2=hF#c4b?RO?2l3B5(J zy%+vIrhZ>k&hhLo=WtrM2QUt;tr@O3{hz1=px3?kq!Q@r>`d!6Zb~2e^k*XaZ1n^l zlHHBh>9D~e%IowiCZ|fr)4H_D#y!?=&#vw1fqQRH->BZ(+oy4U$Nb*jVdMOKfNA1f zM**y|0<8;~oa)5uW)~g?IgQFa$*OOP3&3m`gL^lpwpk0){a^TS>eXxC7O%Q09rq*g zr#S(`+!V$Qj(Y?Bbnl`itJ2~{E7O&)dUHB<_`PZ1;2ynkMB_npvd&P8mK!MFTCfOc z*>2fqf6tWPx__8o3F~If>u!o1zW9%b>t?O7zl0qYrrKZpB24!4$Uk2o-f<^gnaRn&wJh6&Qkn)4s1;SY5Nb-GChg=a*97?DV%SS z&Q-n+s5~E9`>k~SMej@(&eD_2+AadlCgOK0=faAWjUk+C#oN`U7O_VcoakvN{$i%Y z8iD+y!Z9PXZL5)hBjj-T;#ZDUC8UMd%Jt0ajkr$3y&kfUl7?2!$XWb(PQUe<8}j;x z?oA*3>1)#BSv~1!+zxhrt0OoAl?x_iTjk9?IGIi4$la5TuwE&p$YshYma==l)`M9y zUQ8oW?J{?ZznzB%(r51bar()geQB}8-}bX8{(R`1&noZDtVKOI_gVbAUR;+Cvh77rD1O4+_MNBb0_ArLG3$VU!o zn88{i zlf5ma9P4VXlFTPF3pA#asYP4o)a=s;G1lY>H%0!^U8hiQ@@tp{$IYH z`qi74pA@!{o!)?aLb^flQ^FQ+Gxg-;>^bvOpFS2w;ivr}>|~r4fFa&0A?WoGg1 z)|Qgpk2B^Y1XN`O7S#%<|4f^NPc?ChBTC#^krsPWK; zV|f4BaydsieK!72sGTpi30jM$cQ zoScsBOs&)~zvv;d(KxKm)qJzp@$9@T;k}*L)ttpH-$D*o<Q(ku$Vf#BXjw^y+iuJ_4E@D zrSJaTU#HcN{wPBBX`J5yjq`JRJnfyTBdSVYzIM5d^tQ=)>07}5!JGX?<2mgKos$Ai z4*@7tp0v4b_JVZ$(5`g*XWp0o;Rn8u`WCH_2-Nf)wxZLz-29XV-^O=BW8?;PGG?Kd zMOYDNS&-S!;wy{6vaeOVHAJC|yokJ35trlS-o$d8hM23;E!$o;kKyBby9mS4w?p^| zK%`-jS=ODOi0_k5{yG7pO+F2*a*`US$Cb^NP<~dg!26Z;datmYA1K*7i3FhY)G7l1 z^V;vE4_t6u0;G+FTzyvgd{J=S}B71V9FCwl!u3BkyCRrx`+BnHQEH+jGhxdjIHH`5GUXpRF110p7-r&Sg=oT#Kcy0Ct&m*# z%6_*0rWN>{$i0A#viS#AIT3=X_ZX^~I)0OCur3jOo~S+ck2JeDlG14n6UVhp%HMTa z>@^}Z&NH=J`Z3+5w`qj<`{W0YnqcL-Uc%*V`bR!fCDYQ;hhF?MC|fSdo6jEb%M1Izp2G(dzu&b%6@Cj z+J^X>uY8=%F@KSP)-UI|HDAhDT%xM_s&-944YVF~>aDxJ8eCkBxhkU|wwtDFZa7ZE z>0E1W=e3+nPPt)EzVhq*DlU4!iyDX&^7)#rvxI8dQoZH0n*01YVt0UrC?*@O2^n`WGzKLx_5Buinzr zd~fe~k2p2MH|xKpz8dX|$`tEUvusl-*VCp4Fq@H{5Y$5#uJvNFWjV%b9&7MM!#2irCn%V+H`a#FVJ!eWbxga2!~F+0r(awD zjmXJD&AsQ>y*aJ17C9oG!!OSfXwmDpg?w%2W9i<#>(h1Tzad?&DPB6%b2%jYD3=r~ zAdguprSZ7G!l_*Rich}id2b~k0)oEu-@JUq?(nm)w8XSXYVxo8J{-5WvL4IUzL65( z%$KE(_NdHV%15v2^G{YkoF3f1CB5geE7K+Y^Yk9znb89tqEBI*jfhtXA|JhV+MB{7 z{1v!l-`n1Zi(?en1l`LxBi<%rMo;hBlWu%`b-F`OVlI^MJE#lw?xB1utmHM)Ig4L8 z|5d$Pcqo76)^BUsf`Ttl!)x~}=4^5$?Rxbm>|TtrmD`p_^e1>%!$rl%R-9vDL)rfG zPn(>o?@-v%^A8QhiS(NZMK7U~iY{tfPB~^X{hXidpTldP{{38fSEtAPJ+Iro|0D9! ztMRXUHO#=yDV&+B@}~rfojCvQ80hHgNdt!tq$lscKi&9ezaI~F^vvnkFpHU*&u{8Z z2z>akDC;wTx z_PXCs7ryG+cvFIFx7;3ySp4nO^e^S49$(7m zHnjU5JVnfccz^Q2^zn7Kr?>ZClK%dpOVSF>8Q3Wy=tk+dpSe{}#f;+;YkK-ME=al< z25>{XVk<2b==kJ)x36@A6U)x8J0h&X`7s6RPu zGu4S$JF0x_P;cN8-J-f@_x5zpz1!12zv$9*?eYs^GH3S)`W0D2YK-&c*|~I&@<>sd z%MdJFhRCeHD(lgz=LKxnr%i&>^Q^7kw?F-0B= z%b3MqRi&}8-WZlAm)#g}6#>x^lc@VqnB*Tx&HYWQ7sQC?NG}9BoO!jsC74%p7~7)j zt_Z@so`b4J6{uU2lly^>->SslJoWtTk@#z-F-peA;T6-<$mnhN9qMH~t|!;mXq?|I z-@GwB`S87w((rM96LsWqWO9-OjrO$4OKB(K;X1K171yH20lFb<4O>NFF+l{TnE*5u zHwWqol=y4wni~zmoi}_WZGY3RrmHmHU*9}U7OinlCp8Aj_EohWUc-$-z$c$SeoKfi z-0L;o?=QDKkiM$-?Jkn=JEU7H^j(z4knXb2LWj+k4l<=Pi8~rJr4}DxEJO zI9K!S`IUCQ?c}kJ@uy{s5tQMC?dZ#FECl?~YPj(86)*nvW#@%2bpDTVO2frdgwj_| zBd_?FG482~9=Bkti)D2vr#vCf$A4Y})90#ZqC$OkE=T|ORm40T~@^nFOZ|cvh+!(n12o<=`1aZ((oA($z4nG9!GX1j@RfFk+a_wCgk?hOtS2!31HC%+ zk1iY5LUcOv{L|UB`^$yk`C^Y+kcwT|pnEk*!~OQv${G5{<^s(kK-DfDw#%=3nnIai z=z5Qpq1xA-OFOhig`-X6wP1_n8e18QW@gI!S&3juTP&PiFtKa(>j&QPk+4pYx6)iL z$7bvyS*Hi&jrTQ0q>iZ<25bGNX!TdIJIJAk1+v-j+X2_{zS!x&V}N=-ET2KbT4F5x z%thC4D-B!e-(-rKcdP6%VryET3#(~`+vl5aty6Ts0i&bZ@)4$g&SE`Pdx`LGpU>ff z*&o*)lA15H96TLMaUreFrAD4brJOH1Am8J}ZjCs}bgU@?*-X%z_0A6H=nj!d1bt7P zE5_~=2zcGu_pQlMsCJ2{mFGMqy{fMNRq>^eJV%eBv73kN%=|C6uV;ZSCO{ELZ^?y3 z=MtqXx7gDP_OQ^PW2kox8Y$^%qMIjXZ@jk8(||x#rs5M&9k@-+VVNwpTZ{uPWOt{* zOvd7CV5?*E9q!u;DgD!_HJ;P%qMNE`%glhAlEGU!T)E^rKOdEs<5)w~l}}p@#*X+= zQr4GMGe&E~a=1rpW-dNrgCJ%N`b4LdF5h7-4>As-;p-z$DEbtH<*OV$ly@5nZg$ z581Eggs)Jb#_`=8#ZJlKB%l5Bv5llh_u-SR%^WO{Tt0!@tw&_vWpR*q7X#vq zCRmD0-hFmU@7BXm4+?K@o&aZirL6|z6xy+#gvQAA2Tc*-7|GJ#xR>RTZLKq5pMq?c z>N_}ou8TmuO|&#W)d_&MJ=~* z2Opg>GH~lGMnAav+x64yQ`s>4>5=t!eg=QodgMT20c?so(M$wha3(~s!bFF0d18{8 z{h%XvKjuUg=>2k8ZSs8hef3ii;1KJLDf68GjFLuIvXN4tmeD_cs3|1aste0;j8Kv( z%2BUEm#PZgHw07*a4m2}ISX&vuA3DdX;*`8)CW4lSBNZHV8Yy@bZ3F=O;u^ASL52V z9)5D;p$&#u;fbyV=LO8IpXYLbMBLCm!#Hr0U)v%gQ#SGR)Zx3fXz-vNYP8Ns-rJ5Q zb!YTaG1fdRFLvo#^j6=O)epP}SjuP6dZmO`Lty@JL4c)B4C`zU1Osao*g`ZqCi9Au z3Y;ZdJi(&odT>PH2e?H@zdm6EH5dB{f{!#z7sn_=nt|&PQGrHRl}hK``|P9RfUcRQ zQTGNM=~3qRq^GK$e#nm~#WR_qlND)nYBIK-Qrs^u)s8JQnesv$Cmw5t*&5PgHiIE# zaz3{WMBRuK4JP0fx7|B^@rq^l7$^<{{kDkM#VOya2Z%dVYRgJHKXT_jGv)#~6fmI_ zV#!u3+$@9=VB=V5!wpy5?oGC{64xDzMaV~cr}c1zzU=o_|7lf_zMveZvh;B$ALHpL zd|#gM-2MZsDP4M_vM-XmIsUY{bW|dUb#biofHub_-j`Z%xnjn5D1>cSP(N3GWV^|1 zS5}Ll+P1RJw$advcm9*@T_WZdRT$7El{&AgXFBjpU4uSF$hez!hC9%E+@n7m6oboE z80RlO>3n}A&+&~@Ag=t)@4nu5OQ}!Kcs=QU7NPX|0=g16$((7x6CMUMdhM)Gm~(zO z+J>U#w*w-^r3@vdGw3Gq#bF~v9%W)NTK}cu*dcrldC}5vz4~)02fWdss{jR#0p(b{ zU%4d|Tw>I-1~MMx)2Wd;g7A!%$9={HGAaI02BQ9bFWG^OKK~}MbNDdoXqF-Mb}g*U z&io$biH#8YGd|qfLNTyC(l6N&3hpDZST-r?`Ok!hHGM0QB~iOm;bBEAU}whj(~U2X z9n&-V{o+zbmUSZ)s%^6Zl%Y9nx%)@tjO|T}14f>t(nk!;sU3rs=2ALEMBIEo_?Z|e zA=Di|O?+<{m2_=C`L35X*)||4Tj;SMT5el&1V2bGM+=9wJV%aNjXIaWFXL6UQr$F? zPl=Ic5qaCdwY4~~S>dgGq2FoM#Gdv^;#);$$%W&xJLxb2myvjFnWc||LXcr`Ta)eU z%=DWJau{Tx*FqCWqC1*^{>4UnKJyEL?9(d8k?xz+NRXKx40m$%apjBegj9kuvSYCY z=db5>W<1$sXyOjhG-9l!(ymh5;uDSxI7wS-br9{^(AFBaYPr`zHLh!Q=8d%~9}V@f ze}oKu>P!$8P&2URPq@~soLLDFH?;Zb^nH3Qw=~pAyRFK z?;+XC2GYpD-(=nNXb-2X>GvF_x?g>a#E&c~boU*)0jlyd!vUEMn0J}&F7knT)V>7S z8}h=Dif0}x@g9m!aJ7X?8T0H4S?06+EixtBsd!N8`d=RNj* zcq7(Q?^DH>twHte;Q`H^Oz}1Y#?K`+dX09*hyM~NP8XC;1U--VG#gBnAT((~oH2Al zf=R_b8SALcE(ziF5?vQ$jhA56My~#3Iq2)`!CYRlS_!%xDoS-DX^^z-)V>PltgXCK zlNy%%dN#)|yEe_wRml_wnF~C)V^pJV)ol#mmreu-s%UdX-K2ENy4OHjYJc$PANor7 zl35*mKRCWCq{maJu_9>SzVGJzC3%WZP$lPYZXOwQ0l?>0zpuRI`>5ah`R+*YO#eYA zq4%EyjJzNo*ZLRY9KcuLU*_Tf9)NCD*gdg-0i+X0|EJIMU-$%lNGI~AO#T^>2d}pW z|HB)5&flAQdkgRH$g&r9{t1zLL3c0Z>?Pd4i-iAkx$*g%0H4Uq2ig3PH~_C%p0_ir IHt|XRFY90JK>z>% literal 0 HcmV?d00001 diff --git a/restore-dev.bat b/restore-dev.bat index a8e327a..69d9312 100644 --- a/restore-dev.bat +++ b/restore-dev.bat @@ -16,7 +16,7 @@ echo. :: Package cache location echo Unity Package Cache Location: -echo Example: X:\UnityProject\Library\PackageCache\com.justinpbarnett.unity-mcp@1.0.0 +echo Example: X:\UnityProject\Library\PackageCache\com.coplaydev.unity-mcp@1.0.0 set /p "PACKAGE_CACHE_PATH=Enter Unity package cache path: " if "%PACKAGE_CACHE_PATH%"=="" (