diff --git a/module/Core/Command.cpp b/module/Core/Command.cpp index 5ee1fffb..3df748a5 100644 --- a/module/Core/Command.cpp +++ b/module/Core/Command.cpp @@ -39,7 +39,10 @@ Command::Command(std::size_t hash, String name, Listener * ptr, CtrPtr ctr) { 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) { - 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) { - 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) { - 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) { - 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) { - 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; } +// ------------------------------------------------------------------------------------------------ +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) { @@ -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) { @@ -1136,6 +1226,7 @@ void Register(HSQUIRRELVM vm) .Prop(_SC("Associate"), &Listener::GetAssociate, &Listener::SetAssociate) .Prop(_SC("MinArgs"), &Listener::GetMinArgC, &Listener::SetMinArgC) .Prop(_SC("MaxArgs"), &Listener::GetMaxArgC, &Listener::SetMaxArgC) + .Prop(_SC("AliasCount"), &Listener::GetAliases) .Prop(_SC("OnExec"), &Listener::GetOnExec) .Prop(_SC("OnAuth"), &Listener::GetOnAuth) .Prop(_SC("OnAudit"), &Listener::GetOnAudit) @@ -1159,6 +1250,7 @@ void Register(HSQUIRRELVM vm) .Func(_SC("ArgCheck"), &Listener::ArgCheck) .Func(_SC("AuthCheck"), &Listener::AuthCheck) .Func(_SC("GenerateInfo"), &Listener::GenerateInfo) + .FmtFunc(_SC("Rebind"), &Listener::Rebind) ); RootTable(vm).Bind(_SC("SqCmd"), cmdns); diff --git a/module/Core/Command.hpp b/module/Core/Command.hpp index d6a83205..99df6c87 100644 --- a/module/Core/Command.hpp +++ b/module/Core/Command.hpp @@ -255,11 +255,11 @@ public: struct Command { // -------------------------------------------------------------------------------------------- - std::size_t mHash; // The unique hash that identifies this command. - String mName; // The unique name that identifies this command. - Listener* mPtr; // The listener that reacts to this command. - Object mObj; // A strong reference to the script object. - CtrPtr mCtr; // The associated controller. + std::size_t mHash{0}; // The unique hash that identifies this command. + String mName{}; // The unique name that identifies this command. + Listener* mPtr{nullptr}; // The listener that reacts to this command. + Object mObj{}; // A strong reference to the script object. + CtrPtr mCtr{}; // The associated controller. /* -------------------------------------------------------------------------------------------- * Construct a command and the also create a script object from the specified listener. @@ -418,7 +418,7 @@ protected: 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) { @@ -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) { @@ -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); + /* -------------------------------------------------------------------------------------------- + * 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. */ @@ -445,17 +466,10 @@ protected: { // Obtain the unique identifier of the specified 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 - for (; itr != m_Commands.cend(); ++itr) - { - // Are the hashes identical? - if (itr->mHash == hash) - { - break; // We found our command! - } - } + auto itr = std::find_if(m_Commands.cbegin(), m_Commands.cend(), [=](Commands::const_reference c) { + return (c.mHash == hash); + }); // Make sure the command exist before attempting to remove it if (itr != m_Commands.end()) { @@ -468,17 +482,10 @@ protected: */ void Detach(Listener * ptr) { - // Iterator to the found command, if any - auto itr = m_Commands.cbegin(); // Attempt to find the specified command - for (; itr != m_Commands.cend(); ++itr) - { - // Are the instances identical? - if (itr->mPtr == ptr) - { - break; // We found our command! - } - } + auto itr = std::find_if(m_Commands.cbegin(), m_Commands.cend(), [=](Commands::const_reference c) { + return (c.mPtr == ptr); + }); // Make sure the command exists before attempting to remove it if (itr != m_Commands.end()) { @@ -1200,6 +1207,7 @@ public: , m_ArgTags() , m_MinArgc(0) , m_MaxArgc(SQMOD_MAX_CMD_ARGS-1) + , m_Aliases(0) , m_Spec() , m_Help() , m_Info() @@ -1336,6 +1344,42 @@ public: 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. */ @@ -1906,6 +1950,11 @@ public: */ void GenerateInfo(bool full); + /* -------------------------------------------------------------------------------------------- + * Create an alias for this command listener. + */ + Listener & Rebind(StackStrF & name); + protected: // -------------------------------------------------------------------------------------------- @@ -1942,7 +1991,7 @@ protected: 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. @@ -1955,6 +2004,7 @@ private: // -------------------------------------------------------------------------------------------- uint8_t m_MinArgc; // Minimum 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.