-
-
Notifications
You must be signed in to change notification settings - Fork 320
Expand file tree
/
Copy pathEvent.Subscribe.cs
More file actions
98 lines (91 loc) · 5.27 KB
/
Copy pathEvent.Subscribe.cs
File metadata and controls
98 lines (91 loc) · 5.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/*
┌──────────────────────────────────────────────────────────────────┐
│ Repository: GitHub (https://github.com/IvanMurzak/Unity-MCP) │
│ Licensed under the Apache License, Version 2.0. │
│ See the LICENSE file in the project root for more information. │
└──────────────────────────────────────────────────────────────────┘
*/
#nullable enable
using System;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using com.IvanMurzak.McpPlugin;
namespace com.IvanMurzak.Unity.MCP.Editor.API
{
public partial class Tool_Event
{
public const string EventSubscribeToolId = "event-subscribe";
[McpPluginTool
(
EventSubscribeToolId,
Title = "Event / Subscribe"
)]
[Description(
"Waits for a Unity event and returns it. BLOCKING — holds response until event fires or timeout.\n\n" +
"== BUILT-IN EVENTS (no code changes needed) ==\n" +
"- play_mode_changed: Play/Pause/Stop transitions\n" +
"- scene_loaded / scene_opened: Scene loading\n" +
"- compilation_started / compilation_finished: Script compilation (hasErrors flag)\n" +
"- error_logged / warning_logged: Console messages\n" +
"- pause_state_changed: Editor pause toggle\n" +
"- hierarchy_changed: GO created/destroyed/reparented (NOTE: fires on ANY hierarchy change, not just yours)\n" +
"- selection_changed: Editor selection\n\n" +
"== CUSTOM EVENTS (for game logic — RECOMMENDED) ==\n" +
"Built-in events CANNOT detect game-specific moments like 'server callback done' or 'popup closed'.\n" +
"Do NOT use hierarchy_changed as a proxy for server responses — it fires on unrelated changes.\n\n" +
"Dynamic hook via script-execute (NO source code modification, auto-cleanup on play mode exit):\n" +
" Step 1 — Hook game event to McpEventBus (run once before test):\n" +
" script-execute (IMPORTANT: add 'using com.IvanMurzak.Unity.MCP.Editor.API;' for McpEventBus):\n" +
" SomeManager.Instance.OnDataLoaded += () => McpEventBus.Push(\"data_loaded\");\n" +
" Step 2 — Trigger async action (e.g. server request, UI navigation):\n" +
" script-execute(trigger the action that will eventually fire the callback)\n" +
" Step 3 — Subscribe and wait for the callback event:\n" +
" event-subscribe(type='data_loaded', timeoutMs=30000)\n" +
" Step 4 — Hooks die automatically when play mode stops. No cleanup needed.\n\n" +
"ALTERNATIVE: Add McpEventBus.Push() directly in game code (persistent, survives sessions):\n" +
" #if UNITY_EDITOR\n" +
" McpEventBus.Push(\"friend_data_loaded\", source: \"FriendManager\");\n" +
" #endif\n\n" +
"Custom events appear in event-list(filter='custom') after first Push.\n\n" +
"== USAGE PATTERNS ==\n" +
"1. Custom game events (RECOMMENDED — sequential, callback is async):\n" +
" script-execute(hook + trigger async action) → event-subscribe(type='custom_event')\n" +
"2. Built-in events (parallel subscribe + trigger):\n" +
" [parallel] event-subscribe(type='play_mode_changed') + editor-application-set-state(...)\n" +
"3. Compilation wait:\n" +
" script-update-or-create(...) → event-subscribe(type='compilation_finished')\n" +
"4. Background monitoring (non-blocking): use event-watch instead"
)]
public async Task<McpEventSubscribeResult> Subscribe
(
[Description(
"Event type to filter for. " +
"Use a specific type like 'error_logged' or 'compilation_finished'. " +
"Empty string or null matches ANY event type. " +
"Use 'event-list' tool to see all available types."
)]
string? type = null,
[Description(
"Maximum wait time in milliseconds. " +
"Range: 0-120000. Default: 30000 (30 seconds). " +
"Set to 0 to drain pending events without waiting."
)]
int timeoutMs = 30000,
[Description(
"If true, collects ALL matching events until timeout. " +
"If false (default), returns immediately after the FIRST matching event."
)]
bool collectAll = false
)
{
// Special case: timeoutMs=0 means drain pending, no wait
if (timeoutMs == 0)
return McpEventBus.DrainPending(type);
if (timeoutMs < 1000 || timeoutMs > 120000)
throw new ArgumentException(Error.InvalidSubscribeTimeout(timeoutMs));
using var cts = new CancellationTokenSource(timeoutMs + 1000); // Grace period
return await McpEventBus.WaitAsync(type, timeoutMs, collectAll, cts.Token);
}
}
}