forked from Unity-Technologies/UnityCsReference
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDispatcher.cs
More file actions
208 lines (186 loc) · 7.52 KB
/
Copy pathDispatcher.cs
File metadata and controls
208 lines (186 loc) · 7.52 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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Unity.CommandStateObserver
{
/// <summary>
/// Diagnostic flags for command dispatch.
/// </summary>
[Flags]
enum Diagnostics
{
/// <summary>
/// No diagnostic done when dispatching.
/// </summary>
None = 0,
/// <summary>
/// Log all dispatched commands and their handler.
/// </summary>
LogAllCommands = 1 << 0,
/// <summary>
/// Log an error when a command is dispatched while dispatching another command.
/// </summary>
CheckRecursiveDispatch = 1 << 1,
}
/// <summary>
/// The command dispatcher.
/// </summary>
class Dispatcher : ICommandTarget
{
/// <summary>
/// The mapping of command types to command handlers.
/// </summary>
protected readonly Dictionary<Type, ICommandHandlerFunctor> m_CommandHandlers = new Dictionary<Type, ICommandHandlerFunctor>();
/// <summary>
/// The list of actions that needs to be invoked before executing a command.
/// </summary>
protected readonly List<Action<ICommand>> m_CommandPreDispatchCallbacks = new List<Action<ICommand>>();
/// <summary>
/// The command being executed.
/// </summary>
protected ICommand m_CurrentCommand;
/// <summary>
/// Returns true is a command is being dispatched.
/// </summary>
protected bool IsDispatching => m_CurrentCommand != null;
/// <summary>
/// Registers a handler for a command type. Replaces any previously registered handler for the command type.
/// </summary>
/// <param name="commandHandlerFunctor">The command handler.</param>
public void RegisterCommandHandler<TCommand>(ICommandHandlerFunctor commandHandlerFunctor) where TCommand : ICommand
{
if (!IsDispatching)
{
var commandType = typeof(TCommand);
m_CommandHandlers[commandType] = commandHandlerFunctor;
}
else
{
Debug.LogError($"Cannot call {nameof(RegisterCommandHandler)} while dispatching a command.");
}
}
/// <summary>
/// Gets the command handler for a command type.
/// </summary>
/// <typeparam name="TCommand">The type of the command.</typeparam>
/// <returns>The command handler for the command, or null if there is none.</returns>
public ICommandHandlerFunctor GetCommandHandler<TCommand>() where TCommand : ICommand
{
return GetCommandHandler(typeof(TCommand));
}
/// <summary>
/// Gets the command handler for a command type.
/// </summary>
/// <param name="commandType">The type of the command.</param>
/// <returns>The command handler for the command, or null if there is none.</returns>
public ICommandHandlerFunctor GetCommandHandler(Type commandType)
{
return m_CommandHandlers.TryGetValue(commandType, out var functor) ? functor : null;
}
/// <summary>
/// Unregisters the command handler for a command type.
/// </summary>
/// <remarks>
/// Since there is only one command handler registered for a command type, it is not necessary
/// to specify the command handler to unregister.
/// </remarks>
/// <typeparam name="TCommand">The command type.</typeparam>
public void UnregisterCommandHandler<TCommand>() where TCommand : ICommand
{
if (!IsDispatching)
{
m_CommandHandlers.Remove(typeof(TCommand));
}
else
{
Debug.LogError($"Cannot call {nameof(UnregisterCommandHandler)} while dispatching a command.");
}
}
/// <summary>
/// Registers a method to be called before dispatching a command.
/// </summary>
/// <param name="callback">The callback.</param>
/// <exception cref="InvalidOperationException">Thrown when the callback is already registered.</exception>
public void RegisterCommandPreDispatchCallback(Action<ICommand> callback)
{
if (!IsDispatching)
{
if (m_CommandPreDispatchCallbacks.Contains(callback))
throw new InvalidOperationException("Cannot register the same pre-dispatch callback twice.");
m_CommandPreDispatchCallbacks.Add(callback);
}
else
{
Debug.LogError($"Cannot call {nameof(RegisterCommandPreDispatchCallback)} while dispatching a command.");
}
}
/// <summary>
/// Unregisters a pre-dispatch callback.
/// </summary>
/// <param name="callback">The callback.</param>
public void UnregisterCommandPreDispatchCallback(Action<ICommand> callback)
{
if (!IsDispatching)
{
m_CommandPreDispatchCallbacks.Remove(callback);
}
else
{
Debug.LogError($"Cannot call {nameof(UnregisterCommandPreDispatchCallback)} while dispatching a command.");
}
}
/// <summary>
/// Dispatches a command: the command handler registered for this command will be executed.
/// </summary>
/// <param name="command">The command to dispatch.</param>
/// <param name="diagnosticsFlags">Flags to control logging.</param>
public virtual void Dispatch(ICommand command, Diagnostics diagnosticsFlags = Diagnostics.None)
{
if (IsDispatching && (diagnosticsFlags & Diagnostics.CheckRecursiveDispatch) == Diagnostics.CheckRecursiveDispatch)
{
Debug.LogError($"Recursive dispatch detected: command {command.GetType().Name} dispatched during {m_CurrentCommand.GetType().Name}'s dispatch");
}
try
{
m_CurrentCommand = command;
foreach (var callback in m_CommandPreDispatchCallbacks)
{
callback(command);
}
PreDispatchCommand(command);
try
{
var logHandler = (diagnosticsFlags & Diagnostics.LogAllCommands) == Diagnostics.LogAllCommands;
var handler = GetCommandHandler(command.GetType());
handler?.Invoke(command, logHandler);
}
finally
{
PostDispatchCommand(command);
}
}
finally
{
m_CurrentCommand = null;
}
}
/// <summary>
/// Called when a command is dispatched, before the command handler is executed,
/// but after the pre-dispatch callbacks.
/// </summary>
/// <param name="command">The command being dispatched.</param>
protected virtual void PreDispatchCommand(ICommand command)
{
}
/// <summary>
/// Called when a command is dispatched, after the command handler has been executed.
/// </summary>
/// <param name="command">The command being dispatched.</param>
protected virtual void PostDispatchCommand(ICommand command)
{
}
}
}