-
Notifications
You must be signed in to change notification settings - Fork 172
Expand file tree
/
Copy pathCommand.cs
More file actions
445 lines (391 loc) · 17.7 KB
/
Command.cs
File metadata and controls
445 lines (391 loc) · 17.7 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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
using System;
using System.Collections.Generic;
using System.Linq;
using Oxide.Core;
using Oxide.Core.Libraries;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Libraries.Covalence;
using Event = Oxide.Core.Event;
namespace Oxide.Game.Rust.Libraries
{
/// <summary>
/// A library containing functions for adding console and chat commands
/// </summary>
public class Command : Library
{
internal struct PluginCallback
{
public readonly Plugin Plugin;
public readonly string Name;
public Func<ConsoleSystem.Arg, bool> Call;
public PluginCallback(Plugin plugin, string name)
{
Plugin = plugin;
Name = name;
Call = null;
}
public PluginCallback(Plugin plugin, Func<ConsoleSystem.Arg, bool> callback)
{
Plugin = plugin;
Call = callback;
Name = null;
}
}
internal class ConsoleCommand
{
public readonly string Name;
public PluginCallback Callback;
public readonly ConsoleSystem.Command RustCommand;
public Action<ConsoleSystem.Arg> OriginalCallback;
internal readonly Permission permission = Interface.Oxide.GetLibrary<Permission>();
public ConsoleCommand(string name)
{
Name = name;
string[] splitName = Name.Split('.');
RustCommand = new ConsoleSystem.Command
{
Name = splitName[1],
Parent = splitName[0],
FullName = name,
ServerUser = true,
ServerAdmin = true,
Client = true,
ClientInfo = false,
Variable = false,
Call = HandleCommand
};
}
public void AddCallback(Plugin plugin, string name) => Callback = new PluginCallback(plugin, name);
public void AddCallback(Plugin plugin, Func<ConsoleSystem.Arg, bool> callback) => Callback = new PluginCallback(plugin, callback);
public void HandleCommand(ConsoleSystem.Arg arg)
{
Callback.Plugin?.TrackStart();
Callback.Call(arg);
Callback.Plugin?.TrackEnd();
}
}
internal class ChatCommand
{
public readonly string Name;
public readonly Plugin Plugin;
private readonly Action<BasePlayer, string, string[]> _callback;
public ChatCommand(string name, Plugin plugin, Action<BasePlayer, string, string[]> callback)
{
Name = name;
Plugin = plugin;
_callback = callback;
}
public void HandleCommand(BasePlayer sender, string name, string[] args)
{
Plugin?.TrackStart();
_callback?.Invoke(sender, name, args);
Plugin?.TrackEnd();
}
}
// All console commands that plugins have registered
internal readonly Dictionary<string, ConsoleCommand> consoleCommands;
// All chat commands that plugins have registered
internal readonly Dictionary<string, ChatCommand> chatCommands;
// A reference to the plugin removed callbacks
private readonly Dictionary<Plugin, Event.Callback<Plugin, PluginManager>> pluginRemovedFromManager;
/// <summary>
/// Initializes a new instance of the Command class
/// </summary>
public Command()
{
consoleCommands = new Dictionary<string, ConsoleCommand>();
chatCommands = new Dictionary<string, ChatCommand>();
pluginRemovedFromManager = new Dictionary<Plugin, Event.Callback<Plugin, PluginManager>>();
}
/// <summary>
/// Adds a chat command
/// </summary>
/// <param name="name"></param>
/// <param name="plugin"></param>
/// <param name="callback"></param>
[LibraryFunction("AddChatCommand")]
public void AddChatCommand(string name, Plugin plugin, string callback)
{
AddChatCommand(name, plugin, (player, command, args) => plugin.CallHook(callback, player, command, args));
}
/// <summary>
/// Adds a chat command
/// </summary>
/// <param name="command"></param>
/// <param name="plugin"></param>
/// <param name="callback"></param>
public void AddChatCommand(string command, Plugin plugin, Action<BasePlayer, string, string[]> callback)
{
string commandName = command.ToLowerInvariant();
if (!CanOverrideCommand(command, "chat"))
{
string pluginName = plugin?.Name ?? "An unknown plugin";
Interface.Oxide.LogError("{0} tried to register command '{1}', this command already exists and cannot be overridden!", pluginName, commandName);
return;
}
if (chatCommands.TryGetValue(commandName, out ChatCommand cmd))
{
string previousPluginName = cmd.Plugin?.Name ?? "an unknown plugin";
string newPluginName = plugin?.Name ?? "An unknown plugin";
string message = $"{newPluginName} has replaced the '{commandName}' chat command previously registered by {previousPluginName}";
Interface.Oxide.LogWarning(message);
}
if (RustCore.Covalence.CommandSystem.registeredCommands.TryGetValue(commandName, out RustCommandSystem.RegisteredCommand covalenceCommand))
{
string previousPluginName = covalenceCommand.Source?.Name ?? "an unknown plugin";
string newPluginName = plugin?.Name ?? "An unknown plugin";
string message = $"{newPluginName} has replaced the '{commandName}' command previously registered by {previousPluginName}";
Interface.Oxide.LogWarning(message);
RustCore.Covalence.CommandSystem.UnregisterCommand(commandName, covalenceCommand.Source);
}
cmd = new ChatCommand(commandName, plugin, callback);
// Add the new command to collections
chatCommands[commandName] = cmd;
// Hook the unload event
if (plugin != null && !pluginRemovedFromManager.ContainsKey(plugin))
{
pluginRemovedFromManager[plugin] = plugin.OnRemovedFromManager.Add(plugin_OnRemovedFromManager);
}
}
/// <summary>
/// Adds a console command
/// </summary>
/// <param name="command"></param>
/// <param name="plugin"></param>
/// <param name="callback"></param>
[LibraryFunction("AddConsoleCommand")]
public void AddConsoleCommand(string command, Plugin plugin, string callback)
{
AddConsoleCommand(command, plugin, arg => plugin.CallHook(callback, arg) != null);
}
/// <summary>
/// Adds a console command with a delegate callback
/// </summary>
/// <param name="command"></param>
/// <param name="plugin"></param>
/// <param name="callback"></param>
public void AddConsoleCommand(string command, Plugin plugin, Func<ConsoleSystem.Arg, bool> callback)
{
// Hook the unload event
if (plugin != null && !pluginRemovedFromManager.ContainsKey(plugin))
{
pluginRemovedFromManager[plugin] = plugin.OnRemovedFromManager.Add(plugin_OnRemovedFromManager);
}
// Setup console command name
string[] split = command.Split('.');
string parent = split.Length >= 2 ? split[0].Trim() : "global";
string name = split.Length >= 2 ? string.Join(".", split.Skip(1).ToArray()) : split[0].Trim();
string fullName = $"{parent}.{name}";
// Setup a new RustPlugin console command
ConsoleCommand cmd = new ConsoleCommand(fullName);
// Check if the command can be overridden
if (!CanOverrideCommand(parent == "global" ? name : fullName, "console"))
{
string pluginName = plugin?.Name ?? "An unknown plugin";
Interface.Oxide.LogError("{0} tried to register command '{1}', this command already exists and cannot be overridden!", pluginName, fullName);
return;
}
// Check if it already exists in a Rust plugin as a console command
if (consoleCommands.TryGetValue(fullName, out ConsoleCommand consoleCommand))
{
if (consoleCommand.OriginalCallback != null)
{
cmd.OriginalCallback = consoleCommand.OriginalCallback;
}
string previousPluginName = consoleCommand.Callback.Plugin?.Name ?? "an unknown plugin";
string newPluginName = plugin?.Name ?? "An unknown plugin";
string message = $"{newPluginName} has replaced the '{command}' console command previously registered by {previousPluginName}";
Interface.Oxide.LogWarning(message);
ConsoleSystem.Index.Server.Dict.Remove(consoleCommand.RustCommand.FullName);
if (parent == "global")
{
ConsoleSystem.Index.Server.GlobalDict.Remove(consoleCommand.RustCommand.Name);
}
ConsoleSystem.Index.All = ConsoleSystem.Index.Server.Dict.Values.ToArray();
}
if (RustCore.Covalence.CommandSystem.registeredCommands.TryGetValue(parent == "global" ? name : fullName, out RustCommandSystem.RegisteredCommand covalenceCommand))
{
if (covalenceCommand.OriginalCallback != null)
{
cmd.OriginalCallback = covalenceCommand.OriginalCallback;
}
string previousPluginName = covalenceCommand.Source?.Name ?? "an unknown plugin";
string newPluginName = plugin?.Name ?? "An unknown plugin";
string message = $"{newPluginName} has replaced the '{fullName}' command previously registered by {previousPluginName}";
Interface.Oxide.LogWarning(message);
RustCore.Covalence.CommandSystem.UnregisterCommand(parent == "global" ? name : fullName, covalenceCommand.Source);
}
// The command either does not already exist or is replacing a previously registered command
cmd.AddCallback(plugin, callback);
if (ConsoleSystem.Index.Server.Dict.TryGetValue(fullName, out ConsoleSystem.Command rustCommand))
{
// This is a vanilla Rust command which has not yet been hooked by a plugin
if (rustCommand.Variable)
{
string newPluginName = plugin?.Name ?? "An unknown plugin";
Interface.Oxide.LogError($"{newPluginName} tried to register the {name} console variable as a command!");
return;
}
cmd.OriginalCallback = rustCommand.Call;
}
// Register the console command
ConsoleSystem.Index.Server.Dict[fullName] = cmd.RustCommand;
if (parent == "global")
{
ConsoleSystem.Index.Server.GlobalDict[name] = cmd.RustCommand;
}
ConsoleSystem.Index.All = ConsoleSystem.Index.Server.Dict.Values.ToArray();
consoleCommands[fullName] = cmd;
}
/// <summary>
/// Removes a previously registered chat command
/// </summary>
/// <param name="command"></param>
/// <param name="plugin"></param>
[LibraryFunction("RemoveChatCommand")]
public void RemoveChatCommand(string command, Plugin plugin)
{
ChatCommand matchedCommand = chatCommands.Values.Where(x => x.Plugin == plugin).FirstOrDefault(x => x.Name == command);
if (matchedCommand != null)
{
RemoveChatCommand(matchedCommand);
}
}
/// <summary>
/// Removes a previously registered console command
/// </summary>
/// <param name="command"></param>
/// <param name="plugin"></param>
[LibraryFunction("RemoveConsoleCommand")]
public void RemoveConsoleCommand(string command, Plugin plugin)
{
ConsoleCommand matchedCommand = consoleCommands.Values.Where(x => x.Callback.Plugin == plugin).FirstOrDefault(x => x.Name == command);
if (matchedCommand != null)
{
RemoveConsoleCommand(matchedCommand);
}
}
/// <summary>
/// Removes a chat command
/// </summary>
/// <param name="command"></param>
private void RemoveChatCommand(ChatCommand command)
{
if (chatCommands.ContainsKey(command.Name))
{
chatCommands.Remove(command.Name);
}
}
/// <summary>
/// Removes a console command
/// </summary>
/// <param name="command"></param>
private void RemoveConsoleCommand(ConsoleCommand command)
{
if (consoleCommands.ContainsKey(command.Name))
{
// This command is no longer registered by any plugins
consoleCommands.Remove(command.Name);
// If this was originally a vanilla rust command then restore it, otherwise remove it
if (command.OriginalCallback != null)
{
ConsoleSystem.Index.Server.Dict[command.RustCommand.FullName].Call = command.OriginalCallback;
if (command.RustCommand.FullName.StartsWith("global."))
{
ConsoleSystem.Index.Server.GlobalDict[command.RustCommand.Name].Call = command.OriginalCallback;
}
}
else
{
ConsoleSystem.Index.Server.Dict.Remove(command.RustCommand.FullName);
if (command.Name.StartsWith("global."))
{
ConsoleSystem.Index.Server.GlobalDict.Remove(command.RustCommand.Name);
}
ConsoleSystem.Index.All = ConsoleSystem.Index.Server.Dict.Values.ToArray();
}
}
}
/// <summary>
/// Handles the specified chat command
/// </summary>
/// <param name="sender"></param>
/// <param name="name"></param>
/// <param name="args"></param>
internal bool HandleChatCommand(BasePlayer sender, string name, string[] args)
{
if (chatCommands.TryGetValue(name.ToLowerInvariant(), out ChatCommand cmd))
{
cmd.HandleCommand(sender, name, args);
return true;
}
return false;
}
/// <summary>
/// Called when a plugin has been removed from manager
/// </summary>
/// <param name="sender"></param>
/// <param name="manager"></param>
private void plugin_OnRemovedFromManager(Plugin sender, PluginManager manager)
{
// Find all console commands which were registered by the plugin
ConsoleCommand[] commands = consoleCommands.Values.Where(c => c.Callback.Plugin == sender).ToArray();
foreach (ConsoleCommand cmd in commands)
{
RemoveConsoleCommand(cmd);
}
// Remove all chat commands which were registered by the plugin
foreach (ChatCommand cmd in chatCommands.Values.Where(c => c.Plugin == sender).ToArray())
{
RemoveChatCommand(cmd);
}
// Unhook the event
if (pluginRemovedFromManager.TryGetValue(sender, out Event.Callback<Plugin, PluginManager> callback))
{
callback.Remove();
pluginRemovedFromManager.Remove(sender);
}
}
/// <summary>
/// Checks if a command can be overridden
/// </summary>
/// <param name="command"></param>
/// <param name="type"></param>
/// <returns></returns>
private bool CanOverrideCommand(string command, string type)
{
string[] split = command.Split('.');
string parent = split.Length >= 2 ? split[0].Trim() : "global";
string name = split.Length >= 2 ? string.Join(".", split.Skip(1).ToArray()) : split[0].Trim();
string fullname = $"{parent}.{name}";
if (RustCore.Covalence.CommandSystem.registeredCommands.TryGetValue(command, out RustCommandSystem.RegisteredCommand cmd))
{
if (cmd.Source.IsCorePlugin)
{
return false;
}
}
if (type == "chat")
{
if (chatCommands.TryGetValue(command, out ChatCommand chatCommand))
{
if (chatCommand.Plugin.IsCorePlugin)
{
return false;
}
}
}
else if (type == "console")
{
if (consoleCommands.TryGetValue(parent == "global" ? name : fullname, out ConsoleCommand consoleCommand))
{
if (consoleCommand.Callback.Plugin.IsCorePlugin)
{
return false;
}
}
}
return !RustCore.RestrictedCommands.Contains(command) && !RustCore.RestrictedCommands.Contains(fullname);
}
}
}