mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2024-11-08 08:47:17 +01:00
1297 lines
44 KiB
C++
1297 lines
44 KiB
C++
#include "Command.hpp"
|
|
#include "Register.hpp"
|
|
#include "Core.hpp"
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
#include <cstdlib>
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
namespace SqMod {
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
const CmdManager::Pointer _Cmd = CmdManager::Inst();
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdManager::_Finalizer(CmdManager * ptr)
|
|
{
|
|
delete ptr; /* Assuming 'delete' checks for NULL */
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
CmdManager::Pointer CmdManager::Inst()
|
|
{
|
|
if (!_Cmd)
|
|
{
|
|
return Pointer(new CmdManager(), &CmdManager::_Finalizer);
|
|
}
|
|
|
|
return Pointer(nullptr, &CmdManager::_Finalizer);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
CmdManager::CmdManager()
|
|
: m_Commands(), m_Invoker(SQMOD_UNKNOWN), m_Argv()
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
CmdManager::~CmdManager()
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdManager::VMClose()
|
|
{
|
|
// Release the reference to the specified callbacks
|
|
m_OnError.Release2();
|
|
// We must not keep arguments
|
|
m_Argv.fill(CmdArgs::value_type(CMDARG_ANY, NullData()));
|
|
// Reset the argument counter
|
|
m_Argc = 0;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdManager::Bind(const String & name, CmdListener * cmd)
|
|
{
|
|
// If it doesn't exist then it will be created
|
|
m_Commands[name] = cmd;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdManager::Unbind(const String & name)
|
|
{
|
|
m_Commands.erase(name);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Function & CmdManager::GetOnError()
|
|
{
|
|
return m_OnError;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdManager::SetOnError(Function & func)
|
|
{
|
|
m_OnError = func;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Function & CmdManager::GetOnAuth()
|
|
{
|
|
return m_OnAuth;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdManager::SetOnAuth(Function & func)
|
|
{
|
|
m_OnAuth = func;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
const CPlayer & CmdManager::GetInvoker() const
|
|
{
|
|
return m_Invoker;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInt32 CmdManager::GetInvokerID() const
|
|
{
|
|
return m_Invoker;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
const SQChar * CmdManager::GetName()
|
|
{
|
|
return m_Name.c_str();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
const SQChar * CmdManager::GetText()
|
|
{
|
|
return m_Text.c_str();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdManager::Execute(SQInt32 invoker, const String & str)
|
|
{
|
|
// Save the invoker identifier
|
|
m_Invoker.SetID(invoker);
|
|
// Find where the command name ends
|
|
String::size_type split_pos = str.find_first_of(' ');
|
|
// Attempt to separate the command name from arguments
|
|
if (split_pos != String::npos)
|
|
{
|
|
m_Name = str.substr(0, split_pos);
|
|
m_Text = str.substr(split_pos+1);
|
|
}
|
|
// No arguments specified
|
|
else
|
|
{
|
|
m_Name = str;
|
|
m_Text = _SC("");
|
|
}
|
|
// Attempt to find a command with this name
|
|
CmdPool::iterator itr = m_Commands.find(m_Name);
|
|
// See if a command with this name could be found
|
|
if (itr != m_Commands.end())
|
|
{
|
|
// Place a lock on the command
|
|
itr->second->m_Lock = true;
|
|
// Attempt to execute the command
|
|
Exec(*itr->second);
|
|
// Take the lock from the command
|
|
itr->second->m_Lock = false;
|
|
}
|
|
else
|
|
{
|
|
Error(CMDERR_UNKNOWN_COMMAND, _SC("No such command exists: %s"), m_Name.c_str());
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdManager::Exec(CmdListener & cmd)
|
|
{
|
|
// See if the invoker has enough authority to execute this command
|
|
if (!cmd.AuthCheck(m_Invoker))
|
|
{
|
|
Error(CMDERR_INSUFFICIENT_AUTH, _SC("Insufficient authority to execute command"));
|
|
// Command failed
|
|
return;
|
|
}
|
|
// See if an executer was specified
|
|
if (cmd.GetOnExec().IsNull())
|
|
{
|
|
Error(CMDERR_MISSING_EXECUTER, _SC("No executer was specified for this command"));
|
|
// Command failed
|
|
return;
|
|
}
|
|
// See if there are any arguments to parse
|
|
if (!m_Text.empty() && !Parse(cmd.GetMaxArgC()))
|
|
{
|
|
// The error message was reported while parsing
|
|
return;
|
|
}
|
|
// See if we have enough arguments specified
|
|
if (cmd.GetMinArgC() > m_Argc)
|
|
{
|
|
Error(CMDERR_INCOMPLETE_ARGS, _SC("Incomplete command arguments: %u < %u"),
|
|
m_Argc, cmd.GetMinArgC());
|
|
// Command failed
|
|
return;
|
|
}
|
|
// The check during the parsing may not count the last argument
|
|
else if (cmd.GetMaxArgC() < m_Argc)
|
|
{
|
|
Error(CMDERR_EXTRANEOUS_ARGS, _SC("Extraneous command arguments: %u < %u"),
|
|
cmd.GetMaxArgC(), m_Argc);
|
|
// Command failed
|
|
return;
|
|
}
|
|
// Check argument types against the command specifiers
|
|
for (Uint32 arg = 0; arg < m_Argc; ++arg)
|
|
{
|
|
if (!cmd.ArgCheck(arg, m_Argv[arg].first))
|
|
{
|
|
Error(CMDERR_UNSUPPORTED_ARG, _SC("Unsupported command argument: %u -> %s"),
|
|
arg, CmdArgSpecToStr(m_Argv[arg].first));
|
|
// Command failed
|
|
return;
|
|
}
|
|
}
|
|
// Reserve an array for the extracted arguments
|
|
Array args(DefaultVM::Get(), m_Argc);
|
|
// Copy the arguments into the array
|
|
for (SQUint32 arg = 0; arg < m_Argc; ++arg)
|
|
{
|
|
args.Bind(arg, m_Argv[arg].second);
|
|
}
|
|
// Attempt to execute the command with the specified arguments
|
|
bool result = cmd.Execute(m_Invoker, args);
|
|
// See if an error occurred
|
|
if (Error::Occurred(DefaultVM::Get()))
|
|
{
|
|
Error(CMDERR_EXECUTION_FAILED, _SC("Command execution failed: %s"), Error::Message(DefaultVM::Get()).c_str());
|
|
}
|
|
// See if the command failed
|
|
else if (!result)
|
|
{
|
|
Error(CMDERR_EXECUTION_FAILED, _SC("Command execution failed"));
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
bool CmdManager::Parse(SQUint32 max)
|
|
{
|
|
// Clear previous arguments
|
|
m_Argv.fill(CmdArgs::value_type(CMDARG_ANY, NullData()));
|
|
// Reset the argument counter
|
|
m_Argc = 0;
|
|
// Request a temporary buffer
|
|
Buffer buffer = _Core->PullBuffer(128);
|
|
// Internal start and end of the temporary buffer
|
|
SQChar * cur = nullptr, * end = nullptr;
|
|
// The pointer to the currently processed character
|
|
const SQChar * str = m_Text.c_str();
|
|
// Currently and previously processed character
|
|
SQChar ch = 0, pr = 0;
|
|
// When parsing may continue
|
|
bool good = true;
|
|
// Process the specified command text
|
|
while (good)
|
|
{
|
|
// Extract the current character before advancing
|
|
ch = *(str++);
|
|
// See if there's anything left to parse
|
|
if (ch == 0)
|
|
{
|
|
// Finished parsing
|
|
break;
|
|
}
|
|
// Early check to prevent parsing unneeded arguments
|
|
else if (max < m_Argc)
|
|
{
|
|
Error(CMDERR_EXTRANEOUS_ARGS, _SC("Extraneous command arguments: %u < %u"),
|
|
max, m_Argc);
|
|
// Parsing failed
|
|
good = false;
|
|
// Stop parsing
|
|
break;
|
|
}
|
|
// See if we have to extract a string argument
|
|
else if ((ch == '\'' || ch == '"') && pr != '\\')
|
|
{
|
|
// Obtain the internal beginning and ending of the temporary buffer
|
|
cur = buffer.Begin(), end = (buffer.End()-1); /* + null */
|
|
// Attempt to consume the string argument
|
|
while (good)
|
|
{
|
|
// Extract the current character before advancing
|
|
ch = *(str++);
|
|
// See if there's anything left to parse
|
|
if (ch == 0)
|
|
{
|
|
Error(CMDERR_SYNTAX_ERROR, _SC("String not closed properly near command argument: %u"), m_Argc);
|
|
// Parsing failed
|
|
good = false;
|
|
// Stop parsing
|
|
break;
|
|
}
|
|
// First un-escaped single or double quote character ends argument
|
|
if (ch == '\'' || ch == '"')
|
|
{
|
|
// Was this not escaped?
|
|
if (pr != '\\')
|
|
{
|
|
// Terminate the string value in the temporary buffer
|
|
*cur = 0;
|
|
// Stop parsing
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Overwrite last character when replicating
|
|
--cur;
|
|
}
|
|
}
|
|
// See if the temporary buffer needs to scale
|
|
else if (cur == end)
|
|
{
|
|
// Attempt to increase the size of the temporary buffer
|
|
buffer.Increase(512);
|
|
// Obtain the internal beginning and ending of the temporary buffer again
|
|
cur = (buffer.Begin() + (buffer.Size()-512)), end = (buffer.End()-1); /* + null */
|
|
}
|
|
// Simply replicate the character to the temporary buffer
|
|
*(cur++) = ch;
|
|
// Save current argument as the previous one
|
|
pr = ch;
|
|
}
|
|
// See if the argument was valid
|
|
if (!good)
|
|
{
|
|
// Propagate failure
|
|
break;
|
|
}
|
|
// Transform it into a squirrel object
|
|
sq_pushstring(DefaultVM::Get(), buffer.Data(), cur - buffer.Begin());
|
|
// Get the object from the squirrel VM
|
|
Var< SqObj > 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[m_Argc].first = CMDARG_STRING;
|
|
m_Argv[m_Argc].second = var.value;
|
|
// Move to the next argument
|
|
++m_Argc;
|
|
}
|
|
// Ignore space until another valid argument is found
|
|
else if (ch != ' ' && (pr == ' ' || pr == 0))
|
|
{
|
|
// Obtain the internal beginning and ending of the temporary buffer
|
|
cur = buffer.Begin(), end = (buffer.End()-1); /* + null */
|
|
// Move back to the previous character before extracting
|
|
--str;
|
|
// Attempt to consume the argument value
|
|
while (good)
|
|
{
|
|
// Extract the current character before advancing
|
|
ch = *(str++);
|
|
// See if there's anything left to parse
|
|
if (ch == 0)
|
|
{
|
|
// Move to the previous character so the main loop can stop
|
|
--str;
|
|
// Argument ended
|
|
break;
|
|
}
|
|
else if (ch == ' ')
|
|
{
|
|
// Argument ended
|
|
break;
|
|
}
|
|
// See if the buffer needs to scale
|
|
else if (cur == end)
|
|
{
|
|
// Attempt to increase the size of the temporary buffer
|
|
buffer.Increase(512);
|
|
// Obtain the internal beginning and ending of the temporary buffer again
|
|
cur = buffer.Begin() + (buffer.Size()-512), end = (buffer.End()-1); /* + null */
|
|
}
|
|
// Simply replicate the character to the temporary buffer
|
|
*(cur++) = ch;
|
|
// Save current argument as the previous one
|
|
pr = ch;
|
|
}
|
|
// Terminate the string value in the temporary buffer
|
|
*cur = 0;
|
|
// Used to exclude all other checks when a valid type was found
|
|
bool found = false;
|
|
// Attempt to treat the value as an integer number
|
|
if (!found)
|
|
{
|
|
SQChar * next = NULL;
|
|
// Attempt to extract the integer value
|
|
Int64 value = strtol(buffer.Data(), &next, 10);
|
|
// See if this whole string was an integer
|
|
if (next == cur)
|
|
{
|
|
// Transform it into a squirrel object
|
|
Var< SQInteger >::push(DefaultVM::Get(), _SCSQI(value));
|
|
// Get the object from the squirrel VM
|
|
Var< SqObj > 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[m_Argc].first = CMDARG_INTEGER;
|
|
m_Argv[m_Argc].second = var.value;
|
|
// We've found the correct value
|
|
found = true;
|
|
}
|
|
}
|
|
// Attempt to treat the value as a float number
|
|
if (!found)
|
|
{
|
|
SQChar * next = 0;
|
|
// Attempt to extract the floating point value
|
|
Float64 value = strtod(buffer.Data(), &next);
|
|
// See if this whole string was a float value
|
|
if (next == cur)
|
|
{
|
|
// Transform it into a squirrel object
|
|
Var< SQFloat >::push(DefaultVM::Get(), _SCSQF(value));
|
|
// Get the object from the squirrel VM
|
|
Var< SqObj > 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[m_Argc].first = CMDARG_FLOAT;
|
|
m_Argv[m_Argc].second = var.value;
|
|
// We've found the correct value
|
|
found = true;
|
|
}
|
|
}
|
|
// Attempt to treat the value as a boolean
|
|
if (!found)
|
|
{
|
|
if (strcmp(buffer.Data(), "true") == 0 || strcmp(buffer.Data(), "on") == 0)
|
|
{
|
|
// Transform it into a squirrel object
|
|
Var< bool >::push(DefaultVM::Get(), true);
|
|
// Get the object from the squirrel VM
|
|
Var< SqObj > 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[m_Argc].first = CMDARG_BOOLEAN;
|
|
m_Argv[m_Argc].second = var.value;
|
|
// We've found the correct value
|
|
found = true;
|
|
}
|
|
else if (strcmp(buffer.Data(), "false") == 0 || strcmp(buffer.Data(), "off") == 0)
|
|
{
|
|
// Transform it into a squirrel object
|
|
Var< bool >::push(DefaultVM::Get(), false);
|
|
// Get the object from the squirrel VM
|
|
Var< SqObj > 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[m_Argc].first = CMDARG_BOOLEAN;
|
|
m_Argv[m_Argc].second = var.value;
|
|
// We've found the correct value
|
|
found = true;
|
|
}
|
|
}
|
|
// If everything else failed then simply treat the value as a string
|
|
if (!found)
|
|
{
|
|
// Transform it into a squirrel object
|
|
Var< const SQChar * >::push(DefaultVM::Get(), buffer.Data(), (cur - buffer.Begin()));
|
|
// Get the object from the squirrel VM
|
|
Var< SqObj > 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[m_Argc].first = CMDARG_STRING;
|
|
m_Argv[m_Argc].second = var.value;
|
|
}
|
|
// Move to the next argument
|
|
++m_Argc;
|
|
}
|
|
// Save current argument as the previous one
|
|
pr = ch;
|
|
}
|
|
// Return the borrowed buffer
|
|
_Core->PushBuffer(std::move(buffer));
|
|
// Return whether the parsing was successful
|
|
return good;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
CmdListener::CmdListener()
|
|
: CmdListener(_SC(""), _SC(""))
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
CmdListener::CmdListener(const SQChar * name)
|
|
: CmdListener(name, _SC(""), NullArray(), 0, MAX_CMD_ARGS)
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
CmdListener::CmdListener(const SQChar * name, const SQChar * spec)
|
|
: CmdListener(name, spec, NullArray(), 0, MAX_CMD_ARGS)
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
CmdListener::CmdListener(const SQChar * name, const SQChar * spec, Array & tags)
|
|
: CmdListener(name, spec, tags, 0, MAX_CMD_ARGS)
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
CmdListener::CmdListener(const SQChar * name, const SQChar * spec, Uint8 min, Uint8 max)
|
|
: CmdListener(name, spec, NullArray(), min, max)
|
|
{
|
|
/* ... */
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
CmdListener::CmdListener(const SQChar * name, const SQChar * spec, Array & tags, Uint8 min, Uint8 max)
|
|
: m_Args({{CMDARG_ANY}})
|
|
, m_Argt({{_SC("")}})
|
|
, m_MinArgc(0)
|
|
, m_MaxArgc(MAX_CMD_ARGS)
|
|
, m_Name()
|
|
, m_Spec()
|
|
, m_Help()
|
|
, m_Info()
|
|
, m_OnExec()
|
|
, m_OnAuth(_Cmd->GetOnAuth())
|
|
, m_Tag()
|
|
, m_Data()
|
|
, m_Level(SQMOD_UNKNOWN)
|
|
, m_Authority(false)
|
|
, m_Suspended(false)
|
|
, m_Lock(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);
|
|
// Receive notification when the VM is about to be closed to release object references
|
|
_Core->VMClose.Connect< CmdListener, &CmdListener::VMClose >(this);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
CmdListener::~CmdListener()
|
|
{
|
|
// Stop receiving notification when the VM is about to be closed
|
|
_Core->VMClose.Disconnect< CmdListener, &CmdListener::VMClose >(this);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdListener::VMClose()
|
|
{
|
|
// Release the reference to the specified callbacks
|
|
m_OnAuth.Release2();
|
|
m_OnExec.Release2();
|
|
// Release the reference to the specified user data
|
|
m_Data.Release();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInt32 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;
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
const SQChar * CmdListener::ToString() const
|
|
{
|
|
return m_Name.c_str();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
const SQChar * CmdListener::GetTag() const
|
|
{
|
|
return m_Tag.c_str();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdListener::SetTag(const SQChar * tag)
|
|
{
|
|
m_Tag.assign(tag);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SqObj & CmdListener::GetData()
|
|
{
|
|
return m_Data;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdListener::SetData(SqObj & data)
|
|
{
|
|
m_Data = data;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
const SQChar * CmdListener::GetName() const
|
|
{
|
|
return m_Name.c_str();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdListener::SetName(const SQChar * name)
|
|
{
|
|
if (!m_Lock)
|
|
{
|
|
// If there's a name then we're already binded
|
|
if (!m_Name.empty())
|
|
{
|
|
_Cmd->Unbind(name);
|
|
}
|
|
// Assign the new name
|
|
m_Name.assign(name);
|
|
// If the new name is valid then bind to it
|
|
if (!m_Name.empty())
|
|
{
|
|
_Cmd->Bind(name, this);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <change command name> while command is under active lock: %s", m_Name.c_str());
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
const SQChar * CmdListener::GetSpec() const
|
|
{
|
|
return m_Spec.c_str();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdListener::SetSpec(const SQChar * spec)
|
|
{
|
|
if (ProcSpec(spec))
|
|
{
|
|
m_Spec.assign(spec);
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
const SQChar * CmdListener::GetArgTag(SQUint32 arg) const
|
|
{
|
|
if (arg < MAX_CMD_ARGS)
|
|
{
|
|
return m_Argt[arg].c_str();
|
|
}
|
|
else
|
|
{
|
|
LogErr("Unable to <get command argument tag/name> because : argument is out of bounds %u > %u",
|
|
arg, MAX_CMD_ARGS);
|
|
}
|
|
// Argument failed inspection
|
|
return _SC("");
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdListener::SetArgTag(SQUint32 arg, const SQChar * name)
|
|
{
|
|
if (arg < MAX_CMD_ARGS)
|
|
{
|
|
m_Argt[arg].assign(name);
|
|
}
|
|
else
|
|
{
|
|
LogErr("Unable to <set command argument tag/name> because : argument is out of bounds %u > %u",
|
|
arg, MAX_CMD_ARGS);
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Array CmdListener::GetArgTags() const
|
|
{
|
|
// Allocate an array to encapsulate all tags
|
|
Array arr(DefaultVM::Get(), MAX_CMD_ARGS);
|
|
// Put the tags to the allocated array
|
|
for (SQUint32 arg = 0; arg < MAX_CMD_ARGS; ++arg)
|
|
{
|
|
arr.SetValue(arg, m_Argt[arg]);
|
|
}
|
|
// Return the array with the tags
|
|
return arr;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdListener::SetArgTags(Array & tags)
|
|
{
|
|
// Attempt to retrieve the number of specified tags
|
|
const SQUint32 max = _SCU32(tags.Length());
|
|
// If no tags were specified then clear current tags
|
|
if (max == 0)
|
|
{
|
|
m_Argt.fill(Argt::value_type());
|
|
}
|
|
// See if we're in range
|
|
else if (max < MAX_CMD_ARGS)
|
|
{
|
|
// Attempt to get all arguments in one go
|
|
tags.GetArray(m_Argt.data(), max);
|
|
}
|
|
else
|
|
{
|
|
LogErr("Unable to <set command argument tag/name> because : argument is out of bounds %u > %u",
|
|
max, MAX_CMD_ARGS);
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
const SQChar * CmdListener::GetHelp() const
|
|
{
|
|
return m_Help.c_str();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdListener::SetHelp(const SQChar * help)
|
|
{
|
|
m_Help.assign(help);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
const SQChar * CmdListener::GetInfo() const
|
|
{
|
|
return m_Info.c_str();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdListener::SetInfo(const SQChar * info)
|
|
{
|
|
m_Info.assign(info);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Function & CmdListener::GetOnExec()
|
|
{
|
|
return m_OnExec;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdListener::SetOnExec(Function & func)
|
|
{
|
|
m_OnExec = func;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdListener::SetOnExec_Env(SqObj & env, Function & func)
|
|
{
|
|
m_OnExec = Function(env.GetVM(), env, func.GetFunc());
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Function & CmdListener::GetOnAuth()
|
|
{
|
|
return m_OnAuth;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdListener::SetOnAuth(Function & func)
|
|
{
|
|
m_OnAuth = func;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdListener::SetOnAuth_Env(SqObj & env, Function & func)
|
|
{
|
|
m_OnAuth = Function(env.GetVM(), env, func.GetFunc());
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
SQInt32 CmdListener::GetLevel() const
|
|
{
|
|
return m_Level;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdListener::SetLevel(SQInt32 level)
|
|
{
|
|
m_Level = level;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
bool CmdListener::GetAuthority() const
|
|
{
|
|
return m_Authority;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdListener::SetAuthority(bool toggle)
|
|
{
|
|
m_Authority = toggle;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
bool CmdListener::GetSuspended() const
|
|
{
|
|
return m_Suspended;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdListener::SetSuspended(bool toggle)
|
|
{
|
|
m_Suspended = toggle;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Uint8 CmdListener::GetMinArgC() const
|
|
{
|
|
return m_MinArgc;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdListener::SetMinArgC(Uint8 val)
|
|
{
|
|
if (val < MAX_CMD_ARGS && val <= m_MaxArgc)
|
|
{
|
|
m_MinArgc = val;
|
|
}
|
|
else if (val > m_MaxArgc)
|
|
{
|
|
LogWrn("Attempting to <set maximum command arguments> using a value bigger then maximum: %u > %u",
|
|
val, m_MaxArgc);
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <set minimum command arguments> using an out of bounds value %u > %u",
|
|
val, MAX_CMD_ARGS);
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
Uint8 CmdListener::GetMaxArgC() const
|
|
{
|
|
return m_MaxArgc;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdListener::SetMaxArgC(Uint8 val)
|
|
{
|
|
if (val < MAX_CMD_ARGS && val >= m_MinArgc)
|
|
{
|
|
m_MaxArgc = val;
|
|
}
|
|
else if (val < m_MinArgc)
|
|
{
|
|
LogWrn("Attempting to <set maximum command arguments> using a value smaller then minimum: %u < %u",
|
|
val, m_MinArgc);
|
|
}
|
|
else
|
|
{
|
|
LogWrn("Attempting to <set maximum command arguments> using an out of bounds value: %u > %d",
|
|
val, MAX_CMD_ARGS);
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
void CmdListener::GenerateInfo(bool full)
|
|
{
|
|
// Clear any previously generated informational message
|
|
m_Info.clear();
|
|
// Process each supported command argument
|
|
for (SQUint32 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_Argt[idx].empty() || m_Args[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_Argt[arg].empty())
|
|
{
|
|
// Add the name first
|
|
m_Info += m_Argt[arg];
|
|
// Separate the name from the specifiers
|
|
m_Info += ':';
|
|
}
|
|
// If the argument has any explicit types specified
|
|
if (m_Args[arg] != CMDARG_ANY)
|
|
{
|
|
// Does it support integers?
|
|
if (m_Args[arg] & CMDARG_INTEGER)
|
|
{
|
|
m_Info += _SC("integer");
|
|
}
|
|
// Does it support floats?
|
|
if (m_Args[arg] & CMDARG_FLOAT)
|
|
{
|
|
// Add a separator if this is not the first enabled type?
|
|
if (m_Info.back() != ':' && m_Info.back() != '<')
|
|
{
|
|
m_Info += ',';
|
|
}
|
|
// Now add the type name
|
|
m_Info += _SC("float");
|
|
}
|
|
// Does it support booleans?
|
|
if (m_Args[arg] & CMDARG_BOOLEAN)
|
|
{
|
|
// Add a separator if this is not the first enabled type?
|
|
if (m_Info.back() != ':' && m_Info.back() != '<')
|
|
{
|
|
m_Info += ',';
|
|
}
|
|
// Now add the type name
|
|
m_Info += _SC("boolean");
|
|
}
|
|
// Does it support strings?
|
|
if (m_Args[arg] & CMDARG_STRING)
|
|
{
|
|
// Add a separator if this is not the first enabled type?
|
|
if (m_Info.back() != ':' && m_Info.back() != '<')
|
|
{
|
|
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 += '>';
|
|
// If this is not the last argument then add a spacer
|
|
if (arg+1 != m_MaxArgc)
|
|
{
|
|
m_Info += ' ';
|
|
}
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
bool CmdListener::ArgCheck(SQUint32 arg, Uint8 mask) const
|
|
{
|
|
if (arg < MAX_CMD_ARGS)
|
|
{
|
|
return (m_Args[arg] & mask) || m_Args[arg] == CMDARG_ANY;
|
|
}
|
|
else
|
|
{
|
|
LogErr("Unable to <check command argument specifier> because : argument is out of bounds %u > %u",
|
|
arg, MAX_CMD_ARGS);
|
|
}
|
|
// Argument failed inspection
|
|
return false;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
bool CmdListener::AuthCheck(Reference< CPlayer > & player)
|
|
{
|
|
return AuthCheckID(player);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
bool CmdListener::AuthCheckID(SQInt32 id)
|
|
{
|
|
// Allow execution by default
|
|
bool allow = true;
|
|
// Do we need explicit authority verification?
|
|
if (m_Authority)
|
|
{
|
|
// 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, SQInt32 >(id);
|
|
// 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_Level >= 0)
|
|
{
|
|
allow = (Reference< CPlayer >::Get(id).Level >= m_Level);
|
|
}
|
|
}
|
|
// Return result
|
|
return allow;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
bool CmdListener::Execute(CPlayer & invoker, Array & args)
|
|
{
|
|
bool result = false;
|
|
// Was there an executer specified?
|
|
if (!m_OnExec.IsNull())
|
|
{
|
|
// Attempt to evaluate the specified executer
|
|
SharedPtr< bool > ret = m_OnExec.Evaluate< bool, CPlayer &, Array & >(invoker, args);
|
|
// See if the executer succeeded or default to failed
|
|
result = !ret ? false : *ret;
|
|
}
|
|
// Return result
|
|
return result;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
bool CmdListener::ProcSpec(const SQChar * str)
|
|
{
|
|
// Currently processed character
|
|
SQChar ch = 0;
|
|
// When parsing may continue
|
|
bool good = true;
|
|
// Currently processed argument
|
|
SQUint32 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 > MAX_CMD_ARGS)
|
|
{
|
|
LogErr("Unable to <parse command specifier> because : argument is out of bounds %u > %u",
|
|
idx, MAX_CMD_ARGS);
|
|
// Parsing failed
|
|
good = false;
|
|
// Stop parsing
|
|
break;
|
|
}
|
|
// Move 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 stop
|
|
break;
|
|
}
|
|
// Apply the type specifier
|
|
switch(ch)
|
|
{
|
|
// Enable the integer type
|
|
case 'i': m_Args[idx] |= CMDARG_INTEGER; break;
|
|
// Enable the float type
|
|
case 'f': m_Args[idx] |= CMDARG_FLOAT; break;
|
|
// Enable the boolean type
|
|
case 'b': m_Args[idx] |= CMDARG_BOOLEAN; break;
|
|
// Enable the string type
|
|
case 's': m_Args[idx] |= CMDARG_STRING; break;
|
|
// Unknown type!
|
|
default:
|
|
{
|
|
LogErr("Unable to <parse command specifier> because : unknown type specifier %u -> %c",
|
|
idx, ch);
|
|
// Parsing failed
|
|
good = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Reset all argument specifiers if failed
|
|
if (!good)
|
|
{
|
|
m_Args.fill(CMDARG_ANY);
|
|
}
|
|
// Attempt to generate an informational message
|
|
GenerateInfo(false);
|
|
// Return whether the parsing was successful
|
|
return good;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
const SQChar * 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: return _SC("string");
|
|
default: return _SC("unknown");
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
static Function & Cmd_GetOnError()
|
|
{
|
|
return _Cmd->GetOnError();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
static void Cmd_SetOnError(Function & func)
|
|
{
|
|
_Cmd->SetOnError(func);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
static Function & Cmd_GetOnAuth()
|
|
{
|
|
return _Cmd->GetOnAuth();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
static void Cmd_SetOnAuth(Function & func)
|
|
{
|
|
_Cmd->SetOnAuth(func);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
static const CPlayer & Cmd_GetInvoker()
|
|
{
|
|
return _Cmd->GetInvoker();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
static SQInt32 Cmd_GetInvokerID()
|
|
{
|
|
return _Cmd->GetInvokerID();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
static const SQChar * Cmd_GetName()
|
|
{
|
|
return _Cmd->GetName();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
static const SQChar * Cmd_GetText()
|
|
{
|
|
return _Cmd->GetText();
|
|
}
|
|
|
|
// ================================================================================================
|
|
bool Register_Cmd(HSQUIRRELVM vm)
|
|
{
|
|
// // Attempt to create the Cmd namespace
|
|
Sqrat::Table cmdns(vm);
|
|
|
|
// Output debugging information
|
|
LogDbg("Beginning registration of <Cmd Listener> type");
|
|
// Attempt to register the specified type
|
|
cmdns.Bind(_SC("Listener"), Sqrat::Class< CmdListener, NoCopy< CmdListener > >(vm, _SC("Listener"))
|
|
/* Constructors */
|
|
.Ctor()
|
|
.Ctor< const SQChar * >()
|
|
.Ctor< const SQChar *, const SQChar * >()
|
|
.Ctor< const SQChar *, const SQChar *, Array & >()
|
|
.Ctor< const SQChar *, const SQChar *, Uint8, Uint8 >()
|
|
.Ctor< const SQChar *, const SQChar *, Array &, Uint8, Uint8 >()
|
|
/* Metamethods */
|
|
.Func(_SC("_cmp"), &CmdListener::Cmp)
|
|
.Func(_SC("_tostring"), &CmdListener::ToString)
|
|
/* Properties */
|
|
.Prop(_SC("ltag"), &CmdListener::GetTag, &CmdListener::SetTag)
|
|
.Prop(_SC("ldata"), &CmdListener::GetData, &CmdListener::SetData)
|
|
.Prop(_SC("name"), &CmdListener::GetName, &CmdListener::SetName)
|
|
.Prop(_SC("spec"), &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("on_exec"), &CmdListener::GetOnExec, &CmdListener::SetOnExec)
|
|
.Prop(_SC("on_auth"), &CmdListener::GetOnAuth, &CmdListener::SetOnAuth)
|
|
.Prop(_SC("level"), &CmdListener::GetLevel, &CmdListener::SetLevel)
|
|
.Prop(_SC("auth"), &CmdListener::GetAuthority, &CmdListener::SetAuthority)
|
|
.Prop(_SC("authority"), &CmdListener::GetAuthority, &CmdListener::SetAuthority)
|
|
.Prop(_SC("suspended"), &CmdListener::GetSuspended, &CmdListener::SetSuspended)
|
|
.Prop(_SC("min_args"), &CmdListener::GetMinArgC, &CmdListener::SetMinArgC)
|
|
.Prop(_SC("max_args"), &CmdListener::GetMaxArgC, &CmdListener::SetMaxArgC)
|
|
/* Functions */
|
|
.Func(_SC("get_arg_tag"), &CmdListener::GetArgTag)
|
|
.Func(_SC("set_arg_tag"), &CmdListener::SetArgTag)
|
|
.Func(_SC("set_on_exec"), &CmdListener::SetOnExec_Env)
|
|
.Func(_SC("set_on_auth"), &CmdListener::SetOnAuth_Env)
|
|
.Func(_SC("generate_info"), &CmdListener::GenerateInfo)
|
|
.Func(_SC("arg_check"), &CmdListener::ArgCheck)
|
|
.Func(_SC("auth_check"), &CmdListener::AuthCheck)
|
|
.Func(_SC("auth_check_id"), &CmdListener::AuthCheckID)
|
|
);
|
|
|
|
// Output debugging information
|
|
LogDbg("Registration of <Cmd Listener> type was successful");
|
|
|
|
// Output debugging information
|
|
LogDbg("Beginning registration of <Cmd functions> type");
|
|
// Attempt to register the free functions
|
|
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_GetName);
|
|
cmdns.Func(_SC("GetText"), &Cmd_GetText);
|
|
// Output debugging information
|
|
LogDbg("Registration of <Cmd functions> type was successful");
|
|
|
|
// Attempt to bind the namespace to the root table
|
|
Sqrat::RootTable(vm).Bind(_SC("Cmd"), cmdns);
|
|
|
|
// Output debugging information
|
|
LogDbg("Beginning registration of <Cmd Constants> type");
|
|
// Attempt to register the argument types enumeration
|
|
Sqrat::ConstTable(vm).Enum(_SC("ECMDARG"), Sqrat::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)
|
|
);
|
|
// Attempt to register the error codes enumeration
|
|
Sqrat::ConstTable(vm).Enum(_SC("ECMDERR"), Sqrat::Enumeration(vm)
|
|
.Const(_SC("UNKNOWN"), CMDERR_UNKNOWN)
|
|
.Const(_SC("SYNTAX_ERROR"), CMDERR_SYNTAX_ERROR)
|
|
.Const(_SC("UNKNOWN_COMMAND"), CMDERR_UNKNOWN_COMMAND)
|
|
.Const(_SC("MISSING_EXECUTER"), CMDERR_MISSING_EXECUTER)
|
|
.Const(_SC("INSUFFICIENT_AUTH"), CMDERR_INSUFFICIENT_AUTH)
|
|
.Const(_SC("INCOMPLETE_ARGS"), CMDERR_INCOMPLETE_ARGS)
|
|
.Const(_SC("EXTRANEOUS_ARGS"), CMDERR_EXTRANEOUS_ARGS)
|
|
.Const(_SC("UNSUPPORTED_ARG"), CMDERR_UNSUPPORTED_ARG)
|
|
.Const(_SC("EXECUTION_FAILED"), CMDERR_EXECUTION_FAILED)
|
|
);
|
|
// Output debugging information
|
|
LogDbg("Registration of <IRC Constants> type was successful");
|
|
// Registration succeeded
|
|
return true;
|
|
}
|
|
|
|
|
|
} // Namespace:: SqMod
|