Commands

From LugdunonWiki
Jump to: navigation, search
It has been suggested that this page/section be rewritten.
Reason: "Exact application of the information is vague"
Engine Content: This information applies to the engine and therefore to all servers.

Contents

Introduction

In Lugdunon, commands are what drive the game state. Any action that occurs should result in a command being generated to be handled in the update loop. Commands are also the method in which the client and server communicate. Commands are specific to a given server, and are loaded from the classpath based on their inclusion in the WORLD/etc/commands.json file.


Commands are referenced in code by their unique command id. Programmatically, commands are referenced by a short int opcode, meaning that commands are limited to at most 215-1 (32767). With the exception of of net.lugdunon.command.core.ErrorCommand, net.lugdunon.command.core.GetServerStatusCommand, and net.lugdunon.command.core.ConnectToServerCommand the opcode for a command is dynamically assigned at server start and should never be relied upon to remain the same value across server instances.


A command exists as a single instance, loaded and initialized upon server start for server-side commands and upon client connect for client-side commands. Commands are referenced in code via their command id, a unique string that identifies a specific command. How to call commands on the server and client will be examined towards the end of this article.

A quick word about DataView, EnhancedDataInput/OutputStream, and String length

All traffic that passes between the client and server via commands is packaged and unpackaged using an extended version of the default DataView on the client and extended versions of the java.io.DataInputStream and java.io.DataOutputStream on the server side. The enhancements amount to providing an analogue of the java data io functionality in the client as well as providing a method for writing UTF strings with lengths > 216-1. Listed below are the mappings of calls between the client and the server:


Client (Dataview) Server (net.lugdunon.io.EnhancedCharacterInputStream)
readInt8() readByte()
readUint8() readUnsignedByte()
readInt16() readShort()
readUint16() readUnsignedShort()
readInt32() readInt()
readUint32() readInt()
readFloat32() readFloat()
readFloat64() readDouble()
readBoolean() readBoolean()
readString() readUTF()
readLargeString() readLargeUTF()


Client (Dataview) Server (net.lugdunon.io.EnhancedCharacterOutputStream)
writeInt8() writeByte()
writeUint8() writeUnsignedByte()
writeInt16() writeShort()
writeUint16() writeUnsignedShort()
writeInt32() writeInt()
writeUint32() writeInt()
writeFloat32() writeFloat()
writeFloat64() writeDouble()
writeBoolean() writeBoolean()
writeString() writeUTF()
writeLargeString() writeLargeUTF()


One last thing to note. When determining the actual length (in bytes) of a string as it is written using the DataView.writeString() and DataView.writeLargeString() it is necessary to use the String.lengthInBytes() and String.largeLengthInBytes() functions to determine the actual length.

Client Side Command API

On the client-side, commands extend the net.lugdunon.command.core.Command javascript class. There are a few default functions that require overriding:


  1. /**
  2.  * Accepts a parameter containing any data used to populate this command call.
  3.  *
  4.  * @param {Object} initData
  5.  */
  6. net.lugdunon.command.core.Command.prototype.opInit=function(initData){};
  7.  
  8. /**
  9.  * Returns the length (in bytes) of the command call's data.
  10.  *
  11.  * @returns {Number}
  12.  */
  13. net.lugdunon.command.core.Command.prototype.getCommandLength=function(){return(0);};
  14.  
  15. /**
  16.  * Where the command call's data is actually written to the dataView object passed in.
  17.  *
  18.  * @param {DataView} dataView
  19.  */
  20. net.lugdunon.command.core.Command.prototype.buildCommand=function(dataView){};
  21.  
  22. /**
  23.  * Where a command that has come in from the server is processed. The response parameter is a DataView object for reading in the response data.
  24.  *
  25.  * @param {DataView} response
  26.  */
  27. net.lugdunon.command.core.Command.prototype.commandReponse=function(response){};

Server Side Command API

On the server-side, commands extend the net.lugdunon.command.core.Command java class. There are a few abstract methods that require implementing:


  1. /**
  2.  * Returns the length (in bytes) of the command call's data.
  3.  *
  4.  * @return {int}
  5.  */
  6. public abstract int getCommandLength();
  7.  
  8. /**
  9.  * Returns the unique string value that identifies this command.
  10.  *
  11.  * @return {String}
  12.  */
  13. public abstract String getCommandId();
  14.  
  15. /**
  16.  * Returns a string containing the display name of the command.
  17.  *
  18.  * @return {String}
  19.  */
  20. public abstract String getName();
  21.  
  22. /**
  23.  * Returns a string containing a short description of the command.
  24.  *
  25.  * @return {String}
  26.  */
  27. public abstract String getDescription();
  28.  
  29. /**
  30.  * Returns the true if the command has a client side counterpart.
  31.  * The server will not look for a client-side asset for this command when sending the commands code to the client on initial connect.
  32.  *
  33.  * @return {boolean}
  34.  */
  35. public abstract boolean hasClientSide();
  36.  
  37. /**
  38.  * Handles the actual processing of the command and its data.
  39.  *
  40.  * @param request - Provides access to the EnhancedDataInputStream via getData() and (if the command originated from a client) the CommandWebSocket where the command originated via getOrigin().
  41.  *
  42.  * @throws IOException
  43.  * @throws ConsoleFiredCommandNotSupportedException
  44.  */
  45. public abstract void handle(CommandRequest request) throws IOException, ConsoleFiredCommandNotSupportedException;

Console Fired Commands

Console fired commands are a special subset of commands that are initiated via the client-side console. Console commands consist of an initial command identifier, called an opCodeAlias, that always begins with a ‘/’ character. For a full listing of console commands, check the Console Commands section. On the client-side, commands that are capable of being fired via the console must extend the net.lugdunon.command.core.console.ConsoleFiredCommand class and override the following functions:


  1. /**
  2.  * Evaluates the passed in opCodeAlias and determines if this command is a match.
  3.  *
  4.  * For Example, if the console line is '/df impassable' the value of opCodeAlias will be 'df'.
  5.  *
  6.  * @param opCodeAlias - the opCodeAlias, minus the '/' to be checked.
  7.  * @returns {Boolean} - true if the opCodeAlias matches this command.
  8.  */
  9. net.lugdunon.command.core.console.ConsoleFiredCommand.prototype.matchesConsoleOpCodeAlias=function(opCodeAlias)
  10. {
  11.         return(false);
  12. };
  13.  
  14. /**
  15.  * If this command is to be handled on the client side, then this function must be implemented.
  16.  * It should evaluate the passed in data as the console line minus the command opCodeAlias.
  17.  *
  18.  * For Example, if the console line is '/df impassable' the value of consoleMessage will be 'impassable'.
  19.  *
  20.  * @param consoleMessage - the console message minus the opCodeAlias
  21.  */
  22. net.lugdunon.command.core.console.ConsoleFiredCommand.prototype.handle=function(consoleMessage)
  23. {
  24.         ;
  25. };


On the server-side, commands that are capable of being fired via the console must extend the net.lugdunon.command.core.console.ConsoleFiredCommand abstract class and implement the following functions:


  1. /**
  2.  * Evaluates the passed in opCodeAlias and determines if this command is a match.
  3.  *
  4.  * For Example, if the console line is '/df impassable' the value of opCodeAlias will be 'df'.
  5.  *
  6.  * @param {String} opCodeAlias - the opCodeAlias, minus the '/' to be checked.
  7.  * @returns {Boolean} - true if the opCodeAlias matches this command.
  8.  */
  9. public abstract boolean matchesConsoleOpCodeAlias(String opCodeAlias);
  10.  
  11. /**
  12.   * Evaluates the passed in data as the console line minus the command opCodeAlias.
  13.   *
  14.   * For Example, if the console line is '/df impassable' the value of consoleMessage will be 'impassable'.
  15.   *
  16.   * @param {CommandRequest} request - the request where this command originated.
  17.   * @param {String} consoleMessage - the console message minus the opCodeAlias.
  18.   */
  19. public abstract void handle(CommandRequest request, String consoleMessage) throws IOException;

GM Only Commands

GM only commands are commands that are executable by a player only if they have GM status. Such a command needs but to extend the net.lugdunon.command.core.gm.GmOnlyCommand java class instead of net.lugdunon.command.core.Command. And implement the handleAsOp(CommandRequest request) method instead of the handle(CommandRequest request).The client is agnostic on this point so nothing needs to be done there.


  1. /**
  2.  * Commands that are restricted to players that have GM privileges should implement this method
  3.  * instead of handle(CommandRequest request). This method will only be called if the player has
  4.  * GM privileges.
  5.  *
  6.  * @param request - Provides access to the EnhancedDataInputStream via getData() and (if the
  7.  * command originated from a client) the CommandWebSocket where the command originated via getOrigin().
  8.  *
  9.  * @throws IOException
  10.  */
  11. public abstract void handleAsOp(CommandRequest request) throws IOException;

Server Invoked Commands

If a command can be invoked autonomously by the server, or in the course of processing another command, you can implement the net.lugdunon.command.core.IServerInvokedCommand interface which requires the implementation of the following method:


If a command can only be invoked on the server-side, simply leave the handle(CommandRequest request) devoid of logic.


Invoking a server-side command is fairly straightforward. Commands need to be added to the incoming request queue in order to be processed. The net.lugdunon.Game class provides three overloaded instances of the addIncomingRequest() method for this purpose. Access to the singleton instance of the game class can be accomplished by invoking the following method chain State.instance().getGame().


  1. /**
  2.  * Adds a command request to the incoming request queue, where it will be processed in the next update cycle.
  3.  *
  4.  * This particular instance of the addIncomingRequest method is generally reserved for the engine to add an incoming request originated from a client process.
  5.  * For server invoked commands, the other two instances are generally preferred.
  6.  *
  7.  * @param incomingRequest {CommandRequest} the incoming CommandRequest object.
  8.  */
  9. public synchronized void addIncomingRequest(CommandRequest incomingRequest)
  10.  
  11. /**
  12.  * Adds a command request to the incoming request queue, where it will be processed in the next update cycle.
  13.  *
  14.  * This instance of the addIncomingRequest method takes a command id as its only parameter, and should be used when a server invoked command requires no accompanying data.
  15.  *
  16.  * @param commandId {String} the command id of the command to be called when this request is processed.
  17.  *
  18.  * @throws CommandNotSupportedException
  19.  * @throws IsNotServerInvokedCommand
  20.  */
  21. public synchronized void addIncomingRequest(String commandId) throws CommandNotSupportedException, IsNotServerInvokedCommand
  22.  
  23. /**
  24.  * Adds a command request to the incoming request queue, where it will be processed in the next update cycle.
  25.  *
  26.  * This instance of the addIncomingRequest method takes a command id as its only parameter, and should be used when a server invoked command requires accompanying data.
  27.  *
  28.  * @param commandId {String} the command id of the command to be called when this request is processed.
  29.  * @param props {CommandProperties} the command properties object that contains all relevant data needed by the command.
  30.  *
  31.  * @throws CommandNotSupportedException
  32.  * @throws IsNotServerInvokedCommand
  33.  */
  34. public synchronized void addIncomingRequest(String commandId, CommandProperties props) throws CommandNotSupportedException, IsNotServerInvokedCommand


Commands usually require data to process. With server invoked commands, this is accomplished via the net.lugdunon.command.CommandProperties object. A command can then be executed in server-side code like the following example to move a character to tile (0,0):


  1. // account defined previously
  2. // create a new instance of command properties
  3. CommandProperties props=new CommandProperties();
  4.  
  5. // set either the account or character property, depending on the account's game mode
  6. if(account.getGameMode() == Account.MODE_EDIT)
  7. {
  8.         props.setAccount("account",account);
  9. }
  10. else
  11. {
  12.         props.setCharacter("character",account.getActiveCharacter());
  13. }
  14.  
  15. // tell the client that the viewport is in need of updating
  16. props.setBoolean("viewportChanged",true);
  17.  
  18. // set the coordinates for the destination
  19. props.setPoint("location",new Point(0,0));
  20.  
  21. try
  22. {
  23.     // add the 'move player' command request to the incoming request queue for processing
  24.     State.instance().getGame().addIncomingRequest(
  25.         "CORE.COMMAND.PLAYER.MOVE",
  26.         props
  27.     );
  28. }
  29. catch(CommandNotSupportedException e)
  30. {
  31.     e.printStackTrace();
  32. }
  33. catch(IsNotServerInvokedCommand e)
  34. {
  35.     e.printStackTrace();
  36. }

Invoking Commands on the Client

Client-side commmand requests are first built, and then sent to the server in a separate function call. The two functions in question belong to the net.lugdunon.command.Client class. The client singleton instance can be referenced at game.client.


  1. /**
  2.  * Builds a command, and returns the resulting ArrayBuffer object.
  3.  *
  4.  * @param commandId {String} the command id of the command to process.
  5.  * @param data {Object} and object containing any data needed by the command. Passed to the Command.opInit(data) function.
  6.  *
  7.  * @return {ArrayBuffer}
  8.  */
  9. this.buildCommand=function(commandId,data);
  10.  
  11. /**
  12.  * Sends a command to the server.
  13.  *
  14.  * @param command {ArrayBuffer} the command data to send to the server.
  15.  */
  16. this.sendCommand=function(command);


For instance, to call the ‘consume food’ command with the instruction to consume the food located in the 8th action bar item from the left you might use the following code.


  1. // build command
  2. var command=game.client.buildCommand(
  3.         "CORE.COMMAND.CONSUME.FOOD",
  4.         {actionBarIndex:7}
  5. );
  6.  
  7. // send command for processing
  8. game.client.sendCommand(command);