1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2025-04-25 21:57:12 +02: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; return f;
} }
// ------------------------------------------------------------------------------------------------
String & NullString()
{
static String s;
s.resize(0);
return s;
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
CSStr ConvNum< Int8 >::ToStr(Int8 v) CSStr ConvNum< Int8 >::ToStr(Int8 v)
{ {

View File

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

View File

@ -78,13 +78,36 @@ void TerminateCmdManager()
CmdManager::Get().Deinitialize(); 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() CmdManager::Guard::~Guard()
{ {
// Release the command instance if (CmdManager::Get().m_Context == mCurrent)
CmdManager::Get().m_Instance = nullptr; {
// Release the reference to the script object CmdManager::Get().m_Context = mPrevious;
CmdManager::Get().m_Object.Release(); }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -199,15 +222,8 @@ bool CmdManager::Attached(const CmdListener * ptr) const
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
CmdManager::CmdManager() CmdManager::CmdManager()
: m_Buffer(512) : m_Commands()
, m_Commands() , m_Context()
, m_Invoker(SQMOD_UNKNOWN)
, m_Command(64, '\0')
, m_Argument(512, '\0')
, m_Instance(nullptr)
, m_Object()
, m_Argv()
, m_Argc(0)
, m_OnFail() , m_OnFail()
, m_OnAuth() , m_OnAuth()
{ {
@ -243,8 +259,6 @@ void CmdManager::Deinitialize()
} }
// Clear the command list and release all references // Clear the command list and release all references
m_Commands.clear(); m_Commands.clear();
// Release the script resources from this class
m_Argv.clear();
// Release the global callbacks // Release the global callbacks
m_OnFail.ReleaseGently(); m_OnFail.ReleaseGently();
m_OnAuth.ReleaseGently(); m_OnAuth.ReleaseGently();
@ -288,8 +302,10 @@ Int32 CmdManager::Run(Int32 invoker, CCStr command)
// Execution failed! // Execution failed!
return -1; return -1;
} }
// Save the invoker identifier // Create the command execution context
m_Invoker = invoker; 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 // Skip white-space until the command name
while (std::isspace(*command)) while (std::isspace(*command))
{ {
@ -314,27 +330,27 @@ Int32 CmdManager::Run(Int32 invoker, CCStr command)
if (split != '\0') if (split != '\0')
{ {
// Save the command name // Save the command name
m_Command.assign(command, (split - command)); ctx.mCommand.assign(command, (split - command));
// Skip white space after command name // Skip white space after command name
while (std::isspace(*split)) while (std::isspace(*split))
{ {
++split; ++split;
} }
// Save the command argument // Save the command argument
m_Argument.assign(split); ctx.mArgument.assign(split);
} }
// No arguments specified // No arguments specified
else else
{ {
// Save the command name // Save the command name
m_Command.assign(command); ctx.mCommand.assign(command);
// Leave argument empty // Leave argument empty
m_Argument.assign(_SC("")); ctx.mArgument.assign(_SC(""));
} }
// Do we have a valid command name? // Do we have a valid command name?
try try
{ {
ValidateName(m_Command.c_str()); ValidateName(ctx.mCommand.c_str());
} }
catch (const Sqrat::Exception & e) catch (const Sqrat::Exception & e)
{ {
@ -350,91 +366,91 @@ Int32 CmdManager::Run(Int32 invoker, CCStr command)
// Execution failed! // Execution failed!
return -1; return -1;
} }
// Make sure resources are released at the end of this function // We're about to enter execution land, so let's be safe
Guard g; const Guard g(ctx_ref);
// Attempt to find the specified command // Attempt to find the specified command
m_Object = FindByName(m_Command); ctx.mObject = FindByName(ctx.mCommand);
// Have we found anything? // Have we found anything?
if (m_Object.IsNull()) if (ctx.mObject.IsNull())
{ {
// Tell the script callback to deal with the error // 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! // Execution failed!
return -1; return -1;
} }
// Save the command instance // Save the command instance
m_Instance = m_Object.Cast< CmdListener * >(); ctx.mInstance = ctx.mObject.Cast< CmdListener * >();
// Is the command instance valid? (just in case) // Is the command instance valid? (just in case)
if (!m_Instance) if (!ctx.mInstance)
{ {
// Tell the script callback to deal with the error // 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! // Execution failed!
return -1; return -1;
} }
// Attempt to execute the command // Attempt to execute the command
try try
{ {
return Exec(); return Exec(ctx);
} }
catch (...) catch (...)
{ {
// Tell the script callback to deal with the error // 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 // Execution failed
return -1; return -1;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Int32 CmdManager::Exec() Int32 CmdManager::Exec(Context & ctx)
{ {
// Clear previous arguments // Clear previous arguments
m_Argv.clear(); ctx.mArgv.clear();
// Reset the argument counter // Reset the argument counter
m_Argc = 0; ctx.mArgc = 0;
// Make sure the invoker has enough authority to execute this command // 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 // 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! // Execution failed!
return -1; return -1;
} }
// Make sure an executer was specified // 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 // 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! // Execution failed!
return -1; return -1;
} }
// See if there are any arguments to parse // 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 // The error message was reported while parsing
return -1; return -1;
} }
// Make sure we have enough arguments specified // 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 // 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! // Execution failed!
return -1; return -1;
} }
// The check during the parsing may omit the last argument // 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 // 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! // Execution failed!
return -1; return -1;
} }
// Check argument types against the command specifiers // 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 // Tell the script callback to deal with the error
SqError(CMDERR_UNSUPPORTED_ARG, _SC("Unsupported command argument"), arg); SqError(CMDERR_UNSUPPORTED_ARG, _SC("Unsupported command argument"), arg);
@ -445,44 +461,44 @@ Int32 CmdManager::Exec()
// Result of the command execution // Result of the command execution
SQInteger result = -1; SQInteger result = -1;
// Clear any data from the buffer to make room for the error message // 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 // Whether the command execution failed
bool failed = false; bool failed = false;
// Do we have to call the command with an associative container? // 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 // Create the associative container
Table args(DefaultVM::Get()); Table args(DefaultVM::Get());
// Copy the arguments into the table // 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? // 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! // Nope, we have a name for this argument!
else 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 // Attempt to execute the command with the specified arguments
try 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) catch (const Sqrat::Exception & e)
{ {
// Let's store the exception message // 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 // Specify that the command execution failed
failed = true; failed = true;
} }
catch (const std::exception & e) catch (const std::exception & e)
{ {
// Let's store the exception message // 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 // Specify that the command execution failed
failed = true; failed = true;
} }
@ -490,28 +506,28 @@ Int32 CmdManager::Exec()
else else
{ {
// Reserve an array for the extracted arguments // 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 // 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 // Attempt to execute the command with the specified arguments
try 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) catch (const Sqrat::Exception & e)
{ {
// Let's store the exception message // 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 // Specify that the command execution failed
failed = true; failed = true;
} }
catch (const std::exception & e) catch (const std::exception & e)
{ {
// Let's store the exception message // 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 // Specify that the command execution failed
failed = true; failed = true;
} }
@ -520,14 +536,14 @@ Int32 CmdManager::Exec()
if (failed) if (failed)
{ {
// Tell the script callback to deal with the error // 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? // 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 // Then attempt to relay the result to that function
try 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) catch (const Sqrat::Exception & e)
{ {
@ -549,12 +565,12 @@ Int32 CmdManager::Exec()
// Tell the script callback to deal with the error // Tell the script callback to deal with the error
SqError(CMDERR_EXECUTION_ABORTED, _SC("Command execution aborted"), result); SqError(CMDERR_EXECUTION_ABORTED, _SC("Command execution aborted"), result);
// Is there a script callback that handles failures? // 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 // Then attempt to relay the result to that function
try 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) catch (const Sqrat::Exception & e)
{ {
@ -569,12 +585,12 @@ Int32 CmdManager::Exec()
} }
} }
// Is there a callback that must be executed after a successful execution? // 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 // Then attempt to relay the result to that function
try 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) 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? // Is there anything to parse?
if (m_Argument.empty()) if (ctx.mArgument.empty())
{ {
return true; // Done parsing! return true; // Done parsing!
} }
// Obtain the flags of the currently processed argument // 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) // 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 // 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 // Previous and currently processed character
String::value_type prev = 0, elem = 0; String::value_type prev = 0, elem = 0;
// Maximum arguments allowed to be processed // 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 // Process loop result
bool good = true; bool good = true;
// Process the specified command text // Process the specified command text
@ -617,7 +633,7 @@ bool CmdManager::Parse()
// Extract the current characters before advancing // Extract the current characters before advancing
prev = elem, elem = *itr; prev = elem, elem = *itr;
// See if we have anything left to parse or we have what we need already // 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! break; // We only parse what we need or what we have!
} }
@ -627,12 +643,12 @@ bool CmdManager::Parse()
// Remember the current stack size // Remember the current stack size
const StackGuard sg; const StackGuard sg;
// Skip white-space characters // 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? // Anything left to copy to the argument?
if (itr != m_Argument.end()) if (itr != ctx.mArgument.end())
{ {
// Transform it into a script object // 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 // Just push an empty string
else else
@ -640,9 +656,9 @@ bool CmdManager::Parse()
sq_pushstring(DefaultVM::Get(), _SC(""), 0); sq_pushstring(DefaultVM::Get(), _SC(""), 0);
} }
// Get the object from the stack and add it to the argument list along with it's type // 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 // Include this argument into the count
++m_Argc; ++ctx.mArgc;
// Nothing left to parse // Nothing left to parse
break; break;
} }
@ -650,8 +666,8 @@ bool CmdManager::Parse()
else if ((elem == '\'' || elem == '"') && prev != '\\') else if ((elem == '\'' || elem == '"') && prev != '\\')
{ {
// Obtain the beginning and ending of the internal buffer // Obtain the beginning and ending of the internal buffer
SStr str = m_Buffer.Begin< SQChar >(); SStr str = ctx.mBuffer.Begin< SQChar >();
SStr end = (m_Buffer.End< SQChar >() - 1); // + null terminator SStr end = (ctx.mBuffer.End< SQChar >() - 1); // + null terminator
// Save the closing quote type // Save the closing quote type
const SQChar close = elem; const SQChar close = elem;
// Skip the opening quote // Skip the opening quote
@ -665,7 +681,7 @@ bool CmdManager::Parse()
if (elem == '\0') if (elem == '\0')
{ {
// Tell the script callback to deal with the error // 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 // Parsing aborted
good = false; good = false;
// Stop parsing // Stop parsing
@ -692,7 +708,7 @@ bool CmdManager::Parse()
else if (str >= end) else if (str >= end)
{ {
// We should already have a buffer as big as the entire command! // 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 // Parsing aborted
good = false; good = false;
// Stop parsing // Stop parsing
@ -709,7 +725,7 @@ bool CmdManager::Parse()
break; // Propagate the failure! break; // Propagate the failure!
} }
// Swap the beginning and ending of the extracted string // 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 // Make sure the string is null terminated
*end = '\0'; *end = '\0';
// Do we have to make the string lowercase? // Do we have to make the string lowercase?
@ -743,15 +759,15 @@ bool CmdManager::Parse()
sq_pushstring(DefaultVM::Get(), str, end - str - 1); 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 // 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 // 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 // Ignore white-space characters until another valid character is found
else if (!std::isspace(elem) && (std::isspace(prev) || prev == '\0')) else if (!std::isspace(elem) && (std::isspace(prev) || prev == '\0'))
{ {
// Find the first space character that marks the end of the argument // 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 // Obtain both ends of the argument string
CCStr str = &(*itr), end = &(*pos); CCStr str = &(*itr), end = &(*pos);
// Compute the argument string size // Compute the argument string size
@ -777,7 +793,7 @@ bool CmdManager::Parse()
// Transform it into a script object // Transform it into a script object
sq_pushinteger(DefaultVM::Get(), ConvTo< SQInteger >::From(value)); 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 // 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 // We've identified the correct value type
identified = false; identified = false;
} }
@ -801,7 +817,7 @@ bool CmdManager::Parse()
// Transform it into a script object // Transform it into a script object
sq_pushfloat(DefaultVM::Get(), ConvTo< SQFloat >::From(value)); 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 // 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 // We've identified the correct value type
identified = false; identified = false;
} }
@ -841,7 +857,7 @@ bool CmdManager::Parse()
if (identified) if (identified)
{ {
// Get the object from the stack and add it to the argument list along with it's type // 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 // If everything else failed then simply treat the value as a string
@ -853,23 +869,23 @@ bool CmdManager::Parse()
if (arg_flags & CMDARG_LOWER) if (arg_flags & CMDARG_LOWER)
{ {
// Convert all characters from the argument string into the buffer // 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)); *chr = static_cast< CharT >(std::tolower(*str));
} }
// Transform it into a script object // 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? // Do we have to make the string uppercase?
else if (arg_flags & CMDARG_UPPER) else if (arg_flags & CMDARG_UPPER)
{ {
// Convert all characters from the argument string into the buffer // 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)); *chr = static_cast< CharT >(std::toupper(*str));
} }
// Transform it into a script object // 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 else
{ {
@ -877,13 +893,13 @@ bool CmdManager::Parse()
sq_pushstring(DefaultVM::Get(), str, sz); sq_pushstring(DefaultVM::Get(), str, sz);
} }
// Get the object from the stack and add it to the argument list along with it's type // 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 // 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? // Is there anything left to parse?
if (itr >= m_Argument.end()) if (itr >= ctx.mArgument.end())
{ {
break; break;
} }
@ -1726,6 +1742,12 @@ static void Cmd_SetOnAuth(Object & env, Function & func)
CmdManager::Get().SetOnAuth(env, func); CmdManager::Get().SetOnAuth(env, func);
} }
// ------------------------------------------------------------------------------------------------
static bool Cmd_IsContext()
{
return CmdManager::Get().IsContext();
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
static Object & Cmd_GetInvoker() static Object & Cmd_GetInvoker()
{ {
@ -1835,6 +1857,7 @@ void Register_Command(HSQUIRRELVM vm)
cmdns.Func(_SC("GetOnAuth"), &Cmd_GetOnAuth); cmdns.Func(_SC("GetOnAuth"), &Cmd_GetOnAuth);
cmdns.Func(_SC("SetOnAuth"), &Cmd_SetOnAuth); cmdns.Func(_SC("SetOnAuth"), &Cmd_SetOnAuth);
cmdns.Func(_SC("BindAuth"), &Cmd_SetOnAuth); cmdns.Func(_SC("BindAuth"), &Cmd_SetOnAuth);
cmdns.Func(_SC("Context"), &Cmd_IsContext);
cmdns.Func(_SC("Invoker"), &Cmd_GetInvoker); cmdns.Func(_SC("Invoker"), &Cmd_GetInvoker);
cmdns.Func(_SC("InvokerID"), &Cmd_GetInvokerID); cmdns.Func(_SC("InvokerID"), &Cmd_GetInvokerID);
cmdns.Func(_SC("Instance"), &Cmd_GetObject); cmdns.Func(_SC("Instance"), &Cmd_GetObject);

View File

@ -30,15 +30,75 @@ private:
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. * 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. * Copy constructor.
@ -127,26 +187,11 @@ private:
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
typedef std::vector< Command > CmdList; typedef std::vector< Command > CmdList;
// --------------------------------------------------------------------------------------------
typedef std::pair< Uint8, Object > CmdArg;
typedef std::vector< CmdArg > CmdArgs;
private: private:
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
Buffer m_Buffer; // Shared buffer used to extract arguments.
CmdList m_Commands; // List of available command instances. CmdList m_Commands; // List of available command instances.
CtxRef m_Context; // The context of the currently executed command.
// --------------------------------------------------------------------------------------------
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.
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
Function m_OnFail; // Callback when something failed while running a 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()); 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. * Retrieve the identifier of the last invoker.
*/ */
Int32 GetInvoker() const 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 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 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 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 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. * Attempt to execute the specified command.
*/ */
Int32 Exec(); Int32 Exec(Context & ctx);
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Attempt to parse the specified argument. * Attempt to parse the specified argument.
*/ */
bool Parse(); bool Parse(Context & ctx);
public: public: