Difference between revisions of "ICommandBuilder"

From wiki
Jump to: navigation, search
m (Some changes to spacing.)
(changed command classes to public (Staxel won't be able to access internal classes))
 
(4 intermediate revisions by one other user not shown)
Line 5: Line 5:
 
= The Methods =
 
= The Methods =
 
* <code><nowiki>Kind</nowiki></code> is a public string getter which returns a string containing command itself.
 
* <code><nowiki>Kind</nowiki></code> is a public string getter which returns a string containing command itself.
* <code><nowiki>Usage</nowiki></code> is a public string getter which returns a string containing [[Translations|translation]] key for an explanation on how to use the command. This will be displayed when invoking the <code>/help</code> command.
+
* <code><nowiki>Usage</nowiki></code> is a public string getter which returns a string containing the [[Translations|translation]] key for an explanation on how to use the command. This will be displayed when invoking the <code>/help</code> command.
 
* <code><nowiki>Public</nowiki></code> is a public bool getter which returns if the command can be used by public users (true) or only administrators (false).
 
* <code><nowiki>Public</nowiki></code> is a public bool getter which returns if the command can be used by public users (true) or only administrators (false).
 
* <code><nowiki>Execute</nowiki></code> is a public method which will execute the action of the command itself. It is passed the command string pre-broken into an array of tokens based on using space as a delimiter, the [[DataPacket]] data blob itself, the current [[ClientServerConnection]], and the [[ICommandsApi]] interface. It is expected to return a string containing the [[Translations|translation]] key for the response text to print to the console, as well as to initialize an array of strings in the event that the localized response contains one or more parameters to fill in at run-time. In the event that no parameters are needed, this array should be initialized to a 0-length array of objects.
 
* <code><nowiki>Execute</nowiki></code> is a public method which will execute the action of the command itself. It is passed the command string pre-broken into an array of tokens based on using space as a delimiter, the [[DataPacket]] data blob itself, the current [[ClientServerConnection]], and the [[ICommandsApi]] interface. It is expected to return a string containing the [[Translations|translation]] key for the response text to print to the console, as well as to initialize an array of strings in the event that the localized response contains one or more parameters to fill in at run-time. In the event that no parameters are needed, this array should be initialized to a 0-length array of objects.
Line 20: Line 20:
 
namespace SampleCommands
 
namespace SampleCommands
 
{
 
{
     internal class EchoCommand : ICommandBuilder
+
     public class EchoCommand : ICommandBuilder
 
     {
 
     {
 
         public string Kind => "echo";
 
         public string Kind => "echo";
Line 59: Line 59:
 
As the code implies, the first token in the <code><nowiki>bits</nowiki></code> array is always the command itself. Thus, the processing of command parameters should always begin with the second item in the array (index 1).
 
As the code implies, the first token in the <code><nowiki>bits</nowiki></code> array is always the command itself. Thus, the processing of command parameters should always begin with the second item in the array (index 1).
  
In this example, we simply concatenate each command parameter, inserting a space after each parameter except for the last one. Since the response has one string replacement, we initialize <code><nowiki>responseParams</nowiki></code> to a string array of length 1, and store the concatenated string there. Lastly, since the command cannot fail, we always return the response [[Translations|translation]] key.
+
In this example, we simply concatenate each command parameter, inserting a space after each parameter except for the last one. Since the response has one string replacement, we initialize <code><nowiki>responseParams</nowiki></code> to an object array of length 1, and store the concatenated string there. Lastly, since the command cannot fail, we always return the response [[Translations|translation]] key.
  
 
Here is a screenshot of the results of the above command:
 
Here is a screenshot of the results of the above command:
  
 
[[File:Echo.png]]
 
[[File:Echo.png]]
 +
  
 
= Example: isOnline Command =
 
= Example: isOnline Command =
One of the basic features of the <code><nowiki>ICommandsApi</nowiki></code> API is the ability to look up an [[Entity]] by its ID. In this case, a player Entity is the player's username. This function is, aptly enough, called <code><nowiki>FindPlayerEntityId</nowiki></code>. It accepts a string as its only parameter, and returns an [[Entity]] instance if successful, or Null if not.
+
One of the basic features of the <code><nowiki>ICommandsApi</nowiki></code> API is the ability to look up a player [[EntityId]] by name. This function is, aptly enough, called <code><nowiki>FindPlayerEntityId</nowiki></code>. It accepts a string as its only parameter, and returns an [[EntityId]] if successful, or Null if not.
  
 
Using this knowledge as well as the previous example, we can write a simple command to check if a player is online:
 
Using this knowledge as well as the previous example, we can write a simple command to check if a player is online:
Line 77: Line 78:
 
namespace SampleCommands
 
namespace SampleCommands
 
{
 
{
     internal class EchoCommand : ICommandBuilder
+
     public class IsOnlineCommand : ICommandBuilder
 
     {
 
     {
 
         public string Kind => "isOnline";
 
         public string Kind => "isOnline";
Line 126: Line 127:
  
 
[[File:isOnline.png]]
 
[[File:isOnline.png]]
 +
 +
 +
= Example: myInfo Command =
 +
One of the more esoteric parameters to <code><nowiki>Execute</nowiki></code>, yet still useful, is the current [[ClientServerConnection]] instance. The vast majority of the class's fields are private, and so are inaccessible, and the lion's share of methods aren't necessary to call. However, for fetching information about the current user, it is rather useful indeed. The field, [[PublicCredentials]] <code><nowiki>Credentials</nowiki></code> contains various useful pieces of information about the current user, including the user's nickname, in the <code><nowiki>Username</nowiki></code> field.
 +
 +
That said, <code><nowiki>PublicCredentials</nowiki></code> is a member of the <code><nowiki>Staxel.Auth</nowiki></code> assembly, and as such, will not be available if following the existing (as of 2nd February 2018) instructions on the [[Code_Based_Mod|Code-Based Mod]] page. To add a reference to the assembly, follow the same instructions on that page as you did for Staxel.dll and Plukit.Base.dll, but this time, add Staxel.Auth.dll as well. This will allow Visual Studio (or your IDE of choice) to understand references to the <code><nowiki>PublicCredentials</nowiki></code> class.
 +
 +
With all that out of the way, let's make a simple command that prints out the current user's nickname, and the date and time of the user's last login. Note that unless this user has previously connected to a non-local server, the date and time will instead indicate when the user's local profile was created.
 +
 +
<pre>
 +
using Plukit.Base;
 +
using Staxel.Commands;
 +
using Staxel.Server;
 +
 +
namespace SampleCommands
 +
{
 +
    public class MyInfoCommand : ICommandBuilder
 +
    {
 +
        public string Kind => "myInfo";
 +
 +
        public string Usage => "mods.sampleCommands.myInfo.description";
 +
 +
        public bool Public => true;
 +
 +
        public string Execute(string[] bits, Blob blob, ClientServerConnection connection, ICommandsApi api, out object[] responseParams) {
 +
            // Perform basic validation of our (lack of) parameters.
 +
            if (bits.Length > 1) {
 +
                responseParams = new object[0];
 +
                return "mods.sampleCommands.myInfo.description";
 +
            }
 +
 +
            responseParams = new object[3]; // We're going to have 3 parameters to replace in our translation key: Username, LastLogin time, and LastLogin date.
 +
            responseParams[0] = connection.Credentials.Username; // Copy the username into the replacement parameter.
 +
            responseParams[1] = connection.Credentials.LastLogin.ToShortTimeString();
 +
            responseParams[2] = connection.Credentials.LastLogin.ToShortDateString();
 +
 +
            return "mods.sampleCommands.myInfo.response";
 +
        }
 +
    }
 +
}
 +
</pre>
 +
 +
 +
Here is the updated [[Translations|.lang]] file, containing the translation keys for all of the above commands. Note the grand total of *three* replacement parameters in the response key:
 +
 +
<pre>
 +
language.code=en-GB
 +
language=English
 +
 +
mods.sampleCommands.echo.description=^c:F2E200;echo ^c:FFFFFF;(optional)parameters
 +
mods.sampleCommands.echo.response=You wrote: {0-bits}
 +
mods.sampleCommands.isOnline.description=^c:F2E200;isOnline ^c:FFFFFF;player
 +
mods.sampleCommands.isOnline.playerIsOnline={0-player} is connected.
 +
mods.sampleCommands.isOnline.playerIsOffline={0-player} is not connected.
 +
mods.sampleCommands.myInfo.description=^c:F2E200;myInfo ^c:FFFFFF;(no parameters)
 +
mods.sampleCommands.myInfo.response=Username: '{0-username}', Last login: {1-time} on {2-date}.
 +
</pre>
 +
 +
 +
Once more, here is a screenshot of the new /myInfo command in action:
 +
 +
[[File:MyInfo.png]]

Latest revision as of 11:13, 6 February 2018

ICommandBuilder is the basic interface for server-side commands. On startup, Staxel will use the C# class loader to obtain a list of all classes which implement the ICommandBuilder interface. Therefore, no special code is necessary in order to register your command or commands: Simply ensure that each class implements ICommandBuilder, fill out the appropriate methods, and Staxel will handle the rest.


The Methods

  • Kind is a public string getter which returns a string containing command itself.
  • Usage is a public string getter which returns a string containing the translation key for an explanation on how to use the command. This will be displayed when invoking the /help command.
  • Public is a public bool getter which returns if the command can be used by public users (true) or only administrators (false).
  • Execute is a public method which will execute the action of the command itself. It is passed the command string pre-broken into an array of tokens based on using space as a delimiter, the DataPacket data blob itself, the current ClientServerConnection, and the ICommandsApi interface. It is expected to return a string containing the translation key for the response text to print to the console, as well as to initialize an array of strings in the event that the localized response contains one or more parameters to fill in at run-time. In the event that no parameters are needed, this array should be initialized to a 0-length array of objects.


Example: Echo Command

One of the most basic things that one can do is implement a command which simply echoes back what was typed to the user who invoked the command. Here is the code to do exactly that:

using Plukit.Base;
using Staxel.Commands;
using Staxel.Server;

namespace SampleCommands
{
    public class EchoCommand : ICommandBuilder
    {
        public string Kind => "echo";

        public string Usage => "mods.sampleCommands.echo.description";

        public bool Public => true;

        public string Execute(string[] bits, Blob blob, ClientServerConnection connection, ICommandsApi api, out object[] responseParams)
        {
            responseParams = new object[1];
            string concatenatedBits = "";
            for (int i = 1; i < bits.Length; i++)
            {
                concatenatedBits += bits[i];
                if (i < (bits.Length - 1))
                    concatenatedBits += " ";
            }
            responseParams[0] = concatenatedBits;
            return "mods.sampleCommands.echo.response";
        }
    }
}


Likewise, here is the corresponding .lang file:

language.code=en-GB
language=English

mods.sampleCommands.echo.description=^c:F2E200;echo ^c:FFFFFF;(optional)parameters
mods.sampleCommands.echo.response=You wrote: {0-bits}


As the code implies, the first token in the bits array is always the command itself. Thus, the processing of command parameters should always begin with the second item in the array (index 1).

In this example, we simply concatenate each command parameter, inserting a space after each parameter except for the last one. Since the response has one string replacement, we initialize responseParams to an object array of length 1, and store the concatenated string there. Lastly, since the command cannot fail, we always return the response translation key.

Here is a screenshot of the results of the above command:

Echo.png


Example: isOnline Command

One of the basic features of the ICommandsApi API is the ability to look up a player EntityId by name. This function is, aptly enough, called FindPlayerEntityId. It accepts a string as its only parameter, and returns an EntityId if successful, or Null if not.

Using this knowledge as well as the previous example, we can write a simple command to check if a player is online:

using Plukit.Base;
using Staxel.Commands;
using Staxel.Server;

namespace SampleCommands
{
    public class IsOnlineCommand : ICommandBuilder
    {
        public string Kind => "isOnline";

        public string Usage => "mods.sampleCommands.isOnline.description";

        public bool Public => true;

        public string Execute(string[] bits, Blob blob, ClientServerConnection connection, ICommandsApi api, out object[] responseParams) {
            // Perform basic validation of our parameters.
            if (bits.Length < 2 || bits.Length > 2) {
                responseParams = new object[0];
                return "mods.sampleCommands.isOnline.description";
            }

            responseParams = new object[1]; // We're going to have one replacement parameter in our translation key.
            responseParams[0] = bits[1]; // Copy the username into the replacement parameter.

            EntityId id = api.FindPlayerEntityId(bits[1]); // Attempt to find the player.
            if (id.IsNull()) {
                // If we were not able to look up an Entity by that ID, the player is not connected.
                return "mods.sampleCommands.isOnline.playerIsOffline";
            } else {
                // Otherwise, the player is connected.
                return "mods.sampleCommands.isOnline.playerIsOnline";
            }
        }
    }
}


Here is the updated .lang file, containing the translation keys for both the "echo" command as well as our new "isOnline" command:

language.code=en-GB
language=English

mods.sampleCommands.echo.description=^c:F2E200;echo ^c:FFFFFF;(optional)parameters
mods.sampleCommands.echo.response=You wrote: {0-bits}
mods.sampleCommands.isOnline.description=^c:F2E200;isOnline ^c:FFFFFF;player
mods.sampleCommands.isOnline.playerIsOnline={0-player} is connected.
mods.sampleCommands.isOnline.playerIsOffline={0-player} is not connected.


Lastly, here is a screenshot of our new command in action - in particular, it demonstrates the ability to handle a username with spaces, as long as it is enclosed in quotes:

IsOnline.png


Example: myInfo Command

One of the more esoteric parameters to Execute, yet still useful, is the current ClientServerConnection instance. The vast majority of the class's fields are private, and so are inaccessible, and the lion's share of methods aren't necessary to call. However, for fetching information about the current user, it is rather useful indeed. The field, PublicCredentials Credentials contains various useful pieces of information about the current user, including the user's nickname, in the Username field.

That said, PublicCredentials is a member of the Staxel.Auth assembly, and as such, will not be available if following the existing (as of 2nd February 2018) instructions on the Code-Based Mod page. To add a reference to the assembly, follow the same instructions on that page as you did for Staxel.dll and Plukit.Base.dll, but this time, add Staxel.Auth.dll as well. This will allow Visual Studio (or your IDE of choice) to understand references to the PublicCredentials class.

With all that out of the way, let's make a simple command that prints out the current user's nickname, and the date and time of the user's last login. Note that unless this user has previously connected to a non-local server, the date and time will instead indicate when the user's local profile was created.

using Plukit.Base;
using Staxel.Commands;
using Staxel.Server;

namespace SampleCommands
{
    public class MyInfoCommand : ICommandBuilder
    {
        public string Kind => "myInfo";

        public string Usage => "mods.sampleCommands.myInfo.description";

        public bool Public => true;

        public string Execute(string[] bits, Blob blob, ClientServerConnection connection, ICommandsApi api, out object[] responseParams) {
            // Perform basic validation of our (lack of) parameters.
            if (bits.Length > 1) {
                responseParams = new object[0];
                return "mods.sampleCommands.myInfo.description";
            }

            responseParams = new object[3]; // We're going to have 3 parameters to replace in our translation key: Username, LastLogin time, and LastLogin date.
            responseParams[0] = connection.Credentials.Username; // Copy the username into the replacement parameter.
            responseParams[1] = connection.Credentials.LastLogin.ToShortTimeString();
            responseParams[2] = connection.Credentials.LastLogin.ToShortDateString();

            return "mods.sampleCommands.myInfo.response";
        }
    }
}


Here is the updated .lang file, containing the translation keys for all of the above commands. Note the grand total of *three* replacement parameters in the response key:

language.code=en-GB
language=English

mods.sampleCommands.echo.description=^c:F2E200;echo ^c:FFFFFF;(optional)parameters
mods.sampleCommands.echo.response=You wrote: {0-bits}
mods.sampleCommands.isOnline.description=^c:F2E200;isOnline ^c:FFFFFF;player
mods.sampleCommands.isOnline.playerIsOnline={0-player} is connected.
mods.sampleCommands.isOnline.playerIsOffline={0-player} is not connected.
mods.sampleCommands.myInfo.description=^c:F2E200;myInfo ^c:FFFFFF;(no parameters)
mods.sampleCommands.myInfo.response=Username: '{0-username}', Last login: {1-time} on {2-date}.


Once more, here is a screenshot of the new /myInfo command in action:

MyInfo.png