SwiftlyS2
Development

Commands

SwiftlyS2 provides a comprehensive command system that allows you to register custom commands, handle player chat and commands, and manage command permissions. Commands can be registered using attributes or programmatically.

Registering Commands with Attributes

The simplest way to register commands is using the [Command] attribute.

[Command("heal")]
public void OnHealCommand(ICommandContext context)
{
    if (!context.IsSentByPlayer)
    {
        context.Reply("This command can only be used by players!");
        return;
    }

    var player = context.Sender!;
    // code
}

By default, commands are registered with the sw_ prefix, so the above command would be sw_heal.

Register Raw Commands

To register a command without the sw_ prefix, use registerRaw: true:

[Command("teleport", registerRaw: true)]
public void OnTeleportCommand(ICommandContext context)
{
    // This command is registered as "teleport" instead of "sw_teleport"
    context.Reply("Teleporting...");
}

Commands with Permissions

You can require permissions for commands:

[Command("kick", permission: "admin.kick")]
public void OnKickCommand(ICommandContext context)
{
    if (!context.IsSentByPlayer)
    {
        context.Reply("This command can only be used by players!");
        return;
    }

    context.Reply("Player kicked!");
}

Command Aliases

Add multiple aliases to a command using the [CommandAlias] attribute:

[Command("heal")]
[CommandAlias("hp")]
[CommandAlias("restore", registerRaw: true)]
public void OnHealCommand(ICommandContext context)
{
    // Can be called with: sw_heal, sw_hp, or restore
    context.Reply("Healed!");
}

Command Context

The ICommandContext provides information about the command execution:

[Command("info")]
public void OnInfoCommand(ICommandContext context)
{
    // Check if sent by a player
    if (context.IsSentByPlayer)
    {
        var player = context.Sender!;
        context.Reply($"You are {player.Controller.PlayerName}");
    }
    else
    {
        context.Reply("Command sent from server console");
    }

    // Check if silent command
    if (context.IsSlient)
    {
        context.Reply("This is a silent command");
    }

    // Access command arguments
    if (context.Args.Length > 0)
    {
        context.Reply($"First argument: {context.Args[0]}");
    }

    // Get command prefix
    context.Reply($"Command prefix: {context.Prefix}");
}

Programmatic Command Registration

You can also register commands programmatically using Core.Command:

public override void Load()
{
    // Register a command
    Guid commandGuid = Core.Command.RegisterCommand("mycommand", (context) =>
    {
        context.Reply("Command executed!");
    }, registerRaw: false, permission: "");

    // Register an alias
    Core.Command.RegisterCommandAlias("mycommand", "mycmd");
}

Unregistering Commands

// Unregister by GUID
Core.Command.UnregisterCommand(commandGuid);

// Unregister by name
Core.Command.UnregisterCommand("mycommand");

Client Command Hooks

Hook into all client commands to intercept or modify behavior:

Using Attributes

[ClientCommandHookHandler]
public HookResult OnClientCommand(int playerId, string commandLine)
{
    var player = Core.PlayerManager.GetPlayer(playerId);

    if (commandLine.StartsWith("say"))
    {
        Console.WriteLine($"Player {player?.Name} is using say command");
    }

    // Return Continue to allow the command
    return HookResult.Continue;

    // Return Handled to block the command
    // return HookResult.Handled;
}

Programmatic Registration

public override void Load()
{
    Core.Command.HookClientCommand((playerId, commandLine) =>
    {
        Console.WriteLine($"Player {playerId} executed: {commandLine}");
        return HookResult.Continue;
    });
}

Client Chat Hooks

Hook into player chat messages:

Using Attributes

[ClientChatHookHandler]
public HookResult OnClientChat(int playerId, string text, bool teamonly)
{
    var player = Core.PlayerManager.GetPlayer(playerId);

    if (text.Contains("badword"))
    {
        player?.PrintToChat("Please watch your language!");
        return HookResult.Stop; // Block the message
    }

    if (teamonly)
    {
        Console.WriteLine($"Team message from {player?.Name}: {text}");
    }

    return HookResult.Continue;
}

Programmatic Registration

public override void Load()
{
    Guid chatHookGuid = Core.Command.HookClientChat((playerId, text, teamonly) =>
    {
        Console.WriteLine($"Chat from {playerId}: {text}");
        return HookResult.Continue;
    });

    // Unhook later if needed
    Core.Command.UnhookClientChat(chatHookGuid);
}

Complete Example: Admin Commands

public class AdminCommands
{
    [Command("kick", permission: "admin.kick")]
    [CommandAlias("remove")]
    public void OnKickCommand(ICommandContext context)
    {
        if (!context.IsSentByPlayer)
        {
            context.Reply("This command must be used by a player!");
            return;
        }

        if (context.Args.Length < 1)
        {
            context.Reply("Usage: !kick <player>");
            return;
        }

        var targetName = context.Args[0];
        // Find and kick player logic here
        context.Reply($"Kicked player: {targetName}");
    }

    [Command("ban", permission: "admin.ban")]
    public void OnBanCommand(ICommandContext context)
    {
        if (context.Args.Length < 2)
        {
            context.Reply("Usage: !ban <player> <reason>");
            return;
        }

        var targetName = context.Args[0];
        var reason = string.Join(" ", context.Args.Skip(1));

        context.Reply($"Banned {targetName} for: {reason}");
    }

    [Command("god", permission: "admin.god")]
    public void OnGodCommand(ICommandContext context)
    {
        if (!context.IsSentByPlayer)
        {
            context.Reply("This command can only be used by players!");
            return;
        }

        var player = context.Sender!;
        // Toggle god mode logic here
        context.Reply("God mode toggled!");
    }
}

Complete Example: Chat Filter

[ClientChatHookHandler]
public HookResult OnChatFilter(int playerId, string text, bool teamonly)
{
    var player = Core.PlayerManager.GetPlayer(playerId);
    if (player == null) return HookResult.Continue;

    // Block spam (messages too short)
    if (text.Length < 3)
    {
        player.PrintToChat("Message too short!");
        return HookResult.Stop;
    }

    // Replace bad words
    string[] badWords = { "badword1", "badword2" };
    foreach (var badWord in badWords)
    {
        if (text.Contains(badWord, StringComparison.OrdinalIgnoreCase))
        {
            player.PrintToChat("Please watch your language!");
            return HookResult.Stop;
        }
    }

    // Log all chat
    Console.WriteLine($"[{(teamonly ? "TEAM" : "ALL")}] {player.Controller.PlayerName}: {text}");

    return HookResult.Continue;
}

Attribute Reference

[Command] Attribute

Registers a method as a command handler.

Parameters:

  • name (string): The command name
  • registerRaw (bool, optional): If false, adds sw_ prefix. Default: false
  • permission (string, optional): Required permission. Default: ""

[CommandAlias] Attribute

Adds an alias to a command. Can be used multiple times.

Parameters:

  • alias (string): The alias name
  • registerRaw (bool, optional): If false, adds sw_ prefix. Default: false

[ClientCommandHookHandler] Attribute

Marks a method as a client command hook handler.

Method signature:

HookResult MethodName(int playerId, string commandLine)

[ClientChatHookHandler] Attribute

Marks a method as a client chat hook handler.

Method signature:

HookResult MethodName(int playerId, string text, bool teamonly)

Reference

See ICommandService and ICommandContext for more details.

On this page