mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2024-11-08 08:47:17 +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.
1179 lines
46 KiB
C++
1179 lines
46 KiB
C++
// ------------------------------------------------------------------------------------------------
|
|
#include "Command.hpp"
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
namespace SqMod {
|
|
namespace Cmd {
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Controllers Controller::s_Controllers;
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Listener * Listener::s_Head = nullptr;
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Manager::Typename(HSQUIRRELVM vm)
|
|
{
|
|
static const SQChar name[] = _SC("SqCmdManager");
|
|
sq_pushstring(vm, name, sizeof(name));
|
|
return 1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInteger Listener::Typename(HSQUIRRELVM vm)
|
|
{
|
|
static const SQChar name[] = _SC("SqCmdListener");
|
|
sq_pushstring(vm, name, sizeof(name));
|
|
return 1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Guard::Guard(const CtrRef & ctr, Object & invoker)
|
|
: mController(ctr)
|
|
, mPrevious(mController->m_Context)
|
|
, mCurrent(new Context(invoker))
|
|
{
|
|
mController->m_Context = mCurrent;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Guard::~Guard()
|
|
{
|
|
mController->m_Context = mPrevious;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Command::Command(std::size_t hash, const String & name, Listener * ptr, const CtrPtr & ctr)
|
|
: mHash(hash), mName(name), mPtr(ptr), mObj(ptr), mCtr(ctr)
|
|
{
|
|
if (mPtr)
|
|
{
|
|
mPtr->m_Controller = mCtr; // Create controller association
|
|
}
|
|
}
|
|
// ------------------------------------------------------------------------------------------------
|
|
Command::Command(std::size_t hash, const String & name, const Object & obj, const CtrPtr & ctr)
|
|
: mHash(hash), mName(name), mPtr(obj.Cast< Listener * >()), mObj(obj), mCtr(ctr)
|
|
{
|
|
if (mPtr)
|
|
{
|
|
mPtr->m_Controller = mCtr; // Create controller association
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Command::Command(std::size_t hash, const String & name, Object && obj, const CtrPtr & ctr)
|
|
: mHash(hash), mName(name), mPtr(obj.Cast< Listener * >()), mObj(obj), mCtr(ctr)
|
|
{
|
|
if (mPtr)
|
|
{
|
|
mPtr->m_Controller = mCtr; // Create controller association
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Command::Command(std::size_t hash, const String & name, Listener * ptr, const Object & obj, const CtrPtr & ctr)
|
|
: mHash(hash), mName(name), mPtr(ptr), mObj(obj), mCtr(ctr)
|
|
{
|
|
if (mPtr)
|
|
{
|
|
mPtr->m_Controller = mCtr; // Create controller association
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Command::Command(std::size_t hash, const String & name, Listener * ptr, Object && obj, const CtrPtr & ctr)
|
|
: mHash(hash), mName(name), mPtr(ptr), mObj(obj), mCtr(ctr)
|
|
{
|
|
if (mPtr)
|
|
{
|
|
mPtr->m_Controller = mCtr; // Create controller association
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Command::~Command()
|
|
{
|
|
if (mPtr)
|
|
{
|
|
mPtr->m_Controller.Reset(); // Break controller association
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Object & Controller::Attach(Object && obj, Listener * ptr)
|
|
{
|
|
// Is there anything that we can attach
|
|
if (obj.IsNull() && ptr == nullptr)
|
|
{
|
|
STHROWF("Cannot attach invalid command listener");
|
|
}
|
|
// Are we supposed to grab the object?
|
|
else if (obj.IsNull())
|
|
{
|
|
// Obtain the initial stack size
|
|
const StackGuard sg;
|
|
// Push the instance on the stack
|
|
ClassType< Listener >::PushInstance(DefaultVM::Get(), ptr);
|
|
// Grab the instance from the stack
|
|
obj = Var< Object >(DefaultVM::Get(), -1).value;
|
|
}
|
|
// Are we supposed to grab the instance?
|
|
else if (ptr == nullptr)
|
|
{
|
|
// Obtain the initial stack size
|
|
const StackGuard sg(obj.GetVM());
|
|
// Push the object on the stack
|
|
Var< Object & >::push(obj.GetVM(), obj);
|
|
// Grab the instance from the stack
|
|
ptr = Var< Listener * >(obj.GetVM(), -1).value;
|
|
}
|
|
// At this point we should have both the instance and the object
|
|
if (obj.IsNull() || ptr == nullptr)
|
|
{
|
|
STHROWF("Unable to obtain the command listener");
|
|
}
|
|
// Obtain the command name
|
|
const String & name = ptr->GetName();
|
|
// Validate the command name
|
|
if (name.empty())
|
|
{
|
|
STHROWF("Cannot attach command without a name");
|
|
}
|
|
// Obtain the unique identifier of the specified name
|
|
const std::size_t hash = std::hash< String >()(name);
|
|
// Make sure the command doesn't already exist
|
|
for (const auto & cmd : m_Commands)
|
|
{
|
|
// Are the hashes identical?
|
|
if (cmd.mHash == hash)
|
|
{
|
|
// Include information necessary to help identify hash collisions!
|
|
STHROWF("Command '%s' already exists as '%s' for hash (%zu)",
|
|
name.c_str(), cmd.mName.c_str(), hash);
|
|
}
|
|
}
|
|
// Attempt to insert the command
|
|
m_Commands.emplace_back(hash, name, ptr, std::move(obj), m_Manager->GetCtr());
|
|
// Return the script object of the listener
|
|
return m_Commands.back().mObj;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Object Manager::Create(CSStr name, CSStr spec, Array & tags, Uint8 min, Uint8 max, SQInteger auth, bool prot, bool assoc)
|
|
{
|
|
// Is there a controller to store the listener?
|
|
if (!m_Controller)
|
|
{
|
|
m_Controller = CtrRef(new Controller(this)); // Then make one!
|
|
}
|
|
// Script object and listener instance to be created and managed
|
|
Object obj;
|
|
Listener * ptr = nullptr;
|
|
// Create the command listener
|
|
{
|
|
// Create a new instance of this class and make sure it can't get leaked due to exceptions
|
|
AutoDelete< Listener > ad(new Listener(name, spec, tags, min, max, auth, prot, assoc));
|
|
// Transform the instance into a script object
|
|
obj = Object(ad.Get());
|
|
// Validate the obtained script object
|
|
if (obj.IsNull())
|
|
{
|
|
STHROWF("Cannot create the command listener instance");
|
|
}
|
|
// Grab the instance from the auto-deleter and leave the destruction to the script object
|
|
ptr = ad.Grab();
|
|
}
|
|
// Attempt to attach the command listener to the controller
|
|
Object & o = m_Controller->Attach(obj, ptr);
|
|
// Return the script object
|
|
return o;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Int32 Controller::Run(const Guard & guard, CCStr command)
|
|
{
|
|
// Validate the string command
|
|
if (!command || *command == '\0')
|
|
{
|
|
// Tell the script callback to deal with the error
|
|
SqError(CMDERR_EMPTY_COMMAND, _SC("Invalid or empty command name"), guard.mCurrent->mInvoker);
|
|
// Execution failed!
|
|
return -1;
|
|
}
|
|
// Grab a direct reference to the context instance
|
|
Context & ctx = *(guard.mCurrent);
|
|
// Skip white-space until the command name
|
|
while (std::isspace(*command))
|
|
{
|
|
++command;
|
|
}
|
|
// Anything left to process?
|
|
if (*command == '\0')
|
|
{
|
|
// Tell the script callback to deal with the error
|
|
SqError(CMDERR_EMPTY_COMMAND, _SC("Invalid or empty command name"), guard.mCurrent->mInvoker);
|
|
// Execution failed!
|
|
return -1;
|
|
}
|
|
// Where the name ends and argument begins
|
|
CCStr split = command;
|
|
// Find where the command name ends
|
|
while (*split != '\0' && !std::isspace(*split))
|
|
{
|
|
++split;
|
|
}
|
|
// Are there any arguments specified?
|
|
if (split != '\0')
|
|
{
|
|
// Save the command name
|
|
ctx.mCommand.assign(command, (split - command));
|
|
// Skip white space after command name
|
|
while (std::isspace(*split))
|
|
{
|
|
++split;
|
|
}
|
|
// Save the command argument
|
|
ctx.mArgument.assign(split);
|
|
}
|
|
// No arguments specified
|
|
else
|
|
{
|
|
// Save the command name
|
|
ctx.mCommand.assign(command);
|
|
// Leave argument empty
|
|
ctx.mArgument.assign(_SC(""));
|
|
}
|
|
// Do we have a valid command name?
|
|
try
|
|
{
|
|
ValidateName(ctx.mCommand.c_str());
|
|
}
|
|
catch (const Sqrat::Exception & e)
|
|
{
|
|
// Tell the script callback to deal with the error
|
|
SqError(CMDERR_INVALID_COMMAND, e.what(), guard.mCurrent->mInvoker);
|
|
// Execution failed!
|
|
return -1;
|
|
}
|
|
catch (...)
|
|
{
|
|
// Tell the script callback to deal with the error
|
|
SqError(CMDERR_INVALID_COMMAND, _SC("Cannot execute invalid command name"), guard.mCurrent->mInvoker);
|
|
// Execution failed!
|
|
return -1;
|
|
}
|
|
// Attempt to find the specified command
|
|
ctx.mObject = FindByName(ctx.mCommand);
|
|
// Have we found anything?
|
|
if (ctx.mObject.IsNull())
|
|
{
|
|
// Tell the script callback to deal with the error
|
|
SqError(CMDERR_UNKNOWN_COMMAND, _SC("Unable to find the specified command"), ctx.mCommand);
|
|
// Execution failed!
|
|
return -1;
|
|
}
|
|
// Save the command instance
|
|
ctx.mInstance = ctx.mObject.Cast< Listener * >();
|
|
// Is the command instance valid? (just in case)
|
|
if (!ctx.mInstance)
|
|
{
|
|
// Tell the script callback to deal with the error
|
|
SqError(CMDERR_UNKNOWN_COMMAND, _SC("Unable to find the specified command"), ctx.mCommand);
|
|
// Execution failed!
|
|
return -1;
|
|
}
|
|
// Attempt to execute the command
|
|
try
|
|
{
|
|
return Exec(ctx);
|
|
}
|
|
catch (const Sqrat::Exception & e)
|
|
{
|
|
// Tell the script callback to deal with the error
|
|
SqError(CMDERR_EXECUTION_FAILED, _SC("Exceptions occurred during execution"), e.what());
|
|
}
|
|
// Execution failed
|
|
return -1;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Int32 Controller::Exec(Context & ctx)
|
|
{
|
|
// Clear previous arguments
|
|
ctx.mArgv.clear();
|
|
// Reset the argument counter
|
|
ctx.mArgc = 0;
|
|
// Is this command suspended from further executions?
|
|
if (ctx.mInstance->GetSuspended())
|
|
{
|
|
// Tell the script callback to deal with the error
|
|
SqError(CMDERR_COMMAND_SUSPENDED, _SC("The command is currently suspended"), ctx.mInvoker);
|
|
// Execution failed!
|
|
return -1;
|
|
}
|
|
// Make sure the invoker has enough authority to execute this command
|
|
else if (!ctx.mInstance->AuthCheck(ctx.mInvoker))
|
|
{
|
|
// Tell the script callback to deal with the error
|
|
SqError(CMDERR_INSUFFICIENT_AUTH, _SC("Insufficient authority to execute command"), ctx.mInvoker);
|
|
// Execution failed!
|
|
return -1;
|
|
}
|
|
// Make sure an executer was specified
|
|
else if (ctx.mInstance->GetOnExec().IsNull())
|
|
{
|
|
// Tell the script callback to deal with the error
|
|
SqError(CMDERR_MISSING_EXECUTER, _SC("No executer was specified for this command"), ctx.mInvoker);
|
|
// Execution failed!
|
|
return -1;
|
|
}
|
|
// See if there are any arguments to parse
|
|
else if (!ctx.mArgument.empty() && !Parse(ctx))
|
|
{
|
|
// The error message was reported while parsing
|
|
return -1;
|
|
}
|
|
// Make sure we have enough arguments specified
|
|
else if (ctx.mInstance->GetMinArgC() > ctx.mArgc)
|
|
{
|
|
// Tell the script callback to deal with the error
|
|
SqError(CMDERR_INCOMPLETE_ARGS, _SC("Insufficient command arguments"), ctx.mInstance->GetMinArgC());
|
|
// Execution failed!
|
|
return -1;
|
|
}
|
|
// The check during the parsing may omit the last argument
|
|
else if (static_cast< Uint32 >(ctx.mInstance->GetMaxArgC()) < ctx.mArgc)
|
|
{
|
|
// Tell the script callback to deal with the error
|
|
SqError(CMDERR_EXTRANEOUS_ARGS, _SC("Extraneous command arguments"), ctx.mInstance->GetMaxArgC());
|
|
// Execution failed!
|
|
return -1;
|
|
}
|
|
// Check argument types against the command specifiers
|
|
for (Uint32 arg = 0; arg < ctx.mArgc; ++arg)
|
|
{
|
|
if (!ctx.mInstance->ArgCheck(arg, ctx.mArgv[arg].first))
|
|
{
|
|
// Tell the script callback to deal with the error
|
|
SqError(CMDERR_UNSUPPORTED_ARG, _SC("Unsupported command argument"), arg);
|
|
// Execution failed!
|
|
return -1;
|
|
}
|
|
}
|
|
// Result of the command execution
|
|
SQInteger result = -1;
|
|
// Clear any data from the buffer to make room for the error message
|
|
ctx.mBuffer.At(0) = '\0';
|
|
// Whether the command execution failed
|
|
bool failed = false;
|
|
// Do we have to call the command with an associative container?
|
|
if (ctx.mInstance->m_Associate)
|
|
{
|
|
// Create the associative container
|
|
Table args(DefaultVM::Get());
|
|
// Copy the arguments into the table
|
|
for (Uint32 arg = 0; arg < ctx.mArgc; ++arg)
|
|
{
|
|
// Do we have use the argument index as the key?
|
|
if (ctx.mInstance->m_ArgTags[arg].empty())
|
|
{
|
|
args.SetValue(SQInteger(arg), ctx.mArgv[arg].second);
|
|
}
|
|
// Nope, we have a name for this argument!
|
|
else
|
|
{
|
|
args.SetValue(ctx.mInstance->m_ArgTags[arg].c_str(), ctx.mArgv[arg].second);
|
|
}
|
|
}
|
|
// Attempt to execute the command with the specified arguments
|
|
try
|
|
{
|
|
result = ctx.mInstance->Execute(ctx.mInvoker, args);
|
|
}
|
|
catch (const Sqrat::Exception & e)
|
|
{
|
|
// Let's store the exception message
|
|
ctx.mBuffer.Write(0, e.what(), e.Message().size());
|
|
// Specify that the command execution failed
|
|
failed = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Reserve an array for the extracted arguments
|
|
Array args(DefaultVM::Get(), ctx.mArgc);
|
|
// Copy the arguments into the array
|
|
for (Uint32 arg = 0; arg < ctx.mArgc; ++arg)
|
|
{
|
|
args.Bind(SQInteger(arg), ctx.mArgv[arg].second);
|
|
}
|
|
// Attempt to execute the command with the specified arguments
|
|
try
|
|
{
|
|
result = ctx.mInstance->Execute(ctx.mInvoker, args);
|
|
}
|
|
catch (const Sqrat::Exception & e)
|
|
{
|
|
// Let's store the exception message
|
|
ctx.mBuffer.Write(0, e.what(), e.Message().size());
|
|
// Specify that the command execution failed
|
|
failed = true;
|
|
}
|
|
}
|
|
// Was there a runtime exception during the execution?
|
|
if (failed)
|
|
{
|
|
// Tell the script callback to deal with the error
|
|
SqError(CMDERR_EXECUTION_FAILED, _SC("Command execution failed"), ctx.mBuffer.Data());
|
|
// Is there a script callback that handles failures?
|
|
if (!ctx.mInstance->m_OnFail.IsNull())
|
|
{
|
|
// Then attempt to relay the result to that function
|
|
try
|
|
{
|
|
ctx.mInstance->m_OnFail.Execute(ctx.mInvoker, result);
|
|
}
|
|
catch (const Sqrat::Exception & e)
|
|
{
|
|
// Tell the script callback to deal with the error
|
|
SqError(CMDERR_UNRESOLVED_FAILURE, _SC("Unable to resolve command failure"), e.what());
|
|
}
|
|
}
|
|
// Result is invalid at this point
|
|
result = -1;
|
|
}
|
|
// Was the command aborted explicitly?
|
|
else if (!result)
|
|
{
|
|
// Tell the script callback to deal with the error
|
|
SqError(CMDERR_EXECUTION_ABORTED, _SC("Command execution aborted"), result);
|
|
// Is there a script callback that handles failures?
|
|
if (!ctx.mInstance->m_OnFail.IsNull())
|
|
{
|
|
// Then attempt to relay the result to that function
|
|
try
|
|
{
|
|
ctx.mInstance->m_OnFail.Execute(ctx.mInvoker, result);
|
|
}
|
|
catch (const Sqrat::Exception & e)
|
|
{
|
|
// Tell the script callback to deal with the error
|
|
SqError(CMDERR_UNRESOLVED_FAILURE, _SC("Unable to resolve command failure"), e.what());
|
|
}
|
|
}
|
|
}
|
|
// Is there a callback that must be executed after a successful execution?
|
|
else if (!ctx.mInstance->m_OnPost.IsNull())
|
|
{
|
|
// Then attempt to relay the result to that function
|
|
try
|
|
{
|
|
ctx.mInstance->m_OnPost.Execute(ctx.mInvoker, result);
|
|
}
|
|
catch (const Sqrat::Exception & e)
|
|
{
|
|
// Tell the script callback to deal with the error
|
|
SqError(CMDERR_POST_PROCESSING_FAILED, _SC("Unable to complete command post processing"), e.what());
|
|
}
|
|
}
|
|
// Return the result
|
|
return ConvTo< Int32 >::From(result);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
bool Controller::Parse(Context & ctx)
|
|
{
|
|
// Is there anything to parse?
|
|
if (ctx.mArgument.empty())
|
|
{
|
|
return true; // Done parsing!
|
|
}
|
|
// Obtain the flags of the currently processed argument
|
|
Uint8 arg_flags = ctx.mInstance->m_ArgSpec[ctx.mArgc];
|
|
// Adjust the internal buffer if necessary (mostly never)
|
|
ctx.mBuffer.Adjust(ctx.mArgument.size());
|
|
// The iterator to the currently processed character
|
|
String::const_iterator itr = ctx.mArgument.cbegin();
|
|
// Previous and currently processed character
|
|
String::value_type prev = 0, elem = 0;
|
|
// Maximum arguments allowed to be processed
|
|
const Uint8 max_arg = ctx.mInstance->m_MaxArgc;
|
|
// Process loop result
|
|
bool good = true;
|
|
// Process the specified command text
|
|
while (good)
|
|
{
|
|
// Extract the current characters before advancing
|
|
prev = elem, elem = *itr;
|
|
// See if we have anything left to parse or we have what we need already
|
|
if (elem == '\0' || ctx.mArgc >= max_arg)
|
|
{
|
|
break; // We only parse what we need or what we have!
|
|
}
|
|
// Is this a greedy argument?
|
|
else if (arg_flags & CMDARG_GREEDY)
|
|
{
|
|
// Remember the current stack size
|
|
const StackGuard sg;
|
|
// Skip white-space characters
|
|
itr = std::find_if(itr, ctx.mArgument.cend(), IsNotCType(std::isspace));
|
|
// Anything left to copy to the argument?
|
|
if (itr != ctx.mArgument.end())
|
|
{
|
|
// Transform it into a script object
|
|
sq_pushstring(DefaultVM::Get(), &(*itr), std::distance(itr, ctx.mArgument.cend()));
|
|
}
|
|
// Just push an empty string
|
|
else
|
|
{
|
|
sq_pushstring(DefaultVM::Get(), _SC(""), 0);
|
|
}
|
|
// Get the object from the stack and add it to the argument list along with it's type
|
|
ctx.mArgv.emplace_back(CMDARG_STRING, Var< Object >(DefaultVM::Get(), -1).value);
|
|
// Include this argument into the count
|
|
++ctx.mArgc;
|
|
// Nothing left to parse
|
|
break;
|
|
}
|
|
// Do we have to extract a string argument?
|
|
else if ((elem == '\'' || elem == '"') && prev != '\\')
|
|
{
|
|
// Obtain the beginning and ending of the internal buffer
|
|
SStr str = ctx.mBuffer.Begin< SQChar >();
|
|
SStr end = (ctx.mBuffer.End< SQChar >() - 1); // + null terminator
|
|
// Save the closing quote type
|
|
const SQChar close = elem;
|
|
// Skip the opening quote
|
|
++itr;
|
|
// Attempt to consume the string argument
|
|
while (good)
|
|
{
|
|
// Extract the current characters before advancing
|
|
prev = elem, elem = *itr;
|
|
// See if there's anything left to parse
|
|
if (elem == '\0')
|
|
{
|
|
// Tell the script callback to deal with the error
|
|
SqError(CMDERR_SYNTAX_ERROR, _SC("String argument not closed properly"), ctx.mArgc);
|
|
// Parsing aborted
|
|
good = false;
|
|
// Stop parsing
|
|
break;
|
|
}
|
|
// First un-escaped matching quote character ends the argument
|
|
else if (elem == close)
|
|
{
|
|
// Was this not escaped?
|
|
if (prev != '\\')
|
|
{
|
|
// Terminate the string value in the internal buffer
|
|
*str = '\0';
|
|
// Stop parsing
|
|
break;
|
|
}
|
|
// Overwrite last character when replicating
|
|
else
|
|
{
|
|
--str;
|
|
}
|
|
}
|
|
// See if the internal buffer needs to scale
|
|
else if (str >= end)
|
|
{
|
|
// We should already have a buffer as big as the entire command!
|
|
SqError(CMDERR_BUFFER_OVERFLOW, _SC("Command buffer was exceeded unexpectedly"), ctx.mInvoker);
|
|
// Parsing aborted
|
|
good = false;
|
|
// Stop parsing
|
|
break;
|
|
}
|
|
// Simply replicate the character to the internal buffer
|
|
*str = elem;
|
|
// Advance to the next character
|
|
++str, ++itr;
|
|
}
|
|
// See if the argument was valid
|
|
if (!good)
|
|
{
|
|
break; // Propagate the failure!
|
|
}
|
|
// Swap the beginning and ending of the extracted string
|
|
end = str, str = ctx.mBuffer.Begin();
|
|
// Make sure the string is null terminated
|
|
*end = '\0';
|
|
// Do we have to make the string lowercase?
|
|
if (arg_flags & CMDARG_LOWER)
|
|
{
|
|
for (CStr chr = str; chr < end; ++chr)
|
|
{
|
|
*chr = static_cast< SQChar >(std::tolower(*chr));
|
|
}
|
|
}
|
|
// Do we have to make the string uppercase?
|
|
else if (arg_flags & CMDARG_UPPER)
|
|
{
|
|
for (CStr chr = str; chr < end; ++chr)
|
|
{
|
|
*chr = static_cast< SQChar >(std::toupper(*chr));
|
|
}
|
|
}
|
|
// Remember the current stack size
|
|
const StackGuard sg;
|
|
// Was the specified string empty?
|
|
if (str >= end)
|
|
{
|
|
// Just push an empty string
|
|
sq_pushstring(DefaultVM::Get(), _SC(""), 0);
|
|
}
|
|
// Add it to the argument list along with it's type
|
|
else
|
|
{
|
|
// Transform it into a script object
|
|
sq_pushstring(DefaultVM::Get(), str, end - str - 1);
|
|
}
|
|
// Get the object from the stack and add it to the argument list along with it's type
|
|
ctx.mArgv.emplace_back(CMDARG_STRING, Var< Object >(DefaultVM::Get(), -1).value);
|
|
// Advance to the next argument and obtain its flags
|
|
arg_flags = ctx.mInstance->m_ArgSpec[++ctx.mArgc];
|
|
}
|
|
// Ignore white-space characters until another valid character is found
|
|
else if (!std::isspace(elem) && (std::isspace(prev) || prev == '\0'))
|
|
{
|
|
// Find the first space character that marks the end of the argument
|
|
String::const_iterator pos = std::find_if(itr, ctx.mArgument.cend(), IsCType(std::isspace));
|
|
// Obtain both ends of the argument string
|
|
CCStr str = &(*itr), end = &(*pos);
|
|
// Compute the argument string size
|
|
const Uint32 sz = std::distance(itr, pos);
|
|
// Update the main iterator position
|
|
itr = pos;
|
|
// Update the currently processed character
|
|
elem = *itr;
|
|
// Used to exclude all other checks when a valid type was identified
|
|
bool identified = false;
|
|
// Attempt to treat the value as an integer number if possible
|
|
if (!identified && (arg_flags & CMDARG_INTEGER))
|
|
{
|
|
// Let's us know if the whole argument was part of the resulted value
|
|
CStr next = nullptr;
|
|
// Attempt to extract the integer value from the string
|
|
const Int64 value = std::strtoll(str, &next, 10);
|
|
// See if this whole string was indeed an integer
|
|
if (next == end)
|
|
{
|
|
// Remember the current stack size
|
|
const StackGuard sg;
|
|
// Transform it into a script object
|
|
sq_pushinteger(DefaultVM::Get(), ConvTo< SQInteger >::From(value));
|
|
// Get the object from the stack and add it to the argument list along with it's type
|
|
ctx.mArgv.emplace_back(CMDARG_INTEGER, Var< Object >(DefaultVM::Get(), -1).value);
|
|
// We've identified the correct value type
|
|
identified = true;
|
|
}
|
|
}
|
|
// Attempt to treat the value as an floating point number if possible
|
|
if (!identified && (arg_flags & CMDARG_FLOAT))
|
|
{
|
|
// Let's us know if the whole argument was part of the resulted value
|
|
CStr next = nullptr;
|
|
// Attempt to extract the integer value from the string
|
|
#ifdef SQUSEDOUBLE
|
|
const Float64 value = std::strtod(str, &next);
|
|
#else
|
|
const Float32 value = std::strtof(str, &next);
|
|
#endif // SQUSEDOUBLE
|
|
// See if this whole string was indeed an integer
|
|
if (next == end)
|
|
{
|
|
// Remember the current stack size
|
|
const StackGuard sg;
|
|
// Transform it into a script object
|
|
sq_pushfloat(DefaultVM::Get(), ConvTo< SQFloat >::From(value));
|
|
// Get the object from the stack and add it to the argument list along with it's type
|
|
ctx.mArgv.emplace_back(CMDARG_FLOAT, Var< Object >(DefaultVM::Get(), -1).value);
|
|
// We've identified the correct value type
|
|
identified = true;
|
|
}
|
|
|
|
}
|
|
// Attempt to treat the value as a boolean if possible
|
|
if (!identified && (arg_flags & CMDARG_BOOLEAN) && sz <= 5)
|
|
{
|
|
// Allocate memory for enough data to form a boolean value
|
|
CharT lc[6];
|
|
// Don't modify the original string or buffer pointer
|
|
CStr bptr = lc;
|
|
CSStr sptr = str;
|
|
// Fill the temporary buffer with data from the internal buffer
|
|
for (; sptr < end; ++sptr, ++bptr)
|
|
{
|
|
*bptr = std::tolower(*sptr);
|
|
}
|
|
// Terminate the copied string portion
|
|
*bptr = '\0';
|
|
// Remember the current stack size
|
|
const StackGuard sg;
|
|
// Is this a boolean true value?
|
|
if (std::strcmp(lc, "true") == 0 || std::strcmp(lc, "on") == 0)
|
|
{
|
|
// Transform it into a script object
|
|
sq_pushbool(DefaultVM::Get(), true);
|
|
// We've identified the correct value type
|
|
identified = true;
|
|
}
|
|
// Is this a boolean false value?
|
|
else if (std::strcmp(lc, "false") == 0 || std::strcmp(lc, "off") == 0)
|
|
{
|
|
// Transform it into a script object
|
|
sq_pushbool(DefaultVM::Get(), false);
|
|
// We've identified the correct value type
|
|
identified = true;
|
|
}
|
|
// Could the value inside the string be interpreted as a boolean?
|
|
if (identified)
|
|
{
|
|
// Get the object from the stack and add it to the argument list along with it's type
|
|
ctx.mArgv.emplace_back(CMDARG_BOOLEAN, Var< Object >(DefaultVM::Get(), -1).value);
|
|
}
|
|
}
|
|
// If everything else failed then simply treat the value as a string
|
|
if (!identified)
|
|
{
|
|
// Remember the current stack size
|
|
const StackGuard sg;
|
|
// Do we have to make the string lowercase?
|
|
if (arg_flags & CMDARG_LOWER)
|
|
{
|
|
// Convert all characters from the argument string into the buffer
|
|
for (CStr chr = ctx.mBuffer.Data(); str < end; ++str, ++chr)
|
|
{
|
|
*chr = static_cast< CharT >(std::tolower(*str));
|
|
}
|
|
// Transform it into a script object
|
|
sq_pushstring(DefaultVM::Get(), ctx.mBuffer.Get< SQChar >(), sz);
|
|
}
|
|
// Do we have to make the string uppercase?
|
|
else if (arg_flags & CMDARG_UPPER)
|
|
{
|
|
// Convert all characters from the argument string into the buffer
|
|
for (CStr chr = ctx.mBuffer.Data(); str < end; ++str, ++chr)
|
|
{
|
|
*chr = static_cast< CharT >(std::toupper(*str));
|
|
}
|
|
// Transform it into a script object
|
|
sq_pushstring(DefaultVM::Get(), ctx.mBuffer.Get< SQChar >(), sz);
|
|
}
|
|
else
|
|
{
|
|
// Transform it into a script object
|
|
sq_pushstring(DefaultVM::Get(), str, sz);
|
|
}
|
|
// Get the object from the stack and add it to the argument list along with it's type
|
|
ctx.mArgv.emplace_back(CMDARG_STRING, Var< Object >(DefaultVM::Get(), -1).value);
|
|
}
|
|
// Advance to the next argument and obtain its flags
|
|
arg_flags = ctx.mInstance->m_ArgSpec[++ctx.mArgc];
|
|
}
|
|
// Is there anything left to parse?
|
|
if (itr >= ctx.mArgument.end())
|
|
{
|
|
break;
|
|
}
|
|
// Advance to the next character
|
|
++itr;
|
|
}
|
|
// Return whether the parsing was successful
|
|
return good;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Listener::GenerateInfo(bool full)
|
|
{
|
|
// Clear any previously generated informational message
|
|
m_Info.clear();
|
|
// Process each supported command argument
|
|
for (Uint32 arg = 0; arg < m_MaxArgc; ++arg)
|
|
{
|
|
// If this is not a full command request then see if we must stop
|
|
if (!full)
|
|
{
|
|
// Default to stop if criteria are not meet
|
|
bool stop = true;
|
|
// Check all arguments after this and see if there's any left
|
|
for (Uint32 idx = arg; idx < m_MaxArgc; ++idx)
|
|
{
|
|
// If the argument has a name or a type specifier then it's valid
|
|
if (!m_ArgTags[idx].empty() || m_ArgSpec[idx] != CMDARG_ANY)
|
|
{
|
|
// We have more arguments that need to be parsed
|
|
stop = false;
|
|
// Go back to the main loop
|
|
break;
|
|
}
|
|
}
|
|
// Is there any argument left?
|
|
if (stop)
|
|
{
|
|
break; // Stop the main loop as well
|
|
}
|
|
}
|
|
// Begin the argument block
|
|
m_Info.push_back('<');
|
|
// If the current argument is beyond minimum then mark it as optional
|
|
if (arg >= m_MinArgc)
|
|
{
|
|
m_Info.push_back('*');
|
|
}
|
|
// If the argument has a tag/name associated then add it as well
|
|
if (!m_ArgTags[arg].empty())
|
|
{
|
|
// Add the name first
|
|
m_Info.append(m_ArgTags[arg]);
|
|
// Separate the name from the specifiers
|
|
m_Info.push_back(':');
|
|
}
|
|
// Obtain the argument specifier
|
|
const Uint8 spec = m_ArgSpec[arg];
|
|
// Is this a greedy argument?
|
|
if (spec & CMDARG_GREEDY)
|
|
{
|
|
m_Info.append("...");
|
|
}
|
|
// If the argument has any explicit types specified
|
|
else if (spec != CMDARG_ANY)
|
|
{
|
|
// Does it support integers?
|
|
if (spec & CMDARG_INTEGER)
|
|
{
|
|
m_Info.append("integer");
|
|
}
|
|
// Does it support floats?
|
|
if (spec & CMDARG_FLOAT)
|
|
{
|
|
// Add a separator if this is not the first enabled type!
|
|
if (m_Info.back() != ':' && m_Info.back() != '<')
|
|
{
|
|
m_Info.push_back(',');
|
|
}
|
|
// Now add the type name
|
|
m_Info.append("float");
|
|
}
|
|
// Does it support booleans?
|
|
if (spec & CMDARG_BOOLEAN)
|
|
{
|
|
// Add a separator if this is not the first enabled type!
|
|
if (m_Info.back() != ':' && m_Info.back() != '<')
|
|
{
|
|
m_Info.push_back(',');
|
|
}
|
|
// Now add the type name
|
|
m_Info.append("boolean");
|
|
}
|
|
// Does it support strings?
|
|
if (spec & CMDARG_STRING)
|
|
{
|
|
// Add a separator if this is not the first enabled type?
|
|
if (m_Info.back() != ':' && m_Info.back() != '<')
|
|
{
|
|
m_Info.push_back(',');
|
|
}
|
|
// Now add the type name
|
|
m_Info.append("string");
|
|
}
|
|
}
|
|
// Any kind of value is supported by this argument
|
|
else
|
|
{
|
|
m_Info.append("any");
|
|
}
|
|
// Terminate the argument block
|
|
m_Info.push_back('>');
|
|
// Don't process anything after greedy arguments
|
|
if (spec & CMDARG_GREEDY)
|
|
{
|
|
break;
|
|
}
|
|
// If this is not the last argument then add a separator
|
|
else if (arg+1 != m_MaxArgc)
|
|
{
|
|
m_Info.push_back(' ');
|
|
}
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Listener::ProcSpec(CSStr str)
|
|
{
|
|
// Reset current argument specifiers
|
|
std::memset(m_ArgSpec, CMDARG_ANY, sizeof(m_ArgSpec));
|
|
// Make sure we have anything to parse
|
|
if (!str || *str == '\0')
|
|
{
|
|
return;
|
|
}
|
|
// Currently processed argument
|
|
Uint32 idx = 0;
|
|
// Try to apply the specified type specifiers
|
|
try
|
|
{
|
|
// Process until null terminator or an error occurs
|
|
while (*str != 0)
|
|
{
|
|
// See if we need to move to the next argument
|
|
if (*str == '|')
|
|
{
|
|
if (idx >= SQMOD_MAX_CMD_ARGS)
|
|
{
|
|
STHROWF("Extraneous type specifiers: %d >= %d", idx, SQMOD_MAX_CMD_ARGS);
|
|
}
|
|
// Move to the next character
|
|
++str;
|
|
// Advance to the next argument
|
|
++idx;
|
|
}
|
|
// Simply ignore a type specifier delimiter
|
|
else if (*str != ',')
|
|
{
|
|
// Ignore non-alphabetic characters
|
|
while (*str != '\0' && !std::isalpha(*str))
|
|
{
|
|
++str;
|
|
}
|
|
// Apply the type specifier
|
|
switch(*str++)
|
|
{
|
|
// Did we reached the end of the string?
|
|
case '\0':
|
|
break;
|
|
// Is this a greedy argument?
|
|
case 'g':
|
|
{
|
|
m_ArgSpec[idx] = CMDARG_GREEDY;
|
|
} break;
|
|
// Is this a integer type
|
|
case 'i':
|
|
{
|
|
m_ArgSpec[idx] |= CMDARG_INTEGER;
|
|
// Disable greedy argument flag if set
|
|
if (m_ArgSpec[idx] & CMDARG_GREEDY)
|
|
{
|
|
m_ArgSpec[idx] ^= CMDARG_GREEDY;
|
|
}
|
|
} break;
|
|
// Is this a float type
|
|
case 'f':
|
|
{
|
|
m_ArgSpec[idx] |= CMDARG_FLOAT;
|
|
// Disable greedy argument flag if set
|
|
if (m_ArgSpec[idx] & CMDARG_GREEDY)
|
|
{
|
|
m_ArgSpec[idx] ^= CMDARG_GREEDY;
|
|
}
|
|
} break;
|
|
// Is this a boolean type
|
|
case 'b':
|
|
{
|
|
m_ArgSpec[idx] |= CMDARG_BOOLEAN;
|
|
// Disable greedy argument flag if set
|
|
if (m_ArgSpec[idx] & CMDARG_GREEDY)
|
|
{
|
|
m_ArgSpec[idx] ^= CMDARG_GREEDY;
|
|
}
|
|
} break;
|
|
// Is this a string type
|
|
case 's':
|
|
{
|
|
m_ArgSpec[idx] |= CMDARG_STRING;
|
|
// Disable greedy argument flag if set
|
|
if (m_ArgSpec[idx] & CMDARG_GREEDY)
|
|
{
|
|
m_ArgSpec[idx] ^= CMDARG_GREEDY;
|
|
}
|
|
} break;
|
|
// Is this a lowercase string?
|
|
case 'l':
|
|
{
|
|
m_ArgSpec[idx] |= CMDARG_STRING;
|
|
m_ArgSpec[idx] |= CMDARG_LOWER;
|
|
// Disable greedy argument flag if set
|
|
if (m_ArgSpec[idx] & CMDARG_GREEDY)
|
|
{
|
|
m_ArgSpec[idx] ^= CMDARG_GREEDY;
|
|
}
|
|
} break;
|
|
// Is this a uppercase string?
|
|
case 'u':
|
|
{
|
|
m_ArgSpec[idx] |= CMDARG_STRING;
|
|
m_ArgSpec[idx] |= CMDARG_UPPER;
|
|
// Disable greedy argument flag if set
|
|
if (m_ArgSpec[idx] & CMDARG_GREEDY)
|
|
{
|
|
m_ArgSpec[idx] ^= CMDARG_GREEDY;
|
|
}
|
|
} break;
|
|
// Unknown type!
|
|
default: STHROWF("Unknown type specifier (%c) at argument: %u", *str, idx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (const Sqrat::Exception & e)
|
|
{
|
|
// Reset all argument specifiers if failed
|
|
std::memset(m_ArgSpec, CMDARG_ANY, sizeof(m_ArgSpec));
|
|
// Propagate the exception back to the caller
|
|
throw e;
|
|
}
|
|
// Attempt to generate an informational message
|
|
GenerateInfo(false);
|
|
}
|
|
|
|
// ================================================================================================
|
|
void Register(HSQUIRRELVM vm)
|
|
{
|
|
Table cmdns(vm);
|
|
|
|
cmdns.Bind(_SC("Manager"),
|
|
Class< Manager, NoCopy< Manager > >(vm, _SC("SqCmdManager"))
|
|
// Meta-methods
|
|
.Func(_SC("_cmp"), &Manager::Cmp)
|
|
.SquirrelFunc(_SC("_typename"), &Manager::Typename)
|
|
.Func(_SC("_tostring"), &Manager::ToString)
|
|
// Member Properties
|
|
.Prop(_SC("Count"), &Manager::GetCount)
|
|
.Prop(_SC("References"), &Manager::GetRefCount)
|
|
.Prop(_SC("IsContext"), &Manager::IsContext)
|
|
.Prop(_SC("OnFail"), &Manager::GetOnFail)
|
|
.Prop(_SC("OnAuth"), &Manager::GetOnAuth)
|
|
.Prop(_SC("Invoker"), &Manager::GetInvoker)
|
|
.Prop(_SC("Listener"), &Manager::GetListener)
|
|
.Prop(_SC("Command"), &Manager::GetCommand)
|
|
.Prop(_SC("Argument"), &Manager::GetArgument)
|
|
// Member Methods
|
|
.Func(_SC("Run"), &Manager::Run)
|
|
.Func(_SC("Sort"), &Manager::Sort)
|
|
.Func(_SC("Clear"), &Manager::Clear)
|
|
.Func(_SC("Attach"), &Manager::Attach)
|
|
.Func(_SC("FindByName"), &Manager::FindByName)
|
|
.Func(_SC("BindFail"), &Manager::SetOnFail)
|
|
.Func(_SC("BindAuth"), &Manager::SetOnAuth)
|
|
.Func(_SC("GetArray"), &Manager::GetCommandsArray)
|
|
.Func(_SC("GetTable"), &Manager::GetCommandsTable)
|
|
.Func(_SC("Foreach"), &Manager::ForeachCommand)
|
|
// Member Overloads
|
|
.Overload< Object (Manager::*)(CSStr) >(_SC("Create"), &Manager::Create)
|
|
.Overload< Object (Manager::*)(CSStr, CSStr) >(_SC("Create"), &Manager::Create)
|
|
.Overload< Object (Manager::*)(CSStr, CSStr, Array &) >(_SC("Create"), &Manager::Create)
|
|
.Overload< Object (Manager::*)(CSStr, CSStr, Uint8, Uint8) >(_SC("Create"), &Manager::Create)
|
|
.Overload< Object (Manager::*)(CSStr, CSStr, Array &, Uint8, Uint8) >(_SC("Create"), &Manager::Create)
|
|
.Overload< Object (Manager::*)(CSStr, CSStr, Array &, Uint8, Uint8, SQInteger) >(_SC("Create"), &Manager::Create)
|
|
.Overload< Object (Manager::*)(CSStr, CSStr, Array &, Uint8, Uint8, SQInteger, bool) >(_SC("Create"), &Manager::Create)
|
|
.Overload< Object (Manager::*)(CSStr, CSStr, Array &, Uint8, Uint8, SQInteger, bool, bool) >(_SC("Create"), &Manager::Create)
|
|
);
|
|
|
|
cmdns.Bind(_SC("Listener"),
|
|
Class< Listener, NoCopy< Listener > >(vm, _SC("SqCmdListener"))
|
|
// Constructors
|
|
.Ctor< CSStr >()
|
|
.Ctor< CSStr, CSStr >()
|
|
.Ctor< CSStr, CSStr, Array & >()
|
|
.Ctor< CSStr, CSStr, Uint8, Uint8 >()
|
|
.Ctor< CSStr, CSStr, Array &, Uint8, Uint8 >()
|
|
.Ctor< CSStr, CSStr, Array &, Uint8, Uint8, SQInteger >()
|
|
.Ctor< CSStr, CSStr, Array &, Uint8, Uint8, SQInteger, bool >()
|
|
.Ctor< CSStr, CSStr, Array &, Uint8, Uint8, SQInteger, bool, bool >()
|
|
// Meta-methods
|
|
.Func(_SC("_cmp"), &Listener::Cmp)
|
|
.SquirrelFunc(_SC("_typename"), &Listener::Typename)
|
|
.Func(_SC("_tostring"), &Listener::ToString)
|
|
// Member Properties
|
|
.Prop(_SC("References"), &Listener::GetRefCount)
|
|
.Prop(_SC("Attached"), &Listener::Attached)
|
|
.Prop(_SC("Manager"), &Listener::GetManager)
|
|
.Prop(_SC("Name"), &Listener::GetName, &Listener::SetName)
|
|
.Prop(_SC("Data"), &Listener::GetData, &Listener::SetData)
|
|
.Prop(_SC("Spec"), &Listener::GetSpec, &Listener::SetSpec)
|
|
.Prop(_SC("Specifier"), &Listener::GetSpec, &Listener::SetSpec)
|
|
.Prop(_SC("Tags"), &Listener::GetArgTags, &Listener::SetArgTags)
|
|
.Prop(_SC("Help"), &Listener::GetHelp, &Listener::SetHelp)
|
|
.Prop(_SC("Info"), &Listener::GetInfo, &Listener::SetInfo)
|
|
.Prop(_SC("Authority"), &Listener::GetAuthority, &Listener::SetAuthority)
|
|
.Prop(_SC("Protected"), &Listener::GetProtected, &Listener::SetProtected)
|
|
.Prop(_SC("Suspended"), &Listener::GetSuspended, &Listener::SetSuspended)
|
|
.Prop(_SC("Associate"), &Listener::GetAssociate, &Listener::SetAssociate)
|
|
.Prop(_SC("MinArgs"), &Listener::GetMinArgC, &Listener::SetMinArgC)
|
|
.Prop(_SC("MaxArgs"), &Listener::GetMaxArgC, &Listener::SetMaxArgC)
|
|
.Prop(_SC("OnExec"), &Listener::GetOnExec)
|
|
.Prop(_SC("OnAuth"), &Listener::GetOnAuth)
|
|
.Prop(_SC("OnPost"), &Listener::GetOnPost)
|
|
.Prop(_SC("OnFail"), &Listener::GetOnFail)
|
|
// Member Methods
|
|
.Func(_SC("Attach"), &Listener::Attach)
|
|
.Func(_SC("Detach"), &Listener::Detach)
|
|
.Func(_SC("BindExec"), &Listener::SetOnExec)
|
|
.Func(_SC("BindAuth"), &Listener::SetOnAuth)
|
|
.Func(_SC("BindPost"), &Listener::SetOnPost)
|
|
.Func(_SC("BindFail"), &Listener::SetOnFail)
|
|
.Func(_SC("GetArgTag"), &Listener::GetArgTag)
|
|
.Func(_SC("SetArgTag"), &Listener::SetArgTag)
|
|
.Func(_SC("GetArgFlags"), &Listener::GetArgFlags)
|
|
.Func(_SC("ArgCheck"), &Listener::ArgCheck)
|
|
.Func(_SC("AuthCheck"), &Listener::AuthCheck)
|
|
.Func(_SC("GenerateInfo"), &Listener::GenerateInfo)
|
|
);
|
|
|
|
RootTable(vm).Bind(_SC("SqCmd"), cmdns);
|
|
|
|
ConstTable(vm).Enum(_SC("SqCmdArg"), Enumeration(vm)
|
|
.Const(_SC("Any"), CMDARG_ANY)
|
|
.Const(_SC("Integer"), CMDARG_INTEGER)
|
|
.Const(_SC("Float"), CMDARG_FLOAT)
|
|
.Const(_SC("Boolean"), CMDARG_BOOLEAN)
|
|
.Const(_SC("String"), CMDARG_STRING)
|
|
.Const(_SC("Lower"), CMDARG_LOWER)
|
|
.Const(_SC("Upper"), CMDARG_UPPER)
|
|
.Const(_SC("Greedy"), CMDARG_GREEDY)
|
|
);
|
|
|
|
ConstTable(vm).Enum(_SC("SqCmdErr"), Enumeration(vm)
|
|
.Const(_SC("Unknown"), CMDERR_UNKNOWN)
|
|
.Const(_SC("EmptyCommand"), CMDERR_EMPTY_COMMAND)
|
|
.Const(_SC("InvalidCommand"), CMDERR_INVALID_COMMAND)
|
|
.Const(_SC("SyntaxError"), CMDERR_SYNTAX_ERROR)
|
|
.Const(_SC("UnknownCommand"), CMDERR_UNKNOWN_COMMAND)
|
|
.Const(_SC("ListenerSuspended"), CMDERR_COMMAND_SUSPENDED)
|
|
.Const(_SC("InsufficientAuth"), CMDERR_INSUFFICIENT_AUTH)
|
|
.Const(_SC("MissingExecuter"), CMDERR_MISSING_EXECUTER)
|
|
.Const(_SC("IncompleteArgs"), CMDERR_INCOMPLETE_ARGS)
|
|
.Const(_SC("ExtraneousArgs"), CMDERR_EXTRANEOUS_ARGS)
|
|
.Const(_SC("UnsupportedArg"), CMDERR_UNSUPPORTED_ARG)
|
|
.Const(_SC("BufferOverflow"), CMDERR_BUFFER_OVERFLOW)
|
|
.Const(_SC("ExecutionFailed"), CMDERR_EXECUTION_FAILED)
|
|
.Const(_SC("ExecutionAborted"), CMDERR_EXECUTION_ABORTED)
|
|
.Const(_SC("PostProcessingFailed"), CMDERR_POST_PROCESSING_FAILED)
|
|
.Const(_SC("UnresolvedFailure"), CMDERR_UNRESOLVED_FAILURE)
|
|
.Const(_SC("Max"), CMDERR_MAX)
|
|
);
|
|
}
|
|
|
|
} // Namespace:: Cmd
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void Register_Command(HSQUIRRELVM vm)
|
|
{
|
|
Cmd::Register(vm);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* Forward the call to terminate the command manager.
|
|
*/
|
|
void TerminateCommands()
|
|
{
|
|
Cmd::Controller::Terminate();
|
|
Cmd::Listener::Terminate();
|
|
}
|
|
|
|
} // Namespace:: SqMod
|