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

Implement command alias.

This commit is contained in:
Sandu Liviu Catalin 2021-05-08 16:11:50 +03:00
parent 6b8da10deb
commit 5b155731fa
2 changed files with 177 additions and 35 deletions

View File

@ -39,7 +39,10 @@ Command::Command(std::size_t hash, String name, Listener * ptr, CtrPtr ctr)
{ {
if (mPtr) if (mPtr)
{ {
mPtr->m_Controller = mCtr; // Create controller association // Create controller association
mPtr->m_Controller = mCtr;
// Increment alias count
mPtr->AddAliases(1);
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -48,7 +51,10 @@ Command::Command(std::size_t hash, String name, const Object & obj, CtrPtr ctr)
{ {
if (mPtr) if (mPtr)
{ {
mPtr->m_Controller = mCtr; // Create controller association // Create controller association
mPtr->m_Controller = mCtr;
// Increment alias count
mPtr->AddAliases(1);
} }
} }
@ -58,7 +64,10 @@ Command::Command(std::size_t hash, String name, Object && obj, CtrPtr ctr)
{ {
if (mPtr) if (mPtr)
{ {
mPtr->m_Controller = mCtr; // Create controller association // Create controller association
mPtr->m_Controller = mCtr;
// Increment alias count
mPtr->AddAliases(1);
} }
} }
@ -68,7 +77,10 @@ Command::Command(std::size_t hash, String name, Listener * ptr, const Object & o
{ {
if (mPtr) if (mPtr)
{ {
mPtr->m_Controller = mCtr; // Create controller association // Create controller association
mPtr->m_Controller = mCtr;
// Increment alias count
mPtr->AddAliases(1);
} }
} }
@ -78,7 +90,10 @@ Command::Command(std::size_t hash, String name, Listener * ptr, Object && obj, C
{ {
if (mPtr) if (mPtr)
{ {
mPtr->m_Controller = mCtr; // Create controller association // Create controller association
mPtr->m_Controller = mCtr;
// Increment alias count
mPtr->AddAliases(1);
} }
} }
@ -87,7 +102,12 @@ Command::~Command()
{ {
if (mPtr) if (mPtr)
{ {
mPtr->m_Controller.Reset(); // Break controller association // The listener will continue to be associated with the controller as long as an alias points to it
if (mPtr->SubAliases(1) == 0)
{
// Break controller association
mPtr->m_Controller.Reset();
}
} }
} }
@ -150,6 +170,63 @@ Object & Controller::Attach(Object && obj, Listener * ptr)
return m_Commands.back().mObj; return m_Commands.back().mObj;
} }
// ------------------------------------------------------------------------------------------------
Object & Controller::Attach(Object && obj, Listener * ptr, String name)
{
// Is there anything that we can attach
if (obj.IsNull() && ptr == nullptr)
{
STHROWF("Cannot attach invalid command listener");
}
// Are we supposed to grab the object?
else if (obj.IsNull())
{
// Obtain the initial stack size
const StackGuard sg;
// Push the instance on the stack
ClassType< Listener >::PushInstance(SqVM(), ptr);
// Grab the instance from the stack
obj = Var< Object >(SqVM(), -1).value;
}
// Are we supposed to grab the instance?
else if (ptr == nullptr)
{
// Obtain the initial stack size
const StackGuard sg(obj.GetVM());
// Push the object on the stack
Var< Object & >::push(obj.GetVM(), obj);
// Grab the instance from the stack
ptr = Var< Listener * >(obj.GetVM(), -1).value;
}
// At this point we should have both the instance and the object
if (obj.IsNull() || ptr == nullptr)
{
STHROWF("Unable to obtain the command listener");
}
// Validate the command name
if (name.empty())
{
STHROWF("Cannot attach command without a name");
}
// Obtain the unique identifier of the specified name
const std::size_t hash = std::hash< String >()(name);
// Make sure the command doesn't already exist
for (const auto & cmd : m_Commands)
{
// Are the hashes identical?
if (cmd.mHash == hash)
{
// Include information necessary to help identify hash collisions!
STHROWF("Command '{}' already exists as '{}' for hash ({})",
name.c_str(), cmd.mName.c_str(), hash);
}
}
// Attempt to insert the command
m_Commands.emplace_back(hash, std::move(name), ptr, std::move(obj), m_Manager->GetCtr());
// Return the script object of the listener
return m_Commands.back().mObj;
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Object Manager::Create(StackStrF & name, StackStrF & spec, Array & tags, uint8_t min, uint8_t max, SQInteger auth, bool prot, bool assoc) Object Manager::Create(StackStrF & name, StackStrF & spec, Array & tags, uint8_t min, uint8_t max, SQInteger auth, bool prot, bool assoc)
{ {
@ -933,6 +1010,19 @@ void Listener::GenerateInfo(bool full)
} }
} }
// ------------------------------------------------------------------------------------------------
Listener & Listener::Rebind(StackStrF & name)
{
if (m_Controller.Expired())
{
STHROWF("Command must be attached to a manager before having an alias.");
}
// Create an alias for this command listener
m_Controller.Lock()->Attach(Object{}, this, name.ToStr());
// Allow chaining
return *this;
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Listener::ProcSpec(const SQChar * str) void Listener::ProcSpec(const SQChar * str)
{ {
@ -1136,6 +1226,7 @@ void Register(HSQUIRRELVM vm)
.Prop(_SC("Associate"), &Listener::GetAssociate, &Listener::SetAssociate) .Prop(_SC("Associate"), &Listener::GetAssociate, &Listener::SetAssociate)
.Prop(_SC("MinArgs"), &Listener::GetMinArgC, &Listener::SetMinArgC) .Prop(_SC("MinArgs"), &Listener::GetMinArgC, &Listener::SetMinArgC)
.Prop(_SC("MaxArgs"), &Listener::GetMaxArgC, &Listener::SetMaxArgC) .Prop(_SC("MaxArgs"), &Listener::GetMaxArgC, &Listener::SetMaxArgC)
.Prop(_SC("AliasCount"), &Listener::GetAliases)
.Prop(_SC("OnExec"), &Listener::GetOnExec) .Prop(_SC("OnExec"), &Listener::GetOnExec)
.Prop(_SC("OnAuth"), &Listener::GetOnAuth) .Prop(_SC("OnAuth"), &Listener::GetOnAuth)
.Prop(_SC("OnAudit"), &Listener::GetOnAudit) .Prop(_SC("OnAudit"), &Listener::GetOnAudit)
@ -1159,6 +1250,7 @@ void Register(HSQUIRRELVM vm)
.Func(_SC("ArgCheck"), &Listener::ArgCheck) .Func(_SC("ArgCheck"), &Listener::ArgCheck)
.Func(_SC("AuthCheck"), &Listener::AuthCheck) .Func(_SC("AuthCheck"), &Listener::AuthCheck)
.Func(_SC("GenerateInfo"), &Listener::GenerateInfo) .Func(_SC("GenerateInfo"), &Listener::GenerateInfo)
.FmtFunc(_SC("Rebind"), &Listener::Rebind)
); );
RootTable(vm).Bind(_SC("SqCmd"), cmdns); RootTable(vm).Bind(_SC("SqCmd"), cmdns);

View File

@ -255,11 +255,11 @@ public:
struct Command struct Command
{ {
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
std::size_t mHash; // The unique hash that identifies this command. std::size_t mHash{0}; // The unique hash that identifies this command.
String mName; // The unique name that identifies this command. String mName{}; // The unique name that identifies this command.
Listener* mPtr; // The listener that reacts to this command. Listener* mPtr{nullptr}; // The listener that reacts to this command.
Object mObj; // A strong reference to the script object. Object mObj{}; // A strong reference to the script object.
CtrPtr mCtr; // The associated controller. CtrPtr mCtr{}; // The associated controller.
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Construct a command and the also create a script object from the specified listener. * Construct a command and the also create a script object from the specified listener.
@ -418,7 +418,7 @@ protected:
bool Parse(Context & ctx); bool Parse(Context & ctx);
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Attach a command listener to a certain name. * Attach a command listener to the associated name.
*/ */
Object & Attach(Object & obj, Listener * ptr) Object & Attach(Object & obj, Listener * ptr)
{ {
@ -426,7 +426,7 @@ protected:
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Attach a command listener to a certain name. * Attach a command listener to the associated name.
*/ */
Object & Attach(const Object & obj, Listener * ptr) Object & Attach(const Object & obj, Listener * ptr)
{ {
@ -434,10 +434,31 @@ protected:
} }
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Attach a command listener to a certain name. * Attach a command listener to the associated name.
*/ */
Object & Attach(Object && obj, Listener * ptr); Object & Attach(Object && obj, Listener * ptr);
/* --------------------------------------------------------------------------------------------
* Attach a command listener to a certain name.
*/
Object & Attach(Object & obj, Listener * ptr, String name)
{
return Attach(Object(obj), ptr, std::move(name));
}
/* --------------------------------------------------------------------------------------------
* Attach a command listener to a certain name.
*/
Object & Attach(const Object & obj, Listener * ptr, String name)
{
return Attach(Object(obj), ptr, std::move(name));
}
/* --------------------------------------------------------------------------------------------
* Attach a command listener to a certain name.
*/
Object & Attach(Object && obj, Listener * ptr, String name);
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Detach a command listener from a certain name. * Detach a command listener from a certain name.
*/ */
@ -445,17 +466,10 @@ protected:
{ {
// Obtain the unique identifier of the specified name // Obtain the unique identifier of the specified name
const std::size_t hash = std::hash< String >()(name); const std::size_t hash = std::hash< String >()(name);
// Iterator to the found command, if any
auto itr = m_Commands.cbegin();
// Attempt to find the specified command // Attempt to find the specified command
for (; itr != m_Commands.cend(); ++itr) auto itr = std::find_if(m_Commands.cbegin(), m_Commands.cend(), [=](Commands::const_reference c) {
{ return (c.mHash == hash);
// Are the hashes identical? });
if (itr->mHash == hash)
{
break; // We found our command!
}
}
// Make sure the command exist before attempting to remove it // Make sure the command exist before attempting to remove it
if (itr != m_Commands.end()) if (itr != m_Commands.end())
{ {
@ -468,17 +482,10 @@ protected:
*/ */
void Detach(Listener * ptr) void Detach(Listener * ptr)
{ {
// Iterator to the found command, if any
auto itr = m_Commands.cbegin();
// Attempt to find the specified command // Attempt to find the specified command
for (; itr != m_Commands.cend(); ++itr) auto itr = std::find_if(m_Commands.cbegin(), m_Commands.cend(), [=](Commands::const_reference c) {
{ return (c.mPtr == ptr);
// Are the instances identical? });
if (itr->mPtr == ptr)
{
break; // We found our command!
}
}
// Make sure the command exists before attempting to remove it // Make sure the command exists before attempting to remove it
if (itr != m_Commands.end()) if (itr != m_Commands.end())
{ {
@ -1200,6 +1207,7 @@ public:
, m_ArgTags() , m_ArgTags()
, m_MinArgc(0) , m_MinArgc(0)
, m_MaxArgc(SQMOD_MAX_CMD_ARGS-1) , m_MaxArgc(SQMOD_MAX_CMD_ARGS-1)
, m_Aliases(0)
, m_Spec() , m_Spec()
, m_Help() , m_Help()
, m_Info() , m_Info()
@ -1336,6 +1344,42 @@ public:
return m_Name; return m_Name;
} }
/* --------------------------------------------------------------------------------------------
* Increment the number of names that point to this command.
*/
uint16_t AddAliases(uint16_t n)
{
m_Aliases += n;
// Return new alias count
return m_Aliases;
}
/* --------------------------------------------------------------------------------------------
* Decrement the number of names that point to this command.
*/
uint16_t SubAliases(uint16_t n)
{
m_Aliases -= n;
// Return new alias count
return m_Aliases;
}
/* --------------------------------------------------------------------------------------------
* Retrieve the number of names that point to this command.
*/
SQMOD_NODISCARD uint16_t GetAliases() const
{
return m_Aliases;
}
/* --------------------------------------------------------------------------------------------
* Modify the number of names that point to this command.
*/
void SetAliases(uint16_t n)
{
m_Aliases = n;
}
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Retrieve the number of weak references to the managed controller. * Retrieve the number of weak references to the managed controller.
*/ */
@ -1906,6 +1950,11 @@ public:
*/ */
void GenerateInfo(bool full); void GenerateInfo(bool full);
/* --------------------------------------------------------------------------------------------
* Create an alias for this command listener.
*/
Listener & Rebind(StackStrF & name);
protected: protected:
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
@ -1942,7 +1991,7 @@ protected:
private: private:
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
CtrPtr m_Controller; // Manager that controls this command listener. CtrPtr m_Controller; // Manager that controls this command listener.
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
String m_Name; // Name of the command that triggers this listener. String m_Name; // Name of the command that triggers this listener.
@ -1955,6 +2004,7 @@ private:
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
uint8_t m_MinArgc; // Minimum number of arguments supported by this listener. uint8_t m_MinArgc; // Minimum number of arguments supported by this listener.
uint8_t m_MaxArgc; // Maximum number of arguments supported by this listener. uint8_t m_MaxArgc; // Maximum number of arguments supported by this listener.
uint16_t m_Aliases; // Number of aliases that point to this command.
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
String m_Spec; // String used to generate the argument type specification list. String m_Spec; // String used to generate the argument type specification list.