// ------------------------------------------------------------------------------------------------ #include "Command.hpp" #include "Core.hpp" #include "Entity/Player.hpp" // ------------------------------------------------------------------------------------------------ #include #include #include #include #include // ------------------------------------------------------------------------------------------------ namespace SqMod { // ------------------------------------------------------------------------------------------------ CmdManager * _Cmd = NULL; // ------------------------------------------------------------------------------------------------ CSStr CmdArgSpecToStr(Uint8 spec) { switch (spec) { case CMDARG_ANY: return _SC("any"); case CMDARG_INTEGER: return _SC("integer"); case CMDARG_FLOAT: return _SC("float"); case CMDARG_BOOLEAN: return _SC("boolean"); case CMDARG_STRING: case CMDARG_LOWER: case CMDARG_UPPER: case CMDARG_GREEDY: return _SC("string"); default: return _SC("unknown"); } } // ------------------------------------------------------------------------------------------------ CmdManager::CmdManager() : m_Buffer(512) , m_Commands() , m_Invoker(SQMOD_UNKNOWN) , m_Command() , m_Argument() , m_Argv() , m_Argc(0) { /* ... */ } // ------------------------------------------------------------------------------------------------ CmdManager::~CmdManager() { /* ... */ } // ------------------------------------------------------------------------------------------------ void CmdManager::Terminate() { // Release the script resources from command instances for (CmdList::iterator itr = m_Commands.begin(); itr != m_Commands.end(); ++itr) { if (itr->second) { itr->second->m_OnExec.ReleaseGently(); itr->second->m_OnAuth.ReleaseGently(); itr->second->m_OnPost.ReleaseGently(); itr->second->m_OnFail.ReleaseGently(); } } // Release the script resources from this class m_Argv.clear(); m_OnError.ReleaseGently(); m_OnAuth.ReleaseGently(); } // ------------------------------------------------------------------------------------------------ Int32 CmdManager::Run(Int32 invoker, CCStr command) { // Validate the string command if (!command || strlen(command) <= 0) { SqError(CMDERR_EMPTY_COMMAND, _SC("Invalid or empty command name"), invoker); return -1; } // Save the invoker identifier m_Invoker = invoker; // Skip whitespace until the command name while (*command == ' ') ++command; // Find where the command name ends CCStr split = strchr(command, ' '); // Are there any arguments specified? if (split != NULL) { // Save the command name m_Command.assign(command, (split - command)); // Skip white space after command name while (*split == ' ') ++split; // Save the command argument m_Argument.assign(split); } // No arguments specified else { m_Command.assign(command); m_Argument.assign(""); } // Did anything remain after cleaning? if (m_Command.empty()) { SqError(CMDERR_INVALID_COMMAND, _SC("Cannot execute invalid command name"), invoker); return -1; } // Attempt to find the specified command CmdList::iterator itr = m_Commands.find(m_Command); // Have we found anything? if (itr == m_Commands.end()) { SqError(CMDERR_UNKNOWN_COMMAND, _SC("Unable to find the specified command"), m_Command.c_str()); return -1; } // Is the command instance valid? (just in case) else if (!itr->second) { m_Commands.erase(itr); SqError(CMDERR_UNKNOWN_COMMAND, _SC("Unable to find the specified command"), m_Command.c_str()); return -1; } // Save the command instance m_Instance = itr->second; // Place a lock on the command m_Instance->m_Locked = true; // Value returned by the command Int32 ret = -1; // Attempt to execute the command try { ret = Exec(); } catch (...) { SqError(CMDERR_EXECUTION_FAILED, _SC("Exceptions occurred during execution"), m_Invoker); } // Remove the lock from the command m_Instance->m_Protected = false; // Release the command instance m_Instance = NULL; // Return the result return ret; } // ------------------------------------------------------------------------------------------------ Int32 CmdManager::Exec() { // Clear previous arguments m_Argv.clear(); // Reset the argument counter m_Argc = 0; // Make sure the invoker has enough authority to execute this command if (!m_Instance->AuthCheckID(m_Invoker)) { SqError(CMDERR_INSUFFICIENT_AUTH, _SC("Insufficient authority to execute command"), m_Invoker); // Command failed return -1; } // Make sure an executer was specified else if (m_Instance->GetOnExec().IsNull()) { SqError(CMDERR_MISSING_EXECUTER, _SC("No executer was specified for this command"), m_Invoker); // Command failed return -1; } // See if there are any arguments to parse else if (!m_Argument.empty() && !Parse()) { // The error message was reported while parsing return -1; } // Make sure we have enough arguments specified else if (m_Instance->GetMinArgC() > m_Argc) { SqError(CMDERR_INCOMPLETE_ARGS, _SC("Incomplete command arguments"), m_Instance->GetMinArgC()); // Command failed return -1; } // The check during the parsing may omit the last argument else if (m_Instance->GetMaxArgC() < m_Argc) { SqError(CMDERR_EXTRANEOUS_ARGS, _SC("Extraneous command arguments"), m_Instance->GetMaxArgC()); // Command failed return -1; } // Check argument types against the command specifiers for (Uint32 arg = 0; arg < m_Argc; ++arg) { if (!m_Instance->ArgCheck(arg, m_Argv[arg].first)) { SqError(CMDERR_UNSUPPORTED_ARG, _SC("Unsupported command argument"), arg); // Command failed return -1; } } // Result of the command execution SQInteger result = -1; // Do we have to call the command with an associative container? if (m_Instance->m_Associate) { // Create the associative container Table args(DefaultVM::Get()); // Copy the arguments into the table for (Uint32 arg = 0; arg < m_Argc; ++arg) { // Do we have use the argument index as the key? if (m_Instance->m_ArgTags[arg].empty()) { args.SetValue(SQInteger(arg), m_Argv[arg].second); } // Nope, we have a name for this argument! else { args.SetValue(m_Instance->m_ArgTags[arg].c_str(), m_Argv[arg].second); } } // Attempt to execute the command with the specified arguments result = m_Instance->Execute(_Core->GetPlayer(m_Invoker).mObj, args); } else { // Reserve an array for the extracted arguments Array args(DefaultVM::Get(), m_Argc); // Copy the arguments into the array for (Uint32 arg = 0; arg < m_Argc; ++arg) { args.Bind(SQInteger(arg), m_Argv[arg].second); } // Attempt to execute the command with the specified arguments result = m_Instance->Execute(_Core->GetPlayer(m_Invoker).mObj, args); } // See if an error occurred or an exception was thrown if (Error::Occurred(DefaultVM::Get())) { SqError(CMDERR_EXECUTION_FAILED, _SC("Command execution failed"), Error::Message(DefaultVM::Get()).c_str()); if (!m_Instance->m_OnFail.IsNull()) { m_Instance->m_OnFail.Execute(_Core->GetPlayer(m_Invoker).mObj, result); } // Result is invalid at this point result = -1; } // See if the command failed explicitly else if (!result) { SqError(CMDERR_EXECUTION_FAILED, _SC("Command execution failed"), _SC("executor evaluated to false")); if (!m_Instance->m_OnFail.IsNull()) { m_Instance->m_OnFail.Execute(_Core->GetPlayer(m_Invoker).mObj, result); } } else if (!m_Instance->m_OnPost.IsNull()) { m_Instance->m_OnPost.Execute(_Core->GetPlayer(m_Invoker).mObj, result); } // Return the result return Int32(result); } // ------------------------------------------------------------------------------------------------ bool CmdManager::Parse() { // Is there anything to parse? if (m_Argument.empty()) { return true; /* Done parsing */ } // Obtain the flags of the currently processed argument Uint8 arg_flags = m_Instance->m_ArgSpec[m_Argc]; // Adjust the internal buffer if necessary (mostly never) m_Buffer.Adjust< SQChar >(m_Argument.size()); // The iterator to the currently processed character String::iterator itr = m_Argument.begin(); // Previous and currently processed character SQChar prev = 0, elem = 0; // Maximum arguments allowed to be processed const Uint8 max_arg = m_Instance->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 there's anything left to parse if (elem == 0) { break; } // Early check to prevent parsing extraneous arguments else if (m_Argc >= max_arg) { SqError(CMDERR_EXTRANEOUS_ARGS, _SC("Extraneous command arguments"), max_arg); // Parsing aborted good = false; // Stop parsing break; } // Is this a greedy argument? else if (arg_flags & CMDARG_GREEDY) { // Skip whitespace while (*itr == ' ' && itr != m_Argument.end()) ++itr; // Anything left to copy? if (itr != m_Argument.end()) { // Transform it into a script object sq_pushstring(DefaultVM::Get(), &(*itr), std::distance(itr, m_Argument.end())); } // Just push an empty string else { sq_pushstring(DefaultVM::Get(), _SC(""), 0); } // Get the object from the stack Var< Object & > var(DefaultVM::Get(), -1); // Pop the created object from the stack if (!var.value.IsNull()) { sq_pop(DefaultVM::Get(), 1); } // Add it to the argument list along with it's type m_Argv.push_back(CmdArgs::value_type(CMDARG_STRING, var.value)); // Include this argument into the count ++m_Argc; // 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 = m_Buffer.Begin< SQChar >(); CSStr end = (m_Buffer.End< SQChar >()-1); /* + null */ // Save the closing quote type 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) { SqError(CMDERR_SYNTAX_ERROR, _SC("String argument not closed properly"), m_Argc); // 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"), m_Invoker); // Parsing aborted good = false; // Stop parsing break; } // Simply replicate the character to the internal buffer *(str++) = elem; // Advance to the next character ++itr; } // See if the argument was valid if (!good) { // Propagate failure break; } // Do we have to make the string lowercase? else if (arg_flags & CMDARG_LOWER) { for (SStr chr = m_Buffer.Begin< SQChar >(); chr <= str; ++chr) { *chr = (SQChar)tolower(*chr); } } // Do we have to make the string uppercase? else if (arg_flags & CMDARG_UPPER) { for (SStr chr = m_Buffer.Begin< SQChar >(); chr <= str; ++chr) { *chr = (SQChar)toupper(*chr); } } // Transform it into a script object sq_pushstring(DefaultVM::Get(), m_Buffer.Get< SQChar >(), str - m_Buffer.Begin< SQChar >()); // Get the object from the stack Var< Object & > var(DefaultVM::Get(), -1); // Pop the created object from the stack if (!var.value.IsNull()) { sq_pop(DefaultVM::Get(), 1); } // Add it to the argument list along with it's type m_Argv.push_back(CmdArgs::value_type(CMDARG_STRING, var.value)); // Advance to the next argument and obtain its flags arg_flags = m_Instance->m_ArgSpec[++m_Argc]; } // Ignore space characters until another valid character is found else if (elem != ' ' && (prev == ' ' || prev == 0)) { // Find the first character that marks the end of the argument String::iterator pos = std::find(String::iterator(itr), m_Argument.end(), ' '); // Copy all characters within range into the internal buffer const Uint32 sz = m_Buffer.Write(0, &(*itr), std::distance(itr, pos)); // Update the main iterator position itr = pos; // Update the current character elem = *itr; // Make sure the argument string is null terminated m_Buffer.At< SQChar >(sz) = 0; // 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 (!identified) { // Let's us know if the whole argument was part of the resulted value CStr next = NULL; // Attempt to extract the integer value from the string Int64 value = strtol(m_Buffer.Data(), &next, 10); // See if this whole string was indeed an integer if (next == &m_Buffer.At< SQChar >(sz)) { // Transform it into a script object Var< SQInteger >::push(DefaultVM::Get(), SQInteger(value)); // Get the object from the stack Var< Object & > var(DefaultVM::Get(), -1); // Pop the created object from the stack if (!var.value.IsNull()) { sq_pop(DefaultVM::Get(), 1); } // Add it to the argument list along with it's type m_Argv.push_back(CmdArgs::value_type(CMDARG_INTEGER, var.value)); // We identified the correct value identified = true; } } // Attempt to treat the value as an floating point number if (!identified) { // Let's us know if the whole argument was part of the resulted value CStr next = NULL; // Attempt to extract the floating point value from the string Float64 value = strtod(m_Buffer.Data(), &next); // See if this whole string was indeed an floating point if (next == &m_Buffer.At< SQChar >(sz)) { // Transform it into a script object Var< SQFloat >::push(DefaultVM::Get(), SQFloat(value)); // Get the object from the stack Var< Object & > var(DefaultVM::Get(), -1); // Pop the created object from the stack if (!var.value.IsNull()) { sq_pop(DefaultVM::Get(), 1); } // Add it to the argument list along with it's type m_Argv.push_back(CmdArgs::value_type(CMDARG_FLOAT, var.value)); // We identified the correct value identified = true; } } // Attempt to treat the value as a boolean if possible if (!identified && sz <= 6) { // Allocate memory for enough data to form a boolean value SQChar lc[6]; // Fill the temporary buffer with data from the internal buffer snprintf (lc, 6, "%.5s", m_Buffer.Data()); // Convert all characters to lowercase for (Uint32 i = 0; i < 5; ++i) { lc[i] = tolower(lc[i]); } // Is this a boolean true value? if (strcmp(m_Buffer.Data(), "true") == 0 || strcmp(m_Buffer.Data(), "on") == 0) { // Transform it into a script object Var< bool >::push(DefaultVM::Get(), true); // Get the object from the stack Var< Object & > var(DefaultVM::Get(), -1); // Pop the created object from the stack if (!var.value.IsNull()) { sq_pop(DefaultVM::Get(), 1); } // Add it to the argument list along with it's type m_Argv.push_back(CmdArgs::value_type(CMDARG_BOOLEAN, var.value)); // We identified the correct value identified = true; } // Is this a boolean false value? else if (strcmp(m_Buffer.Data(), "false") == 0 || strcmp(m_Buffer.Data(), "off") == 0) { // Transform it into a script object Var< bool >::push(DefaultVM::Get(), false); // Get the object from the stack Var< Object & > var(DefaultVM::Get(), -1); // Pop the created object from the stack if (!var.value.IsNull()) { sq_pop(DefaultVM::Get(), 1); } // Add it to the argument list along with it's type m_Argv.push_back(CmdArgs::value_type(CMDARG_BOOLEAN, var.value)); // We identified the correct value identified = true; } } // If everything else failed then simply treat the value as a string if (!identified) { // Do we have to make the string lowercase? if (arg_flags & CMDARG_LOWER) { for (Uint32 n = 0; n < sz; ++n) { m_Buffer.At< SQChar >(n) = (SQChar)tolower(m_Buffer.At< SQChar >(n)); } } // Do we have to make the string uppercase? else if (arg_flags & CMDARG_UPPER) { for (Uint32 n = 0; n < sz; ++n) { m_Buffer.At< SQChar >(n) = (SQChar)toupper(m_Buffer.At< SQChar >(n)); } } // Transform it into a script object sq_pushstring(DefaultVM::Get(), m_Buffer.Get< SQChar >(), sz); // Get the object from the stack Var< Object & > var(DefaultVM::Get(), -1); // Pop the created object from the stack if (!var.value.IsNull()) { sq_pop(DefaultVM::Get(), 1); } // Add it to the argument list along with it's type m_Argv.push_back(CmdArgs::value_type(CMDARG_STRING, var.value)); } // Advance to the next argument and obtain its flags arg_flags = m_Instance->m_ArgSpec[++m_Argc]; } // Is there anything left to parse? if (itr >= m_Argument.end()) { break; } // Advance to the next character ++itr; } // Return whether the parsing was successful return good; } // ------------------------------------------------------------------------------------------------ void CmdListener::Init(CSStr name, CSStr spec, Array & tags, Uint8 min, Uint8 max) { m_Name.assign(""); // Initialize the specifiers and tags to default values for (Uint8 n = 0; n < SQMOD_MAX_CMD_ARGS; ++n) { m_ArgSpec[n] = CMDARG_ANY; m_ArgTags[n].assign(""); } m_MinArgc = 0; m_MaxArgc = (SQMOD_MAX_CMD_ARGS - 1); m_Spec.assign(""); m_Help.assign(""); m_Info.assign(""); m_OnAuth = _Cmd->GetOnAuth(); m_Authority = -1; m_Protected = false; m_Suspended = false; m_Associate = false; m_Locked = false; // Set the minimum and maximum allowed arguments SetMinArgC(min); SetMaxArgC(max); // Extract the specified argument tags SetArgTags(tags); // Bind to the specified command name SetName(name); // Apply the specified argument rules SetSpec(spec); } // ------------------------------------------------------------------------------------------------ CmdListener::CmdListener(CSStr name) { Init(name, _SC(""), NullArray(), 0, SQMOD_MAX_CMD_ARGS-1); } CmdListener::CmdListener(CSStr name, CSStr spec) { Init(name, spec, NullArray(), 0, SQMOD_MAX_CMD_ARGS-1); } CmdListener::CmdListener(CSStr name, CSStr spec, Array & tags) { Init(name, spec, tags, 0, SQMOD_MAX_CMD_ARGS-1); } CmdListener::CmdListener(CSStr name, CSStr spec, Uint8 min, Uint8 max) { Init(name, spec, NullArray(), min, max); } CmdListener::CmdListener(CSStr name, CSStr spec, Array & tags, Uint8 min, Uint8 max) { Init(name, spec, tags, min, max); } // ------------------------------------------------------------------------------------------------ CmdListener::~CmdListener() { // See the instance must be detached if (!m_Name.empty()) _Cmd->Detach(m_Name); // Release callbacks m_OnExec.ReleaseGently(); m_OnAuth.ReleaseGently(); m_OnPost.ReleaseGently(); m_OnFail.ReleaseGently(); } // ------------------------------------------------------------------------------------------------ Int32 CmdListener::Cmp(const CmdListener & o) const { if (m_Name == o.m_Name) return 0; else if (m_Name.size() > o.m_Name.size()) return 1; else return -1; } // ------------------------------------------------------------------------------------------------ CSStr CmdListener::ToString() const { return m_Name.c_str(); } // ------------------------------------------------------------------------------------------------ Uint8 CmdListener::GetArgFlags(Uint32 idx) const { if (idx < SQMOD_MAX_CMD_ARGS) return m_ArgSpec[idx]; return CMDARG_ANY; } // ------------------------------------------------------------------------------------------------ CSStr CmdListener::GetName() const { return m_Name.c_str(); } // ------------------------------------------------------------------------------------------------ void CmdListener::SetName(CSStr name) { if (m_Locked) SqThrow("Cannot change locked command: %s", m_Name.c_str()); else if (!name || strlen(name) <= 0) SqThrow("Invalid command name: null"); else { // Detach from the current name if necessary if (!m_Name.empty()) _Cmd->Detach(name); // Now it's safe to assign the new name m_Name.assign(name); // We know the new name is valid _Cmd->Attach(m_Name, this); } } // ------------------------------------------------------------------------------------------------ CSStr CmdListener::GetSpec() const { return m_Spec.c_str(); } // ------------------------------------------------------------------------------------------------ void CmdListener::SetSpec(CSStr spec) { if (ProcSpec(spec)) m_Spec.assign(spec); } // ------------------------------------------------------------------------------------------------ Array CmdListener::GetArgTags() const { // Allocate an array to encapsulate all tags Array arr(DefaultVM::Get(), SQMOD_MAX_CMD_ARGS); // Put the tags to the allocated array for (Uint32 arg = 0; arg < SQMOD_MAX_CMD_ARGS; ++arg) arr.SetValue(arg, m_ArgTags[arg]); // Return the array with the tags return arr; } // ------------------------------------------------------------------------------------------------ void CmdListener::SetArgTags(Array & tags) { // Attempt to retrieve the number of specified tags const Uint32 max = static_cast< Uint32 >(tags.Length()); // If no tags were specified then clear current tags if (tags.IsNull() || max == 0) { for (Uint8 n = 0; n < SQMOD_MAX_CMD_ARGS; ++n) { m_ArgTags[n].assign(""); } } // See if we're in range else if (max < SQMOD_MAX_CMD_ARGS) // Attempt to get all arguments in one go tags.GetArray(m_ArgTags, max); else SqThrow("Argument tag (%u) is out of range (%u)", max, SQMOD_MAX_CMD_ARGS); } // ------------------------------------------------------------------------------------------------ CSStr CmdListener::GetHelp() const { return m_Help.c_str(); } // ------------------------------------------------------------------------------------------------ void CmdListener::SetHelp(CSStr help) { m_Help.assign(help); } // ------------------------------------------------------------------------------------------------ CSStr CmdListener::GetInfo() const { return m_Info.c_str(); } // ------------------------------------------------------------------------------------------------ void CmdListener::SetInfo(CSStr info) { m_Info.assign(info); } // ------------------------------------------------------------------------------------------------ Int32 CmdListener::GetAuthority() const { return m_Authority; } // ------------------------------------------------------------------------------------------------ void CmdListener::SetAuthority(Int32 level) { m_Authority = level; } // ------------------------------------------------------------------------------------------------ bool CmdListener::GetProtected() const { return m_Protected; } // ------------------------------------------------------------------------------------------------ void CmdListener::SetProtected(bool toggle) { m_Protected = toggle; } // ------------------------------------------------------------------------------------------------ bool CmdListener::GetSuspended() const { return m_Suspended; } // ------------------------------------------------------------------------------------------------ void CmdListener::SetSuspended(bool toggle) { m_Suspended = toggle; } // ------------------------------------------------------------------------------------------------ bool CmdListener::GetAssociate() const { return m_Associate; } // ------------------------------------------------------------------------------------------------ void CmdListener::SetAssociate(bool toggle) { m_Associate = toggle; } // ------------------------------------------------------------------------------------------------ Uint8 CmdListener::GetMinArgC() const { return m_MinArgc; } // ------------------------------------------------------------------------------------------------ void CmdListener::SetMinArgC(Uint8 val) { // Assuming (m_MaxArgc < SQMOD_MAX_CMD_ARGS) is always true if (val <= m_MaxArgc) m_MinArgc = val; else SqThrow("Minimum argument (%u) exceeds maximum (%u)", val, m_MaxArgc); } // ------------------------------------------------------------------------------------------------ Uint8 CmdListener::GetMaxArgC() const { return m_MaxArgc; } // ------------------------------------------------------------------------------------------------ void CmdListener::SetMaxArgC(Uint8 val) { if (val < SQMOD_MAX_CMD_ARGS && val >= m_MinArgc) m_MaxArgc = val; else if (val < m_MinArgc) SqThrow("Minimum argument (%u) exceeds maximum (%u)", m_MinArgc, val); else SqThrow("Argument (%u) is out of total range (%u)", val, SQMOD_MAX_CMD_ARGS); } // ------------------------------------------------------------------------------------------------ bool CmdListener::GetLocked() const { return m_Locked; } // ------------------------------------------------------------------------------------------------ Function & CmdListener::GetOnExec() { return m_OnExec; } // ------------------------------------------------------------------------------------------------ void CmdListener::SetOnExec(Object & env, Function & func) { if (!m_Name.empty()) m_OnExec = Function(env.GetVM(), env.GetObject(), func.GetFunc()); else SqThrow("Invalid commands cannot store script resources"); } // ------------------------------------------------------------------------------------------------ Function & CmdListener::GetOnAuth() { return m_OnAuth; } // ------------------------------------------------------------------------------------------------ void CmdListener::SetOnAuth(Object & env, Function & func) { if (!m_Name.empty()) m_OnAuth = Function(env.GetVM(), env.GetObject(), func.GetFunc()); else SqThrow("Invalid commands cannot store script resources"); } // ------------------------------------------------------------------------------------------------ Function & CmdListener::GetOnPost() { return m_OnPost; } // ------------------------------------------------------------------------------------------------ void CmdListener::SetOnPost(Object & env, Function & func) { if (!m_Name.empty()) m_OnPost = Function(env.GetVM(), env.GetObject(), func.GetFunc()); else SqThrow("Invalid commands cannot store script resources"); } // ------------------------------------------------------------------------------------------------ Function & CmdListener::GetOnFail() { return m_OnFail; } // ------------------------------------------------------------------------------------------------ void CmdListener::SetOnFail(Object & env, Function & func) { if (!m_Name.empty()) m_OnFail = Function(env.GetVM(), env.GetObject(), func.GetFunc()); else SqThrow("Invalid commands cannot store script resources"); } // ------------------------------------------------------------------------------------------------ CSStr CmdListener::GetArgTag(Uint32 arg) const { if (arg < SQMOD_MAX_CMD_ARGS) return m_ArgTags[arg].c_str(); SqThrow("Argument (%u) is out of total range (%u)", arg, SQMOD_MAX_CMD_ARGS); return g_EmptyStr; } // ------------------------------------------------------------------------------------------------ void CmdListener::SetArgTag(Uint32 arg, CSStr name) { if (arg < SQMOD_MAX_CMD_ARGS) m_ArgTags[arg].assign(name); else SqThrow("Argument (%u) is out of total range (%u)", arg, SQMOD_MAX_CMD_ARGS); } // ------------------------------------------------------------------------------------------------ void CmdListener::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) // Stop the main loop as well break; } // Begin the argument block m_Info += '<'; // If the current argument is beyond minimum then mark it as optional if (arg >= m_MinArgc) m_Info += '*'; // If the argument has a tag/name associated then add it as well if (!m_ArgTags[arg].empty()) { // Add the name first m_Info += m_ArgTags[arg]; // Separate the name from the specifiers m_Info += ':'; } // Is this a greedy argument? if (m_ArgSpec[arg] & CMDARG_GREEDY) { m_Info += _SC("..."); } // If the argument has any explicit types specified else if (m_ArgSpec[arg] != CMDARG_ANY) { // Does it support integers? if (m_ArgSpec[arg] & CMDARG_INTEGER) m_Info += _SC("integer"); // Does it support floats? if (m_ArgSpec[arg] & CMDARG_FLOAT) { // Add a separator if this is not the first enabled type? if (m_Info[m_Info.size()-1] != ':' && m_Info[m_Info.size()-1] != '<') m_Info += ','; // Now add the type name m_Info += _SC("float"); } // Does it support booleans? if (m_ArgSpec[arg] & CMDARG_BOOLEAN) { // Add a separator if this is not the first enabled type? if (m_Info[m_Info.size()-1] != ':' && m_Info[m_Info.size()-1] != '<') m_Info += ','; // Now add the type name m_Info += _SC("boolean"); } // Does it support strings? if (m_ArgSpec[arg] & CMDARG_STRING) { // Add a separator if this is not the first enabled type? if (m_Info[m_Info.size()-1] != ':' && m_Info[m_Info.size()-1] != '<') m_Info += ','; // Now add the type name m_Info += _SC("string"); } } // Any kind of value is supported by this argument else m_Info += _SC("any"); // Stop the argument block m_Info += '>'; // Don't process anything after greedy arguments if (m_ArgSpec[arg] & CMDARG_GREEDY) { break; } // If this is not the last argument then add a separator else if (arg+1 != m_MaxArgc) m_Info += ' '; } } // ------------------------------------------------------------------------------------------------ bool CmdListener::ArgCheck(Uint32 arg, Uint8 flag) const { if (arg < SQMOD_MAX_CMD_ARGS) { const Uint8 f = m_ArgSpec[arg]; return (f == CMDARG_ANY) || /* Requires check? */ (f & flag) || /* Exact match? */ (f & CMDARG_GREEDY && flag & CMDARG_STRING); } SqThrow("Argument (%u) is out of total range (%u)", arg, SQMOD_MAX_CMD_ARGS); return false; } // ------------------------------------------------------------------------------------------------ bool CmdListener::AuthCheck(CPlayer & player) { return AuthCheckID(player.GetID()); } // ------------------------------------------------------------------------------------------------ bool CmdListener::AuthCheckID(Int32 id) { // Do we need explicit authority verification? if (!m_Protected) return true; // Allow execution by default bool allow = true; // Was there a custom authority inspector specified? if (!m_OnAuth.IsNull()) { // Ask the specified authority inspector if this execution should be allowed SharedPtr< bool > ret = m_OnAuth.Evaluate< bool, Object & >(_Core->GetPlayer(id).mObj); // See what the custom authority inspector said or default to disallow allow = (!ret ? false : *ret); } // Can we use the default authority system? else if (m_Authority >= 0) allow = (_Core->GetPlayer(id).mAuthority >= m_Authority); // Return result return allow; } // ------------------------------------------------------------------------------------------------ SQInteger CmdListener::Execute(Object & invoker, Array & args) { // Attempt to evaluate the specified executer knowing the manager did the validations SharedPtr< SQInteger > ret = m_OnExec.Evaluate< SQInteger, Object &, Array & >(invoker, args); // See if the executer succeeded and return the result or default to failed return (!ret ? 0 : *ret); } // ------------------------------------------------------------------------------------------------ SQInteger CmdListener::Execute(Object & invoker, Table & args) { // Attempt to evaluate the specified executer knowing the manager did the validations SharedPtr< SQInteger > ret = m_OnExec.Evaluate< SQInteger, Object &, Table & >(invoker, args); // See if the executer succeeded and return the result or default to failed return (!ret ? 0 : *ret); } // ------------------------------------------------------------------------------------------------ bool CmdListener::ProcSpec(CSStr str) { // Reset current argument specifiers memset(m_ArgSpec, CMDARG_ANY, sizeof(m_ArgSpec)); // Currently processed character SQChar ch = 0; // When parsing may continue bool good = true; // Currently processed argument Uint32 idx = 0; // Process each character in the specified command while (good) { // Extract the current character before advancing ch = *(str++); // See if there are still things left to parse if (ch == 0) // Finished parsing successfully break; // See if we need to move to the next argument else if (ch == '|') { if (idx >= SQMOD_MAX_CMD_ARGS) { SqThrow("Extraneous type specifiers: %d >= %d", idx, SQMOD_MAX_CMD_ARGS); // Parsing failed good = false; // Stop parsing break; } // Advance to the next argument ++idx; } // Simply ignore a type specifier delimiter else if (ch != ',') { // Consume space when found if (ch == ' ') while (good) { ch = *(str++); // Stop when the text ends or on the first non-space character if (ch == 0 || ch != ' ') break; } // See if there is a specifier left if (!good) // Propagate the stop break; // Apply the type specifier switch(ch) { // 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: { SqThrow("Unknown type specifier (%c) at argument: %u", ch, idx); // Parsing failed good = false; } break; } } } // Reset all argument specifiers if failed if (!good) memset(m_ArgSpec, CMDARG_ANY, sizeof(m_ArgSpec)); // Attempt to generate an informational message GenerateInfo(false); // Return whether the parsing was successful return good; } /* ------------------------------------------------------------------------------------------------ * Forward the call to run a command. */ Int32 RunCommand(Int32 invoker, CSStr command) { return _Cmd->Run(invoker, command); } /* ------------------------------------------------------------------------------------------------ * Forward the call to terminate the command system. */ void TerminateCommand() { _Cmd->Terminate(); } // ------------------------------------------------------------------------------------------------ static Function & Cmd_GetOnError() { return _Cmd->GetOnError(); } // ------------------------------------------------------------------------------------------------ static void Cmd_SetOnError(Object & env, Function & func) { _Cmd->SetOnError(env, func); } // ------------------------------------------------------------------------------------------------ static Function & Cmd_GetOnAuth() { return _Cmd->GetOnAuth(); } // ------------------------------------------------------------------------------------------------ static void Cmd_SetOnAuth(Object & env, Function & func) { _Cmd->SetOnAuth(env, func); } // ------------------------------------------------------------------------------------------------ static Object & Cmd_GetInvoker() { return _Core->GetPlayer(_Cmd->GetInvoker()).mObj; } // ------------------------------------------------------------------------------------------------ static Int32 Cmd_GetInvokerID() { return _Cmd->GetInvoker(); } // ------------------------------------------------------------------------------------------------ static CSStr Cmd_GetCommand() { return _Cmd->GetCommand(); } // ------------------------------------------------------------------------------------------------ static CSStr Cmd_GetArgument() { return _Cmd->GetArgument(); } // ================================================================================================ void Register_Command(HSQUIRRELVM vm) { Table cmdns(vm); cmdns.Bind(_SC("Listener"), Class< CmdListener, NoCopy< CmdListener > >(vm, _SC("Listener")) /* Constructors */ .Ctor< CSStr >() .Ctor< CSStr, CSStr >() .Ctor< CSStr, CSStr, Array & >() .Ctor< CSStr, CSStr, Uint8, Uint8 >() .Ctor< CSStr, CSStr, Array &, Uint8, Uint8 >() /* Metamethods */ .Func(_SC("_cmp"), &CmdListener::Cmp) .Func(_SC("_tostring"), &CmdListener::ToString) /* Properties */ .Prop(_SC("Name"), &CmdListener::GetName, &CmdListener::SetName) .Prop(_SC("Spec"), &CmdListener::GetSpec, &CmdListener::SetSpec) .Prop(_SC("Specifier"), &CmdListener::GetSpec, &CmdListener::SetSpec) .Prop(_SC("Tags"), &CmdListener::GetArgTags, &CmdListener::SetArgTags) .Prop(_SC("Help"), &CmdListener::GetHelp, &CmdListener::SetHelp) .Prop(_SC("Info"), &CmdListener::GetInfo, &CmdListener::SetInfo) .Prop(_SC("Authority"), &CmdListener::GetAuthority, &CmdListener::SetAuthority) .Prop(_SC("Protected"), &CmdListener::GetProtected, &CmdListener::SetProtected) .Prop(_SC("Suspended"), &CmdListener::GetSuspended, &CmdListener::SetSuspended) .Prop(_SC("Associate"), &CmdListener::GetAssociate, &CmdListener::SetAssociate) .Prop(_SC("MinArgs"), &CmdListener::GetMinArgC, &CmdListener::SetMinArgC) .Prop(_SC("MaxArgs"), &CmdListener::GetMaxArgC, &CmdListener::SetMaxArgC) .Prop(_SC("Locked"), &CmdListener::GetLocked) .Prop(_SC("OnExec"), &CmdListener::GetOnExec) .Prop(_SC("OnAuth"), &CmdListener::GetOnAuth) .Prop(_SC("OnPost"), &CmdListener::GetOnPost) .Prop(_SC("OnFail"), &CmdListener::GetOnFail) /* Functions */ .Func(_SC("BindExec"), &CmdListener::SetOnExec) .Func(_SC("BindAuth"), &CmdListener::SetOnAuth) .Func(_SC("BindPost"), &CmdListener::SetOnPost) .Func(_SC("BindFail"), &CmdListener::SetOnFail) .Func(_SC("GetArgTag"), &CmdListener::GetArgTag) .Func(_SC("SetArgTag"), &CmdListener::SetArgTag) .Func(_SC("GenerateInfo"), &CmdListener::GenerateInfo) .Func(_SC("ArgCheck"), &CmdListener::ArgCheck) .Func(_SC("AuthCheck"), &CmdListener::AuthCheck) .Func(_SC("AuthCheckID"), &CmdListener::AuthCheckID) ); cmdns.Func(_SC("GetOnError"), &Cmd_GetOnError); cmdns.Func(_SC("SetOnError"), &Cmd_SetOnError); cmdns.Func(_SC("GetOnAuth"), &Cmd_GetOnAuth); cmdns.Func(_SC("SetOnAuth"), &Cmd_SetOnAuth); cmdns.Func(_SC("GetInvoker"), &Cmd_GetInvoker); cmdns.Func(_SC("GetInvokerID"), &Cmd_GetInvokerID); cmdns.Func(_SC("GetName"), &Cmd_GetCommand); cmdns.Func(_SC("GetText"), &Cmd_GetArgument); RootTable(vm).Bind(_SC("SqCmd"), cmdns); ConstTable(vm).Enum(_SC("CmdArg"), 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("CmdErr"), 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("MissingExecuter"), CMDERR_MISSING_EXECUTER) .Const(_SC("InsufficientAuth"), CMDERR_INSUFFICIENT_AUTH) .Const(_SC("IncompleteArgs"), CMDERR_INCOMPLETE_ARGS) .Const(_SC("ExtraneousArgs"), CMDERR_EXTRANEOUS_ARGS) .Const(_SC("UnsupportedArg"), CMDERR_UNSUPPORTED_ARG) .Const(_SC("ExecutionFailed"), CMDERR_EXECUTION_FAILED) .Const(_SC("BufferOverflow"), CMDERR_BUFFER_OVERFLOW) .Const(_SC("Max"), CMDERR_MAX) ); } } // Namespace:: SqMod