1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-08 00:37:15 +01:00

Fix the command system which would fail on recursive command execution.

This commit is contained in:
Sandu Liviu Catalin 2016-06-08 16:26:55 +03:00
parent 410a60cfe5
commit 6489dfdf08
4 changed files with 243 additions and 124 deletions

View File

@ -268,6 +268,14 @@ Function & NullFunction()
return f;
}
// ------------------------------------------------------------------------------------------------
String & NullString()
{
static String s;
s.resize(0);
return s;
}
// ------------------------------------------------------------------------------------------------
CSStr ConvNum< Int8 >::ToStr(Int8 v)
{

View File

@ -118,6 +118,11 @@ Array & NullArray();
*/
Function & NullFunction();
/* ------------------------------------------------------------------------------------------------
* Retrieve a reference to a null string.
*/
String & NullString();
/* ------------------------------------------------------------------------------------------------
* Compute the next power of two for the specified number.
*/

View File

@ -78,13 +78,36 @@ void TerminateCmdManager()
CmdManager::Get().Deinitialize();
}
// ------------------------------------------------------------------------------------------------
CmdManager::Context::Context(Int32 invoker)
: mBuffer(512)
, mInvoker(invoker)
, mCommand()
, mArgument()
, mInstance(nullptr)
, mObject()
, mArgv()
, mArgc(0)
{
// Reserve enough space upfront
mCommand.reserve(64);
mArgument.reserve(512);
}
// ------------------------------------------------------------------------------------------------
CmdManager::Guard::Guard(const CtxRef & ctx)
: mPrevious(CmdManager::Get().m_Context), mCurrent(ctx)
{
CmdManager::Get().m_Context = mCurrent;
}
// ------------------------------------------------------------------------------------------------
CmdManager::Guard::~Guard()
{
// Release the command instance
CmdManager::Get().m_Instance = nullptr;
// Release the reference to the script object
CmdManager::Get().m_Object.Release();
if (CmdManager::Get().m_Context == mCurrent)
{
CmdManager::Get().m_Context = mPrevious;
}
}
// ------------------------------------------------------------------------------------------------
@ -199,15 +222,8 @@ bool CmdManager::Attached(const CmdListener * ptr) const
// ------------------------------------------------------------------------------------------------
CmdManager::CmdManager()
: m_Buffer(512)
, m_Commands()
, m_Invoker(SQMOD_UNKNOWN)
, m_Command(64, '\0')
, m_Argument(512, '\0')
, m_Instance(nullptr)
, m_Object()
, m_Argv()
, m_Argc(0)
: m_Commands()
, m_Context()
, m_OnFail()
, m_OnAuth()
{
@ -243,8 +259,6 @@ void CmdManager::Deinitialize()
}
// Clear the command list and release all references
m_Commands.clear();
// Release the script resources from this class
m_Argv.clear();
// Release the global callbacks
m_OnFail.ReleaseGently();
m_OnAuth.ReleaseGently();
@ -288,8 +302,10 @@ Int32 CmdManager::Run(Int32 invoker, CCStr command)
// Execution failed!
return -1;
}
// Save the invoker identifier
m_Invoker = invoker;
// Create the command execution context
CtxRef ctx_ref(new Context(invoker));
// Grab a direct reference to the context instance
Context & ctx = *ctx_ref;
// Skip white-space until the command name
while (std::isspace(*command))
{
@ -314,27 +330,27 @@ Int32 CmdManager::Run(Int32 invoker, CCStr command)
if (split != '\0')
{
// Save the command name
m_Command.assign(command, (split - command));
ctx.mCommand.assign(command, (split - command));
// Skip white space after command name
while (std::isspace(*split))
{
++split;
}
// Save the command argument
m_Argument.assign(split);
ctx.mArgument.assign(split);
}
// No arguments specified
else
{
// Save the command name
m_Command.assign(command);
ctx.mCommand.assign(command);
// Leave argument empty
m_Argument.assign(_SC(""));
ctx.mArgument.assign(_SC(""));
}
// Do we have a valid command name?
try
{
ValidateName(m_Command.c_str());
ValidateName(ctx.mCommand.c_str());
}
catch (const Sqrat::Exception & e)
{
@ -350,91 +366,91 @@ Int32 CmdManager::Run(Int32 invoker, CCStr command)
// Execution failed!
return -1;
}
// Make sure resources are released at the end of this function
Guard g;
// We're about to enter execution land, so let's be safe
const Guard g(ctx_ref);
// Attempt to find the specified command
m_Object = FindByName(m_Command);
ctx.mObject = FindByName(ctx.mCommand);
// Have we found anything?
if (m_Object.IsNull())
if (ctx.mObject.IsNull())
{
// Tell the script callback to deal with the error
SqError(CMDERR_UNKNOWN_COMMAND, _SC("Unable to find the specified command"), m_Command);
SqError(CMDERR_UNKNOWN_COMMAND, _SC("Unable to find the specified command"), ctx.mCommand);
// Execution failed!
return -1;
}
// Save the command instance
m_Instance = m_Object.Cast< CmdListener * >();
ctx.mInstance = ctx.mObject.Cast< CmdListener * >();
// Is the command instance valid? (just in case)
if (!m_Instance)
if (!ctx.mInstance)
{
// Tell the script callback to deal with the error
SqError(CMDERR_UNKNOWN_COMMAND, _SC("Unable to find the specified command"), m_Command);
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();
return Exec(ctx);
}
catch (...)
{
// Tell the script callback to deal with the error
SqError(CMDERR_EXECUTION_FAILED, _SC("Exceptions occurred during execution"), m_Invoker);
SqError(CMDERR_EXECUTION_FAILED, _SC("Exceptions occurred during execution"), ctx.mInvoker);
}
// Execution failed
return -1;
}
// ------------------------------------------------------------------------------------------------
Int32 CmdManager::Exec()
Int32 CmdManager::Exec(Context & ctx)
{
// Clear previous arguments
m_Argv.clear();
ctx.mArgv.clear();
// Reset the argument counter
m_Argc = 0;
ctx.mArgc = 0;
// Make sure the invoker has enough authority to execute this command
if (!m_Instance->AuthCheckID(m_Invoker))
if (!ctx.mInstance->AuthCheckID(ctx.mInvoker))
{
// Tell the script callback to deal with the error
SqError(CMDERR_INSUFFICIENT_AUTH, _SC("Insufficient authority to execute command"), m_Invoker);
SqError(CMDERR_INSUFFICIENT_AUTH, _SC("Insufficient authority to execute command"), ctx.mInvoker);
// Execution failed!
return -1;
}
// Make sure an executer was specified
else if (m_Instance->GetOnExec().IsNull())
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"), m_Invoker);
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 (!m_Argument.empty() && !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 (m_Instance->GetMinArgC() > m_Argc)
else if (ctx.mInstance->GetMinArgC() > ctx.mArgc)
{
// Tell the script callback to deal with the error
SqError(CMDERR_INCOMPLETE_ARGS, _SC("Incomplete command arguments"), m_Instance->GetMinArgC());
SqError(CMDERR_INCOMPLETE_ARGS, _SC("Incomplete command arguments"), ctx.mInstance->GetMinArgC());
// Execution failed!
return -1;
}
// The check during the parsing may omit the last argument
else if (m_Instance->GetMaxArgC() < m_Argc)
else if (ctx.mInstance->GetMaxArgC() < ctx.mArgc)
{
// Tell the script callback to deal with the error
SqError(CMDERR_EXTRANEOUS_ARGS, _SC("Extraneous command arguments"), m_Instance->GetMaxArgC());
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 < m_Argc; ++arg)
for (Uint32 arg = 0; arg < ctx.mArgc; ++arg)
{
if (!m_Instance->ArgCheck(arg, m_Argv[arg].first))
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);
@ -445,44 +461,44 @@ Int32 CmdManager::Exec()
// Result of the command execution
SQInteger result = -1;
// Clear any data from the buffer to make room for the error message
m_Buffer.At(0) = '\0';
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 (m_Instance->m_Associate)
if (ctx.mInstance->m_Associate)
{
// Create the associative container
Table args(DefaultVM::Get());
// Copy the arguments into the table
for (Uint32 arg = 0; arg < m_Argc; ++arg)
for (Uint32 arg = 0; arg < ctx.mArgc; ++arg)
{
// Do we have use the argument index as the key?
if (m_Instance->m_ArgTags[arg].empty())
if (ctx.mInstance->m_ArgTags[arg].empty())
{
args.SetValue(SQInteger(arg), m_Argv[arg].second);
args.SetValue(SQInteger(arg), ctx.mArgv[arg].second);
}
// Nope, we have a name for this argument!
else
{
args.SetValue(m_Instance->m_ArgTags[arg].c_str(), m_Argv[arg].second);
args.SetValue(ctx.mInstance->m_ArgTags[arg].c_str(), ctx.mArgv[arg].second);
}
}
// Attempt to execute the command with the specified arguments
try
{
result = m_Instance->Execute(Core::Get().GetPlayer(m_Invoker).mObj, args);
result = ctx.mInstance->Execute(Core::Get().GetPlayer(ctx.mInvoker).mObj, args);
}
catch (const Sqrat::Exception & e)
{
// Let's store the exception message
m_Buffer.Write(0, e.Message().c_str(), e.Message().size());
ctx.mBuffer.Write(0, e.Message().c_str(), e.Message().size());
// Specify that the command execution failed
failed = true;
}
catch (const std::exception & e)
{
// Let's store the exception message
m_Buffer.WriteF(0, "Application exception occurred [%s]", e.what());
ctx.mBuffer.WriteF(0, "Application exception occurred [%s]", e.what());
// Specify that the command execution failed
failed = true;
}
@ -490,28 +506,28 @@ Int32 CmdManager::Exec()
else
{
// Reserve an array for the extracted arguments
Array args(DefaultVM::Get(), m_Argc);
Array args(DefaultVM::Get(), ctx.mArgc);
// Copy the arguments into the array
for (Uint32 arg = 0; arg < m_Argc; ++arg)
for (Uint32 arg = 0; arg < ctx.mArgc; ++arg)
{
args.Bind(SQInteger(arg), m_Argv[arg].second);
args.Bind(SQInteger(arg), ctx.mArgv[arg].second);
}
// Attempt to execute the command with the specified arguments
try
{
result = m_Instance->Execute(Core::Get().GetPlayer(m_Invoker).mObj, args);
result = ctx.mInstance->Execute(Core::Get().GetPlayer(ctx.mInvoker).mObj, args);
}
catch (const Sqrat::Exception & e)
{
// Let's store the exception message
m_Buffer.Write(0, e.Message().c_str(), e.Message().size());
ctx.mBuffer.Write(0, e.Message().c_str(), e.Message().size());
// Specify that the command execution failed
failed = true;
}
catch (const std::exception & e)
{
// Let's store the exception message
m_Buffer.WriteF(0, "Application exception occurred [%s]", e.what());
ctx.mBuffer.WriteF(0, "Application exception occurred [%s]", e.what());
// Specify that the command execution failed
failed = true;
}
@ -520,14 +536,14 @@ Int32 CmdManager::Exec()
if (failed)
{
// Tell the script callback to deal with the error
SqError(CMDERR_EXECUTION_FAILED, _SC("Command execution failed"), m_Buffer.Data());
SqError(CMDERR_EXECUTION_FAILED, _SC("Command execution failed"), ctx.mBuffer.Data());
// Is there a script callback that handles failures?
if (!m_Instance->m_OnFail.IsNull())
if (!ctx.mInstance->m_OnFail.IsNull())
{
// Then attempt to relay the result to that function
try
{
m_Instance->m_OnFail.Execute(Core::Get().GetPlayer(m_Invoker).mObj, result);
ctx.mInstance->m_OnFail.Execute(Core::Get().GetPlayer(ctx.mInvoker).mObj, result);
}
catch (const Sqrat::Exception & e)
{
@ -549,12 +565,12 @@ Int32 CmdManager::Exec()
// 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 (!m_Instance->m_OnFail.IsNull())
if (!ctx.mInstance->m_OnFail.IsNull())
{
// Then attempt to relay the result to that function
try
{
m_Instance->m_OnFail.Execute(Core::Get().GetPlayer(m_Invoker).mObj, result);
ctx.mInstance->m_OnFail.Execute(Core::Get().GetPlayer(ctx.mInvoker).mObj, result);
}
catch (const Sqrat::Exception & e)
{
@ -569,12 +585,12 @@ Int32 CmdManager::Exec()
}
}
// Is there a callback that must be executed after a successful execution?
else if (!m_Instance->m_OnPost.IsNull())
else if (!ctx.mInstance->m_OnPost.IsNull())
{
// Then attempt to relay the result to that function
try
{
m_Instance->m_OnPost.Execute(Core::Get().GetPlayer(m_Invoker).mObj, result);
ctx.mInstance->m_OnPost.Execute(Core::Get().GetPlayer(ctx.mInvoker).mObj, result);
}
catch (const Sqrat::Exception & e)
{
@ -592,23 +608,23 @@ Int32 CmdManager::Exec()
}
// ------------------------------------------------------------------------------------------------
bool CmdManager::Parse()
bool CmdManager::Parse(Context & ctx)
{
// Is there anything to parse?
if (m_Argument.empty())
if (ctx.mArgument.empty())
{
return true; // Done parsing!
}
// Obtain the flags of the currently processed argument
Uint8 arg_flags = m_Instance->m_ArgSpec[m_Argc];
Uint8 arg_flags = ctx.mInstance->m_ArgSpec[ctx.mArgc];
// Adjust the internal buffer if necessary (mostly never)
m_Buffer.Adjust(m_Argument.size());
ctx.mBuffer.Adjust(ctx.mArgument.size());
// The iterator to the currently processed character
String::const_iterator itr = m_Argument.cbegin();
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 = m_Instance->m_MaxArgc;
const Uint8 max_arg = ctx.mInstance->m_MaxArgc;
// Process loop result
bool good = true;
// Process the specified command text
@ -617,7 +633,7 @@ bool CmdManager::Parse()
// 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' || m_Argc >= max_arg)
if (elem == '\0' || ctx.mArgc >= max_arg)
{
break; // We only parse what we need or what we have!
}
@ -627,12 +643,12 @@ bool CmdManager::Parse()
// Remember the current stack size
const StackGuard sg;
// Skip white-space characters
itr = std::find_if(itr, m_Argument.cend(), IsNotCType(std::isspace));
itr = std::find_if(itr, ctx.mArgument.cend(), IsNotCType(std::isspace));
// Anything left to copy to the argument?
if (itr != m_Argument.end())
if (itr != ctx.mArgument.end())
{
// Transform it into a script object
sq_pushstring(DefaultVM::Get(), &(*itr), std::distance(itr, m_Argument.cend()));
sq_pushstring(DefaultVM::Get(), &(*itr), std::distance(itr, ctx.mArgument.cend()));
}
// Just push an empty string
else
@ -640,9 +656,9 @@ bool CmdManager::Parse()
sq_pushstring(DefaultVM::Get(), _SC(""), 0);
}
// Get the object from the stack and add it to the argument list along with it's type
m_Argv.emplace_back(CMDARG_STRING, Var< Object >(DefaultVM::Get(), -1).value);
ctx.mArgv.emplace_back(CMDARG_STRING, Var< Object >(DefaultVM::Get(), -1).value);
// Include this argument into the count
++m_Argc;
++ctx.mArgc;
// Nothing left to parse
break;
}
@ -650,8 +666,8 @@ bool CmdManager::Parse()
else if ((elem == '\'' || elem == '"') && prev != '\\')
{
// Obtain the beginning and ending of the internal buffer
SStr str = m_Buffer.Begin< SQChar >();
SStr end = (m_Buffer.End< SQChar >() - 1); // + null terminator
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
@ -665,7 +681,7 @@ bool CmdManager::Parse()
if (elem == '\0')
{
// Tell the script callback to deal with the error
SqError(CMDERR_SYNTAX_ERROR, _SC("String argument not closed properly"), m_Argc);
SqError(CMDERR_SYNTAX_ERROR, _SC("String argument not closed properly"), ctx.mArgc);
// Parsing aborted
good = false;
// Stop parsing
@ -692,7 +708,7 @@ bool CmdManager::Parse()
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"), m_Invoker);
SqError(CMDERR_BUFFER_OVERFLOW, _SC("Command buffer was exceeded unexpectedly"), ctx.mInvoker);
// Parsing aborted
good = false;
// Stop parsing
@ -709,7 +725,7 @@ bool CmdManager::Parse()
break; // Propagate the failure!
}
// Swap the beginning and ending of the extracted string
end = str, str = m_Buffer.Begin();
end = str, str = ctx.mBuffer.Begin();
// Make sure the string is null terminated
*end = '\0';
// Do we have to make the string lowercase?
@ -743,15 +759,15 @@ bool CmdManager::Parse()
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
m_Argv.emplace_back(CMDARG_STRING, Var< Object >(DefaultVM::Get(), -1).value);
ctx.mArgv.emplace_back(CMDARG_STRING, Var< Object >(DefaultVM::Get(), -1).value);
// Advance to the next argument and obtain its flags
arg_flags = m_Instance->m_ArgSpec[++m_Argc];
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, m_Argument.cend(), IsCType(std::isspace));
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
@ -777,7 +793,7 @@ bool CmdManager::Parse()
// 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
m_Argv.emplace_back(CMDARG_INTEGER, Var< Object >(DefaultVM::Get(), -1).value);
ctx.mArgv.emplace_back(CMDARG_INTEGER, Var< Object >(DefaultVM::Get(), -1).value);
// We've identified the correct value type
identified = false;
}
@ -801,7 +817,7 @@ bool CmdManager::Parse()
// 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
m_Argv.emplace_back(CMDARG_FLOAT, Var< Object >(DefaultVM::Get(), -1).value);
ctx.mArgv.emplace_back(CMDARG_FLOAT, Var< Object >(DefaultVM::Get(), -1).value);
// We've identified the correct value type
identified = false;
}
@ -841,7 +857,7 @@ bool CmdManager::Parse()
if (identified)
{
// Get the object from the stack and add it to the argument list along with it's type
m_Argv.emplace_back(CMDARG_BOOLEAN, Var< Object >(DefaultVM::Get(), -1).value);
ctx.mArgv.emplace_back(CMDARG_BOOLEAN, Var< Object >(DefaultVM::Get(), -1).value);
}
}
// If everything else failed then simply treat the value as a string
@ -853,23 +869,23 @@ bool CmdManager::Parse()
if (arg_flags & CMDARG_LOWER)
{
// Convert all characters from the argument string into the buffer
for (CStr chr = m_Buffer.Data(); str < end; ++str, ++chr)
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(), m_Buffer.Get< SQChar >(), sz);
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 = m_Buffer.Data(); str < end; ++str, ++chr)
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(), m_Buffer.Get< SQChar >(), sz);
sq_pushstring(DefaultVM::Get(), ctx.mBuffer.Get< SQChar >(), sz);
}
else
{
@ -877,13 +893,13 @@ bool CmdManager::Parse()
sq_pushstring(DefaultVM::Get(), str, sz);
}
// Get the object from the stack and add it to the argument list along with it's type
m_Argv.emplace_back(CMDARG_STRING, Var< Object >(DefaultVM::Get(), -1).value);
ctx.mArgv.emplace_back(CMDARG_STRING, Var< Object >(DefaultVM::Get(), -1).value);
}
// Advance to the next argument and obtain its flags
arg_flags = m_Instance->m_ArgSpec[++m_Argc];
arg_flags = ctx.mInstance->m_ArgSpec[++ctx.mArgc];
}
// Is there anything left to parse?
if (itr >= m_Argument.end())
if (itr >= ctx.mArgument.end())
{
break;
}
@ -1726,6 +1742,12 @@ static void Cmd_SetOnAuth(Object & env, Function & func)
CmdManager::Get().SetOnAuth(env, func);
}
// ------------------------------------------------------------------------------------------------
static bool Cmd_IsContext()
{
return CmdManager::Get().IsContext();
}
// ------------------------------------------------------------------------------------------------
static Object & Cmd_GetInvoker()
{
@ -1835,6 +1857,7 @@ void Register_Command(HSQUIRRELVM vm)
cmdns.Func(_SC("GetOnAuth"), &Cmd_GetOnAuth);
cmdns.Func(_SC("SetOnAuth"), &Cmd_SetOnAuth);
cmdns.Func(_SC("BindAuth"), &Cmd_SetOnAuth);
cmdns.Func(_SC("Context"), &Cmd_IsContext);
cmdns.Func(_SC("Invoker"), &Cmd_GetInvoker);
cmdns.Func(_SC("InvokerID"), &Cmd_GetInvokerID);
cmdns.Func(_SC("Instance"), &Cmd_GetObject);

View File

@ -30,15 +30,75 @@ private:
private:
// --------------------------------------------------------------------------------------------
typedef std::pair< Uint8, Object > CmdArg; // Can hold the argument value and type.
typedef std::vector< CmdArg > CmdArgs; // A list of extracted arguments.
/* --------------------------------------------------------------------------------------------
* Helper class implementing RAII to release the command object and instance.
* Holds the execution context of a command.
*/
struct Guard
struct Context
{
public:
// ----------------------------------------------------------------------------------------
Buffer mBuffer; // Shared buffer used to extract arguments.
// ----------------------------------------------------------------------------------------
const Int32 mInvoker; // The identifier of the last player that invoked a command.
String mCommand; // The extracted command name.
String mArgument; // The extracted command argument.
CmdListener* mInstance; // Pointer to the currently executed command.
Object mObject; // Script object of the currently exectued command.
// ----------------------------------------------------------------------------------------
CmdArgs mArgv; // Extracted command arguments.
Uint32 mArgc; // Extracted arguments count.
/* ----------------------------------------------------------------------------------------
* Default constructor.
*/
Guard() = default;
Context(Int32 invoker);
/* ----------------------------------------------------------------------------------------
* 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;
};
// --------------------------------------------------------------------------------------------
typedef SharedPtr< Context > CtxRef; // Shared reference to an execution context.
/* --------------------------------------------------------------------------------------------
* Helper class implementing RAII to release the command context.
*/
struct Guard
{
public:
// ----------------------------------------------------------------------------------------
CtxRef mPrevious; // Previous context when this guard was created.
CtxRef mCurrent; // The context managed by this guard.
/* ----------------------------------------------------------------------------------------
* Default constructor.
*/
Guard(const CtxRef & ctx);
/* ----------------------------------------------------------------------------------------
* Copy constructor.
@ -127,26 +187,11 @@ private:
// --------------------------------------------------------------------------------------------
typedef std::vector< Command > CmdList;
// --------------------------------------------------------------------------------------------
typedef std::pair< Uint8, Object > CmdArg;
typedef std::vector< CmdArg > CmdArgs;
private:
// --------------------------------------------------------------------------------------------
Buffer m_Buffer; // Shared buffer used to extract arguments.
CmdList m_Commands; // List of available command instances.
// --------------------------------------------------------------------------------------------
Int32 m_Invoker; // The identifier of the last player that invoked a command.
String m_Command; // The extracted command name.
String m_Argument; // The extracted command argument.
CmdListener* m_Instance; // Pointer to the currently executed command.
Object m_Object; // Script object of the currently exectued command.
// --------------------------------------------------------------------------------------------
CmdArgs m_Argv; // Extracted command arguments.
Uint32 m_Argc; // Extracted arguments count.
CtxRef m_Context; // The context of the currently executed command.
// --------------------------------------------------------------------------------------------
Function m_OnFail; // Callback when something failed while running a command.
@ -301,12 +346,26 @@ public:
m_OnAuth = Function(env.GetVM(), env, func.GetFunc());
}
/* --------------------------------------------------------------------------------------------
* Retrieve the identifier of the last invoker.
*/
bool IsContext() const
{
return !!m_Context;
}
/* --------------------------------------------------------------------------------------------
* Retrieve the identifier of the last invoker.
*/
Int32 GetInvoker() const
{
return m_Invoker;
// See if there's an execution context available
if (!m_Context)
{
STHROWF("No active execution context");
}
// Return the requested information
return m_Context->mInvoker;
}
/* --------------------------------------------------------------------------------------------
@ -314,7 +373,13 @@ public:
*/
const CmdListener * GetInstance() const
{
return m_Instance;
// See if there's an execution context available
if (!m_Context)
{
STHROWF("No active execution context");
}
// Return the requested information
return m_Context->mInstance;
}
/* --------------------------------------------------------------------------------------------
@ -322,7 +387,13 @@ public:
*/
const Object & GetObject() const
{
return m_Object;
// See if there's an execution context available
if (!m_Context)
{
STHROWF("No active execution context");
}
// Return the requested information
return m_Context->mObject;
}
/* --------------------------------------------------------------------------------------------
@ -330,7 +401,13 @@ public:
*/
const String & GetCommand() const
{
return m_Command;
// See if there's an execution context available
if (!m_Context)
{
STHROWF("No active execution context");
}
// Return the requested information
return m_Context->mCommand;
}
/* --------------------------------------------------------------------------------------------
@ -338,7 +415,13 @@ public:
*/
const String & GetArgument() const
{
return m_Argument;
// See if there's an execution context available
if (!m_Context)
{
STHROWF("No active execution context");
}
// Return the requested information
return m_Context->mArgument;
}
/* --------------------------------------------------------------------------------------------
@ -351,12 +434,12 @@ protected:
/* --------------------------------------------------------------------------------------------
* Attempt to execute the specified command.
*/
Int32 Exec();
Int32 Exec(Context & ctx);
/* --------------------------------------------------------------------------------------------
* Attempt to parse the specified argument.
*/
bool Parse();
bool Parse(Context & ctx);
public: