Skip to content

Commit 0a9a006

Browse files
Command execution optimization
1 parent 06a0c96 commit 0a9a006

2 files changed

Lines changed: 134 additions & 32 deletions

File tree

Rocket/Rocket.Core/Commands/RocketCommandManager.cs

Lines changed: 133 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -293,69 +293,140 @@ private static string[] ParseCommand(string input)
293293
return result.ToArray();
294294
}
295295

296-
public bool Execute(IRocketPlayer player, string command)
296+
private static void Reply(IRocketPlayer player, string message, Color color)
297297
{
298-
// Strip leading slash without allocating when absent
299-
if (command.Length > 0 && command[0] == '/')
300-
command = command.Substring(1);
301-
302-
string[] commandParts = ParseCommand(command); // skip expensive Regex entirely.
303-
304-
name = commandParts[0];
305-
306-
// Build parameters array without Skip(1) which allocates an iterator.
307-
string[] parameters;
308-
if (commandParts.Length > 1)
298+
if (player is ConsolePlayer)
309299
{
310-
parameters = new string[commandParts.Length - 1];
311-
System.Array.Copy(commandParts, 1, parameters, 0, parameters.Length);
300+
Logging.Logger.Log(message, ConsoleColor.Gray);
301+
return;
312302
}
313-
else
303+
304+
SDG.Unturned.SteamPlayer steamPlayer = SDG.Unturned.PlayerTool.getSteamPlayer(
305+
new Steamworks.CSteamID(ulong.Parse(player.Id)));
306+
307+
if (steamPlayer != null)
314308
{
315-
parameters = System.Array.Empty<string>();
309+
SDG.Unturned.ChatManager.serverSendMessage(
310+
message,
311+
color,
312+
null,
313+
steamPlayer
314+
);
316315
}
316+
}
317+
private bool TryResolveCommand(
318+
ref IRocketPlayer player,
319+
string command,
320+
out string[] commandParts,
321+
out IRocketCommand rocketCommand)
322+
{
323+
commandParts = null;
324+
rocketCommand = null;
325+
326+
if (string.IsNullOrEmpty(command))
327+
return false;
328+
329+
// Strip leading slash only when present
330+
if (command[0] == '/')
331+
command = command.Substring(1);
332+
333+
commandParts = ParseCommand(command); // skip expensive Regex entirely.
334+
335+
string name = commandParts[0];
317336

318337
if (player == null)
319338
player = new ConsolePlayer();
320339

321-
IRocketCommand rocketCommand = GetCommand(name);
340+
rocketCommand = GetCommand(name);
341+
322342
if (rocketCommand == null)
343+
{
344+
Reply(player, R.Translate("command_not_found"), Color.red);
323345
return false;
346+
}
324347

325-
// AllowedCaller checks — unchanged logic, early-exit order preserved.
326-
if (rocketCommand.AllowedCaller == AllowedCaller.Player && player is ConsolePlayer)
348+
// AllowedCaller validation only.
349+
// This is fundamental command validity, not permission policy.
350+
if (rocketCommand.AllowedCaller == AllowedCaller.Player &&
351+
player is ConsolePlayer)
327352
{
328353
Logging.Logger.Log("This command can't be called from console");
329354
return false;
330355
}
331-
if (rocketCommand.AllowedCaller == AllowedCaller.Console && !(player is ConsolePlayer))
356+
357+
if (rocketCommand.AllowedCaller == AllowedCaller.Console &&
358+
!(player is ConsolePlayer))
359+
{
360+
Reply(player, "This command can only be called from console", Color.red);
361+
return false;
362+
}
363+
364+
return true;
365+
}
366+
367+
private bool ValidateCommand(
368+
IRocketPlayer player,
369+
IRocketCommand rocketCommand)
370+
{
371+
if (!(player is ConsolePlayer) &&
372+
!R.Permissions.HasPermission(player, rocketCommand))
332373
{
333-
Logging.Logger.Log("This command can only be called from console");
374+
Reply(player, R.Translate("command_no_permission"), Color.red);
334375
return false;
335376
}
336377

337378
double cooldown = GetCooldown(player, rocketCommand);
338-
if (cooldown != -1)
379+
380+
if (cooldown > 0)
339381
{
340-
Logging.Logger.Log("This command is still on cooldown");
382+
Reply(player,
383+
R.Translate("command_cooldown", cooldown),
384+
Color.red);
385+
341386
return false;
342387
}
343388

389+
return true;
390+
}
391+
392+
public bool ExecuteResolved(
393+
IRocketPlayer player,
394+
string[] commandParts,
395+
IRocketCommand rocketCommand)
396+
{
397+
string[] parameters;
398+
399+
if (commandParts.Length > 1)
400+
{
401+
int parameterCount = commandParts.Length - 1;
402+
403+
parameters = new string[parameterCount];
404+
405+
Array.Copy(commandParts, 1, parameters, 0, parameterCount);
406+
}
407+
else
408+
{
409+
parameters = Array.Empty<string>();
410+
}
411+
344412
try
345413
{
346414
bool cancelCommand = false;
347415

348-
// Snapshot the delegate once — avoids repeated null checks and
349-
// repeated GetInvocationList() + Cast<> allocations per handler.
350416
ExecuteCommand snapshot = OnExecuteCommand;
417+
351418
if (snapshot != null)
352419
{
353420
Delegate[] handlers = snapshot.GetInvocationList();
421+
354422
for (int i = 0; i < handlers.Length; i++)
355423
{
356424
try
357425
{
358-
((ExecuteCommand)handlers[i])(player, rocketCommand, ref cancelCommand);
426+
((ExecuteCommand)handlers[i])(
427+
player,
428+
rocketCommand,
429+
ref cancelCommand);
359430
}
360431
catch (Exception ex)
361432
{
@@ -371,6 +442,7 @@ public bool Execute(IRocketPlayer player, string command)
371442
{
372443
rocketCommand.Execute(player, parameters);
373444

445+
// Preserve Rocket compatibility behavior
374446
if (!player.HasPermission("*"))
375447
SetCooldown(player, rocketCommand);
376448
}
@@ -382,16 +454,48 @@ public bool Execute(IRocketPlayer player, string command)
382454
{
383455
// intentionally swallowed
384456
}
385-
// Other exceptions propagate to the outer catch as before.
386457
}
387458
catch (Exception ex)
388459
{
389460
Logging.Logger.LogError(
390-
"An error occured while executing " + rocketCommand.Name +
391-
" [" + string.Join(", ", parameters) + "]: " + ex.ToString());
461+
"An error occured while executing " +
462+
rocketCommand.Name +
463+
" [" + string.Join(", ", parameters) + "]: " +
464+
ex);
392465
}
393466

394467
return true;
468+
}
469+
470+
public bool ExecuteWithCheck(IRocketPlayer player, string command)
471+
{
472+
if (!TryResolveCommand(
473+
ref player,
474+
command,
475+
out string[] commandParts,
476+
out IRocketCommand rocketCommand))
477+
{
478+
return false;
479+
}
480+
481+
if (!ValidateCommand(player, rocketCommand))
482+
return false;
483+
484+
return ExecuteResolved(player, commandParts, rocketCommand);
485+
}
486+
487+
public bool Execute(IRocketPlayer player, string command)
488+
{
489+
if (!TryResolveCommand(
490+
ref player,
491+
command,
492+
out string[] commandParts,
493+
out IRocketCommand rocketCommand))
494+
{
495+
return false;
496+
}
497+
498+
return ExecuteResolved(player, commandParts, rocketCommand);
395499
}
396500

397501
public void RegisterFromAssembly(Assembly assembly)

Rocket/Rocket.Core/Extensions/IRocketPlayerExtension.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ public static class IRocketPlayerExtension
88
{
99
public static bool HasPermission(this IRocketPlayer player, string permission)
1010
{
11-
return R.Permissions.HasPermission(player, permission) || player.IsAdmin;
12-
/*
11+
// return R.Permissions.HasPermission(player, permission) || player.IsAdmin;
1312
var provider = R.Permissions;
1413

1514
if (player.IsAdmin)
@@ -22,7 +21,6 @@ public static bool HasPermission(this IRocketPlayer player, string permission)
2221

2322
// FALLBACK PATH (legacy compatibility)
2423
return provider.HasPermission(player, new List<string>(1) { permission });
25-
*/
2624
}
2725

2826
public static bool HasPermissions(this IRocketPlayer player, List<string> permissions)

0 commit comments

Comments
 (0)