1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2025-02-20 19:57:12 +01:00

Implement association of arbitrary user data in command listeners.

Also keep track of all listener instances by having them link to eachother as a double linked list. This should make it easy to release any script resources at shutdown.
Now that all listeners are being tracked, binding callbacks is less strict and does not require a listener to be attached anymore.
This commit is contained in:
Sandu Liviu Catalin 2016-10-29 22:08:38 +03:00
parent 0111cc5a46
commit 4ad9402d8a
2 changed files with 87 additions and 24 deletions

View File

@ -8,6 +8,9 @@ namespace Cmd {
// ------------------------------------------------------------------------------------------------
Controllers Controller::s_Controllers;
// ------------------------------------------------------------------------------------------------
Listener * Listener::s_Head = nullptr;
// ------------------------------------------------------------------------------------------------
SQInteger Manager::Typename(HSQUIRRELVM vm)
{
@ -1090,6 +1093,7 @@ void Register(HSQUIRRELVM vm)
.Prop(_SC("Attached"), &Listener::Attached)
.Prop(_SC("Manager"), &Listener::GetManager)
.Prop(_SC("Name"), &Listener::GetName, &Listener::SetName)
.Prop(_SC("Data"), &Listener::GetData, &Listener::SetData)
.Prop(_SC("Spec"), &Listener::GetSpec, &Listener::SetSpec)
.Prop(_SC("Specifier"), &Listener::GetSpec, &Listener::SetSpec)
.Prop(_SC("Tags"), &Listener::GetArgTags, &Listener::SetArgTags)
@ -1168,6 +1172,7 @@ void Register_Command(HSQUIRRELVM vm)
void TerminateCommands()
{
Cmd::Controller::Terminate();
Cmd::Listener::Terminate();
}
} // Namespace:: SqMod

View File

@ -1116,6 +1116,25 @@ class Listener
public:
/* --------------------------------------------------------------------------------------------
* Release any trace of script resources from all the listener instances.
*/
static void Terminate()
{
// Go forward and release resources
for (Listener * node = s_Head; node != nullptr; node = node->m_Next)
{
node->m_Data.Release();
}
// Go backwards and release resources
for (Listener * node = s_Head; node != nullptr; node = node->m_Prev)
{
node->m_Data.Release();
}
// Kinda useless but Squirrel doesn't play nice with loose references
// Better safe than sorry
}
/* --------------------------------------------------------------------------------------------
* Convenience constructor.
*/
@ -1200,6 +1219,8 @@ public:
, m_Protected(prot)
, m_Suspended(false)
, m_Associate(assoc)
, m_Prev(nullptr)
, m_Next(s_Head)
{
// Initialize the specifiers to default values
for (Uint8 n = 0; n < SQMOD_MAX_CMD_ARGS; ++n)
@ -1215,6 +1236,22 @@ public:
SetMaxArgC(max);
// Generate information for the command
GenerateInfo(false);
// We're the head element now
s_Head = this;
// Was there a previous head?
if (m_Next != nullptr)
{
// Steal previous element from previous head
m_Prev = m_Next->m_Prev;
// Did that head element had a previous element?
if (m_Prev != nullptr)
{
// Tell it we're the next element now
m_Prev->m_Next = this;
}
// Tell the previous head that we're the previous element now
m_Next->m_Prev = this;
}
}
/* --------------------------------------------------------------------------------------------
@ -1242,6 +1279,23 @@ public:
m_OnAuth.ReleaseGently();
m_OnPost.ReleaseGently();
m_OnFail.ReleaseGently();
// Is there an element behind us?
if (m_Prev != nullptr)
{
// Tell it to point to the element ahead of us
m_Prev->m_Next = m_Next;
}
// Is there an element ahead of us?
if (m_Next != nullptr)
{
// Tell it to point to the element behind us
m_Next->m_Prev = m_Prev;
}
// Are we the head element in the chain?
if (s_Head == this)
{
s_Head = m_Next == nullptr ? m_Prev : m_Next;
}
}
/* --------------------------------------------------------------------------------------------
@ -1387,6 +1441,22 @@ public:
}
}
/* --------------------------------------------------------------------------------------------
* Retrieve the associated user data.
*/
Object & GetData()
{
return m_Data;
}
/* --------------------------------------------------------------------------------------------
* Modify the associated user data.
*/
void SetData(Object & data)
{
m_Data = data;
}
/* --------------------------------------------------------------------------------------------
* Retrieve the argument specification string.
*/
@ -1645,13 +1715,8 @@ public:
*/
void SetOnExec(Object & env, Function & func)
{
// Make sure that we are allowed to store script resources
if (m_Controller.Expired())
{
STHROWF("Detached commands cannot store script resources");
}
// Are we supposed to unbind current callback?
else if (func.IsNull())
if (func.IsNull())
{
m_OnExec.ReleaseGently();
}
@ -1679,13 +1744,8 @@ public:
*/
void SetOnAuth(Object & env, Function & func)
{
// Make sure that we are allowed to store script resources
if (m_Controller.Expired())
{
STHROWF("Detached commands cannot store script resources");
}
// Are we supposed to unbind current callback?
else if (func.IsNull())
if (func.IsNull())
{
m_OnAuth.ReleaseGently();
}
@ -1713,13 +1773,8 @@ public:
*/
void SetOnPost(Object & env, Function & func)
{
// Make sure that we are allowed to store script resources
if (m_Controller.Expired())
{
STHROWF("Detached listeners cannot store script resources");
}
// Are we supposed to unbind current callback?
else if (func.IsNull())
if (func.IsNull())
{
m_OnPost.ReleaseGently();
}
@ -1747,13 +1802,8 @@ public:
*/
void SetOnFail(Object & env, Function & func)
{
// Make sure that we are allowed to store script resources
if (m_Controller.Expired())
{
STHROWF("Detached listeners cannot store script resources");
}
// Are we supposed to unbind current callback?
else if (func.IsNull())
if (func.IsNull())
{
m_OnFail.ReleaseGently();
}
@ -1914,6 +1964,7 @@ private:
// --------------------------------------------------------------------------------------------
String m_Name; // Name of the command that triggers this listener.
Object m_Data; // Arbitrary user data associated with this particular instance.
// --------------------------------------------------------------------------------------------
ArgSpec m_ArgSpec; // List of argument type specifications.
@ -1941,6 +1992,13 @@ private:
bool m_Protected; // Whether explicit authentication of the invoker is required.
bool m_Suspended; // Whether this command should block further invocations.
bool m_Associate; // Whether arguments are sent as table instead of array.
// --------------------------------------------------------------------------------------------
Listener * m_Prev; // Previous listener in the chain.
Listener * m_Next; // Next listener in the chain.
// --------------------------------------------------------------------------------------------
static Listener * s_Head; // The head of the listener chain.
};
} // Namespace:: Cmd