mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2025-07-08 18:07:12 +02:00
Initial implementation of the new event system.
Initial implementation of the new signals and slots class. Fixed command parsing which compared a pointer to a character. Buffer overflow fix in routines which used the limits from the entity tasks. Switched from Sqrat::Object to Sqrat::LightObj in most places to avoid the overhead of the VM pointer. Various other adjustments and improvements. The plugin is currently in a broken state and crashes at shutdown. The bug is unknown at this point.
This commit is contained in:
cbp
shared/Base
source
Base
Command.cppCore.cppCore.hppCoreEntity.cppCoreEvents.cppCoreFuncs.cppCoreInst.cppCoreUtils.cppEntity
Blip.cppBlip.hppCheckpoint.cppCheckpoint.hppKeybind.cppKeybind.hppObject.cppObject.hppPickup.cppPickup.hppPlayer.cppPlayer.hppVehicle.cppVehicle.hpp
Main.cppMisc
Signal.cppSignal.hppSqBase.hppTasks.cppTasks.hpp@ -365,12 +365,12 @@ void SetServerOption(Int32 option_id, bool toggle)
|
||||
}
|
||||
else
|
||||
{
|
||||
Core::Get().EmitServerOption(option_id, toggle, 0, NullObject());
|
||||
Core::Get().EmitServerOption(option_id, toggle, 0, NullLightObj());
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void SetServerOptionEx(Int32 option_id, bool toggle, Int32 header, Object & payload)
|
||||
void SetServerOptionEx(Int32 option_id, bool toggle, Int32 header, LightObj & payload)
|
||||
{
|
||||
if (_Func->SetServerOption(static_cast< vcmpServerOption >(option_id),
|
||||
toggle) == vcmpErrorArgumentOutOfBounds)
|
||||
|
@ -170,7 +170,7 @@ void SetServerOption(Int32 option_id, bool toggle);
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Modify a server option.
|
||||
*/
|
||||
void SetServerOptionEx(Int32 option_id, bool toggle, Int32 header, Object & payload);
|
||||
void SetServerOptionEx(Int32 option_id, bool toggle, Int32 header, LightObj & payload);
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Retrieve the world bounds.
|
||||
|
@ -18,7 +18,7 @@
|
||||
namespace SqMod {
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
static const Object & FindPlayer(Object & by)
|
||||
static const LightObj & FindPlayer(Object & by)
|
||||
{
|
||||
switch (by.GetType())
|
||||
{
|
||||
@ -45,7 +45,7 @@ static const Object & FindPlayer(Object & by)
|
||||
default: STHROWF("Unsupported search identifier");
|
||||
}
|
||||
// Default to a null object
|
||||
return NullObject();
|
||||
return NullLightObj();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
@ -1,973 +0,0 @@
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
#include "Misc/Signal.hpp"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
namespace SqMod {
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQMODE_DECL_TYPENAME(Typename, _SC("SqSignalWrapper"))
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Signal::SignalContainer Signal::s_Signals;
|
||||
Signal::FreeSignals Signal::s_FreeSignals;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Signal::Terminate()
|
||||
{
|
||||
// Terminate named signals
|
||||
for (const auto & s : s_Signals)
|
||||
{
|
||||
// Clear slots
|
||||
s.second.mPtr->Clear();
|
||||
// Release the name
|
||||
s.second.mPtr->m_Name.clear();
|
||||
// Release whatever is in the user data
|
||||
s.second.mPtr->m_Data.Release();
|
||||
}
|
||||
// Finally clear the container itself
|
||||
s_Signals.clear();
|
||||
// Create a copy so we don't invalidate iterators when destructor removes the instances
|
||||
FreeSignals fsig(s_FreeSignals);
|
||||
// Terminate anonymous signals
|
||||
for (const auto & s : fsig)
|
||||
{
|
||||
// Clear slots
|
||||
s->Clear();
|
||||
// Release whatever is in the user data
|
||||
s->m_Data.Release();
|
||||
}
|
||||
// Finally clear the container itself
|
||||
s_FreeSignals.clear();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Object Signal::CreateFree()
|
||||
{
|
||||
// Remember the current stack size
|
||||
const StackGuard sg;
|
||||
// Create the signal instance
|
||||
DeleteGuard< Signal > dg(new Signal());
|
||||
// Attempt to create the signal instance
|
||||
ClassType< Signal >::PushInstance(DefaultVM::Get(), dg.Get());
|
||||
// This is now managed by the script
|
||||
dg.Release();
|
||||
// Return the created signal
|
||||
return Var< Object >(DefaultVM::Get(), -1).value;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Object Signal::Create(const StackStrF & name)
|
||||
{
|
||||
// Validate the signal name
|
||||
if (name.mLen <= 0)
|
||||
{
|
||||
return CreateFree();
|
||||
}
|
||||
// Create a copy of the name
|
||||
String sname(name.mPtr, name.mLen);
|
||||
// Compute the hash of the specified name
|
||||
const std::size_t hash = std::hash< String >{}(sname);
|
||||
// See if the signal already exists
|
||||
for (const auto & e : s_Signals)
|
||||
{
|
||||
if (e.first == hash)
|
||||
{
|
||||
return e.second.mObj; // Found a match so let's return it
|
||||
}
|
||||
}
|
||||
// Remember the current stack size
|
||||
const StackGuard sg;
|
||||
// Create the signal instance
|
||||
DeleteGuard< Signal > dg(new Signal(std::move(sname)));
|
||||
// Grab the signal instance pointer
|
||||
Signal * ptr = dg.Get();
|
||||
// Attempt to create the signal instance
|
||||
ClassType< Signal >::PushInstance(DefaultVM::Get(), ptr);
|
||||
// This is now managed by the script
|
||||
dg.Release();
|
||||
// Grab a reference to the instance created on the stack
|
||||
s_Signals.emplace_back(hash, SignalReference(ptr, Var< Object >(DefaultVM::Get(), -1).value));
|
||||
// Return the created signal
|
||||
return s_Signals.back().second.mObj;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Signal::Remove(const StackStrF & name)
|
||||
{
|
||||
// Validate the signal name
|
||||
if (name.mLen <= 0)
|
||||
{
|
||||
STHROWF("Signals without names cannot be removed manually");
|
||||
}
|
||||
// Create a copy of the name
|
||||
String sname(name.mPtr, name.mLen);
|
||||
// Compute the hash of the specified name
|
||||
const std::size_t hash = std::hash< String >{}(sname);
|
||||
// Iterator to the existing signal, if any
|
||||
SignalContainer::const_iterator itr = s_Signals.cbegin();
|
||||
// Search for a signal with this name
|
||||
for (; itr != s_Signals.cend(); ++itr)
|
||||
{
|
||||
if (itr->first == hash)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Did we find anything?
|
||||
if (itr != s_Signals.cend())
|
||||
{
|
||||
// Clear the name
|
||||
itr->second.mPtr->m_Name.clear();
|
||||
// Put it on the free list
|
||||
s_FreeSignals.push_back(itr->second.mPtr);
|
||||
// Finally, remove it from the named list
|
||||
s_Signals.erase(itr);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Object Signal::Fetch(const StackStrF & name)
|
||||
{
|
||||
// Validate the signal name
|
||||
if (name.mLen <= 0)
|
||||
{
|
||||
STHROWF("Signals without names cannot be retrieved manually");
|
||||
}
|
||||
// Create a copy of the name
|
||||
String sname(name.mPtr, name.mLen);
|
||||
// Compute the hash of the specified name
|
||||
const std::size_t hash = std::hash< String >{}(sname);
|
||||
// Search for a signal with this name
|
||||
for (const auto & e : s_Signals)
|
||||
{
|
||||
if (e.first == hash)
|
||||
{
|
||||
return e.second.mObj; // Found a match so let's return it
|
||||
}
|
||||
}
|
||||
// No such signal exists
|
||||
STHROWF("Unknown signal named (%s)", sname.c_str());
|
||||
// SHOULD NOT REACH THIS POINT!
|
||||
return NullObject();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Signal::Signal()
|
||||
: m_Head(nullptr), m_Data(), m_Name()
|
||||
{
|
||||
s_FreeSignals.push_back(this);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Signal::Signal(const CSStr name)
|
||||
: Signal(String(name ? name : _SC("")))
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Signal::Signal(const String & name)
|
||||
: Signal(String(name))
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Signal::Signal(String && name)
|
||||
: m_Head(nullptr), m_Data(), m_Name(std::move(name))
|
||||
{
|
||||
if (m_Name.empty())
|
||||
{
|
||||
s_FreeSignals.push_back(this);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Signal::~Signal()
|
||||
{
|
||||
Clear();
|
||||
if (m_Name.empty())
|
||||
{
|
||||
s_FreeSignals.erase(std::remove(s_FreeSignals.begin(), s_FreeSignals.end(), this),
|
||||
s_FreeSignals.end());
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQInteger Signal::Count() const
|
||||
{
|
||||
// Don't attempt to count anything if there's no head
|
||||
if (m_Head == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Final count
|
||||
Uint32 count = 0;
|
||||
// Walk down the chain and count all nodes
|
||||
for (Slot * node = m_Head; node != nullptr; node = node->mNext)
|
||||
{
|
||||
++count;
|
||||
}
|
||||
// Return the count
|
||||
return count;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Signal::Clear()
|
||||
{
|
||||
// Don't attempt to clear anything if there's no head
|
||||
if (m_Head == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Walk down the chain and delete all nodes
|
||||
for (Slot * node = m_Head, * next = nullptr; node != nullptr; node = next)
|
||||
{
|
||||
// Grab the next node upfront
|
||||
next = node->mNext;
|
||||
// Delete the node instance
|
||||
delete node;
|
||||
}
|
||||
// Reset the head
|
||||
m_Head = nullptr;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Signal::Connect(Object & env, Function & func)
|
||||
{
|
||||
// Don't attempt to search anything if there's no head
|
||||
if (m_Head == nullptr)
|
||||
{
|
||||
m_Head = new Slot(env, func, nullptr);
|
||||
// We're done here
|
||||
return;
|
||||
}
|
||||
|
||||
const Slot slot{env, func};
|
||||
// Don't attempt to search anything if there's only one element
|
||||
if (m_Head->mNext == nullptr)
|
||||
{
|
||||
// Is it already inserted?
|
||||
if (*m_Head != slot)
|
||||
{
|
||||
m_Head = new Slot(env, func, m_Head);
|
||||
}
|
||||
// We're done here
|
||||
return;
|
||||
}
|
||||
|
||||
// Walk down the chain and find an already matching node
|
||||
for (Slot * node = m_Head; node != nullptr; node = node->mNext)
|
||||
{
|
||||
if (*node == slot)
|
||||
{
|
||||
return; // Already connected
|
||||
}
|
||||
}
|
||||
// Connect now
|
||||
m_Head = new Slot(env, func, m_Head);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
bool Signal::Connected(Object & env, Function & func) const
|
||||
{
|
||||
// Don't attempt to search anything if there's no head
|
||||
if (m_Head == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const Slot slot{env, func};
|
||||
// Walk down the chain and find a matching node
|
||||
for (Slot * node = m_Head; node != nullptr; node = node->mNext)
|
||||
{
|
||||
if (*node == slot)
|
||||
{
|
||||
return true; // Found it
|
||||
}
|
||||
}
|
||||
// No such slot exists
|
||||
return false;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Signal::Disconnect(Object & env, Function & func)
|
||||
{
|
||||
// Don't attempt to search anything if there's no head
|
||||
if (m_Head == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const Slot slot{env, func};
|
||||
// Walk down the chain and remove the matching nodes
|
||||
for (Slot * node = m_Head, * next = nullptr; node != nullptr; node = next)
|
||||
{
|
||||
// Grab the next node upfront
|
||||
next = node->mNext;
|
||||
// Is this our node?
|
||||
if (*node == slot)
|
||||
{
|
||||
// Is this the head?
|
||||
if (node == m_Head)
|
||||
{
|
||||
m_Head = next; // Move the head to the next one
|
||||
}
|
||||
// Detach it from the chain
|
||||
node->Detach();
|
||||
// Delete the node instance
|
||||
delete node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Signal::DisconnectThis(Object & env)
|
||||
{
|
||||
// Don't attempt to search anything if there's no head
|
||||
if (m_Head == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const SQHash hash = Slot{env, NullFunction()}.mEnvHash;
|
||||
// Walk down the chain and remove the matching nodes
|
||||
for (Slot * node = m_Head, * next = nullptr; node != nullptr; node = next)
|
||||
{
|
||||
// Grab the next node upfront
|
||||
next = node->mNext;
|
||||
// Is this our node?
|
||||
if (node->mEnvHash == hash)
|
||||
{
|
||||
// Is this the head?
|
||||
if (node == m_Head)
|
||||
{
|
||||
m_Head = next; // Move the head to the next one
|
||||
}
|
||||
// Detach it from the chain
|
||||
node->Detach();
|
||||
// Delete the node instance
|
||||
delete node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Signal::DisconnectFunc(Function & func)
|
||||
{
|
||||
// Don't attempt to search anything if there's no head
|
||||
if (m_Head == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const SQHash hash = Slot{NullObject(), func}.mFuncHash;
|
||||
// Walk down the chain and remove the matching nodes
|
||||
for (Slot * node = m_Head, * next = nullptr; node != nullptr; node = next)
|
||||
{
|
||||
// Grab the next node upfront
|
||||
next = node->mNext;
|
||||
// Is this our node?
|
||||
if (node->mFuncHash == hash)
|
||||
{
|
||||
// Is this the head?
|
||||
if (node == m_Head)
|
||||
{
|
||||
m_Head = next; // Move the head to the next one
|
||||
}
|
||||
// Detach it from the chain
|
||||
node->Detach();
|
||||
// Delete the node instance
|
||||
delete node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Uint32 Signal::CountThis(Object & env) const
|
||||
{
|
||||
// Don't attempt to count anything if there's no head
|
||||
if (m_Head == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const SQHash hash = Slot{env, NullFunction()}.mEnvHash;
|
||||
// Final count
|
||||
Uint32 count = 0;
|
||||
// Walk down the chain and count the matching nodes
|
||||
for (Slot * node = m_Head; node != nullptr; node = node->mNext)
|
||||
{
|
||||
// Is this our node?
|
||||
if (node->mEnvHash == hash)
|
||||
{
|
||||
++count;
|
||||
}
|
||||
}
|
||||
// Return the count
|
||||
return count;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Uint32 Signal::CountFunc(Function & func) const
|
||||
{
|
||||
// Don't attempt to count anything if there's no head
|
||||
if (m_Head == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const SQHash hash = Slot{NullObject(), func}.mFuncHash;
|
||||
// Final count
|
||||
Uint32 count = 0;
|
||||
// Walk down the chain and count the matching nodes
|
||||
for (Slot * node = m_Head; node != nullptr; node = node->mNext)
|
||||
{
|
||||
// Is this our node?
|
||||
if (node->mFuncHash == hash)
|
||||
{
|
||||
++count;
|
||||
}
|
||||
}
|
||||
// Return the count
|
||||
return count;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Signal::Head(Object & env, Function & func)
|
||||
{
|
||||
// Don't attempt to search anything if there's no head
|
||||
if (m_Head == nullptr)
|
||||
{
|
||||
m_Head = new Slot(env, func, nullptr);
|
||||
// We're done here
|
||||
return;
|
||||
}
|
||||
|
||||
const Slot slot{env, func};
|
||||
// Don't attempt to search anything if there's only one element
|
||||
if (m_Head->mNext == nullptr)
|
||||
{
|
||||
// Is it already inserted?
|
||||
if (*m_Head != slot)
|
||||
{
|
||||
m_Head = new Slot(env, func, m_Head);
|
||||
}
|
||||
// We're done here
|
||||
return;
|
||||
}
|
||||
|
||||
// Grab the head node
|
||||
Slot * node = m_Head;
|
||||
// Walk down the chain and find a matching node
|
||||
for (; node != nullptr; node = node->mNext)
|
||||
{
|
||||
if (*node == slot)
|
||||
{
|
||||
break; // Found it
|
||||
}
|
||||
}
|
||||
// Have we found anything?
|
||||
if (node == nullptr)
|
||||
{
|
||||
m_Head = new Slot(env, func, m_Head); // Lead everyone
|
||||
}
|
||||
// Is it the head already?
|
||||
else if (m_Head != node)
|
||||
{
|
||||
node->AttachNext(m_Head);
|
||||
// We're the head now
|
||||
m_Head = node;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Signal::Tail(Object & env, Function & func)
|
||||
{
|
||||
// Don't attempt to search anything if there's no head
|
||||
if (m_Head == nullptr)
|
||||
{
|
||||
m_Head = new Slot(env, func, nullptr);
|
||||
// We're done here
|
||||
return;
|
||||
}
|
||||
|
||||
const Slot slot{env, func};
|
||||
// Don't attempt to search anything if there's only one element
|
||||
if (m_Head->mNext == nullptr)
|
||||
{
|
||||
// Is it already inserted?
|
||||
if (*m_Head != slot)
|
||||
{
|
||||
m_Head->mNext = new Slot(env, func, nullptr);
|
||||
// Link with the head
|
||||
m_Head->mNext->mPrev = m_Head;
|
||||
}
|
||||
// We're done here
|
||||
return;
|
||||
}
|
||||
|
||||
// Grab the head node
|
||||
Slot * node = m_Head, * prev = nullptr;
|
||||
// Walk down the chain and find a matching node
|
||||
for (; node != nullptr; prev = node, node = node->mNext)
|
||||
{
|
||||
if (*node == slot)
|
||||
{
|
||||
break; // Found it
|
||||
}
|
||||
}
|
||||
// Have we found anything?
|
||||
if (node == nullptr)
|
||||
{
|
||||
// Create the slot now
|
||||
node = new Slot(env, func, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Walk down the chain until the end
|
||||
while (prev->mNext != nullptr)
|
||||
{
|
||||
prev = prev->mNext;
|
||||
}
|
||||
// Finally, detach the node from it's current position
|
||||
node->Detach();
|
||||
}
|
||||
// Knowing 'prev' points to last element
|
||||
node->AttachPrev(prev);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQInteger Signal::SqEmit(HSQUIRRELVM vm)
|
||||
{
|
||||
const Int32 top = sq_gettop(vm);
|
||||
// The signal instance
|
||||
Signal * signal = nullptr;
|
||||
// Attempt to extract the signal instance
|
||||
try
|
||||
{
|
||||
signal = Var< Signal * >(vm, 1).value;
|
||||
}
|
||||
catch (const Sqrat::Exception & e)
|
||||
{
|
||||
return sq_throwerror(vm, e.what());
|
||||
}
|
||||
// Do we have a valid signal instance?
|
||||
if (!signal)
|
||||
{
|
||||
return sq_throwerror(vm, "Invalid signal instance");
|
||||
}
|
||||
// Walk down the chain and trigger slots
|
||||
for (Slot * node = signal->m_Head, * next = nullptr; node != nullptr; node = next)
|
||||
{
|
||||
// Grab the next node upfront
|
||||
next = node->mNext;
|
||||
// Remember the current stack size
|
||||
const StackGuard sg(vm);
|
||||
// Push the callback object
|
||||
sq_pushobject(vm, node->mFuncRef);
|
||||
// Is there an explicit environment?
|
||||
if (sq_isnull(node->mEnvRef))
|
||||
{
|
||||
sq_pushroottable(vm);
|
||||
}
|
||||
else
|
||||
{
|
||||
sq_pushobject(vm, node->mEnvRef);
|
||||
}
|
||||
// Are there any parameters to forward?
|
||||
if (top > 1)
|
||||
{
|
||||
for (SQInteger i = 2; i <= top; ++i)
|
||||
{
|
||||
sq_push(vm, i);
|
||||
}
|
||||
}
|
||||
// Make the function call and store the result
|
||||
const SQRESULT res = sq_call(vm, top, false, ErrorHandling::IsEnabled());
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
}
|
||||
// Specify that we don't return anything
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQInteger Signal::SqQuery(HSQUIRRELVM vm)
|
||||
{
|
||||
const Int32 top = sq_gettop(vm);
|
||||
// Do we have the collector environment?
|
||||
if (top <= 1)
|
||||
{
|
||||
return sq_throwerror(vm, "Missing collector environment");
|
||||
}
|
||||
// Do we have the collector function?
|
||||
else if (top <= 2)
|
||||
{
|
||||
return sq_throwerror(vm, "Missing collector callback");
|
||||
}
|
||||
// The signal instance
|
||||
Signal * signal = nullptr;
|
||||
// Attempt to extract the signal instance and collector
|
||||
try
|
||||
{
|
||||
signal = Var< Signal * >(vm, 1).value;
|
||||
}
|
||||
catch (const Sqrat::Exception & e)
|
||||
{
|
||||
return sq_throwerror(vm, e.what());
|
||||
}
|
||||
// Do we have a valid signal instance?
|
||||
if (!signal)
|
||||
{
|
||||
return sq_throwerror(vm, "Invalid signal instance");
|
||||
}
|
||||
// The collector
|
||||
HSQOBJECT cenv, cfunc;
|
||||
// Grab the collector environment
|
||||
SQRESULT res = sq_getstackobj(vm, 2, &cenv);
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
// Was there a valid environment?
|
||||
else if (sq_isnull(cenv))
|
||||
{
|
||||
// Remember the current stack size
|
||||
const StackGuard sg(vm);
|
||||
// Default to the root table
|
||||
sq_pushroottable(vm);
|
||||
// Try to grab the collector environment again
|
||||
SQRESULT res = sq_getstackobj(vm, -1, &cenv);
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
}
|
||||
// Grab the collector function
|
||||
res = sq_getstackobj(vm, 3, &cfunc);
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
// Some dummy checks to make sure the collector is a callable object
|
||||
else if (!sq_isfunction(cfunc) && !sq_isclosure(cfunc) && !sq_isnativeclosure(cfunc))
|
||||
{
|
||||
return sq_throwerror(vm, "Invalid collector");
|
||||
}
|
||||
// Walk down the chain and trigger slots
|
||||
for (Slot * node = signal->m_Head, * next = nullptr; node != nullptr; node = next)
|
||||
{
|
||||
// Grab the next node upfront
|
||||
next = node->mNext;
|
||||
// Remember the current stack size
|
||||
const StackGuard sg(vm);
|
||||
// Push the callback object
|
||||
sq_pushobject(vm, node->mFuncRef);
|
||||
// Is there an explicit environment?
|
||||
if (sq_isnull(node->mEnvRef))
|
||||
{
|
||||
sq_pushroottable(vm);
|
||||
}
|
||||
else
|
||||
{
|
||||
sq_pushobject(vm, node->mEnvRef);
|
||||
}
|
||||
// Are there any parameters to forward?
|
||||
if (top > 3)
|
||||
{
|
||||
for (SQInteger i = 4; i <= top; ++i)
|
||||
{
|
||||
sq_push(vm, i);
|
||||
}
|
||||
}
|
||||
// Make the function call and store the result
|
||||
res = sq_call(vm, top - 2, true, ErrorHandling::IsEnabled());
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
// Push the collector onto the stack
|
||||
sq_pushobject(vm, cfunc);
|
||||
sq_pushobject(vm, cenv);
|
||||
// Push the returned value
|
||||
sq_push(vm, -3);
|
||||
// Make the function call and store the result
|
||||
res = sq_call(vm, 2, false, ErrorHandling::IsEnabled());
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
}
|
||||
// Specify that we don't return anything
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQInteger Signal::SqConsume(HSQUIRRELVM vm)
|
||||
{
|
||||
const Int32 top = sq_gettop(vm);
|
||||
// The signal instance
|
||||
Signal * signal = nullptr;
|
||||
// Attempt to extract the signal instance
|
||||
try
|
||||
{
|
||||
signal = Var< Signal * >(vm, 1).value;
|
||||
}
|
||||
catch (const Sqrat::Exception & e)
|
||||
{
|
||||
return sq_throwerror(vm, e.what());
|
||||
}
|
||||
// Do we have a valid signal instance?
|
||||
if (!signal)
|
||||
{
|
||||
return sq_throwerror(vm, "Invalid signal instance");
|
||||
}
|
||||
// Default to not consumed
|
||||
SQBool ret = SQFalse;
|
||||
// Walk down the chain and trigger slots
|
||||
for (Slot * node = signal->m_Head, * next = nullptr; node != nullptr; node = next)
|
||||
{
|
||||
// Grab the next node upfront
|
||||
next = node->mNext;
|
||||
// Remember the current stack size
|
||||
const StackGuard sg(vm);
|
||||
// Push the callback object
|
||||
sq_pushobject(vm, node->mFuncRef);
|
||||
// Is there an explicit environment?
|
||||
if (sq_isnull(node->mEnvRef))
|
||||
{
|
||||
sq_pushroottable(vm);
|
||||
}
|
||||
else
|
||||
{
|
||||
sq_pushobject(vm, node->mEnvRef);
|
||||
}
|
||||
// Are there any parameters to forward?
|
||||
if (top > 1)
|
||||
{
|
||||
for (SQInteger i = 2; i <= top; ++i)
|
||||
{
|
||||
sq_push(vm, i);
|
||||
}
|
||||
}
|
||||
// Make the function call and store the result
|
||||
const SQRESULT res = sq_call(vm, top, true, ErrorHandling::IsEnabled());
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
// Is the returned value not null?
|
||||
else if (sq_gettype(vm, -1) != OT_NULL)
|
||||
{
|
||||
// Obtain the returned value
|
||||
sq_tobool(vm, -1, &ret);
|
||||
// Should we proceed to the next slot or stop here?
|
||||
if (ret == SQTrue)
|
||||
{
|
||||
break; // The slot consumed the signal
|
||||
}
|
||||
}
|
||||
}
|
||||
// Forward the returned value to the invoker
|
||||
sq_pushbool(vm, ret);
|
||||
// Specify that we returned something
|
||||
return 1;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQInteger Signal::SqApprove(HSQUIRRELVM vm)
|
||||
{
|
||||
const Int32 top = sq_gettop(vm);
|
||||
// The signal instance
|
||||
Signal * signal = nullptr;
|
||||
// Attempt to extract the signal instance
|
||||
try
|
||||
{
|
||||
signal = Var< Signal * >(vm, 1).value;
|
||||
}
|
||||
catch (const Sqrat::Exception & e)
|
||||
{
|
||||
return sq_throwerror(vm, e.what());
|
||||
}
|
||||
// Do we have a valid signal instance?
|
||||
if (!signal)
|
||||
{
|
||||
return sq_throwerror(vm, "Invalid signal instance");
|
||||
}
|
||||
// Default to approved
|
||||
SQBool ret = SQTrue;
|
||||
// Walk down the chain and trigger slots
|
||||
for (Slot * node = signal->m_Head, * next = nullptr; node != nullptr; node = next)
|
||||
{
|
||||
// Grab the next node upfront
|
||||
next = node->mNext;
|
||||
// Remember the current stack size
|
||||
const StackGuard sg(vm);
|
||||
// Push the callback object
|
||||
sq_pushobject(vm, node->mFuncRef);
|
||||
// Is there an explicit environment?
|
||||
if (sq_isnull(node->mEnvRef))
|
||||
{
|
||||
sq_pushroottable(vm);
|
||||
}
|
||||
else
|
||||
{
|
||||
sq_pushobject(vm, node->mEnvRef);
|
||||
}
|
||||
// Are there any parameters to forward?
|
||||
if (top > 1)
|
||||
{
|
||||
for (SQInteger i = 2; i <= top; ++i)
|
||||
{
|
||||
sq_push(vm, i);
|
||||
}
|
||||
}
|
||||
// Make the function call and store the result
|
||||
const SQRESULT res = sq_call(vm, top, true, ErrorHandling::IsEnabled());
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
// Is the returned value not null?
|
||||
else if (sq_gettype(vm, -1) != OT_NULL)
|
||||
{
|
||||
// Obtain the returned value
|
||||
sq_tobool(vm, -1, &ret);
|
||||
// Should we proceed to the next slot or stop here?
|
||||
if (ret == SQFalse)
|
||||
{
|
||||
break; // The slot did not approve the signal
|
||||
}
|
||||
}
|
||||
}
|
||||
// Forward the returned value to the invoker
|
||||
sq_pushbool(vm, ret);
|
||||
// Specify that we returned something
|
||||
return 1;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQInteger Signal::SqRequest(HSQUIRRELVM vm)
|
||||
{
|
||||
const Int32 top = sq_gettop(vm);
|
||||
// The signal instance
|
||||
Signal * signal = nullptr;
|
||||
// Attempt to extract the signal instance
|
||||
try
|
||||
{
|
||||
signal = Var< Signal * >(vm, 1).value;
|
||||
}
|
||||
catch (const Sqrat::Exception & e)
|
||||
{
|
||||
return sq_throwerror(vm, e.what());
|
||||
}
|
||||
// Do we have a valid signal instance?
|
||||
if (!signal)
|
||||
{
|
||||
return sq_throwerror(vm, "Invalid signal instance");
|
||||
}
|
||||
// Walk down the chain and trigger slots
|
||||
for (Slot * node = signal->m_Head, * next = nullptr; node != nullptr; node = next)
|
||||
{
|
||||
// Grab the next node upfront
|
||||
next = node->mNext;
|
||||
// Remember the current stack size
|
||||
const StackGuard sg(vm);
|
||||
// Push the callback object
|
||||
sq_pushobject(vm, node->mFuncRef);
|
||||
// Is there an explicit environment?
|
||||
if (sq_isnull(node->mEnvRef))
|
||||
{
|
||||
sq_pushroottable(vm);
|
||||
}
|
||||
else
|
||||
{
|
||||
sq_pushobject(vm, node->mEnvRef);
|
||||
}
|
||||
// Are there any parameters to forward?
|
||||
if (top > 1)
|
||||
{
|
||||
for (SQInteger i = 2; i <= top; ++i)
|
||||
{
|
||||
sq_push(vm, i);
|
||||
}
|
||||
}
|
||||
// Make the function call and store the result
|
||||
const SQRESULT res = sq_call(vm, top, true, ErrorHandling::IsEnabled());
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
// Is the returned value not null?
|
||||
else if (sq_gettype(vm, -1) != OT_NULL)
|
||||
{
|
||||
// Push back the returned value
|
||||
sq_push(vm, -1);
|
||||
// Specify that we returned something
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// Specify that we returned nothing
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ================================================================================================
|
||||
void Register_Signal(HSQUIRRELVM vm)
|
||||
{
|
||||
RootTable(vm).Bind(Typename::Str,
|
||||
Class< Signal, NoConstructor< Signal > >(vm, Typename::Str)
|
||||
// Meta-methods
|
||||
.SquirrelFunc(_SC("_typename"), &Typename::Fn)
|
||||
.Func(_SC("_tostring"), &Signal::ToString)
|
||||
// Core Properties
|
||||
.Prop(_SC("Data"), &Signal::GetData, &Signal::SetData)
|
||||
.Prop(_SC("Slots"), &Signal::Count)
|
||||
// Core Methods
|
||||
.Func(_SC("Clear"), &Signal::Clear)
|
||||
.Func(_SC("Connect"), &Signal::Connect)
|
||||
.Func(_SC("Connected"), &Signal::Connected)
|
||||
.Func(_SC("Disconnect"), &Signal::Disconnect)
|
||||
.Func(_SC("EliminateThis"), &Signal::DisconnectThis)
|
||||
.Func(_SC("EliminateFunc"), &Signal::DisconnectFunc)
|
||||
.Func(_SC("CountThis"), &Signal::CountThis)
|
||||
.Func(_SC("CountFunc"), &Signal::CountFunc)
|
||||
.Func(_SC("Head"), &Signal::Head)
|
||||
.Func(_SC("Tail"), &Signal::Tail)
|
||||
// Squirrel Functions
|
||||
.SquirrelFunc(_SC("Emit"), &Signal::SqEmit)
|
||||
.SquirrelFunc(_SC("Broadcast"), &Signal::SqEmit)
|
||||
.SquirrelFunc(_SC("Query"), &Signal::SqQuery)
|
||||
.SquirrelFunc(_SC("Consume"), &Signal::SqConsume)
|
||||
.SquirrelFunc(_SC("Approve"), &Signal::SqApprove)
|
||||
.SquirrelFunc(_SC("Request"), &Signal::SqRequest)
|
||||
);
|
||||
|
||||
RootTable(vm)
|
||||
.FmtFunc(_SC("SqSignal"), &Signal::Fetch)
|
||||
.FmtFunc(_SC("SqCreateSignal"), &Signal::Create)
|
||||
.FmtFunc(_SC("SqRemoveSignal"), &Signal::Remove);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Forward the call to terminate the signals.
|
||||
*/
|
||||
void TerminateSignals()
|
||||
{
|
||||
Signal::Terminate();
|
||||
}
|
||||
|
||||
} // Namespace:: SqMod
|
@ -1,539 +0,0 @@
|
||||
#ifndef _MISC_SIGNAL_HPP_
|
||||
#define _MISC_SIGNAL_HPP_
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
#include "Base/Shared.hpp"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
namespace SqMod {
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
class Signal;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Helper class used to reference and keep track of signal instances.
|
||||
*/
|
||||
struct SignalReference
|
||||
{
|
||||
// --------------------------------------------------------------------------------------------
|
||||
Signal * mPtr; // A raw pointer to the signal instance.
|
||||
Object mObj; // A managed reference to the signal instance.
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Explicit constructor.
|
||||
*/
|
||||
SignalReference(Signal * ptr, const Object & ref)
|
||||
: mPtr(ptr), mObj(ref)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copy constructor.
|
||||
*/
|
||||
SignalReference(const SignalReference & o) = default;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Move constructor.
|
||||
*/
|
||||
SignalReference(SignalReference && o) = default;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Destructor.
|
||||
*/
|
||||
~SignalReference() = default;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copy assignment operator.
|
||||
*/
|
||||
SignalReference & operator = (const SignalReference & o) = default;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Move assignment operator.
|
||||
*/
|
||||
SignalReference & operator = (SignalReference && o) = default;
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Utility class for for delivering events to one or more listeners.
|
||||
*/
|
||||
class Signal
|
||||
{
|
||||
private:
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Used to store a script callback and the unique hash associated with it for quick searching.
|
||||
*/
|
||||
struct Slot
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------
|
||||
SQHash mEnvHash; // The hash of the specified environment.
|
||||
SQHash mFuncHash; // The hash of the specified callback.
|
||||
HSQOBJECT mEnvRef; // The specified script environment.
|
||||
HSQOBJECT mFuncRef; // The specified script callback.
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
Slot * mPrev; // Previous slot in the chain.
|
||||
Slot * mNext; // Next slot in the chain.
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Forwarding constructor.
|
||||
*/
|
||||
Slot(Object & env, Function & func)
|
||||
: Slot(env.GetObject(), func.GetFunc(), nullptr)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Forwarding constructor.
|
||||
*/
|
||||
Slot(Object & env, Function & func, Slot * head)
|
||||
: Slot(env.GetObject(), func.GetFunc(), head)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Base constructor.
|
||||
*/
|
||||
Slot(HSQOBJECT & env, HSQOBJECT & func, Slot * head)
|
||||
: mEnvHash(0)
|
||||
, mFuncHash(0)
|
||||
, mEnvRef(env)
|
||||
, mFuncRef(func)
|
||||
, mPrev(nullptr)
|
||||
, mNext(head)
|
||||
{
|
||||
HSQUIRRELVM vm = DefaultVM::Get();
|
||||
// Remember the current stack size
|
||||
const StackGuard sg(vm);
|
||||
// Is there an explicit environment?
|
||||
if (!sq_isnull(mEnvRef))
|
||||
{
|
||||
// Keep a reference to this environment
|
||||
sq_addref(vm, &mEnvRef);
|
||||
// Push the environment on the stack
|
||||
sq_pushobject(vm, mEnvRef);
|
||||
// Grab the hash of the environment object
|
||||
mEnvHash = sq_gethash(vm, -1);
|
||||
}
|
||||
// Is there an explicit function?
|
||||
if (!sq_isnull(mFuncRef))
|
||||
{
|
||||
// Keep a reference to this function
|
||||
sq_addref(vm, &mFuncRef);
|
||||
// Push the callback on the stack
|
||||
sq_pushobject(vm, mFuncRef);
|
||||
// Grab the hash of the callback object
|
||||
mFuncHash = sq_gethash(vm, -1);
|
||||
}
|
||||
// Was there a previous head?
|
||||
if (mNext != nullptr)
|
||||
{
|
||||
// Steal the slot before the head
|
||||
mPrev = mNext->mPrev;
|
||||
// Did that head slot had a slot behind it?
|
||||
if (mPrev != nullptr)
|
||||
{
|
||||
// Tell it we're the next slot now
|
||||
mPrev->mNext = this;
|
||||
}
|
||||
// Tell the previous head that we're behind it now
|
||||
mNext->mPrev = this;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Copy constructor. (disabled)
|
||||
*/
|
||||
Slot(const Slot & o) = delete;
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Move constructor.
|
||||
*/
|
||||
Slot(Slot && o)
|
||||
: mEnvHash(o.mEnvHash)
|
||||
, mFuncHash(o.mFuncHash)
|
||||
, mEnvRef(o.mEnvRef)
|
||||
, mFuncRef(o.mFuncRef)
|
||||
, mPrev(o.mPrev)
|
||||
, mNext(o.mNext)
|
||||
{
|
||||
// Take ownership
|
||||
sq_resetobject(&o.mEnvRef);
|
||||
sq_resetobject(&o.mFuncRef);
|
||||
o.mPrev = nullptr;
|
||||
o.mNext = nullptr;
|
||||
// Attach to the chain
|
||||
Attach();
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Destructor.
|
||||
*/
|
||||
~Slot()
|
||||
{
|
||||
Release();
|
||||
// Detach from the chain
|
||||
Detach();
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Copy assignment operator. (disabled)
|
||||
*/
|
||||
Slot & operator = (const Slot & o) = delete;
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Move assignment operator.
|
||||
*/
|
||||
Slot & operator = (Slot && o)
|
||||
{
|
||||
if (this != &o)
|
||||
{
|
||||
// Release current resources, if any
|
||||
Release();
|
||||
// Replicate data
|
||||
mEnvHash = o.mEnvHash;
|
||||
mFuncHash = o.mFuncHash;
|
||||
mEnvRef = o.mEnvRef;
|
||||
mFuncRef = o.mFuncRef;
|
||||
mPrev = o.mPrev;
|
||||
mNext = o.mNext;
|
||||
// Take ownership
|
||||
sq_resetobject(&o.mEnvRef);
|
||||
sq_resetobject(&o.mFuncRef);
|
||||
o.mPrev = nullptr;
|
||||
o.mNext = nullptr;
|
||||
// Attach to the chain
|
||||
Attach();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Equality comparison operator.
|
||||
*/
|
||||
bool operator == (const Slot & o) const
|
||||
{
|
||||
return (mEnvHash == o.mEnvHash) && (mFuncHash == o.mFuncHash);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Inequality comparison operator.
|
||||
*/
|
||||
bool operator != (const Slot & o) const
|
||||
{
|
||||
return (mEnvHash != o.mEnvHash) || (mFuncHash != o.mFuncHash);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Release managed script resources.
|
||||
*/
|
||||
void Release()
|
||||
{
|
||||
// Should we release any environment object?
|
||||
if (!sq_isnull(mEnvRef))
|
||||
{
|
||||
sq_release(DefaultVM::Get(), &mEnvRef);
|
||||
sq_resetobject(&mEnvRef);
|
||||
}
|
||||
// Should we release any callback object?
|
||||
if (!sq_isnull(mFuncRef))
|
||||
{
|
||||
sq_release(DefaultVM::Get(), &mFuncRef);
|
||||
sq_resetobject(&mFuncRef);
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Attach the slot to the chain.
|
||||
*/
|
||||
void Attach()
|
||||
{
|
||||
// Is there a slot after us?
|
||||
if (mNext != nullptr)
|
||||
{
|
||||
// Tell it that we're behind it
|
||||
mNext->mPrev = this;
|
||||
}
|
||||
// Is there a node behind us?
|
||||
if (mPrev != nullptr)
|
||||
{
|
||||
// Tell it that we're ahead of it
|
||||
mPrev->mNext = this;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Detach the slot from the chain.
|
||||
*/
|
||||
void Detach()
|
||||
{
|
||||
// Is there a slot after us?
|
||||
if (mNext != nullptr)
|
||||
{
|
||||
// Tell it we're no longer behind it
|
||||
mNext->mPrev = mPrev;
|
||||
}
|
||||
// Is there a node behind us?
|
||||
if (mPrev != nullptr)
|
||||
{
|
||||
// Tell it we're no longer ahead of it
|
||||
mPrev->mNext = mNext;
|
||||
}
|
||||
mPrev = mNext = nullptr;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Attach the slot after another slot.
|
||||
*/
|
||||
void AttachNext(Slot * node)
|
||||
{
|
||||
Detach();
|
||||
// Assign the slot ahead of us
|
||||
mNext = node;
|
||||
// Is there a node to attach to?
|
||||
if (mNext != nullptr)
|
||||
{
|
||||
// Steal the previous slot
|
||||
mPrev = mNext->mPrev;
|
||||
// Was there a previous slot?
|
||||
if (mPrev != nullptr)
|
||||
{
|
||||
mPrev->mNext = this; // Tell it we're the next slot now
|
||||
}
|
||||
// Place ourself behind
|
||||
mNext->mPrev = this;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Attach the slot before another slot.
|
||||
*/
|
||||
void AttachPrev(Slot * node)
|
||||
{
|
||||
Detach();
|
||||
// Assign the slot behind us
|
||||
mPrev = node;
|
||||
// Is there a node to attach to?
|
||||
if (mPrev != nullptr)
|
||||
{
|
||||
// Steal the next slot
|
||||
mNext = mPrev->mNext;
|
||||
// Was there a next slot?
|
||||
if (mNext != nullptr)
|
||||
{
|
||||
mNext->mPrev = this; // Tell it we're the previous slot now
|
||||
}
|
||||
// Place ourself ahead
|
||||
mPrev->mNext = this;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
Slot * m_Head; // The chain of slots bound to this signal.
|
||||
Object m_Data; // User data associated with this instance.
|
||||
String m_Name; // The name that identifies this signal.
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Default constructor.
|
||||
*/
|
||||
Signal();
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Base constructor.
|
||||
*/
|
||||
explicit Signal(const CSStr name);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Base constructor.
|
||||
*/
|
||||
Signal(const String & name);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Base constructor.
|
||||
*/
|
||||
Signal(String && name);
|
||||
|
||||
public:
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copy constructor. (disabled)
|
||||
*/
|
||||
Signal(const Signal & o) = delete;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Move constructor. (disabled)
|
||||
*/
|
||||
Signal(Signal && o) = delete;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Destructor.
|
||||
*/
|
||||
~Signal();
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copy assignment operator. (disabled)
|
||||
*/
|
||||
Signal & operator = (const Signal & o) = delete;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Move assignment operator. (disabled)
|
||||
*/
|
||||
Signal & operator = (Signal && o) = delete;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Used by the script engine to convert an instance of this type to a string.
|
||||
*/
|
||||
const String & ToString() const
|
||||
{
|
||||
return m_Name;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Retrieve the associated user data.
|
||||
*/
|
||||
Object & GetData()
|
||||
{
|
||||
return m_Data;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Modify the associated user data.
|
||||
*/
|
||||
void SetData(Object & data)
|
||||
{
|
||||
m_Data = data;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* The number of slots connected to the signal.
|
||||
*/
|
||||
SQInteger Count() const;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Clear all slots connected to the signal.
|
||||
*/
|
||||
void Clear();
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Connect a function with a specific environment to the signal.
|
||||
*/
|
||||
void Connect(Object & env, Function & func);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* See whether a function with a specific environment is connected to the signal.
|
||||
*/
|
||||
bool Connected(Object & env, Function & func) const;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Disconnect a function with a specific environment from the signal.
|
||||
*/
|
||||
void Disconnect(Object & env, Function & func);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Disconnect all functions with a specific environment from the signal.
|
||||
*/
|
||||
void DisconnectThis(Object & env);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Disconnect all matching functions regardless of the environment from the signal.
|
||||
*/
|
||||
void DisconnectFunc(Function & func);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Count all functions with a specific environment from the signal.
|
||||
*/
|
||||
Uint32 CountThis(Object & env) const;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Count all matching functions regardless of the environment from the signal.
|
||||
*/
|
||||
Uint32 CountFunc(Function & func) const;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Make sure that specified slot is positioned ahead of all other slots.
|
||||
*/
|
||||
void Head(Object & env, Function & func);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Make sure that specified slot is positioned behind of all other slots.
|
||||
*/
|
||||
void Tail(Object & env, Function & func);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Emit the event to the connected slots.
|
||||
*/
|
||||
static SQInteger SqEmit(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Emit the event to the connected slots and collect returned values.
|
||||
*/
|
||||
static SQInteger SqQuery(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Emit the event to the connected slots and see if they consume it.
|
||||
*/
|
||||
static SQInteger SqConsume(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Emit the event to the connected slots and see if they approve it.
|
||||
*/
|
||||
static SQInteger SqApprove(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Emit the event to the connected slots and see if they return something.
|
||||
*/
|
||||
static SQInteger SqRequest(HSQUIRRELVM vm);
|
||||
|
||||
private:
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
typedef std::pair< std::size_t, SignalReference > SignalElement;
|
||||
typedef std::vector< SignalElement > SignalContainer;
|
||||
typedef std::vector< Signal * > FreeSignals;
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
static SignalContainer s_Signals; // List of all created signals.
|
||||
static FreeSignals s_FreeSignals; // List of signals without a name.
|
||||
|
||||
public:
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Terminate all signal instances and release any script resources.
|
||||
*/
|
||||
static void Terminate();
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Create a free signal without a specific name.
|
||||
*/
|
||||
static Object CreateFree();
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Create a new signal with the specified name.
|
||||
*/
|
||||
static Object Create(const StackStrF & name);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Remove the signal with the specified name.
|
||||
*/
|
||||
static void Remove(const StackStrF & name);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Retrieve the signal with the specified name.
|
||||
*/
|
||||
static Object Fetch(const StackStrF & name);
|
||||
};
|
||||
|
||||
} // Namespace:: SqMod
|
||||
|
||||
#endif // _MISC_SIGNAL_HPP_
|
Reference in New Issue
Block a user