mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2024-11-12 18:57:16 +01:00
4ad9402d8a
Also keep track of all listener instances by having them link to eachother as a double linked list. This should make it easy to release any script resources at shutdown. Now that all listeners are being tracked, binding callbacks is less strict and does not require a listener to be attached anymore.
2008 lines
69 KiB
C++
2008 lines
69 KiB
C++
#ifndef _COMMAND_HPP_
|
|
#define _COMMAND_HPP_
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
#include "Base/Shared.hpp"
|
|
#include "Base/Buffer.hpp"
|
|
#include "Logger.hpp"
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
#include <cctype>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <map>
|
|
#include <vector>
|
|
#include <iterator>
|
|
#include <algorithm>
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
namespace SqMod {
|
|
namespace Cmd {
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* Forward declarations.
|
|
*/
|
|
class Context;
|
|
class Guard;
|
|
class Command;
|
|
class Controller;
|
|
class Manager;
|
|
class Listener;
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* Helper typedefs.
|
|
*/
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
typedef SharedPtr< Context > CtxRef; // Shared reference to an execution context.
|
|
typedef WeakPtr< Context > CtxPtr; // Shared reference to an execution context.
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
typedef SharedPtr< Controller > CtrRef; // Shared reference to a command controller.
|
|
typedef WeakPtr< Controller > CtrPtr; // Shared reference to a command controller.
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
typedef std::pair< Uint8, Object > Argument; // Can hold the argument value and type.
|
|
typedef std::vector< Argument > Arguments; // A list of extracted arguments.
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
typedef std::vector< Command > Commands; // List of attached command instances.
|
|
typedef std::vector< Controller * > Controllers; // List of active controllers.
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* Types of arguments supported by the command system.
|
|
*/
|
|
enum CmdArgType
|
|
{
|
|
CMDARG_ANY = 0,
|
|
CMDARG_INTEGER = (1 << 1),
|
|
CMDARG_FLOAT = (1 << 2),
|
|
CMDARG_BOOLEAN = (1 << 3),
|
|
CMDARG_STRING = (1 << 4),
|
|
CMDARG_LOWER = (1 << 5),
|
|
CMDARG_UPPER = (1 << 6),
|
|
CMDARG_GREEDY = (1 << 7)
|
|
};
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* Types of errors reported by the command system.
|
|
*/
|
|
enum CmdError
|
|
{
|
|
// The command failed for unknown reasons
|
|
CMDERR_UNKNOWN = 0,
|
|
// The command failed to execute because there was nothing to execute
|
|
CMDERR_EMPTY_COMMAND,
|
|
// The command failed to execute because the command name was invalid after processing
|
|
CMDERR_INVALID_COMMAND,
|
|
// The command failed to execute because there was a syntax error in the arguments
|
|
CMDERR_SYNTAX_ERROR,
|
|
// The command failed to execute because there was no such command
|
|
CMDERR_UNKNOWN_COMMAND,
|
|
// The command failed to execute because the it's currently suspended
|
|
CMDERR_COMMAND_SUSPENDED,
|
|
// The command failed to execute because the invoker does not have the proper authority
|
|
CMDERR_INSUFFICIENT_AUTH,
|
|
// The command failed to execute because there was no callback to handle the execution
|
|
CMDERR_MISSING_EXECUTER,
|
|
// The command was unable to execute because the argument limit was not reached
|
|
CMDERR_INCOMPLETE_ARGS,
|
|
// The command was unable to execute because the argument limit was exceeded
|
|
CMDERR_EXTRANEOUS_ARGS,
|
|
// Command was unable to execute due to argument type mismatch
|
|
CMDERR_UNSUPPORTED_ARG,
|
|
// The command arguments contained more data than the internal buffer can handle
|
|
CMDERR_BUFFER_OVERFLOW,
|
|
// The command failed to complete execution due to a runtime exception
|
|
CMDERR_EXECUTION_FAILED,
|
|
// The command completed the execution but returned a negative result
|
|
CMDERR_EXECUTION_ABORTED,
|
|
// The post execution callback failed to execute due to a runtime exception
|
|
CMDERR_POST_PROCESSING_FAILED,
|
|
// The callback that was supposed to deal with the failure also failed due to a runtime exception
|
|
CMDERR_UNRESOLVED_FAILURE,
|
|
// Maximum command error identifier
|
|
CMDERR_MAX
|
|
};
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
inline CSStr ValidateName(CSStr name)
|
|
{
|
|
// Is the name empty?
|
|
if (!name || *name == '\0')
|
|
{
|
|
STHROWF("Invalid or empty command name");
|
|
}
|
|
// Create iterator to name start
|
|
CSStr str = name;
|
|
// Inspect name characters
|
|
while (*str != '\0')
|
|
{
|
|
// Does it contain spaces?
|
|
if (std::isspace(*str) != 0)
|
|
{
|
|
STHROWF("Command names cannot contain spaces");
|
|
}
|
|
// Move to the next character
|
|
++str;
|
|
}
|
|
// Return the name
|
|
return name;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
inline CSStr ArgSpecToStr(Uint8 spec)
|
|
{
|
|
switch (spec)
|
|
{
|
|
case CMDARG_ANY: return _SC("any");
|
|
case CMDARG_INTEGER: return _SC("integer");
|
|
case CMDARG_FLOAT: return _SC("float");
|
|
case CMDARG_BOOLEAN: return _SC("boolean");
|
|
case CMDARG_STRING:
|
|
case CMDARG_LOWER:
|
|
case CMDARG_UPPER:
|
|
case CMDARG_GREEDY: return _SC("string");
|
|
default: return _SC("unknown");
|
|
}
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* Holds the context of a command execution.
|
|
*/
|
|
struct Context
|
|
{
|
|
public:
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
Buffer mBuffer; // Shared buffer used to extract arguments and process data.
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
const Object mInvoker; // Reference to the entity that invoked the command.
|
|
String mCommand; // Command name extracted from the command string.
|
|
String mArgument; // Command argument extracted from the command string.
|
|
Listener* mInstance; // Pointer to the currently executed command listener.
|
|
Object mObject; // Script object of the currently executed command.
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
Arguments mArgv; // Extracted command arguments.
|
|
Uint32 mArgc; // Extracted arguments count.
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Default constructor.
|
|
*/
|
|
Context(Object & invoker)
|
|
: mBuffer(512)
|
|
, mInvoker(invoker)
|
|
, mCommand()
|
|
, mArgument()
|
|
, mInstance(nullptr)
|
|
, mObject()
|
|
, mArgv()
|
|
, mArgc(0)
|
|
{
|
|
// Reserve enough space upfront
|
|
mCommand.reserve(64);
|
|
mArgument.reserve(512);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Copy constructor. (disabled)
|
|
*/
|
|
Context(const Context & o) = delete;
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Move constructor. (disabled)
|
|
*/
|
|
Context(Context && o) = delete;
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Copy assignment operator. (disabled)
|
|
*/
|
|
Context & operator = (const Context & o) = delete;
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Move assignment operator. (disabled)
|
|
*/
|
|
Context & operator = (Context && o) = delete;
|
|
};
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* Helper class implementing RAII to release the command context.
|
|
*/
|
|
struct Guard
|
|
{
|
|
public:
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
CtrRef mController; // Reference to the guarded controller.
|
|
CtxRef mPrevious; // Previous context when this guard was created.
|
|
CtxRef mCurrent; // The context managed by this guard.
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Default constructor.
|
|
*/
|
|
Guard(const CtrRef & ctr, Object & invoker);
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Copy constructor.
|
|
*/
|
|
Guard(const Guard & o) = delete;
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Move constructor.
|
|
*/
|
|
Guard(Guard && o) = delete;
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Destructor.
|
|
*/
|
|
~Guard();
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Copy assignment operator.
|
|
*/
|
|
Guard & operator = (const Guard & o) = delete;
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Move assignment operator.
|
|
*/
|
|
Guard & operator = (Guard && o) = delete;
|
|
};
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* Structure that represents a unique command in the pool.
|
|
*/
|
|
struct Command
|
|
{
|
|
// --------------------------------------------------------------------------------------------
|
|
std::size_t mHash; // The unique hash that identifies this command.
|
|
String mName; // The unique name that identifies this command.
|
|
Listener* mPtr; // The listener that reacts to this command.
|
|
Object mObj; // A strong reference to the script object.
|
|
CtrPtr mCtr; // The associated contoller.
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Construct a command and the also create a script object from the specified listener.
|
|
*/
|
|
Command(std::size_t hash, const String & name, Listener * ptr, const CtrPtr & ctr);
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Construct a command and extract the listener from the specified script object.
|
|
*/
|
|
Command(std::size_t hash, const String & name, const Object & obj, const CtrPtr & ctr);
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Construct a command and extract the listener from the specified script object.
|
|
*/
|
|
Command(std::size_t hash, const String & name, Object && obj, const CtrPtr & ctr);
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Construct a command with the given parameters.
|
|
*/
|
|
Command(std::size_t hash, const String & name, Listener * ptr, const Object & obj, const CtrPtr & ctr);
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Construct a command with the given parameters.
|
|
*/
|
|
Command(std::size_t hash, const String & name, Listener * ptr, Object && obj, const CtrPtr & ctr);
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Copy constructor.
|
|
*/
|
|
Command(const Command & o) = delete;
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Move constructor.
|
|
*/
|
|
Command(Command && o)
|
|
: mHash(o.mHash)
|
|
, mName(std::move(o.mName))
|
|
, mPtr(o.mPtr)
|
|
, mObj(o.mObj)
|
|
, mCtr(o.mCtr)
|
|
{
|
|
o.mPtr = nullptr;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Destructor.
|
|
*/
|
|
~Command();
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Copy assignment operator.
|
|
*/
|
|
Command & operator = (const Command & o) = delete;
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Move assignment operator.
|
|
*/
|
|
Command & operator = (Command && o)
|
|
{
|
|
if (this != &o)
|
|
{
|
|
mHash = o.mHash;
|
|
mName = std::move(o.mName);
|
|
mPtr = o.mPtr;
|
|
mObj = o.mObj;
|
|
mCtr = o.mCtr;
|
|
o.mPtr = nullptr;
|
|
}
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* Holds a list of commands to execute as well as authentication or failure resolvers.
|
|
*/
|
|
struct Controller
|
|
{
|
|
// --------------------------------------------------------------------------------------------
|
|
friend class Guard; // Allow the guard to swap the execution context.
|
|
friend class Manager; // Allow the manager to manage the controller.
|
|
friend class Listener; // Allow the listener to attach and detach.
|
|
|
|
private:
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
Commands m_Commands; // List of available command instances.
|
|
CtxRef m_Context; // Context of the currently executed command.
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
Function m_OnFail; // Callback when something failed while running a command.
|
|
Function m_OnAuth; // Callback to authenticate execution for a certain invoker.
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
Manager * m_Manager;
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
static Controllers s_Controllers;
|
|
|
|
protected:
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Default constructor.
|
|
*/
|
|
Controller(Manager * mgr)
|
|
: m_Commands()
|
|
, m_Context()
|
|
, m_OnFail()
|
|
, m_OnAuth()
|
|
, m_Manager(mgr)
|
|
{
|
|
s_Controllers.push_back(this);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Copy constructor. (disabled)
|
|
*/
|
|
Controller(const Controller & o) = delete;
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Move constructor. (disabled)
|
|
*/
|
|
Controller(Controller && o) = delete;
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Copy assignment operator. (disabled)
|
|
*/
|
|
Controller & operator = (const Controller & o) = delete;
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Move assignment operator. (disabled)
|
|
*/
|
|
Controller & operator = (Controller && o) = delete;
|
|
|
|
protected:
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Forward error message to the error callback.
|
|
*/
|
|
template < typename T > void SqError(Int32 type, CSStr msg, T data)
|
|
{
|
|
// Is there a callback that deals with errors?
|
|
if (m_OnFail.IsNull())
|
|
{
|
|
return;
|
|
}
|
|
// Attempt to forward the error to that callback
|
|
try
|
|
{
|
|
m_OnFail.Execute< Int32, CSStr, T >(type, msg, data);
|
|
}
|
|
catch (const Sqrat::Exception & e)
|
|
{
|
|
// The debugger probably took care of reporting the details
|
|
LogErr("Command error callback failed [%s]", e.what());
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Execute one of the managed commands.
|
|
*/
|
|
Int32 Run(const Guard & guard, CSStr command);
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Attempt to execute the specified command.
|
|
*/
|
|
Int32 Exec(Context & ctx);
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Attempt to parse the specified argument.
|
|
*/
|
|
bool Parse(Context & ctx);
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Attach a command listener to a certain name.
|
|
*/
|
|
Object & Attach(Object & obj, Listener * ptr)
|
|
{
|
|
return Attach(Object(obj), ptr);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Attach a command listener to a certain name.
|
|
*/
|
|
Object & Attach(const Object & obj, Listener * ptr)
|
|
{
|
|
return Attach(Object(obj), ptr);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Attach a command listener to a certain name.
|
|
*/
|
|
Object & Attach(Object && obj, Listener * ptr);
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Detach a command listener from a certain name.
|
|
*/
|
|
void Detach(const String & name)
|
|
{
|
|
// Obtain the unique identifier of the specified name
|
|
const std::size_t hash = std::hash< String >()(name);
|
|
// Iterator to the found command, if any
|
|
Commands::const_iterator itr = m_Commands.cbegin();
|
|
// Attempt to find the specified command
|
|
for (; itr != m_Commands.cend(); ++itr)
|
|
{
|
|
// Are the hashes identical?
|
|
if (itr->mHash == hash)
|
|
{
|
|
break; // We found our command!
|
|
}
|
|
}
|
|
// Make sure the command exist before attempting to remove it
|
|
if (itr != m_Commands.end())
|
|
{
|
|
m_Commands.erase(itr);
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Detach a command listener from a certain name.
|
|
*/
|
|
void Detach(Listener * ptr)
|
|
{
|
|
// Iterator to the found command, if any
|
|
Commands::const_iterator itr = m_Commands.cbegin();
|
|
// Attempt to find the specified command
|
|
for (; itr != m_Commands.cend(); ++itr)
|
|
{
|
|
// Are the instances identical?
|
|
if (itr->mPtr == ptr)
|
|
{
|
|
break; // We found our command!
|
|
}
|
|
}
|
|
// Make sure the command exists before attempting to remove it
|
|
if (itr != m_Commands.end())
|
|
{
|
|
m_Commands.erase(itr);
|
|
}
|
|
}
|
|
|
|
public:
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Terminate the all controllers by releasing their command listeners and callbacks.
|
|
*/
|
|
static void Terminate()
|
|
{
|
|
for (auto & ctr : s_Controllers)
|
|
{
|
|
// Clear the command listeners
|
|
ctr->Clear();
|
|
// Release the script callbacks, if any
|
|
ctr->m_OnFail.ReleaseGently();
|
|
ctr->m_OnAuth.ReleaseGently();
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Destructor.
|
|
*/
|
|
~Controller()
|
|
{
|
|
s_Controllers.erase(std::remove(s_Controllers.begin(), s_Controllers.end(), this),
|
|
s_Controllers.end());
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the currently active execution context.
|
|
*/
|
|
const CtxRef & GetCtx() const
|
|
{
|
|
return m_Context;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* See whether a certain name exist in the command list.
|
|
*/
|
|
bool Attached(const String & name) const
|
|
{
|
|
// Obtain the unique identifier of the specified name
|
|
const std::size_t hash = std::hash< String >()(name);
|
|
// Attempt to find the specified command
|
|
for (const auto & cmd : m_Commands)
|
|
{
|
|
// Are the hashes identical?
|
|
if (cmd.mHash == hash)
|
|
{
|
|
return true; // We found our command!
|
|
}
|
|
}
|
|
// No such command exists
|
|
return false;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* See whether a certain instance exist in the command list.
|
|
*/
|
|
bool Attached(const Listener * ptr) const
|
|
{
|
|
// Attempt to find the specified command
|
|
for (const auto & cmd : m_Commands)
|
|
{
|
|
// Are the instances identical?
|
|
if (cmd.mPtr == ptr)
|
|
{
|
|
return true; // We found our command!
|
|
}
|
|
}
|
|
// No such command exists
|
|
return false;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Sort the command list in an ascending order.
|
|
*/
|
|
void Sort()
|
|
{
|
|
std::sort(m_Commands.begin(), m_Commands.end(),
|
|
[](Commands::const_reference a, Commands::const_reference b) -> bool {
|
|
return (a.mName < b.mName);
|
|
});
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Detach all the associated command listeners.
|
|
*/
|
|
void Clear()
|
|
{
|
|
m_Commands.clear();
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Locate and retrieve a command listener by name.
|
|
*/
|
|
const Object & FindByName(const String & name)
|
|
{
|
|
// Obtain the unique identifier of the specified name
|
|
const std::size_t hash = std::hash< String >()(name);
|
|
// Attempt to find the specified command
|
|
for (const auto & cmd : m_Commands)
|
|
{
|
|
// Are the hashes identical?
|
|
if (cmd.mHash == hash)
|
|
{
|
|
return cmd.mObj; // We found our command!
|
|
}
|
|
}
|
|
// No such command exist
|
|
return NullObject();
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the error callback.
|
|
*/
|
|
Function & GetOnFail()
|
|
{
|
|
return m_OnFail;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Modify the error callback.
|
|
*/
|
|
void SetOnFail(Object & env, Function & func)
|
|
{
|
|
// Are we supposed to unbind current callback?
|
|
if (func.IsNull())
|
|
{
|
|
m_OnFail.ReleaseGently();
|
|
}
|
|
// Was there a custom environment specified?
|
|
else if (env.IsNull())
|
|
{
|
|
m_OnFail = func;
|
|
}
|
|
else
|
|
{
|
|
m_OnFail = Function(env.GetVM(), env.GetObject(), func.GetFunc());
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the authentication callback.
|
|
*/
|
|
Function & GetOnAuth()
|
|
{
|
|
return m_OnAuth;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Modify the authentication callback.
|
|
*/
|
|
void SetOnAuth(Object & env, Function & func)
|
|
{
|
|
// Are we supposed to unbind current callback?
|
|
if (func.IsNull())
|
|
{
|
|
m_OnAuth.ReleaseGently();
|
|
}
|
|
// Was there a custom environment specified?
|
|
if (env.IsNull())
|
|
{
|
|
m_OnAuth = func;
|
|
}
|
|
else
|
|
{
|
|
m_OnAuth = Function(env.GetVM(), env.GetObject(), func.GetFunc());
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* See whether an execution context is currently active.
|
|
*/
|
|
bool IsContext() const
|
|
{
|
|
return !!m_Context;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the invoker from the current execution context.
|
|
*/
|
|
const Object & GetInvoker() const
|
|
{
|
|
// See if there's an execution context available
|
|
if (!m_Context)
|
|
{
|
|
STHROWF("No active execution context");
|
|
}
|
|
// Return the requested information
|
|
return m_Context->mInvoker;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the listener object from the current execution context.
|
|
*/
|
|
const Object & GetListener() const
|
|
{
|
|
// See if there's an execution context available
|
|
if (!m_Context)
|
|
{
|
|
STHROWF("No active execution context");
|
|
}
|
|
// Return the requested information
|
|
return m_Context->mObject;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the command name from the current execution context.
|
|
*/
|
|
const String & GetCommand() const
|
|
{
|
|
// See if there's an execution context available
|
|
if (!m_Context)
|
|
{
|
|
STHROWF("No active execution context");
|
|
}
|
|
// Return the requested information
|
|
return m_Context->mCommand;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the command argument from the current execution context.
|
|
*/
|
|
const String & GetArgument() const
|
|
{
|
|
// See if there's an execution context available
|
|
if (!m_Context)
|
|
{
|
|
STHROWF("No active execution context");
|
|
}
|
|
// Return the requested information
|
|
return m_Context->mArgument;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve all command listeners in an array.
|
|
*/
|
|
Array GetCommandsArray() const
|
|
{
|
|
// Allocate an array with an adequate size
|
|
Array arr(DefaultVM::Get(), m_Commands.size());
|
|
// Index of the currently processed command listener
|
|
SQInteger index = 0;
|
|
// Populate the array with the command listeners
|
|
for (const auto & cmd : m_Commands)
|
|
{
|
|
arr.SetValue(index++, cmd.mObj);
|
|
}
|
|
// Return the resulted array
|
|
return arr;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve all command listeners in a table.
|
|
*/
|
|
Table GetCommandsTable() const
|
|
{
|
|
// Allocate an empty table
|
|
Table tbl(DefaultVM::Get());
|
|
// Populate the table with the command listeners
|
|
for (const auto & cmd : m_Commands)
|
|
{
|
|
tbl.SetValue(cmd.mName.c_str(), cmd.mObj);
|
|
}
|
|
// Return the resulted table
|
|
return tbl;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Process all command listeners with a function.
|
|
*/
|
|
void ForeachCommand(Function & func) const
|
|
{
|
|
// Make sure that the specified function works
|
|
if (func.IsNull())
|
|
{
|
|
return;
|
|
}
|
|
// Process all the managed command listeners
|
|
for (const auto & cmd : m_Commands)
|
|
{
|
|
func.Execute(cmd.mObj);
|
|
}
|
|
}
|
|
};
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* Allows interaction with a command controller from script.
|
|
*/
|
|
class Manager
|
|
{
|
|
private:
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
CtrRef m_Controller; // Reference to the managed controller.
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the associated controller if valid otherwise throw an exception.
|
|
*/
|
|
const CtrRef & GetValid() const
|
|
{
|
|
// Validate the managed controller
|
|
if (!m_Controller)
|
|
{
|
|
STHROWF("No controller associated with this manager");
|
|
}
|
|
// Return the controller reference
|
|
return m_Controller;
|
|
}
|
|
|
|
public:
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Default constructor.
|
|
*/
|
|
Manager()
|
|
: m_Controller(new Controller(this))
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Copy constructor. (disabled)
|
|
*/
|
|
Manager(const Manager & o) = delete;
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Move constructor. (disabled)
|
|
*/
|
|
Manager(Manager && o) = delete;
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Copy assignment operator. (disabled)
|
|
*/
|
|
Manager & operator = (const Manager & o) = delete;
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Move assignment operator. (disabled)
|
|
*/
|
|
Manager & operator = (Manager && o) = delete;
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Used by the script engine to compare two instances of this type.
|
|
*/
|
|
Int32 Cmp(const Manager & o) const
|
|
{
|
|
if (m_Controller == o.m_Controller)
|
|
{
|
|
return 0;
|
|
}
|
|
else if (m_Controller.Get() > o.m_Controller.Get())
|
|
{
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Used by the script engine to convert this instance to a string.
|
|
*/
|
|
CSStr ToString() const
|
|
{
|
|
return ToStrF("%d", m_Controller.Count());
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Used by the script engine to retrieve the name from instances of this type.
|
|
*/
|
|
static SQInteger Typename(HSQUIRRELVM vm);
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the associated controller reference.
|
|
*/
|
|
const CtrRef & GetCtr() const
|
|
{
|
|
return m_Controller;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the number of references to the managed controller.
|
|
*/
|
|
Uint32 GetRefCount() const
|
|
{
|
|
return m_Controller.Count();
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Run a command under a specific invoker.
|
|
*/
|
|
Int32 Run(Object & invoker, CSStr command)
|
|
{
|
|
return GetValid()->Run(Guard(m_Controller, invoker), command);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Sort the command list in an ascending order.
|
|
*/
|
|
void Sort()
|
|
{
|
|
GetValid()->Sort();
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Detach all the associated command listeners.
|
|
*/
|
|
void Clear()
|
|
{
|
|
GetValid()->Clear();
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Attach a command listener to the managed controller.
|
|
*/
|
|
void Attach(Object & obj)
|
|
{
|
|
GetValid()->Attach(obj, nullptr);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Locate and retrieve a command listener by name.
|
|
*/
|
|
const Object & FindByName(const String & name)
|
|
{
|
|
return GetValid()->FindByName(name);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the number of managed command listeners.
|
|
*/
|
|
SQInteger GetCount() const
|
|
{
|
|
return ConvTo< SQInteger >::From(GetValid()->m_Commands.size());
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* See whether an execution context is currently active.
|
|
*/
|
|
bool IsContext() const
|
|
{
|
|
return GetValid()->IsContext();
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the error callback.
|
|
*/
|
|
Function & GetOnFail()
|
|
{
|
|
return GetValid()->GetOnFail();
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Modify the error callback.
|
|
*/
|
|
void SetOnFail(Object & env, Function & func)
|
|
{
|
|
GetValid()->SetOnFail(env, func);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the authentication callback.
|
|
*/
|
|
Function & GetOnAuth()
|
|
{
|
|
return GetValid()->GetOnAuth();
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Modify the authentication callback.
|
|
*/
|
|
void SetOnAuth(Object & env, Function & func)
|
|
{
|
|
GetValid()->SetOnAuth(env, func);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the invoker from the current execution context.
|
|
*/
|
|
const Object & GetInvoker() const
|
|
{
|
|
return GetValid()->GetInvoker();
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the listener object from the current execution context.
|
|
*/
|
|
const Object & GetListener() const
|
|
{
|
|
return GetValid()->GetListener();
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the command name from the current execution context.
|
|
*/
|
|
const String & GetCommand() const
|
|
{
|
|
return GetValid()->GetCommand();
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the command argument from the current execution context.
|
|
*/
|
|
const String & GetArgument() const
|
|
{
|
|
return GetValid()->GetArgument();
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve all command listeners in an array.
|
|
*/
|
|
Array GetCommandsArray() const
|
|
{
|
|
return GetValid()->GetCommandsArray();
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve all command listeners in a table.
|
|
*/
|
|
Table GetCommandsTable() const
|
|
{
|
|
return GetValid()->GetCommandsTable();
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Process all command listeners with a function.
|
|
*/
|
|
void ForeachCommand(Object & env, Function & func) const
|
|
{
|
|
if (env.IsNull())
|
|
{
|
|
GetValid()->ForeachCommand(func);
|
|
}
|
|
else
|
|
{
|
|
Function fn(env.GetVM(), env, func.GetFunc());
|
|
GetValid()->ForeachCommand(fn);
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Create command instances and obtain the associated object.
|
|
*/
|
|
Object Create(CSStr name)
|
|
{
|
|
return Create(name, _SC(""), NullArray(), 0, SQMOD_MAX_CMD_ARGS-1, -1, false, false);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Create command instances and obtain the associated object.
|
|
*/
|
|
Object Create(CSStr name, CSStr spec)
|
|
{
|
|
return Create(name, spec, NullArray(), 0, SQMOD_MAX_CMD_ARGS-1, -1, false, false);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Create command instances and obtain the associated object.
|
|
*/
|
|
Object Create(CSStr name, CSStr spec, Array & tags)
|
|
{
|
|
return Create(name, spec, tags, 0, SQMOD_MAX_CMD_ARGS-1, -1, false, false);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Create command instances and obtain the associated object.
|
|
*/
|
|
Object Create(CSStr name, CSStr spec, Uint8 min, Uint8 max)
|
|
{
|
|
return Create(name, spec, NullArray(), min, max, -1, false, false);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Create command instances and obtain the associated object.
|
|
*/
|
|
Object Create(CSStr name, CSStr spec, Array & tags, Uint8 min, Uint8 max)
|
|
{
|
|
return Create(name, spec, tags, min, max, -1, false, false);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Create command instances and obtain the associated object.
|
|
*/
|
|
Object Create(CSStr name, CSStr spec, Array & tags, Uint8 min, Uint8 max, SQInteger auth)
|
|
{
|
|
return Create(name, spec, tags, min, max, auth, auth >= 0, false);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Create command instances and obtain the associated object.
|
|
*/
|
|
Object Create(CSStr name, CSStr spec, Array & tags, Uint8 min, Uint8 max, SQInteger auth, bool prot)
|
|
{
|
|
return Create(name, spec, tags, min, max, auth, prot, false);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Create command instances and obtain the associated object.
|
|
*/
|
|
Object Create(CSStr name, CSStr spec, Array & tags, Uint8 min, Uint8 max, SQInteger auth, bool prot, bool assoc);
|
|
};
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* Attaches to a command name and listens for invocations.
|
|
*/
|
|
class Listener
|
|
{
|
|
// --------------------------------------------------------------------------------------------
|
|
friend class Controller; // Allow the controller to execute this listener.
|
|
friend class Command; // Allow the command to interact with this listener.
|
|
friend class Manager; // Allow the manager to interact with this listener.
|
|
|
|
public:
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Release any trace of script resources from all the listener instances.
|
|
*/
|
|
static void Terminate()
|
|
{
|
|
// Go forward and release resources
|
|
for (Listener * node = s_Head; node != nullptr; node = node->m_Next)
|
|
{
|
|
node->m_Data.Release();
|
|
}
|
|
// Go backwards and release resources
|
|
for (Listener * node = s_Head; node != nullptr; node = node->m_Prev)
|
|
{
|
|
node->m_Data.Release();
|
|
}
|
|
// Kinda useless but Squirrel doesn't play nice with loose references
|
|
// Better safe than sorry
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Convenience constructor.
|
|
*/
|
|
Listener(CSStr name)
|
|
: Listener(name, _SC(""), NullArray(), 0, SQMOD_MAX_CMD_ARGS-1, -1, false, false)
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Convenience constructor.
|
|
*/
|
|
Listener(CSStr name, CSStr spec)
|
|
: Listener(name, spec, NullArray(), 0, SQMOD_MAX_CMD_ARGS-1, -1, false, false)
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Convenience constructor.
|
|
*/
|
|
Listener(CSStr name, CSStr spec, Array & tags)
|
|
: Listener(name, spec, tags, 0, SQMOD_MAX_CMD_ARGS-1, -1, false, false)
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Convenience constructor.
|
|
*/
|
|
Listener(CSStr name, CSStr spec, Uint8 min, Uint8 max)
|
|
: Listener(name, spec, NullArray(), min, max, -1, false, false)
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Convenience constructor.
|
|
*/
|
|
Listener(CSStr name, CSStr spec, Array & tags, Uint8 min, Uint8 max)
|
|
: Listener(name, spec, tags, min, max, -1, false, false)
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Convenience constructor.
|
|
*/
|
|
Listener(CSStr name, CSStr spec, Array & tags, Uint8 min, Uint8 max, SQInteger auth)
|
|
: Listener(name, spec, tags, min, max, auth, auth >= 0, false)
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Convenience constructor.
|
|
*/
|
|
Listener(CSStr name, CSStr spec, Array & tags, Uint8 min, Uint8 max, SQInteger auth, bool prot)
|
|
: Listener(name, spec, tags, min, max, auth, prot, false)
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Base constructor.
|
|
*/
|
|
Listener(CSStr name, CSStr spec, Array & tags, Uint8 min, Uint8 max, SQInteger auth, bool prot, bool assoc)
|
|
: m_Controller()
|
|
, m_Name(ValidateName(name))
|
|
, m_ArgSpec()
|
|
, m_ArgTags()
|
|
, m_MinArgc(0)
|
|
, m_MaxArgc(SQMOD_MAX_CMD_ARGS-1)
|
|
, m_Spec()
|
|
, m_Help()
|
|
, m_Info()
|
|
, m_OnExec()
|
|
, m_OnAuth()
|
|
, m_OnPost()
|
|
, m_OnFail()
|
|
, m_Authority(ConvTo< Int32 >::From(auth))
|
|
, m_Protected(prot)
|
|
, m_Suspended(false)
|
|
, m_Associate(assoc)
|
|
, m_Prev(nullptr)
|
|
, m_Next(s_Head)
|
|
{
|
|
// Initialize the specifiers to default values
|
|
for (Uint8 n = 0; n < SQMOD_MAX_CMD_ARGS; ++n)
|
|
{
|
|
m_ArgSpec[n] = CMDARG_ANY;
|
|
}
|
|
// Apply the specified argument rules/specifications
|
|
SetSpec(spec);
|
|
// Extract the specified argument tags
|
|
SetArgTags(tags);
|
|
// Set the specified minimum and maximum allowed arguments
|
|
SetMinArgC(min);
|
|
SetMaxArgC(max);
|
|
// Generate information for the command
|
|
GenerateInfo(false);
|
|
// We're the head element now
|
|
s_Head = this;
|
|
// Was there a previous head?
|
|
if (m_Next != nullptr)
|
|
{
|
|
// Steal previous element from previous head
|
|
m_Prev = m_Next->m_Prev;
|
|
// Did that head element had a previous element?
|
|
if (m_Prev != nullptr)
|
|
{
|
|
// Tell it we're the next element now
|
|
m_Prev->m_Next = this;
|
|
}
|
|
// Tell the previous head that we're the previous element now
|
|
m_Next->m_Prev = this;
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Copy constructor. (disabled)
|
|
*/
|
|
Listener(const Listener & o) = delete;
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Move constructor. (disabled)
|
|
*/
|
|
Listener(Listener && o) = delete;
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Destructor.
|
|
*/
|
|
~Listener()
|
|
{
|
|
// Detach the command
|
|
if (!m_Controller.Expired())
|
|
{
|
|
m_Controller.Lock()->Detach(this);
|
|
}
|
|
// Release callbacks
|
|
m_OnExec.ReleaseGently();
|
|
m_OnAuth.ReleaseGently();
|
|
m_OnPost.ReleaseGently();
|
|
m_OnFail.ReleaseGently();
|
|
// Is there an element behind us?
|
|
if (m_Prev != nullptr)
|
|
{
|
|
// Tell it to point to the element ahead of us
|
|
m_Prev->m_Next = m_Next;
|
|
}
|
|
// Is there an element ahead of us?
|
|
if (m_Next != nullptr)
|
|
{
|
|
// Tell it to point to the element behind us
|
|
m_Next->m_Prev = m_Prev;
|
|
}
|
|
// Are we the head element in the chain?
|
|
if (s_Head == this)
|
|
{
|
|
s_Head = m_Next == nullptr ? m_Prev : m_Next;
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Copy assignment operator. (disabled)
|
|
*/
|
|
Listener & operator = (const Listener & o) = delete;
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Move assignment operator. (disabled)
|
|
*/
|
|
Listener & operator = (Listener && o) = delete;
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Used by the script engine to compare two instances of this type.
|
|
*/
|
|
Int32 Cmp(const Listener & o) const
|
|
{
|
|
if (m_Name == o.m_Name)
|
|
{
|
|
return 0;
|
|
}
|
|
else if (m_Name.size() > o.m_Name.size())
|
|
{
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Used by the script engine to convert this instance to a string.
|
|
*/
|
|
const String & ToString() const
|
|
{
|
|
return m_Name;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Used by the script engine to retrieve the name from instances of this type.
|
|
*/
|
|
static SQInteger Typename(HSQUIRRELVM vm);
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the number of weak references to the managed controller.
|
|
*/
|
|
Uint32 GetRefCount() const
|
|
{
|
|
return m_Controller.Count();
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Attach the listener instance to the associated command name.
|
|
*/
|
|
void Attach(const Manager & mgr)
|
|
{
|
|
// Is the associated name even valid?
|
|
if (m_Name.empty())
|
|
{
|
|
STHROWF("Invalid or empty command name");
|
|
}
|
|
// Detach from the current command controller, if any
|
|
Detach();
|
|
// Is the specified manager valid to even attempt association?
|
|
if (mgr.GetCtr())
|
|
{
|
|
// Attempt to associate with it's controller
|
|
mgr.GetCtr()->Attach(NullObject(), this);
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Detach the listener instance from the associated command name.
|
|
*/
|
|
void Detach()
|
|
{
|
|
// Make sure that we are even associated with a controller
|
|
if (!m_Controller.Expired())
|
|
{
|
|
// Detach from the associated controller
|
|
m_Controller.Lock()->Detach(this);
|
|
// Release the controller reference
|
|
m_Controller.Reset();
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* See whether the listener instance is attached to the associated command name.
|
|
*/
|
|
bool Attached() const
|
|
{
|
|
return (!m_Controller.Expired() && m_Controller.Lock()->Attached(this));
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the manager associated with this command listener instance.
|
|
*/
|
|
Object GetManager() const
|
|
{
|
|
// Are we even associated with a controller?
|
|
if (m_Controller.Expired())
|
|
{
|
|
return NullObject(); // Default to null
|
|
}
|
|
// Obtain the initial stack size
|
|
const StackGuard sg;
|
|
// Push the instance on the stack
|
|
ClassType< Manager >::PushInstance(DefaultVM::Get(), m_Controller.Lock()->m_Manager);
|
|
// Grab the instance from the stack
|
|
return Var< Object >(DefaultVM::Get(), -1).value;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the name that triggers this command listener instance.
|
|
*/
|
|
const String & GetName() const
|
|
{
|
|
return m_Name;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the name that triggers this command listener instance.
|
|
*/
|
|
void SetName(CSStr name)
|
|
{
|
|
// Validate the specified name
|
|
ValidateName(name);
|
|
// Is this command already attached to a name?
|
|
if (!m_Controller.Expired() && m_Controller.Lock()->Attached(this))
|
|
{
|
|
const CtrRef ctr = m_Controller.Lock();
|
|
// Detach from the current name if necessary
|
|
ctr->Detach(this);
|
|
// Now it's safe to assign the new name
|
|
m_Name.assign(name);
|
|
// We know the new name is valid
|
|
ctr->Attach(NullObject(), this);
|
|
}
|
|
else
|
|
{
|
|
m_Name.assign(name); // Just assign the name
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the associated user data.
|
|
*/
|
|
Object & GetData()
|
|
{
|
|
return m_Data;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Modify the associated user data.
|
|
*/
|
|
void SetData(Object & data)
|
|
{
|
|
m_Data = data;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the argument specification string.
|
|
*/
|
|
const String & GetSpec() const
|
|
{
|
|
return m_Spec;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Modify the argument specification string.
|
|
*/
|
|
void SetSpec(CSStr spec)
|
|
{
|
|
// Attempt to process the specified string
|
|
ProcSpec(spec);
|
|
// Assign the specifier, if any
|
|
if (spec)
|
|
{
|
|
m_Spec.assign(spec);
|
|
}
|
|
else
|
|
{
|
|
m_Spec.clear();
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the argument tags array.
|
|
*/
|
|
Array GetArgTags() const
|
|
{
|
|
// Allocate an array to encapsulate all tags
|
|
Array arr(DefaultVM::Get(), SQMOD_MAX_CMD_ARGS);
|
|
// Put the tags to the allocated array
|
|
for (Uint32 arg = 0; arg < SQMOD_MAX_CMD_ARGS; ++arg)
|
|
{
|
|
arr.SetValue(arg, m_ArgTags[arg]);
|
|
}
|
|
// Return the array with the tags
|
|
return arr;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Modify the argument tags array.
|
|
*/
|
|
void SetArgTags(Array & tags)
|
|
{
|
|
// Preliminary checks before even attempting anything
|
|
if (tags.GetType() != OT_ARRAY || tags.IsNull())
|
|
{
|
|
for (Uint8 n = 0; n < SQMOD_MAX_CMD_ARGS; ++n)
|
|
{
|
|
m_ArgTags[n].clear();
|
|
}
|
|
// We're done here!
|
|
return;
|
|
}
|
|
// Attempt to retrieve the number of specified tags
|
|
const Uint32 max = ConvTo< Uint32 >::From(tags.Length());
|
|
// If no tags were specified then clear current tags
|
|
if (!max)
|
|
{
|
|
for (Uint8 n = 0; n < SQMOD_MAX_CMD_ARGS; ++n)
|
|
{
|
|
m_ArgTags[n].clear();
|
|
}
|
|
}
|
|
// See if we're in range
|
|
else if (max < SQMOD_MAX_CMD_ARGS)
|
|
{
|
|
// Attempt to get all arguments in one go
|
|
tags.GetArray< String >(m_ArgTags, max);
|
|
}
|
|
else
|
|
{
|
|
STHROWF("Argument tag (%u) is out of range (%d)", max, SQMOD_MAX_CMD_ARGS);
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the help message associated with this command listener instance.
|
|
*/
|
|
const String & GetHelp() const
|
|
{
|
|
return m_Help;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Modify the help message associated with this command listener instance.
|
|
*/
|
|
void SetHelp(CSStr help)
|
|
{
|
|
if (help)
|
|
{
|
|
m_Help.assign(help);
|
|
}
|
|
else
|
|
{
|
|
m_Help.clear();
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the informational message associated with this command listener instance.
|
|
*/
|
|
const String & GetInfo() const
|
|
{
|
|
return m_Info;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Modify the informational message associated with this command listener instance.
|
|
*/
|
|
void SetInfo(CSStr info)
|
|
{
|
|
if (info)
|
|
{
|
|
m_Info.assign(info);
|
|
}
|
|
else
|
|
{
|
|
m_Info.clear();
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the authority level required to execute this command listener instance.
|
|
*/
|
|
SQInteger GetAuthority() const
|
|
{
|
|
return m_Authority;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Modify the authority level required to execute this command listener instance.
|
|
*/
|
|
void SetAuthority(SQInteger level)
|
|
{
|
|
m_Authority = ConvTo< Int32 >::From(level);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* See whether this command listener instance requires explicit authority inspection.
|
|
*/
|
|
bool GetProtected() const
|
|
{
|
|
return m_Protected;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Set whether this command listener instance requires explicit authority inspection.
|
|
*/
|
|
void SetProtected(bool toggle)
|
|
{
|
|
m_Protected = toggle;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* See whether this command listener instance is currently ignoring calls.
|
|
*/
|
|
bool GetSuspended() const
|
|
{
|
|
return m_Suspended;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Set whether this command listener instance should start ignoring calls.
|
|
*/
|
|
void SetSuspended(bool toggle)
|
|
{
|
|
m_Suspended = toggle;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* See whether this command listener instance receives arguments in an associative container.
|
|
*/
|
|
bool GetAssociate() const
|
|
{
|
|
return m_Associate;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Set whether this command listener instance receives arguments in an associative container.
|
|
*/
|
|
void SetAssociate(bool toggle)
|
|
{
|
|
m_Associate = toggle;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the maximum arguments supported by this command listener.
|
|
*/
|
|
Uint8 GetMinArgC() const
|
|
{
|
|
return m_MinArgc;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Modify the minimum arguments supported by this command listener.
|
|
*/
|
|
void SetMinArgC(SQInteger val)
|
|
{
|
|
// Limit the specified number withing allowed range
|
|
val = ConvTo< Uint8 >::From(val);
|
|
// Perform a range check on the specified argument index
|
|
if (val >= SQMOD_MAX_CMD_ARGS)
|
|
{
|
|
STHROWF("Argument (%d) is out of total range (%d)", val, SQMOD_MAX_CMD_ARGS);
|
|
}
|
|
else if (static_cast< Uint8 >(val) > m_MaxArgc)
|
|
{
|
|
STHROWF("Minimum argument (%d) exceeds maximum (%u)", val, m_MaxArgc);
|
|
}
|
|
// Apply the specified value
|
|
m_MinArgc = static_cast< Uint8 >(val);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the maximum arguments supported by this command listener.
|
|
*/
|
|
SQInteger GetMaxArgC() const
|
|
{
|
|
return m_MaxArgc;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Modify the maximum arguments supported by this command listener.
|
|
*/
|
|
void SetMaxArgC(SQInteger val)
|
|
{
|
|
// Limit the specified number withing allowed range
|
|
val = ConvTo< Uint8 >::From(val);
|
|
// Perform a range check on the specified argument index
|
|
if (val >= SQMOD_MAX_CMD_ARGS)
|
|
{
|
|
STHROWF("Argument (%d) is out of total range (%d)", val, SQMOD_MAX_CMD_ARGS);
|
|
}
|
|
else if (static_cast< Uint8 >(val) < m_MinArgc)
|
|
{
|
|
STHROWF("Maximum argument (%d) exceeds minimum (%u)", val, m_MinArgc);
|
|
}
|
|
// Apply the specified value
|
|
m_MaxArgc = static_cast< Uint8 >(val);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the function that must be called when this command listener is executed.
|
|
*/
|
|
Function & GetOnExec()
|
|
{
|
|
return m_OnExec;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Modify the function that must be called when this command listener is executed.
|
|
*/
|
|
void SetOnExec(Object & env, Function & func)
|
|
{
|
|
// Are we supposed to unbind current callback?
|
|
if (func.IsNull())
|
|
{
|
|
m_OnExec.ReleaseGently();
|
|
}
|
|
// Was there a custom environment specified?
|
|
else if (env.IsNull())
|
|
{
|
|
m_OnExec = func;
|
|
}
|
|
else
|
|
{
|
|
m_OnExec = Function(env.GetVM(), env.GetObject(), func.GetFunc());
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the function that must be called when this command listener needs to authenticate.
|
|
*/
|
|
Function & GetOnAuth()
|
|
{
|
|
return m_OnAuth;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Modify the function that must be called when this command listener needs to authenticate.
|
|
*/
|
|
void SetOnAuth(Object & env, Function & func)
|
|
{
|
|
// Are we supposed to unbind current callback?
|
|
if (func.IsNull())
|
|
{
|
|
m_OnAuth.ReleaseGently();
|
|
}
|
|
// Was there a custom environment specified?
|
|
else if (env.IsNull())
|
|
{
|
|
m_OnAuth = func;
|
|
}
|
|
else
|
|
{
|
|
m_OnAuth = Function(env.GetVM(), env.GetObject(), func.GetFunc());
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the function that must be called when this command listener finished execution.
|
|
*/
|
|
Function & GetOnPost()
|
|
{
|
|
return m_OnPost;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Modify the function that must be called when this command listener finished execution.
|
|
*/
|
|
void SetOnPost(Object & env, Function & func)
|
|
{
|
|
// Are we supposed to unbind current callback?
|
|
if (func.IsNull())
|
|
{
|
|
m_OnPost.ReleaseGently();
|
|
}
|
|
// Was there a custom environment specified?
|
|
else if (env.IsNull())
|
|
{
|
|
m_OnPost = func;
|
|
}
|
|
else
|
|
{
|
|
m_OnPost = Function(env.GetVM(), env.GetObject(), func.GetFunc());
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the function that must be called when this command listener failed execution.
|
|
*/
|
|
Function & GetOnFail()
|
|
{
|
|
return m_OnFail;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Modify the function that must be called when this command listener failed execution.
|
|
*/
|
|
void SetOnFail(Object & env, Function & func)
|
|
{
|
|
// Are we supposed to unbind current callback?
|
|
if (func.IsNull())
|
|
{
|
|
m_OnFail.ReleaseGently();
|
|
}
|
|
// Was there a custom environment specified?
|
|
else if (env.IsNull())
|
|
{
|
|
m_OnFail = func;
|
|
}
|
|
else
|
|
{
|
|
m_OnFail = Function(env.GetVM(), env.GetObject(), func.GetFunc());
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the tag of a certain argument.
|
|
*/
|
|
const String & GetArgTag(Uint32 arg) const
|
|
{
|
|
// Perform a range check on the specified argument index
|
|
if (arg >= SQMOD_MAX_CMD_ARGS)
|
|
{
|
|
STHROWF("Argument (%u) is out of total range (%u)", arg, SQMOD_MAX_CMD_ARGS);
|
|
}
|
|
// Return the requested information
|
|
return m_ArgTags[arg];
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Modify the tag of a certain argument.
|
|
*/
|
|
void SetArgTag(Uint32 arg, CSStr name)
|
|
{
|
|
// Perform a range check on the specified argument index
|
|
if (arg >= SQMOD_MAX_CMD_ARGS)
|
|
{
|
|
STHROWF("Argument (%u) is out of total range (%u)", arg, SQMOD_MAX_CMD_ARGS);
|
|
}
|
|
// The string type doesn't appreciate null values
|
|
else if (name != nullptr)
|
|
{
|
|
m_ArgTags[arg].assign(name);
|
|
}
|
|
// Clear previous name in this case
|
|
else
|
|
{
|
|
m_ArgTags[arg].clear();
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Retrieve the flags of the specified argument.
|
|
*/
|
|
Uint8 GetArgFlags(Uint32 idx) const
|
|
{
|
|
// Perform a range check on the specified argument index
|
|
if (idx >= SQMOD_MAX_CMD_ARGS)
|
|
{
|
|
STHROWF("Argument (%u) is out of total range (%u)", idx, SQMOD_MAX_CMD_ARGS);
|
|
}
|
|
// Return the requested information
|
|
return m_ArgSpec[idx];
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* See whether whether the specified argument can be used on this command listener instance.
|
|
*/
|
|
bool ArgCheck(Uint32 arg, Uint8 flag) const
|
|
{
|
|
// Perform a range check on the specified argument index
|
|
if (arg >= SQMOD_MAX_CMD_ARGS)
|
|
{
|
|
STHROWF("Argument (%u) is out of total range (%u)", arg, SQMOD_MAX_CMD_ARGS);
|
|
}
|
|
// Retrieve the argument flags
|
|
const Uint8 f = m_ArgSpec[arg];
|
|
// Perform the requested check
|
|
return (f == CMDARG_ANY) || ((f & flag) != 0x0) || ((f & CMDARG_GREEDY) && (flag & CMDARG_STRING));
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* See whether the specified invoker entity has the proper authority to run this command.
|
|
*/
|
|
bool AuthCheck(const Object & invoker)
|
|
{
|
|
// Do we need explicit authority verification?
|
|
if (!m_Protected)
|
|
{
|
|
return true; // Anyone can invoke this command
|
|
}
|
|
// Was there a custom authority inspector specified?
|
|
else if (!m_OnAuth.IsNull())
|
|
{
|
|
// Ask the specified authority inspector if this execution should be allowed
|
|
const SharedPtr< bool > ret = m_OnAuth.Evaluate< bool, const Object &, Listener * >(invoker, this);
|
|
// See what the custom authority inspector said or default to disallow
|
|
return !ret ? false : *ret;
|
|
}
|
|
// Was there a global authority inspector specified?
|
|
else if (!m_Controller.Expired() && !m_Controller.Lock()->GetOnAuth().IsNull())
|
|
{
|
|
// Ask the specified authority inspector if this execution should be allowed
|
|
const SharedPtr< bool > ret = m_Controller.Lock()->GetOnAuth().Evaluate< bool, const Object &, Listener * >(invoker, this);
|
|
// See what the custom authority inspector said or default to disallow
|
|
return !ret ? false : *ret;
|
|
}
|
|
// A negative authority level is considered to be the same as unprotected
|
|
else if (m_Authority < 0)
|
|
{
|
|
return true;
|
|
}
|
|
// Default to blocking this execution
|
|
return false;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Use the command listener argument properties to generate an informational message.
|
|
*/
|
|
void GenerateInfo(bool full);
|
|
|
|
protected:
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
typedef Uint8 ArgSpec[SQMOD_MAX_CMD_ARGS];
|
|
typedef String ArgTags[SQMOD_MAX_CMD_ARGS];
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Execute the designated callback by passing the arguments in their specified order.
|
|
*/
|
|
SQInteger Execute(const Object & invoker, const Array & args)
|
|
{
|
|
// Attempt to evaluate the specified executer knowing the manager did the validations
|
|
SharedPtr< SQInteger > ret = m_OnExec.Evaluate< SQInteger, const Object &, const Array & >(invoker, args);
|
|
// See if the executer succeeded and return the result or default to failed
|
|
return (!ret ? 0 : *ret);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Execute the designated callback by passing the arguments using an associative container.
|
|
*/
|
|
SQInteger Execute(const Object & invoker, const Table & args)
|
|
{
|
|
// Attempt to evaluate the specified executer knowing the manager did the validations
|
|
SharedPtr< SQInteger > ret = m_OnExec.Evaluate< SQInteger, const Object &, const Table & >(invoker, args);
|
|
// See if the executer succeeded and return the result or default to failed
|
|
return (!ret ? 0 : *ret);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------
|
|
* Process the specified string and extract the argument properties in it.
|
|
*/
|
|
void ProcSpec(CSStr spec);
|
|
|
|
private:
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
CtrPtr m_Controller; // Manager that controls this command listener.
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
String m_Name; // Name of the command that triggers this listener.
|
|
Object m_Data; // Arbitrary user data associated with this particular instance.
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
ArgSpec m_ArgSpec; // List of argument type specifications.
|
|
ArgTags m_ArgTags; // List of argument tags/names.
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
Uint8 m_MinArgc; // Minimum number of arguments supported by this listener.
|
|
Uint8 m_MaxArgc; // Maximum number of arguments supported by this listener.
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
String m_Spec; // String used to generate the argument type specification list.
|
|
String m_Help; // String describing what the command is supposed to do.
|
|
String m_Info; // String with syntax information for the command.
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
Function m_OnExec; // Function to call when the command is executed.
|
|
Function m_OnAuth; // Function to call when the invoker must be authenticated.
|
|
Function m_OnPost; // Function to call after the command was successfully executed.
|
|
Function m_OnFail; // Function to call after the command execution failed.
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
Int32 m_Authority; // Built-in authority level required to execute this command.
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
bool m_Protected; // Whether explicit authentication of the invoker is required.
|
|
bool m_Suspended; // Whether this command should block further invocations.
|
|
bool m_Associate; // Whether arguments are sent as table instead of array.
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
Listener * m_Prev; // Previous listener in the chain.
|
|
Listener * m_Next; // Next listener in the chain.
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
static Listener * s_Head; // The head of the listener chain.
|
|
};
|
|
|
|
} // Namespace:: Cmd
|
|
} // Namespace:: SqMod
|
|
|
|
#endif // _COMMAND_HPP_
|