From 1d37c3a6cf0aecdd2f4d2a745030710d8b60482f Mon Sep 17 00:00:00 2001 From: Rokojori Date: Sun, 4 Oct 2026 22:14:14 +0200 Subject: [PATCH] Hexagon Coord --- RokojoriPlugin.cs | 2 +- Runtime/Actions/Visual/TweenFloat.cs | 4 +- Runtime/Godot/Nodes.cs | 15 +- Runtime/Interactions/Interactor.cs | 3 +- Runtime/Interactions/MultiRayCaster.cs | 4 +- Runtime/Math/Hexagon/HexagonCoord.cs | 169 ++++++++++++++++ Runtime/Math/Hexagon/HexagonCoord.cs.uid | 1 + Runtime/Random/RandomEngine.cs | 6 + Runtime/Time/TimelineManager.cs | 24 ++- Tools/mcp/McpServer.cs | 247 +++++++++++++---------- Tools/mcp/PendingRequest.cs | 25 +++ Tools/mcp/PendingRequest.cs.uid | 1 + Tools/mcp/PendingToolCall.cs | 24 --- Tools/mcp/PendingToolCall.cs.uid | 1 - 14 files changed, 385 insertions(+), 141 deletions(-) create mode 100644 Runtime/Math/Hexagon/HexagonCoord.cs create mode 100644 Runtime/Math/Hexagon/HexagonCoord.cs.uid create mode 100644 Tools/mcp/PendingRequest.cs create mode 100644 Tools/mcp/PendingRequest.cs.uid delete mode 100644 Tools/mcp/PendingToolCall.cs delete mode 100644 Tools/mcp/PendingToolCall.cs.uid diff --git a/RokojoriPlugin.cs b/RokojoriPlugin.cs index 39cec82..6277768 100644 --- a/RokojoriPlugin.cs +++ b/RokojoriPlugin.cs @@ -241,7 +241,7 @@ public partial class RokojoriPlugin: EditorPlugin public override void _Process( double delta ) { - mcpServer.ProcessPendingToolCalls(); + mcpServer.ProcessPendingRequests(); var editingScene = EditorInterface.Singleton.GetEditedSceneRoot(); diff --git a/Runtime/Actions/Visual/TweenFloat.cs b/Runtime/Actions/Visual/TweenFloat.cs index c9284b0..12da22a 100644 --- a/Runtime/Actions/Visual/TweenFloat.cs +++ b/Runtime/Actions/Visual/TweenFloat.cs @@ -88,7 +88,7 @@ namespace Rokojori protected override void _OnTrigger() { - this.LogInfo( "Started Float Tween" ); + // this.LogInfo( "Started Float Tween" ); // if ( Engine.IsEditorHint() ) @@ -119,7 +119,7 @@ namespace Rokojori AnimationManager.StartAnimation( this, target, targetMemberPath ); - this.LogInfo( "Start Value Float Tween", HierarchyName.OfAny( target ), target.GetType().Name, targetMemberPath, ">>", startValue ); + // this.LogInfo( "Start Value Float Tween", HierarchyName.OfAny( target ), target.GetType().Name, targetMemberPath, ">>", startValue ); _timeID = TimelineManager.ScheduleSpanWith( duration, ( span, type )=> diff --git a/Runtime/Godot/Nodes.cs b/Runtime/Godot/Nodes.cs index 139889a..bb64c2a 100644 --- a/Runtime/Godot/Nodes.cs +++ b/Runtime/Godot/Nodes.cs @@ -855,21 +855,26 @@ namespace Rokojori RemoveAndDeleteChildren( parent, includeInternal ); } - public static void DestroyChildAt( this Node parent, int index, bool includeInternal = false, bool queue = true ) + public static void DestroyChild( this Node parent, Node child, bool includeInternal = false, bool queue = true ) { - var node = parent.GetChild( index, includeInternal ); - parent.RemoveChild( node ); + parent.RemoveChild( child ); if ( ! queue ) { - node.Free(); + child.Free(); } else { - node.QueueFree(); + child.QueueFree(); } } + public static void DestroyChildAt( this Node parent, int index, bool includeInternal = false, bool queue = true ) + { + var node = parent.GetChild( index, includeInternal ); + DestroyChild( parent, node, includeInternal, queue ); + } + public static void DestroyChildrenRange( this Node parent, int startIndex, int range, bool includeInternal = false, bool queue = true ) { parent.LogInfo( startIndex, range, parent.GetChildCount() ); diff --git a/Runtime/Interactions/Interactor.cs b/Runtime/Interactions/Interactor.cs index e14c10b..88bf708 100644 --- a/Runtime/Interactions/Interactor.cs +++ b/Runtime/Interactions/Interactor.cs @@ -31,7 +31,8 @@ namespace Rokojori public void _OnSensor( SensorEvent se ) { - this.LogInfo( "SensorEvent:", se ); + // this.LogInfo( "SensorEvent:", se ); + if ( pointer == null || pointer.pointable == null ) { return; diff --git a/Runtime/Interactions/MultiRayCaster.cs b/Runtime/Interactions/MultiRayCaster.cs index cfa71ec..c89b146 100644 --- a/Runtime/Interactions/MultiRayCaster.cs +++ b/Runtime/Interactions/MultiRayCaster.cs @@ -22,8 +22,8 @@ namespace Rokojori [Export] public int maxHits = 3; - List collisions = new List(); - int numCollisions = 0; + // List collisions = new List(); + // int numCollisions = 0; [Export] public Vector3 to; diff --git a/Runtime/Math/Hexagon/HexagonCoord.cs b/Runtime/Math/Hexagon/HexagonCoord.cs new file mode 100644 index 0000000..6e38e92 --- /dev/null +++ b/Runtime/Math/Hexagon/HexagonCoord.cs @@ -0,0 +1,169 @@ +using Godot; +using System.Collections.Generic; + +namespace Rokojori; + + +public struct HexagonCoord +{ + public readonly int q; + public readonly int r; + + public HexagonCoord( int q, int r ) + { + this.q = q; + this.r = r; + } + + public readonly static HexagonCoord Center = new HexagonCoord( 0, 0 ); + public HexagonCoord Middle => Center; + + public readonly static HexagonCoord East = new HexagonCoord( 1, 0 ); + public HexagonCoord Left => East; + + public readonly static HexagonCoord NorthEast = new HexagonCoord( 1, -1 ); + public HexagonCoord LeftUp => NorthEast; + + public readonly static HexagonCoord NorthWest = new HexagonCoord( 0, -1 ); + public HexagonCoord RightUp => NorthWest; + + public readonly static HexagonCoord West = new HexagonCoord( -1, 0 ); + public HexagonCoord Right => West; + + public readonly static HexagonCoord SouthWest = new HexagonCoord( -1, 1 ); + public HexagonCoord RightDown => SouthWest; + + public readonly static HexagonCoord SouthEast = new HexagonCoord( 0, 1 ); + public HexagonCoord LeftDown => SouthEast; + + + static readonly HexagonCoord[] directions = new HexagonCoord[] + { + new HexagonCoord( 1, 0 ), + + new HexagonCoord( 1, -1 ), + new HexagonCoord( 0, -1 ), + + new HexagonCoord( -1, 0 ), + new HexagonCoord( -1, 1 ), + new HexagonCoord( 0, 1 ) + }; + + public static HexagonCoord Direction( int direction ) + { + return directions[ direction ]; + } + + public HexagonCoord Neighbor( int direction ) + { + var d = directions[ direction ]; + return this + directions[ direction ]; + } + + public Vector3 ToWorldPosition( float size ) + { + var x = size * 1.5f * q; + var z = size * ( Mathf.Sqrt( 3f ) * 0.5f * q + Mathf.Sqrt( 3f ) * r ); + + return new Vector3( x, 0, z ); + } + + public static HexagonCoord operator +(HexagonCoord a, HexagonCoord b) + { + return new HexagonCoord( + a.q + b.q, + a.r + b.r + ); + } + + public static HexagonCoord operator -(HexagonCoord a, HexagonCoord b) + { + return new HexagonCoord( + a.q - b.q, + a.r - b.r + ); + } + + public static HexagonCoord operator -(HexagonCoord a) + { + return new HexagonCoord( + -a.q, + -a.r + ); + } + + public static HexagonCoord operator *(HexagonCoord a, int scalar) + { + return new HexagonCoord( + a.q * scalar, + a.r * scalar + ); + } + + public static HexagonCoord operator *(int scalar, HexagonCoord a) + { + return a * scalar; + } + + public static HexagonCoord operator /(HexagonCoord a, int scalar) + { + return new HexagonCoord( + a.q / scalar, + a.r / scalar + ); + } + + public static bool operator ==(HexagonCoord a, HexagonCoord b) + { + return a.q == b.q && a.r == b.r; + } + + public static bool operator !=(HexagonCoord a, HexagonCoord b) + { + return !(a == b); + } + + public override bool Equals( object obj ) + { + return obj is HexagonCoord other && other.q == q && other.r == r; + } + + public override int GetHashCode() + { + return q * 397 ^ r; + } + + public static List Ring( int radius ) + { + return Ring( HexagonCoord.Center, radius ); + } + + public static List Ring( HexagonCoord center, int radius ) + { + var results = new List(); + + if ( radius == 0 ) + { + results.Add( center ); + return results; + } + + var hex = center; + + for ( int i = 0; i < radius; i++ ) + { + hex = hex.Neighbor( 4 ); + } + + for ( int side = 0; side < 6; side++ ) + { + for ( int step = 0; step < radius; step++ ) + { + results.Add( hex ); + hex = hex.Neighbor( side ); + } + } + + return results; + } +} diff --git a/Runtime/Math/Hexagon/HexagonCoord.cs.uid b/Runtime/Math/Hexagon/HexagonCoord.cs.uid new file mode 100644 index 0000000..e177bbd --- /dev/null +++ b/Runtime/Math/Hexagon/HexagonCoord.cs.uid @@ -0,0 +1 @@ +uid://d2y27n85ddcum diff --git a/Runtime/Random/RandomEngine.cs b/Runtime/Random/RandomEngine.cs index c4c61e5..aa73869 100644 --- a/Runtime/Random/RandomEngine.cs +++ b/Runtime/Random/RandomEngine.cs @@ -214,6 +214,11 @@ namespace Rokojori return IntegerExclusiveInRange( 0, max ); } + public HexagonCoord HexagonDirection() + { + return HexagonCoord.Direction( IntegerInclusiveInRange( 0, 5 ) ); + } + public char FromText( string source ) { @@ -243,6 +248,7 @@ namespace Rokojori return list[ index ]; } + #endif public T GrabFromRange( List list, int start, int end ) diff --git a/Runtime/Time/TimelineManager.cs b/Runtime/Time/TimelineManager.cs index 71e29c7..acfcd5b 100644 --- a/Runtime/Time/TimelineManager.cs +++ b/Runtime/Time/TimelineManager.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System; using Godot; using Rokojori.Extensions; +using System.Linq; namespace Rokojori; @@ -92,6 +93,11 @@ public partial class TimelineManager:NetworkNode { Engine.TimeScale = GetRunnerByTimeline( timeScaleTimeline ).modulatedSpeed; } + + if ( Engine.IsEditorHint() && _runners == null ) + { + Initialize(); + } _runners.ForEach( r => @@ -195,7 +201,23 @@ public partial class TimelineManager:NetworkNode public TimelineRunner GetRunnerByTimeline( Timeline timeline ) { - return GetRunner( _runners.FindIndex( r => r.timeLine == timeline ) ); + if ( _runners == null ) + { + RJLog.Error( "Runners is null" ); + + return null; + } + + var runner = GetRunner( _runners.FindIndex( r => r?.timeLine == timeline ) ); + + if ( runner == null ) + { + var inManager = timelines.Contains( timeline ); + + RJLog.Log( "No runner found for:", timeline, "Is in manager:", inManager ); + } + + return runner; } public TimelineRunner GetRunner( int index ) diff --git a/Tools/mcp/McpServer.cs b/Tools/mcp/McpServer.cs index 61363dc..4e1151b 100644 --- a/Tools/mcp/McpServer.cs +++ b/Tools/mcp/McpServer.cs @@ -5,20 +5,31 @@ using System; using System.Collections.Concurrent; using System.Net; using System.Text; -using System.Threading; namespace Rokojori.Tools { using Rokojori; // Owned by RokojoriPlugin: Start()/Stop() from _EnterTree/_ExitTree, - // ProcessPendingToolCalls() from the existing _Process tick. + // ProcessPendingRequests() from the existing _Process tick. // - // Threading: HttpListener runs its accept loop on a background thread. initialize, - // notifications/initialized and tools/list are pure protocol/metadata and are answered - // directly on that thread. Only tools/call touches Godot APIs (ResourceLoader, PackedScene, - // the scene tree) - it is enqueued and the background thread blocks until the main thread - // drains the queue and signals the result back. + // Threading: HttpListener accepts via BeginGetContext/EndGetContext (thread-pool callbacks), + // not a dedicated foreground/background Thread blocked in GetContext() - no thread is ever + // blocked waiting on anything in this class, which matters because a blocked thread with a + // stack frame into this assembly keeps Godot's C# hot reload from unloading it until that + // thread returns, and relying on Stop()/Close() to reliably abort such a thread has not held + // up in practice. + // + // More fundamentally: the HTTP callback thread does raw byte I/O only - read the request body + // in, hand it off untouched, write the response bytes out when told to. It never parses JSON, + // dispatches protocol methods, or executes a tool. That's not just about tools/call touching + // Godot APIs directly (ResourceLoader, PackedScene, the scene tree) - the shared JSON library + // this server depends on (JSONParser) calls RJLog on malformed input, and RJLog ultimately + // calls GD.Print/GD.PrintErr, which are themselves Godot engine API calls. Every one of those + // is only safe from the main thread; calling into the engine from a background thread can + // corrupt native-side state in ways that surface as hot-reload failures. So ALL of it - JSON. + // Parse included - is deferred to ProcessPendingRequests() on the main thread via + // pendingRequests, and the response is written from there too. public class McpServer { public const string FlagFilePath = "res://.godot-mcp"; @@ -26,13 +37,12 @@ namespace Rokojori.Tools public const int PortRangeCount = 20; HttpListener listener; - Thread listenerThread; volatile bool running = false; int boundPort = -1; string projectPath = ""; readonly McpProtocolHandler protocol = new McpProtocolHandler(); - readonly ConcurrentQueue pendingCalls = new ConcurrentQueue(); + readonly ConcurrentQueue pendingRequests = new ConcurrentQueue(); public bool IsRunning { get { return running; } } public int BoundPort { get { return boundPort; } } @@ -70,9 +80,7 @@ namespace Rokojori.Tools } running = true; - listenerThread = new Thread( ListenLoop ); - listenerThread.IsBackground = true; - listenerThread.Start(); + BeginAccept(); RJLog.Log( "MCP", "Server started on port", boundPort, "for project", projectPath ); } @@ -86,27 +94,19 @@ namespace Rokojori.Tools running = false; - // A tools/call request blocks the single HTTP thread on pending.waitHandle, waiting for - // a future _Process() tick to dequeue and signal it. If that tick never comes (e.g. the - // editor is pausing the main loop to hot-reload this very assembly), the thread stays - // alive holding a reference into this assembly and the reload fails to unload it. Stop() - // always runs on the main thread, never concurrently with _Process(), so it can safely - // stand in for the tick that won't arrive and release any still-queued call right now. - while ( pendingCalls.TryDequeue( out var pending ) ) + // Any request still waiting for a future _Process() tick to dequeue it would otherwise sit + // unanswered forever if that tick never comes (e.g. the editor pausing the main loop to + // hot-reload this very assembly). Stop() always runs on the main thread, never concurrently + // with _Process(), so it can safely stand in for the tick that won't arrive and answer any + // still-queued request right now instead of leaving its HTTP response hanging. + while ( pendingRequests.TryDequeue( out var pending ) ) { - pending.outcome = ToolCallOutcome.Error( "MCP server is stopping" ); - pending.waitHandle.Set(); + AbortPendingRequest( pending ); } - // The accept loop is parked inside the blocking listener.GetContext() call from its previous - // iteration. HttpListener.Stop()/Close() are documented to abort that call, but that has not - // proven reliable in practice - if it doesn't, the thread stays parked forever, which blocks - // Godot's assembly unload on every future hot reload, not just one that catches a request - // in flight. Firing one harmless loopback request guarantees GetContext() returns (with that - // request) so the loop can observe running == false on its own and exit, instead of depending - // on Stop()/Close() to interrupt it. - WakeListenerThread(); - + // Closing the listener faults any pending BeginGetContext registration, which invokes + // OnGetContext on a thread-pool callback thread with an exception from EndGetContext() - + // handled there by simply not chaining another BeginAccept(). No dedicated thread to join. try { listener?.Stop(); @@ -116,57 +116,101 @@ namespace Rokojori.Tools { } - listenerThread?.Join( 2000 ); listener = null; boundPort = -1; RJLog.Log( "MCP", "Server stopped." ); } - void WakeListenerThread() + // Stop() runs on the main thread, so parsing here (to recover the request's id for a clean + // JSON-RPC error) is safe even though JSON.Parse can call RJLog on malformed input. + void AbortPendingRequest( PendingRequest pending ) { - if ( boundPort <= 0 ) + JSONData id = new JSONValue(); + + try + { + var parsed = JSON.Parse( pending.body ); + var envelope = parsed != null ? parsed.AsObject() : null; + var parsedId = envelope != null ? envelope.Get( "id" ) : null; + + if ( parsedId != null ) + { + id = parsedId; + } + } + catch ( Exception ) + { + } + + TryWriteJson( pending.response, protocol.ToolCallResult( id, ToolCallOutcome.Error( "MCP server is stopping" ) ) ); + } + + void BeginAccept() + { + if ( ! running || listener == null ) { return; } try { - using ( var client = new System.Net.Sockets.TcpClient() ) - { - client.Connect( "127.0.0.1", boundPort ); - - var bytes = Encoding.ASCII.GetBytes( - "GET /__mcp_shutdown HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n" - ); - - client.GetStream().Write( bytes, 0, bytes.Length ); - } + listener.BeginGetContext( OnGetContext, null ); } catch ( Exception ) { + // Listener already stopped/closed - nothing to accept. } } - // Drains tool-call requests queued by the HTTP thread. Must run on the main thread. - public void ProcessPendingToolCalls() + void OnGetContext( IAsyncResult ar ) { - while ( pendingCalls.TryDequeue( out var pending ) ) + HttpListenerContext context; + + try { - ToolCallOutcome outcome; + context = listener.EndGetContext( ar ); + } + catch ( Exception ) + { + // Stop() closed the listener while this accept was pending - end the chain here. + return; + } + + try + { + HandleContext( context ); + } + catch ( Exception e ) + { + // This runs on a thread-pool callback thread, not the main thread. Console.Error is + // plain .NET, not a Godot API - unlike RJLog, it carries no risk of touching the engine + // from the wrong thread (see the class comment above). + Console.Error.WriteLine( "[MCP] Unhandled error handling request: " + e ); try { - outcome = protocol.ExecuteTool( pending.toolName, pending.arguments ); + context.Response.StatusCode = 500; + context.Response.OutputStream.Close(); } - catch ( Exception e ) + catch ( Exception ) { - RJLog.Error( "MCP", "Tool execution failed", pending.toolName, e ); - outcome = ToolCallOutcome.Error( e.Message ); } + } + finally + { + BeginAccept(); + } + } - pending.outcome = outcome; - pending.waitHandle.Set(); + // Drains requests queued by the HTTP callback thread and handles them fully - JSON parsing, + // protocol dispatch, tool execution, and writing the HTTP response - on the main thread. + // Nothing on the HTTP side ever blocks waiting for this to happen. + public void ProcessPendingRequests() + { + while ( pendingRequests.TryDequeue( out var pending ) ) + { + HandleJsonRpc( pending.body, pending.response ); } } @@ -194,41 +238,9 @@ namespace Rokojori.Tools } } - void ListenLoop() - { - while ( running ) - { - HttpListenerContext context; - - try - { - context = listener.GetContext(); - } - catch ( Exception ) - { - break; - } - - try - { - HandleContext( context ); - } - catch ( Exception e ) - { - RJLog.Error( "MCP", "Unhandled error handling request", e ); - - try - { - context.Response.StatusCode = 500; - context.Response.OutputStream.Close(); - } - catch ( Exception ) - { - } - } - } - } - + // Runs on the HTTP callback thread. The /project-path and 404 branches only touch plain .NET + // request/response objects and cached strings/ints, so they're answered directly here. A + // /mcp POST body is handed off untouched - see the class comment for why. void HandleContext( HttpListenerContext context ) { var request = context.Request; @@ -257,9 +269,10 @@ namespace Rokojori.Tools body = reader.ReadToEnd(); } - HandleJsonRpc( body, response ); + pendingRequests.Enqueue( new PendingRequest( body, response ) ); } + // Runs on the main thread only, dequeued from pendingRequests by ProcessPendingRequests(). void HandleJsonRpc( string body, HttpListenerResponse response ) { JSONObject envelope; @@ -276,7 +289,7 @@ namespace Rokojori.Tools } catch ( Exception ) { - WriteJson( response, 200, JsonRpc.BuildError( new JSONValue(), -32700, "Parse error" ) ); + TryWriteJson( response, JsonRpc.BuildError( new JSONValue(), -32700, "Parse error" ) ); return; } @@ -287,32 +300,31 @@ namespace Rokojori.Tools if ( isNotification ) { - WriteEmpty( response, 202 ); + TryWriteEmpty( response, 202 ); return; } switch ( method ) { case "initialize": - WriteJson( response, 200, protocol.Initialize( id, paramsObj ) ); + TryWriteJson( response, protocol.Initialize( id, paramsObj ) ); break; case "tools/list": - WriteJson( response, 200, protocol.ToolsList( id ) ); + TryWriteJson( response, protocol.ToolsList( id ) ); break; case "tools/call": - var outcome = DispatchToolCallBlocking( paramsObj ); - WriteJson( response, 200, protocol.ToolCallResult( id, outcome ) ); + HandleToolCall( id, paramsObj, response ); break; default: - WriteJson( response, 200, JsonRpc.BuildError( id, -32601, "Method not found: " + method ) ); + TryWriteJson( response, JsonRpc.BuildError( id, -32601, "Method not found: " + method ) ); break; } } - ToolCallOutcome DispatchToolCallBlocking( JSONObject paramsObj ) + void HandleToolCall( JSONData id, JSONObject paramsObj, HttpListenerResponse response ) { var toolName = JsonUtil.GetStringOrNull( paramsObj, "name" ); var arguments = paramsObj != null ? paramsObj.GetObject( "arguments" ) : null; @@ -322,17 +334,44 @@ namespace Rokojori.Tools arguments = new JSONObject(); } - var pending = new PendingToolCall( toolName, arguments ); - pendingCalls.Enqueue( pending ); + ToolCallOutcome outcome; - var completed = pending.waitHandle.Wait( TimeSpan.FromSeconds( 30 ) ); - - if ( ! completed ) + try { - return ToolCallOutcome.Error( "Timed out waiting for the main thread to execute tool: " + toolName ); + outcome = protocol.ExecuteTool( toolName, arguments ); + } + catch ( Exception e ) + { + RJLog.Error( "MCP", "Tool execution failed", toolName, e ); + outcome = ToolCallOutcome.Error( e.Message ); } - return pending.outcome ?? ToolCallOutcome.Error( "Tool produced no result: " + toolName ); + TryWriteJson( response, protocol.ToolCallResult( id, outcome ) ); + } + + void TryWriteJson( HttpListenerResponse response, JSONData data ) + { + try + { + WriteJson( response, 200, data ); + } + catch ( Exception e ) + { + // Client likely disconnected before the response could be written - nothing to recover. + RJLog.Error( "MCP", "Failed to write response", e ); + } + } + + void TryWriteEmpty( HttpListenerResponse response, int statusCode ) + { + try + { + WriteEmpty( response, statusCode ); + } + catch ( Exception e ) + { + RJLog.Error( "MCP", "Failed to write response", e ); + } } static void WriteJson( HttpListenerResponse response, int statusCode, JSONData data ) diff --git a/Tools/mcp/PendingRequest.cs b/Tools/mcp/PendingRequest.cs new file mode 100644 index 0000000..2e81d9e --- /dev/null +++ b/Tools/mcp/PendingRequest.cs @@ -0,0 +1,25 @@ +#if TOOLS + +using System.Net; + +namespace Rokojori.Tools +{ + // A raw HTTP request body + its response, captured by the HTTP callback thread and handed + // off untouched. Parsing the body (JSON.Parse, via the shared JSON library) and all protocol/ + // tool dispatch happens later on the main thread - not here, since that parsing can itself log + // through RJLog (a Godot engine API) on malformed input, which is only safe from the main + // thread. The callback thread's job ends at reading bytes in and, later, writing bytes out. + public class PendingRequest + { + public string body; + public HttpListenerResponse response; + + public PendingRequest( string body, HttpListenerResponse response ) + { + this.body = body; + this.response = response; + } + } +} + +#endif diff --git a/Tools/mcp/PendingRequest.cs.uid b/Tools/mcp/PendingRequest.cs.uid new file mode 100644 index 0000000..08a6c57 --- /dev/null +++ b/Tools/mcp/PendingRequest.cs.uid @@ -0,0 +1 @@ +uid://cijeep5twxvdm diff --git a/Tools/mcp/PendingToolCall.cs b/Tools/mcp/PendingToolCall.cs deleted file mode 100644 index caf6fef..0000000 --- a/Tools/mcp/PendingToolCall.cs +++ /dev/null @@ -1,24 +0,0 @@ -#if TOOLS - -using System.Threading; - -namespace Rokojori.Tools -{ - using Rokojori; - - public class PendingToolCall - { - public string toolName; - public JSONObject arguments; - public ManualResetEventSlim waitHandle = new ManualResetEventSlim( false ); - public ToolCallOutcome outcome; - - public PendingToolCall( string toolName, JSONObject arguments ) - { - this.toolName = toolName; - this.arguments = arguments; - } - } -} - -#endif diff --git a/Tools/mcp/PendingToolCall.cs.uid b/Tools/mcp/PendingToolCall.cs.uid deleted file mode 100644 index 3062528..0000000 --- a/Tools/mcp/PendingToolCall.cs.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dy170v7wjkcj5