2025-10-04 08:23:28 +08:00
using System ;
using System.IO ;
using UnityEditor ;
using System.Net ;
using System.Net.Sockets ;
using System.Security.Cryptography ;
using System.Text ;
using System.Threading ;
using Newtonsoft.Json ;
using UnityEngine ;
namespace MCPForUnity.Editor.Helpers
{
/// <summary>
/// Manages dynamic port allocation and persistent storage for MCP for Unity
/// </summary>
public static class PortManager
{
private static bool IsDebugEnabled ( )
{
try { return EditorPrefs . GetBool ( "MCPForUnity.DebugLogs" , false ) ; }
catch { return false ; }
}
private const int DefaultPort = 6400 ;
private const int MaxPortAttempts = 100 ;
private const string RegistryFileName = "unity-mcp-port.json" ;
[Serializable]
public class PortConfig
{
public int unity_port ;
public string created_date ;
public string project_path ;
}
/// <summary>
/// Get the port to use - either from storage or discover a new one
/// Will try stored port first, then fallback to discovering new port
/// </summary>
/// <returns>Port number to use</returns>
public static int GetPortWithFallback ( )
{
// Try to load stored port first, but only if it's from the current project
var storedConfig = GetStoredPortConfig ( ) ;
if ( storedConfig ! = null & &
storedConfig . unity_port > 0 & &
string . Equals ( storedConfig . project_path ? ? string . Empty , Application . dataPath ? ? string . Empty , StringComparison . OrdinalIgnoreCase ) & &
IsPortAvailable ( storedConfig . unity_port ) )
{
if ( IsDebugEnabled ( ) ) Debug . Log ( $"<b><color=#2EA3FF>MCP-FOR-UNITY</color></b>: Using stored port {storedConfig.unity_port} for current project" ) ;
return storedConfig . unity_port ;
}
// If stored port exists but is currently busy, wait briefly for release
if ( storedConfig ! = null & & storedConfig . unity_port > 0 )
{
if ( WaitForPortRelease ( storedConfig . unity_port , 1500 ) )
{
if ( IsDebugEnabled ( ) ) Debug . Log ( $"<b><color=#2EA3FF>MCP-FOR-UNITY</color></b>: Stored port {storedConfig.unity_port} became available after short wait" ) ;
return storedConfig . unity_port ;
}
Feature/session based instance routing (#369)
* Add support for multiple Unity instances
* fix port detection
* add missing unity_instance parameter
* add instance params for resources
* Fix CodeRabbit review feedback
- Fix partial framed response handling in port discovery
Add _recv_exact() helper to ensure complete frame reading
Prevents healthy Unity instances from being misidentified as offline
- Remove unused default_conn variables in server.py (2 files)
Fixes Ruff F841 lint error that would block CI/CD
- Preserve sync/async nature of resources in wrapper
Check if original function is coroutine before wrapping
Prevents 'dict object is not awaitable' runtime errors
- Fix reconnection to preserve instance_id
Add instance_id tracking to UnityConnection dataclass
Reconnection now targets the same Unity instance instead of any available one
Prevents operations from being applied to wrong project
- Add instance logging to manage_asset for debugging
Helps troubleshoot multi-instance scenarios
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
* Fix CodeRabbit feedback: reconnection fallback and annotations safety
Address 3 CodeRabbit review comments:
1. Critical: Guard reconnection fallback to prevent wrong instance routing
- When instance_id is set but rediscovery fails, now raises ConnectionError
- Added 'from e' to preserve exception chain for better debugging
- Prevents silently connecting to different Unity instance
- Ensures multi-instance routing integrity
2. Minor: Guard __annotations__ access in resource registration
- Use getattr(func, '__annotations__', {}) instead of direct access
- Prevents AttributeError for functions without type hints
3. Minor: Remove unused get_type_hints import
- Clean up unused import in resources/__init__.py
All changes applied to both Server/ and MCPForUnity/ directories.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Fix instance sorting and logging issues
- Fix sorting logic for instances without heartbeat data: use epoch timestamp instead of current time to properly deprioritize instances with None last_heartbeat
- Use logger.exception() instead of logger.error() in disconnect_all() to include stack traces for better debugging
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* update uv.lock to prepare for merging into main
* Restore Python 3.10 lockfiles and package list_unity_instances tool
* Deduplicate Unity instance discovery by port
* Scope status-file reload checks to the active instance
* refactor: implement FastMCP middleware for session-based instance routing
Replaces module-level session_state.py with UnityInstanceMiddleware class
that follows FastMCP best practices. Middleware intercepts all tool calls
via on_call_tool hook and injects active Unity instance into request state.
Key changes:
- Add UnityInstanceMiddleware class with on_call_tool hook
- Tools now use ctx.get_state("unity_instance") instead of direct session_state calls
- Remove unity_instance parameter from all tool schemas to prevent LLM hallucination
- Convert list_unity_instances tool to unity_instances resource (read-only data)
- Update error messages to reference unity://instances resource
- Add set_state/get_state methods to DummyContext test helper
- All 67 tests passing (55 passed, 5 skipped, 7 xpassed)
Architecture benefits:
- Centralized session management in middleware
- Standard FastMCP patterns (middleware + request state)
- Cleaner separation of concerns
- Prevents AI hallucination of invalid instance IDs
* fix: convert resource templates to static resources for discoverability
Convert MCP resources from URI templates with query parameters to static
resources to fix discoverability in MCP clients like Claude Code.
Changes:
- Remove {?force_refresh} from unity://instances
- Remove {?unity_instance} from mcpforunity://menu-items
- Remove {?unity_instance} from mcpforunity://tests
- Keep {mode} path parameter in mcpforunity://tests/{mode} (legitimate)
Root cause: Query parameters {?param} trigger ResourceTemplate registration,
which are listed via resources/templates/list instead of resources/list.
Claude Code's ListMcpResourcesTool only queries resources/list, making
templates undiscoverable.
Solution: Remove optional query parameters from URIs. Instance routing is
handled by middleware/context, and force_refresh was cache control that
doesn't belong in resource identity.
Impact: Resources now discoverable via standard resources/list endpoint and
work with all MCP clients including Claude Code and Cursor.
Requires FastMCP >=2.13.0 for proper RFC 6570 query parameter support.
* feat: improve material properties and sync Server resources
Material Property Improvements (ManageAsset.cs):
- Add GetMainColorPropertyName() helper that auto-detects shader color properties
- Tries _BaseColor (URP), _Color (Standard), _MainColor, _Tint, _TintColor
- Update both named and array color property handling to use auto-detection
- Add warning messages when color properties don't exist on materials
- Split HasProperty check from SetColor to enable error reporting
This fixes the issue where simple color array format [r,g,b,a] defaulted to
_Color property, causing silent failures with URP Lit shader which uses _BaseColor.
Server Resource Sync:
- Sync Server/resources with MCPForUnity/UnityMcpServer~/src/resources
- Remove query parameters from resource URIs for discoverability
- Use session-based instance routing via get_unity_instance_from_context()
* fix: repair instance routing and simplify get_unity_instance_from_context
PROBLEM:
Instance routing was failing - scripts went to wrong Unity instances.
Script1 (intended: ramble) -> went to UnityMCPTests ❌
Script2 (intended: UnityMCPTests) -> went to ramble ❌
ROOT CAUSE:
Two incompatible approaches for accessing active instance:
1. Middleware: ctx.set_state() / ctx.get_state() - used by most tools
2. Legacy: ctx.request_context.meta - used by script tools
Script tools were reading from wrong location, middleware had no effect.
FIX:
1. Updated get_unity_instance_from_context() to read from ctx.get_state()
2. Removed legacy request_context.meta code path (98 lines removed)
3. Single source of truth: middleware state only
TESTING:
- Added comprehensive test suite (21 tests) covering all scenarios
- Tests middleware state management, session isolation, race conditions
- Tests reproduce exact 4-script failure scenario
- All 88 tests pass (76 passed + 5 skipped + 7 xpassed)
- Verified fix with live 4-script test: 100% success rate
Files changed:
- Server/tools/__init__.py: Simplified from 75 lines to 15 lines
- MCPForUnity/UnityMcpServer~/src/tools/__init__.py: Same simplification
- tests/test_instance_routing_comprehensive.py: New comprehensive test suite
* refactor: standardize instance extraction and remove dead imports
- Standardize all 18 tools to use get_unity_instance_from_context() helper
instead of direct ctx.get_state() calls for consistency
- Remove dead session_state imports from with_unity_instance decorator
that would cause ModuleNotFoundError at runtime
- Update README.md with concise instance routing documentation
* fix: critical timezone and import bugs from code review
- Remove incorrect port safety check that treated reclaimed ports as errors
(GetPortWithFallback may legitimately return same port if it became available)
- Fix timezone-aware vs naive datetime mixing in unity_connection.py sorting
(use timestamp() for comparison to avoid TypeError)
- Normalize all datetime comparisons in port_discovery.py to UTC
(file_mtime and last_heartbeat now consistently timezone-aware)
- Add missing send_with_unity_instance import in Server/tools/manage_script.py
(was causing NameError at runtime on lines 108 and 488)
All 88 tests pass (76 passed + 5 skipped + 7 xpassed)
---------
Co-authored-by: Sakura <sakurachan@qq.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-06 01:43:36 +08:00
// Port is still busy after waiting - find a new available port instead
if ( IsDebugEnabled ( ) ) Debug . Log ( $"<b><color=#2EA3FF>MCP-FOR-UNITY</color></b>: Stored port {storedConfig.unity_port} is occupied by another instance, finding alternative..." ) ;
int newPort = FindAvailablePort ( ) ;
SavePort ( newPort ) ;
return newPort ;
2025-10-04 08:23:28 +08:00
}
// If no valid stored port, find a new one and save it
Feature/session based instance routing (#369)
* Add support for multiple Unity instances
* fix port detection
* add missing unity_instance parameter
* add instance params for resources
* Fix CodeRabbit review feedback
- Fix partial framed response handling in port discovery
Add _recv_exact() helper to ensure complete frame reading
Prevents healthy Unity instances from being misidentified as offline
- Remove unused default_conn variables in server.py (2 files)
Fixes Ruff F841 lint error that would block CI/CD
- Preserve sync/async nature of resources in wrapper
Check if original function is coroutine before wrapping
Prevents 'dict object is not awaitable' runtime errors
- Fix reconnection to preserve instance_id
Add instance_id tracking to UnityConnection dataclass
Reconnection now targets the same Unity instance instead of any available one
Prevents operations from being applied to wrong project
- Add instance logging to manage_asset for debugging
Helps troubleshoot multi-instance scenarios
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
* Fix CodeRabbit feedback: reconnection fallback and annotations safety
Address 3 CodeRabbit review comments:
1. Critical: Guard reconnection fallback to prevent wrong instance routing
- When instance_id is set but rediscovery fails, now raises ConnectionError
- Added 'from e' to preserve exception chain for better debugging
- Prevents silently connecting to different Unity instance
- Ensures multi-instance routing integrity
2. Minor: Guard __annotations__ access in resource registration
- Use getattr(func, '__annotations__', {}) instead of direct access
- Prevents AttributeError for functions without type hints
3. Minor: Remove unused get_type_hints import
- Clean up unused import in resources/__init__.py
All changes applied to both Server/ and MCPForUnity/ directories.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Fix instance sorting and logging issues
- Fix sorting logic for instances without heartbeat data: use epoch timestamp instead of current time to properly deprioritize instances with None last_heartbeat
- Use logger.exception() instead of logger.error() in disconnect_all() to include stack traces for better debugging
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* update uv.lock to prepare for merging into main
* Restore Python 3.10 lockfiles and package list_unity_instances tool
* Deduplicate Unity instance discovery by port
* Scope status-file reload checks to the active instance
* refactor: implement FastMCP middleware for session-based instance routing
Replaces module-level session_state.py with UnityInstanceMiddleware class
that follows FastMCP best practices. Middleware intercepts all tool calls
via on_call_tool hook and injects active Unity instance into request state.
Key changes:
- Add UnityInstanceMiddleware class with on_call_tool hook
- Tools now use ctx.get_state("unity_instance") instead of direct session_state calls
- Remove unity_instance parameter from all tool schemas to prevent LLM hallucination
- Convert list_unity_instances tool to unity_instances resource (read-only data)
- Update error messages to reference unity://instances resource
- Add set_state/get_state methods to DummyContext test helper
- All 67 tests passing (55 passed, 5 skipped, 7 xpassed)
Architecture benefits:
- Centralized session management in middleware
- Standard FastMCP patterns (middleware + request state)
- Cleaner separation of concerns
- Prevents AI hallucination of invalid instance IDs
* fix: convert resource templates to static resources for discoverability
Convert MCP resources from URI templates with query parameters to static
resources to fix discoverability in MCP clients like Claude Code.
Changes:
- Remove {?force_refresh} from unity://instances
- Remove {?unity_instance} from mcpforunity://menu-items
- Remove {?unity_instance} from mcpforunity://tests
- Keep {mode} path parameter in mcpforunity://tests/{mode} (legitimate)
Root cause: Query parameters {?param} trigger ResourceTemplate registration,
which are listed via resources/templates/list instead of resources/list.
Claude Code's ListMcpResourcesTool only queries resources/list, making
templates undiscoverable.
Solution: Remove optional query parameters from URIs. Instance routing is
handled by middleware/context, and force_refresh was cache control that
doesn't belong in resource identity.
Impact: Resources now discoverable via standard resources/list endpoint and
work with all MCP clients including Claude Code and Cursor.
Requires FastMCP >=2.13.0 for proper RFC 6570 query parameter support.
* feat: improve material properties and sync Server resources
Material Property Improvements (ManageAsset.cs):
- Add GetMainColorPropertyName() helper that auto-detects shader color properties
- Tries _BaseColor (URP), _Color (Standard), _MainColor, _Tint, _TintColor
- Update both named and array color property handling to use auto-detection
- Add warning messages when color properties don't exist on materials
- Split HasProperty check from SetColor to enable error reporting
This fixes the issue where simple color array format [r,g,b,a] defaulted to
_Color property, causing silent failures with URP Lit shader which uses _BaseColor.
Server Resource Sync:
- Sync Server/resources with MCPForUnity/UnityMcpServer~/src/resources
- Remove query parameters from resource URIs for discoverability
- Use session-based instance routing via get_unity_instance_from_context()
* fix: repair instance routing and simplify get_unity_instance_from_context
PROBLEM:
Instance routing was failing - scripts went to wrong Unity instances.
Script1 (intended: ramble) -> went to UnityMCPTests ❌
Script2 (intended: UnityMCPTests) -> went to ramble ❌
ROOT CAUSE:
Two incompatible approaches for accessing active instance:
1. Middleware: ctx.set_state() / ctx.get_state() - used by most tools
2. Legacy: ctx.request_context.meta - used by script tools
Script tools were reading from wrong location, middleware had no effect.
FIX:
1. Updated get_unity_instance_from_context() to read from ctx.get_state()
2. Removed legacy request_context.meta code path (98 lines removed)
3. Single source of truth: middleware state only
TESTING:
- Added comprehensive test suite (21 tests) covering all scenarios
- Tests middleware state management, session isolation, race conditions
- Tests reproduce exact 4-script failure scenario
- All 88 tests pass (76 passed + 5 skipped + 7 xpassed)
- Verified fix with live 4-script test: 100% success rate
Files changed:
- Server/tools/__init__.py: Simplified from 75 lines to 15 lines
- MCPForUnity/UnityMcpServer~/src/tools/__init__.py: Same simplification
- tests/test_instance_routing_comprehensive.py: New comprehensive test suite
* refactor: standardize instance extraction and remove dead imports
- Standardize all 18 tools to use get_unity_instance_from_context() helper
instead of direct ctx.get_state() calls for consistency
- Remove dead session_state imports from with_unity_instance decorator
that would cause ModuleNotFoundError at runtime
- Update README.md with concise instance routing documentation
* fix: critical timezone and import bugs from code review
- Remove incorrect port safety check that treated reclaimed ports as errors
(GetPortWithFallback may legitimately return same port if it became available)
- Fix timezone-aware vs naive datetime mixing in unity_connection.py sorting
(use timestamp() for comparison to avoid TypeError)
- Normalize all datetime comparisons in port_discovery.py to UTC
(file_mtime and last_heartbeat now consistently timezone-aware)
- Add missing send_with_unity_instance import in Server/tools/manage_script.py
(was causing NameError at runtime on lines 108 and 488)
All 88 tests pass (76 passed + 5 skipped + 7 xpassed)
---------
Co-authored-by: Sakura <sakurachan@qq.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-06 01:43:36 +08:00
int foundPort = FindAvailablePort ( ) ;
SavePort ( foundPort ) ;
return foundPort ;
2025-10-04 08:23:28 +08:00
}
/// <summary>
/// Discover and save a new available port (used by Auto-Connect button)
/// </summary>
/// <returns>New available port</returns>
public static int DiscoverNewPort ( )
{
int newPort = FindAvailablePort ( ) ;
SavePort ( newPort ) ;
if ( IsDebugEnabled ( ) ) Debug . Log ( $"<b><color=#2EA3FF>MCP-FOR-UNITY</color></b>: Discovered and saved new port: {newPort}" ) ;
return newPort ;
}
/// <summary>
/// Find an available port starting from the default port
/// </summary>
/// <returns>Available port number</returns>
private static int FindAvailablePort ( )
{
// Always try default port first
if ( IsPortAvailable ( DefaultPort ) )
{
if ( IsDebugEnabled ( ) ) Debug . Log ( $"<b><color=#2EA3FF>MCP-FOR-UNITY</color></b>: Using default port {DefaultPort}" ) ;
return DefaultPort ;
}
if ( IsDebugEnabled ( ) ) Debug . Log ( $"<b><color=#2EA3FF>MCP-FOR-UNITY</color></b>: Default port {DefaultPort} is in use, searching for alternative..." ) ;
// Search for alternatives
for ( int port = DefaultPort + 1 ; port < DefaultPort + MaxPortAttempts ; port + + )
{
if ( IsPortAvailable ( port ) )
{
if ( IsDebugEnabled ( ) ) Debug . Log ( $"<b><color=#2EA3FF>MCP-FOR-UNITY</color></b>: Found available port {port}" ) ;
return port ;
}
}
throw new Exception ( $"No available ports found in range {DefaultPort}-{DefaultPort + MaxPortAttempts}" ) ;
}
/// <summary>
/// Check if a specific port is available for binding
/// </summary>
/// <param name="port">Port to check</param>
/// <returns>True if port is available</returns>
public static bool IsPortAvailable ( int port )
{
try
{
var testListener = new TcpListener ( IPAddress . Loopback , port ) ;
testListener . Start ( ) ;
testListener . Stop ( ) ;
return true ;
}
catch ( SocketException )
{
return false ;
}
}
/// <summary>
/// Check if a port is currently being used by MCP for Unity
/// This helps avoid unnecessary port changes when Unity itself is using the port
/// </summary>
/// <param name="port">Port to check</param>
/// <returns>True if port appears to be used by MCP for Unity</returns>
public static bool IsPortUsedByMCPForUnity ( int port )
{
try
{
// Try to make a quick connection to see if it's an MCP for Unity server
using var client = new TcpClient ( ) ;
var connectTask = client . ConnectAsync ( IPAddress . Loopback , port ) ;
if ( connectTask . Wait ( 100 ) ) // 100ms timeout
{
// If connection succeeded, it's likely the MCP for Unity server
return client . Connected ;
}
return false ;
}
catch
{
return false ;
}
}
/// <summary>
/// Wait for a port to become available for a limited amount of time.
/// Used to bridge the gap during domain reload when the old listener
/// hasn't released the socket yet.
/// </summary>
private static bool WaitForPortRelease ( int port , int timeoutMs )
{
int waited = 0 ;
const int step = 100 ;
while ( waited < timeoutMs )
{
if ( IsPortAvailable ( port ) )
{
return true ;
}
// If the port is in use by an MCP instance, continue waiting briefly
if ( ! IsPortUsedByMCPForUnity ( port ) )
{
// In use by something else; don't keep waiting
return false ;
}
Thread . Sleep ( step ) ;
waited + = step ;
}
return IsPortAvailable ( port ) ;
}
/// <summary>
/// Save port to persistent storage
/// </summary>
/// <param name="port">Port to save</param>
private static void SavePort ( int port )
{
try
{
var portConfig = new PortConfig
{
unity_port = port ,
created_date = DateTime . UtcNow . ToString ( "O" ) ,
project_path = Application . dataPath
} ;
string registryDir = GetRegistryDirectory ( ) ;
Directory . CreateDirectory ( registryDir ) ;
string registryFile = GetRegistryFilePath ( ) ;
string json = JsonConvert . SerializeObject ( portConfig , Formatting . Indented ) ;
// Write to hashed, project-scoped file
File . WriteAllText ( registryFile , json , new System . Text . UTF8Encoding ( false ) ) ;
// Also write to legacy stable filename to avoid hash/case drift across reloads
string legacy = Path . Combine ( GetRegistryDirectory ( ) , RegistryFileName ) ;
File . WriteAllText ( legacy , json , new System . Text . UTF8Encoding ( false ) ) ;
if ( IsDebugEnabled ( ) ) Debug . Log ( $"<b><color=#2EA3FF>MCP-FOR-UNITY</color></b>: Saved port {port} to storage" ) ;
}
catch ( Exception ex )
{
Debug . LogWarning ( $"Could not save port to storage: {ex.Message}" ) ;
}
}
/// <summary>
/// Load port from persistent storage
/// </summary>
/// <returns>Stored port number, or 0 if not found</returns>
private static int LoadStoredPort ( )
{
try
{
string registryFile = GetRegistryFilePath ( ) ;
if ( ! File . Exists ( registryFile ) )
{
// Backwards compatibility: try the legacy file name
string legacy = Path . Combine ( GetRegistryDirectory ( ) , RegistryFileName ) ;
if ( ! File . Exists ( legacy ) )
{
return 0 ;
}
registryFile = legacy ;
}
string json = File . ReadAllText ( registryFile ) ;
var portConfig = JsonConvert . DeserializeObject < PortConfig > ( json ) ;
return portConfig ? . unity_port ? ? 0 ;
}
catch ( Exception ex )
{
Debug . LogWarning ( $"Could not load port from storage: {ex.Message}" ) ;
return 0 ;
}
}
/// <summary>
/// Get the current stored port configuration
/// </summary>
/// <returns>Port configuration if exists, null otherwise</returns>
public static PortConfig GetStoredPortConfig ( )
{
try
{
string registryFile = GetRegistryFilePath ( ) ;
if ( ! File . Exists ( registryFile ) )
{
// Backwards compatibility: try the legacy file
string legacy = Path . Combine ( GetRegistryDirectory ( ) , RegistryFileName ) ;
if ( ! File . Exists ( legacy ) )
{
return null ;
}
registryFile = legacy ;
}
string json = File . ReadAllText ( registryFile ) ;
return JsonConvert . DeserializeObject < PortConfig > ( json ) ;
}
catch ( Exception ex )
{
Debug . LogWarning ( $"Could not load port config: {ex.Message}" ) ;
return null ;
}
}
private static string GetRegistryDirectory ( )
{
return Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . UserProfile ) , ".unity-mcp" ) ;
}
private static string GetRegistryFilePath ( )
{
string dir = GetRegistryDirectory ( ) ;
string hash = ComputeProjectHash ( Application . dataPath ) ;
string fileName = $"unity-mcp-port-{hash}.json" ;
return Path . Combine ( dir , fileName ) ;
}
private static string ComputeProjectHash ( string input )
{
try
{
using SHA1 sha1 = SHA1 . Create ( ) ;
byte [ ] bytes = Encoding . UTF8 . GetBytes ( input ? ? string . Empty ) ;
byte [ ] hashBytes = sha1 . ComputeHash ( bytes ) ;
var sb = new StringBuilder ( ) ;
foreach ( byte b in hashBytes )
{
sb . Append ( b . ToString ( "x2" ) ) ;
}
return sb . ToString ( ) [ . . 8 ] ; // short, sufficient for filenames
}
catch
{
return "default" ;
}
}
}
}