diff --git a/cbp/Module.cbp b/cbp/Module.cbp
index 641dd29c..67862dd4 100644
--- a/cbp/Module.cbp
+++ b/cbp/Module.cbp
@@ -550,6 +550,8 @@
+
+
diff --git a/source/Core.cpp b/source/Core.cpp
index 8fbb08e4..63be4e16 100644
--- a/source/Core.cpp
+++ b/source/Core.cpp
@@ -43,11 +43,16 @@ namespace SqMod {
extern bool RegisterAPI(HSQUIRRELVM vm);
// ------------------------------------------------------------------------------------------------
+extern void InitializeTasks();
extern void InitializeRoutines();
+extern void TerminateTasks();
extern void TerminateRoutines();
extern void TerminateCommands();
extern void TerminateSignals();
+// ------------------------------------------------------------------------------------------------
+extern void CleanupTasks(Int32 id, Int32 type);
+
// ------------------------------------------------------------------------------------------------
extern Buffer GetRealFilePath(CSStr path);
@@ -353,8 +358,9 @@ bool Core::Initialize()
}
}
- // Initialize routines
+ // Initialize routines and tasks
InitializeRoutines();
+ InitializeTasks();
// Initialization successful
return true;
@@ -478,8 +484,9 @@ void Core::Terminate(bool shutdown)
const ContainerCleaner cc_blips(m_Blips, ENT_BLIP, !shutdown);
const ContainerCleaner cc_keybinds(m_Keybinds, ENT_KEYBIND, !shutdown);
cLogDbg(m_Verbosity >= 1, "Terminating routines an commands");
- // Release all resources from routines
+ // Release all resources from routines and tasks
TerminateRoutines();
+ TerminateTasks();
// Release all resources from command managers
TerminateCommands();
// Release all resources from signals
@@ -956,6 +963,8 @@ void Core::BlipInst::Destroy(bool destroy, Int32 header, Object & payload)
mInst = nullptr;
// Release the script object, if any
mObj.Release();
+ // Release tasks, if any
+ CleanupTasks(mID, ENT_BLIP);
// Are we supposed to clean up this entity? (only at reload)
if (destroy && VALID_ENTITY(mID) && (mFlags & ENF_OWNED))
{
@@ -995,6 +1004,8 @@ void Core::CheckpointInst::Destroy(bool destroy, Int32 header, Object & payload)
mInst = nullptr;
// Release the script object, if any
mObj.Release();
+ // Release tasks, if any
+ CleanupTasks(mID, ENT_CHECKPOINT);
// Are we supposed to clean up this entity? (only at reload)
if (destroy && VALID_ENTITY(mID) && (mFlags & ENF_OWNED))
{
@@ -1034,6 +1045,8 @@ void Core::KeybindInst::Destroy(bool destroy, Int32 header, Object & payload)
mInst = nullptr;
// Release the script object, if any
mObj.Release();
+ // Release tasks, if any
+ CleanupTasks(mID, ENT_KEYBIND);
// Are we supposed to clean up this entity? (only at reload)
if (destroy && VALID_ENTITY(mID) && (mFlags & ENF_OWNED))
{
@@ -1073,6 +1086,8 @@ void Core::ObjectInst::Destroy(bool destroy, Int32 header, Object & payload)
mInst = nullptr;
// Release the script object, if any
mObj.Release();
+ // Release tasks, if any
+ CleanupTasks(mID, ENT_OBJECT);
// Are we supposed to clean up this entity? (only at reload)
if (destroy && VALID_ENTITY(mID) && (mFlags & ENF_OWNED))
{
@@ -1112,6 +1127,8 @@ void Core::PickupInst::Destroy(bool destroy, Int32 header, Object & payload)
mInst = nullptr;
// Release the script object, if any
mObj.Release();
+ // Release tasks, if any
+ CleanupTasks(mID, ENT_PICKUP);
// Are we supposed to clean up this entity? (only at reload)
if (destroy && VALID_ENTITY(mID) && (mFlags & ENF_OWNED))
{
@@ -1153,6 +1170,8 @@ void Core::PlayerInst::Destroy(bool /*destroy*/, Int32 header, Object & payload)
mInst = nullptr;
// Release the script object, if any
mObj.Release();
+ // Release tasks, if any
+ CleanupTasks(mID, ENT_PLAYER);
// Reset the instance to it's initial state
Core::ResetInst(*this);
// Don't release the callbacks abruptly
@@ -1184,6 +1203,8 @@ void Core::VehicleInst::Destroy(bool destroy, Int32 header, Object & payload)
mInst = nullptr;
// Release the script object, if any
mObj.Release();
+ // Release tasks, if any
+ CleanupTasks(mID, ENT_VEHICLE);
// Are we supposed to clean up this entity? (only at reload)
if (destroy && VALID_ENTITY(mID) && (mFlags & ENF_OWNED))
{
diff --git a/source/Entity/Blip.cpp b/source/Entity/Blip.cpp
index d3e56141..fab376f4 100644
--- a/source/Entity/Blip.cpp
+++ b/source/Entity/Blip.cpp
@@ -1,6 +1,7 @@
// ------------------------------------------------------------------------------------------------
#include "Entity/Blip.hpp"
#include "Core.hpp"
+#include "Tasks.hpp"
// ------------------------------------------------------------------------------------------------
namespace SqMod {
@@ -357,6 +358,9 @@ void Register_CBlip(HSQUIRRELVM vm)
(_SC("Create"), &Blip_Create)
// Raw Squirrel Methods
.SquirrelFunc(_SC("NullInst"), &CBlip::SqGetNull)
+ .SquirrelFunc(_SC("MakeTask"), &Tasks::MakeTask< CBlip, ENT_BLIP >)
+ .SquirrelFunc(_SC("DropTask"), &Tasks::DropTask< CBlip, ENT_BLIP >)
+ .SquirrelFunc(_SC("DoesTask"), &Tasks::DoesTask< CBlip, ENT_BLIP >)
);
}
diff --git a/source/Entity/Checkpoint.cpp b/source/Entity/Checkpoint.cpp
index 86b16b8e..9b69c1d5 100644
--- a/source/Entity/Checkpoint.cpp
+++ b/source/Entity/Checkpoint.cpp
@@ -4,6 +4,7 @@
#include "Base/Color4.hpp"
#include "Base/Vector3.hpp"
#include "Core.hpp"
+#include "Tasks.hpp"
// ------------------------------------------------------------------------------------------------
namespace SqMod {
@@ -578,6 +579,9 @@ void Register_CCheckpoint(HSQUIRRELVM vm)
(_SC("Create"), &Checkpoint_Create)
// Raw Squirrel Methods
.SquirrelFunc(_SC("NullInst"), &CCheckpoint::SqGetNull)
+ .SquirrelFunc(_SC("MakeTask"), &Tasks::MakeTask< CCheckpoint, ENT_CHECKPOINT >)
+ .SquirrelFunc(_SC("DropTask"), &Tasks::DropTask< CCheckpoint, ENT_CHECKPOINT >)
+ .SquirrelFunc(_SC("DoesTask"), &Tasks::DoesTask< CCheckpoint, ENT_CHECKPOINT >)
);
}
diff --git a/source/Entity/Keybind.cpp b/source/Entity/Keybind.cpp
index 29989913..6133685b 100644
--- a/source/Entity/Keybind.cpp
+++ b/source/Entity/Keybind.cpp
@@ -1,6 +1,7 @@
// ------------------------------------------------------------------------------------------------
#include "Entity/Keybind.hpp"
#include "Core.hpp"
+#include "Tasks.hpp"
// ------------------------------------------------------------------------------------------------
namespace SqMod {
@@ -240,6 +241,9 @@ void Register_CKeybind(HSQUIRRELVM vm)
(_SC("Create"), &Keybind_Create)
// Raw Squirrel Methods
.SquirrelFunc(_SC("NullInst"), &CKeybind::SqGetNull)
+ .SquirrelFunc(_SC("MakeTask"), &Tasks::MakeTask< CKeybind, ENT_KEYBIND >)
+ .SquirrelFunc(_SC("DropTask"), &Tasks::DropTask< CKeybind, ENT_KEYBIND >)
+ .SquirrelFunc(_SC("DoesTask"), &Tasks::DoesTask< CKeybind, ENT_KEYBIND >)
);
}
diff --git a/source/Entity/Object.cpp b/source/Entity/Object.cpp
index 333311f2..94d2b0e5 100644
--- a/source/Entity/Object.cpp
+++ b/source/Entity/Object.cpp
@@ -4,6 +4,7 @@
#include "Base/Quaternion.hpp"
#include "Base/Vector3.hpp"
#include "Core.hpp"
+#include "Tasks.hpp"
// ------------------------------------------------------------------------------------------------
namespace SqMod {
@@ -991,6 +992,9 @@ void Register_CObject(HSQUIRRELVM vm)
(_SC("Create"), &Object_Create)
// Raw Squirrel Methods
.SquirrelFunc(_SC("NullInst"), &CObject::SqGetNull)
+ .SquirrelFunc(_SC("MakeTask"), &Tasks::MakeTask< CObject, ENT_OBJECT >)
+ .SquirrelFunc(_SC("DropTask"), &Tasks::DropTask< CObject, ENT_OBJECT >)
+ .SquirrelFunc(_SC("DoesTask"), &Tasks::DoesTask< CObject, ENT_OBJECT >)
);
}
diff --git a/source/Entity/Pickup.cpp b/source/Entity/Pickup.cpp
index 87cb7366..75f15240 100644
--- a/source/Entity/Pickup.cpp
+++ b/source/Entity/Pickup.cpp
@@ -3,6 +3,7 @@
#include "Entity/Player.hpp"
#include "Base/Vector3.hpp"
#include "Core.hpp"
+#include "Tasks.hpp"
// ------------------------------------------------------------------------------------------------
namespace SqMod {
@@ -494,6 +495,9 @@ void Register_CPickup(HSQUIRRELVM vm)
(_SC("Create"), &Pickup_Create)
// Raw Squirrel Methods
.SquirrelFunc(_SC("NullInst"), &CPickup::SqGetNull)
+ .SquirrelFunc(_SC("MakeTask"), &Tasks::MakeTask< CPickup, ENT_PICKUP >)
+ .SquirrelFunc(_SC("DropTask"), &Tasks::DropTask< CPickup, ENT_PICKUP >)
+ .SquirrelFunc(_SC("DoesTask"), &Tasks::DoesTask< CPickup, ENT_PICKUP >)
);
}
diff --git a/source/Entity/Player.cpp b/source/Entity/Player.cpp
index f6057b52..882d330c 100644
--- a/source/Entity/Player.cpp
+++ b/source/Entity/Player.cpp
@@ -6,6 +6,7 @@
#include "Base/Vector3.hpp"
#include "Library/Utils/Buffer.hpp"
#include "Core.hpp"
+#include "Tasks.hpp"
// ------------------------------------------------------------------------------------------------
#include
@@ -2529,6 +2530,9 @@ void Register_CPlayer(HSQUIRRELVM vm)
.SquirrelFunc(_SC("Text"), &CPlayer::Announce)
.SquirrelFunc(_SC("TextEx"), &CPlayer::AnnounceEx)
.SquirrelFunc(_SC("NullInst"), &CPlayer::SqGetNull)
+ .SquirrelFunc(_SC("MakeTask"), &Tasks::MakeTask< CPlayer, ENT_PLAYER >)
+ .SquirrelFunc(_SC("DropTask"), &Tasks::DropTask< CPlayer, ENT_PLAYER >)
+ .SquirrelFunc(_SC("DoesTask"), &Tasks::DoesTask< CPlayer, ENT_PLAYER >)
);
}
diff --git a/source/Entity/Vehicle.cpp b/source/Entity/Vehicle.cpp
index 0873d7ad..4099482e 100644
--- a/source/Entity/Vehicle.cpp
+++ b/source/Entity/Vehicle.cpp
@@ -5,6 +5,7 @@
#include "Base/Vector2.hpp"
#include "Base/Vector3.hpp"
#include "Core.hpp"
+#include "Tasks.hpp"
// ------------------------------------------------------------------------------------------------
namespace SqMod {
@@ -1927,6 +1928,9 @@ void Register_CVehicle(HSQUIRRELVM vm)
(_SC("Create"), &Vehicle_Create)
// Raw Squirrel Methods
.SquirrelFunc(_SC("NullInst"), &CVehicle::SqGetNull)
+ .SquirrelFunc(_SC("MakeTask"), &Tasks::MakeTask< CVehicle, ENT_VEHICLE >)
+ .SquirrelFunc(_SC("DropTask"), &Tasks::DropTask< CVehicle, ENT_VEHICLE >)
+ .SquirrelFunc(_SC("DoesTask"), &Tasks::DoesTask< CVehicle, ENT_VEHICLE >)
);
}
diff --git a/source/Main.cpp b/source/Main.cpp
index c1d49011..febc316e 100644
--- a/source/Main.cpp
+++ b/source/Main.cpp
@@ -14,6 +14,7 @@ static bool g_Reload = false;
// ------------------------------------------------------------------------------------------------
extern void InitExports();
+extern void ProcessTasks();
extern void ProcessRoutines();
/* ------------------------------------------------------------------------------------------------
@@ -175,8 +176,9 @@ static void OnServerFrame(float elapsed_time)
Core::Get().EmitServerFrame(elapsed_time);
}
SQMOD_CATCH_EVENT_EXCEPTION(OnServerFrame)
- // Process routines, if any
+ // Process routines and tasks, if any
ProcessRoutines();
+ ProcessTasks();
// See if a reload was requested
SQMOD_RELOAD_CHECK(g_Reload)
}
diff --git a/source/SqBase.hpp b/source/SqBase.hpp
index 8ff02bbe..f529c779 100644
--- a/source/SqBase.hpp
+++ b/source/SqBase.hpp
@@ -636,6 +636,7 @@ enum EntityType
*/
#define SQMOD_STACK_SIZE 2048
+#define SQMOD_MAX_TASKS 1024
#define SQMOD_MAX_ROUTINES 1024
#define SQMOD_MAX_CMD_ARGS 12
#define SQMOD_PLAYER_MSG_PREFIXES 16
diff --git a/source/Tasks.cpp b/source/Tasks.cpp
new file mode 100644
index 00000000..86216359
--- /dev/null
+++ b/source/Tasks.cpp
@@ -0,0 +1,481 @@
+// ------------------------------------------------------------------------------------------------
+#include "Tasks.hpp"
+#include "Core.hpp"
+#include "Library/Chrono.hpp"
+
+// ------------------------------------------------------------------------------------------------
+#include
+#include
+#include
+
+// ------------------------------------------------------------------------------------------------
+#include
+
+// ------------------------------------------------------------------------------------------------
+namespace SqMod {
+
+// ------------------------------------------------------------------------------------------------
+Uint32 Tasks::s_Used = 0;
+Tasks::Time Tasks::s_Last = 0;
+Tasks::Time Tasks::s_Prev = 0;
+Tasks::Interval Tasks::s_Intervals[SQMOD_MAX_TASKS];
+Tasks::Task Tasks::s_Tasks[SQMOD_MAX_TASKS];
+
+// ------------------------------------------------------------------------------------------------
+void Tasks::Task::Init(HSQOBJECT & env, HSQOBJECT & func, Interval intrv, Iterator itr, Int32 id, Int32 type)
+{
+ // Initialize the callback hash
+ mHash = 0;
+ // Initialize the callback objects
+ mEnv = env;
+ mFunc = func;
+ // Initialize the task options
+ mIterations = itr;
+ mInterval = intrv;
+ // Initialize the entity information
+ mEntity = ConvTo< Int16 >::From(id);
+ mType = ConvTo< Uint8 >::From(type);
+ // Grab the virtual machine once
+ HSQUIRRELVM vm = DefaultVM::Get();
+ // Is there a valid environment?
+ if (!sq_isnull(mEnv))
+ {
+ sq_addref(vm, &mEnv); // Keep a reference to this environment
+ }
+ // Remember the current stack size
+ const StackGuard sg(vm);
+ // Is there a valid function?
+ if (!sq_isnull(mFunc))
+ {
+ // Keep a reference to this function
+ sq_addref(vm, &mFunc);
+ // Push the callback on the stack
+ sq_pushobject(vm, mFunc);
+ // Grab the hash of the callback object
+ mHash = sq_gethash(vm, -1);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Tasks::Task::Release()
+{
+ // Should we release any environment object?
+ if (!sq_isnull(mEnv))
+ {
+ sq_release(DefaultVM::Get(), &mEnv);
+ sq_resetobject(&mEnv);
+ }
+ // Should we release any callback object?
+ if (!sq_isnull(mFunc))
+ {
+ sq_release(DefaultVM::Get(), &mFunc);
+ sq_resetobject(&mFunc);
+ }
+ // Reset member variables as well
+ mHash = 0;
+ mIterations = 0;
+ mInterval = 0;
+ mEntity = -1;
+ mType = -1;
+}
+
+// ------------------------------------------------------------------------------------------------
+Tasks::Interval Tasks::Task::Execute()
+{
+ // Are we even a valid task?
+ if (INVALID_ENTITY(mEntity))
+ {
+ return 0; // Dunno how we got here but it ends now
+ }
+ // Grab the virtual machine once
+ HSQUIRRELVM vm = DefaultVM::Get();
+ // Push the function on the stack
+ sq_pushobject(vm, mFunc);
+ // Push the environment on the stack
+ sq_pushobject(vm, mEnv);
+ // Push function parameters, if any
+ for (Uint32 n = 0; n < mArgc; ++n)
+ {
+ sq_pushobject(vm, mArgv[n].mObj);
+ }
+ // Make the function call and store the result
+ const SQRESULT res = sq_call(vm, mArgc + 1, false, ErrorHandling::IsEnabled());
+ // Validate the result
+ if (SQ_FAILED(res))
+ {
+ // Destroy ourself on error
+ Release();
+ Clear();
+ }
+ // Decrease the number of iterations if necessary
+ if (mIterations && (--mIterations) == 0)
+ {
+ // This routine reached the end of it's life
+ Release();
+ Clear();
+ }
+ // Return the current interval
+ return mInterval;
+}
+
+// ------------------------------------------------------------------------------------------------
+void Tasks::Process()
+{
+ // Is this the first call?
+ if (s_Last == 0)
+ {
+ s_Last = Chrono::GetCurrentSysTime();
+ // We'll do it text time
+ return;
+ }
+ // Backup the last known time-stamp
+ s_Prev = s_Last;
+ // Get the current time-stamp
+ s_Last = Chrono::GetCurrentSysTime();
+ // Calculate the elapsed time
+ const Int32 delta = Int32((s_Last - s_Prev) / 1000L);
+ // Process all active tasks
+ for (Interval * itr = s_Intervals; itr != (s_Intervals + SQMOD_MAX_TASKS); ++itr)
+ {
+ // Is this task valid?
+ if (*itr)
+ {
+ // Decrease the elapsed time
+ (*itr) -= delta;
+ // Have we completed the routine interval?
+ if ((*itr) <= 0)
+ {
+ // Reset the elapsed time
+ (*itr) = s_Tasks[itr - s_Intervals].Execute();
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Tasks::Initialize()
+{
+ std::memset(s_Intervals, 0, sizeof(s_Intervals));
+}
+
+// ------------------------------------------------------------------------------------------------
+void Tasks::Deinitialize()
+{
+ // Release any script resources that the tasks might store
+ for (auto & t : s_Tasks)
+ {
+ t.Release();
+ t.Clear();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Object & Tasks::FindEntity(Int32 id, Int32 type)
+{
+ switch (type)
+ {
+ case ENT_BLIP: return Core::Get().GetBlip(id).mObj;
+ case ENT_CHECKPOINT: return Core::Get().GetCheckpoint(id).mObj;
+ case ENT_KEYBIND: return Core::Get().GetKeybind(id).mObj;
+ case ENT_OBJECT: return Core::Get().GetObject(id).mObj;
+ case ENT_PICKUP: return Core::Get().GetPickup(id).mObj;
+ case ENT_PLAYER: return Core::Get().GetPlayer(id).mObj;
+ case ENT_VEHICLE: return Core::Get().GetVehicle(id).mObj;
+ default: return NullObject();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+SQInteger Tasks::FindUnused()
+{
+ for (const auto & t : s_Tasks)
+ {
+ if (INVALID_ENTITY(t.mEntity))
+ {
+ return (&t - s_Tasks); // Return the index of this element
+ }
+ }
+ // No available slot
+ return -1;
+}
+
+// ------------------------------------------------------------------------------------------------
+SQInteger Tasks::Create(Int32 id, Int32 type, HSQUIRRELVM vm)
+{
+ // See if we have where to store this task
+ if (s_Used >= SQMOD_MAX_TASKS)
+ {
+ return sq_throwerror(vm, "Reached the maximum number of tasks");
+ }
+ // Grab the top of the stack
+ const SQInteger top = sq_gettop(vm);
+ // See if too many arguments were specified
+ if (top > 18) /* 4 base + 14 parameters = 18 */
+ {
+ return sq_throwerror(vm, "Too many parameter specified");
+ }
+ // Was there a callback specified?
+ else if (top <= 1)
+ {
+ return sq_throwerror(vm, "Missing task callback");
+ }
+
+ SQRESULT res = SQ_OK;
+ // Prepare some objects for the environment and callback
+ HSQOBJECT env = FindEntity(id, type).GetObject(), func;
+ // Validate the callback type
+ if (sq_gettype(vm, 2) != OT_CLOSURE && sq_gettype(vm, 2) != OT_NATIVECLOSURE)
+ {
+ return sq_throwerror(vm, "Invalid callback type");
+ }
+ // Fetch the specified callback
+ res = sq_getstackobj(vm, 2, &func);
+ // Validate the result
+ if (SQ_FAILED(res))
+ {
+ return res; // Propagate the error
+ }
+
+ // The number of iterations and interval to execute the task
+ SQInteger intrv = 0, itr = 0;
+ // Was there an interval specified?
+ if (top > 2)
+ {
+ // Grab the interval from the stack
+ res = sq_getinteger(vm, 3, &intrv);
+ // Validate the result
+ if (SQ_FAILED(res))
+ {
+ return res; // Propagate the error
+ }
+ }
+ // Was there a number of iterations specified?
+ if (top > 3)
+ {
+ // Grab the iterations from the stack
+ res = sq_getinteger(vm, 4, &itr);
+ // Validate the result
+ if (SQ_FAILED(res))
+ {
+ return res; // Propagate the error
+ }
+ }
+
+ // Obtain the identifier of a free slot
+ const SQInteger slot = FindUnused();
+ // Validate the slot, although it shouldn't be necessary
+ if (slot < 0)
+ {
+ return sq_throwerror(vm, "Unable to acquire a task slot");
+ }
+
+ // At this point we can grab a reference to our slot
+ Task & task = s_Tasks[slot];
+ // Were there any arguments specified?
+ if (top > 4)
+ {
+ // Grab a pointer to the arguments array
+ Argument * args = task.mArgv;
+ // Reset the argument counter
+ task.mArgc = 0;
+ // Grab the specified arguments from the stack
+ for (SQInteger i = 5; i <= top; ++i)
+ {
+ res = sq_getstackobj(vm, i, &(args[task.mArgc].mObj));
+ // Validate the result
+ if (SQ_FAILED(res))
+ {
+ // Clear previous arguments
+ task.Clear();
+ // Propagate the error
+ return res;
+ }
+ // Keep a strong reference to the argument
+ sq_addref(vm, &(args[task.mArgc].mObj));
+ // Increase the argument counter
+ ++task.mArgc;
+ }
+ }
+ // Alright, at this point we can initialize the slot
+ task.Init(env, func, intrv, itr, id, type);
+ // Now initialize the interval
+ s_Intervals[slot] = intrv;
+ // Increase the number of used slots
+ ++s_Used;
+ // Specify that this function doesn't return anything
+ return 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+SQInteger Tasks::Find(Int32 id, Int32 type, SQInteger & pos, HSQUIRRELVM vm)
+{
+ // Grab the top of the stack
+ const SQInteger top = sq_gettop(vm);
+ // Was there a callback specified?
+ if (top <= 1)
+ {
+ return sq_throwerror(vm, "Missing task callback");
+ }
+
+ SQRESULT res = SQ_OK;
+ // Grab the hash of the callback object
+ const SQHash chash = sq_gethash(vm, 2);
+ // Should we include the iterations in the criteria?
+ if (top > 3)
+ {
+ SQInteger intrv = 0;
+ // Grab the interval from the stack
+ res = sq_getinteger(vm, 3, &intrv);
+ // Validate the result
+ if (SQ_FAILED(res))
+ {
+ return res; // Propagate the error
+ }
+ // Attempt to find the requested task
+ for (const auto & t : s_Tasks)
+ {
+ if (t.mHash == chash && t.mEntity == id && t.mType == type && t.mInterval == intrv)
+ {
+ pos = static_cast< SQInteger >(&t - s_Tasks); // Store the index of this element
+ }
+ }
+ }
+ // Should we include the interval in the criteria?
+ else if (top > 2)
+ {
+ SQInteger intrv = 0, sqitr = 0;
+ // Grab the interval from the stack
+ res = sq_getinteger(vm, 3, &intrv);
+ // Validate the result
+ if (SQ_FAILED(res))
+ {
+ return res; // Propagate the error
+ }
+ // Grab the iterations from the stack
+ res = sq_getinteger(vm, 4, &sqitr);
+ // Validate the result
+ if (SQ_FAILED(res))
+ {
+ return res; // Propagate the error
+ }
+ // Cast iterations to the right type
+ const Iterator itr = ConvTo< Iterator >::From(sqitr);
+ // Attempt to find the requested task
+ for (const auto & t : s_Tasks)
+ {
+ if (t.mHash == chash && t.mEntity == id && t.mType == type && t.mInterval == intrv && t.mIterations == itr)
+ {
+ pos = static_cast< SQInteger >(&t - s_Tasks); // Store the index of this element
+ }
+ }
+ }
+ else
+ {
+ // Attempt to find the requested task
+ for (const auto & t : s_Tasks)
+ {
+ if (t.mHash == chash && t.mEntity == id && t.mType == type)
+ {
+ pos = static_cast< SQInteger >(&t - s_Tasks); // Store the index of this element
+ }
+ }
+ }
+ // We could not find such task
+ return res;
+}
+
+// ------------------------------------------------------------------------------------------------
+SQInteger Tasks::Remove(Int32 id, Int32 type, HSQUIRRELVM vm)
+{
+ // Default to not found
+ SQInteger pos = -1;
+ // Perform a search
+ SQRESULT res = Find(id, type, pos, vm);
+ // Did the search failed?
+ if (SQ_FAILED(res))
+ {
+ return res; // Propagate the error
+ }
+ // Did we find anything?
+ else if (pos < 0)
+ {
+ return sq_throwerror(vm, "Unable to locate such task");
+ }
+ else
+ {
+ // Release task resources
+ s_Tasks[pos].Release();
+ s_Tasks[pos].Clear();
+ // Reset the timer
+ s_Intervals[pos] = 0;
+ }
+ // Specify that we don't return anything
+ return 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+SQInteger Tasks::Exists(Int32 id, Int32 type, HSQUIRRELVM vm)
+{
+ // Default to not found
+ SQInteger pos = -1;
+ // Perform a search
+ SQRESULT res = Find(id, type, pos, vm);
+ // Did the search failed?
+ if (SQ_FAILED(res))
+ {
+ return res; // Propagate the error
+ }
+ // Push a boolean on whether this task was found
+ sq_pushbool(vm, pos >= 0);
+ // Specify that we're returning a value
+ return 1;
+}
+
+// ------------------------------------------------------------------------------------------------
+void Tasks::Cleanup(Int32 id, Int32 type)
+{
+ for (auto & t : s_Tasks)
+ {
+ if (t.mEntity == id && t.mType == type)
+ {
+ t.Release();
+ t.Clear();
+ // Also disable the timer
+ s_Intervals[&t - s_Tasks] = 0;
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------------------------------
+ * Forward the call to process tasks.
+*/
+void ProcessTasks()
+{
+ Tasks::Process();
+}
+
+/* ------------------------------------------------------------------------------------------------
+ * Forward the call to initialize tasks.
+*/
+void InitializeTasks()
+{
+ Tasks::Initialize();
+}
+
+/* ------------------------------------------------------------------------------------------------
+ * Forward the call to terminate tasks.
+*/
+void TerminateTasks()
+{
+ Tasks::Deinitialize();
+}
+
+/* ------------------------------------------------------------------------------------------------
+ * Forward the call to cleanup certain tasks.
+*/
+void CleanupTasks(Int32 id, Int32 type)
+{
+ Tasks::Cleanup(id, type);
+}
+
+} // Namespace:: SqMod
diff --git a/source/Tasks.hpp b/source/Tasks.hpp
new file mode 100644
index 00000000..84d0692d
--- /dev/null
+++ b/source/Tasks.hpp
@@ -0,0 +1,397 @@
+#ifndef _TASKS_HPP_
+#define _TASKS_HPP_
+
+// ------------------------------------------------------------------------------------------------
+#include "Base/Shared.hpp"
+
+// ------------------------------------------------------------------------------------------------
+namespace SqMod {
+
+/* ------------------------------------------------------------------------------------------------
+ * Execute callbacks for specific entities after specific intervals of time.
+*/
+class Tasks
+{
+public:
+
+ /* --------------------------------------------------------------------------------------------
+ * Simplify future changes to a single point of change.
+ */
+ typedef Int64 Time;
+ typedef SQInteger Interval;
+ typedef Uint32 Iterator;
+
+private:
+
+ /* --------------------------------------------------------------------------------------------
+ * Helper structure to keep track of a script object with minimal impact on memory.
+ */
+ struct Argument
+ {
+ // ----------------------------------------------------------------------------------------
+ HSQOBJECT mObj; // Reference to the managed script object.
+
+ /* ----------------------------------------------------------------------------------------
+ * Default constructor. (null)
+ */
+ Argument()
+ : mObj()
+ {
+ sq_resetobject(&mObj); // Initialize the object
+ }
+
+ /* ----------------------------------------------------------------------------------------
+ * Forwarding constructor.
+ */
+ Argument(Object & obj)
+ : Argument(obj.GetObject())
+ {
+ /* ... */
+ }
+
+ /* ----------------------------------------------------------------------------------------
+ * Base constructor.
+ */
+ Argument(HSQOBJECT & obj)
+ : mObj(obj)
+ {
+ // Is there a valid object?
+ if (!sq_isnull(mObj))
+ {
+ sq_addref(DefaultVM::Get(), &mObj); // Keep a reference it
+ }
+ }
+
+ /* ----------------------------------------------------------------------------------------
+ * Copy constructor. (disabled)
+ */
+ Argument(const Argument & o) = delete;
+
+ /* ----------------------------------------------------------------------------------------
+ * Move constructor.
+ */
+ Argument(Argument && o)
+ : mObj(o.mObj)
+ {
+ sq_resetobject(&o.mObj); // Take ownership
+ }
+
+ /* ----------------------------------------------------------------------------------------
+ * Destructor.
+ */
+ ~Argument()
+ {
+ Release();
+ }
+
+ /* ----------------------------------------------------------------------------------------
+ * Copy assignment operator. (disabled)
+ */
+ Argument & operator = (const Argument & o) = delete;
+
+ /* ----------------------------------------------------------------------------------------
+ * Move assignment operator.
+ */
+ Argument & operator = (Argument && o)
+ {
+ if (this != &o)
+ {
+ // Release current resources, if any
+ Release();
+ // Replicate data
+ mObj = o.mObj;
+ // Take ownership
+ sq_resetobject(&o.mObj);
+ }
+
+ return *this;
+ }
+
+ /* ----------------------------------------------------------------------------------------
+ * Release managed script resources.
+ */
+ void Release()
+ {
+ // Should we release any object?
+ if (!sq_isnull(mObj))
+ {
+ sq_release(DefaultVM::Get(), &mObj);
+ sq_resetobject(&mObj);
+ }
+ }
+ };
+
+ /* --------------------------------------------------------------------------------------------
+ * Structure that represents a task and keeps track of the task information.
+ */
+ struct Task
+ {
+ // ----------------------------------------------------------------------------------------
+ SQHash mHash; // The hash of the referenced function object.
+ HSQOBJECT mEnv; // A reference to the managed environment object.
+ HSQOBJECT mFunc; // A reference to the managed function object.
+ Iterator mIterations; // Number of iterations before self destruct.
+ Interval mInterval; // Interval between task invocations.
+ Int16 mEntity; // The identifier of the entity to which is belongs.
+ Uint8 mType; // The type of the entity to which is belongs.
+ Uint8 mArgc; // The number of arguments that the task must forward.
+ Argument mArgv[14]; // The arguments that the task must forward.
+
+ /* ----------------------------------------------------------------------------------------
+ * Default constructor.
+ */
+ Task()
+ : mHash(0)
+ , mEnv()
+ , mFunc()
+ , mIterations(0)
+ , mInterval(0)
+ , mEntity(-1)
+ , mType(-1)
+ , mArgc(0)
+ , mArgv()
+ {
+ sq_resetobject(&mEnv);
+ sq_resetobject(&mFunc);
+ }
+
+ /* ----------------------------------------------------------------------------------------
+ * Copy constructor. (disabled)
+ */
+ Task(const Task & o) = delete;
+
+ /* ----------------------------------------------------------------------------------------
+ * Move constructor. (disabled)
+ */
+ Task(Task && o) = delete;
+
+ /* ----------------------------------------------------------------------------------------
+ * Release managed script resources.
+ */
+ ~Task()
+ {
+ Release();
+ Clear();
+ }
+
+ /* ----------------------------------------------------------------------------------------
+ * Copy assignment operator. (disabled)
+ */
+ Task & operator = (const Task & o) = delete;
+
+ /* ----------------------------------------------------------------------------------------
+ * Move assignment operator. (disabled)
+ */
+ Task & operator = (Task && o) = delete;
+
+ /* ----------------------------------------------------------------------------------------
+ * Initializes the task parameters. (assumes previous values are already released)
+ */
+ void Init(HSQOBJECT & env, HSQOBJECT & func, Interval intrv, Iterator itr, Int32 id, Int32 type);
+
+ /* ----------------------------------------------------------------------------------------
+ * Clear the arguments.
+ */
+ void Clear()
+ {
+ // Now release the arguments
+ for (auto & a : mArgv)
+ {
+ a.Release();
+ }
+ // Reset the counter
+ mArgc = 0;
+ }
+
+ /* ----------------------------------------------------------------------------------------
+ * Release managed script resources.
+ */
+ void Release();
+
+ /* ----------------------------------------------------------------------------------------
+ * Execute the managed task.
+ */
+ Interval Execute();
+ };
+
+ // --------------------------------------------------------------------------------------------
+ static Uint32 s_Used; // The number of occupied slots.
+ static Time s_Last; // Last time point.
+ static Time s_Prev; // Previous time point.
+ static Interval s_Intervals[SQMOD_MAX_TASKS]; // List of intervals to be processed.
+ static Task s_Tasks[SQMOD_MAX_TASKS]; // List of tasks to be executed.
+
+ /* --------------------------------------------------------------------------------------------
+ * Default constructor. (disabled)
+ */
+ Tasks() = delete;
+
+ /* --------------------------------------------------------------------------------------------
+ * Copy constructor. (disabled)
+ */
+ Tasks(const Tasks & o) = delete;
+
+ /* --------------------------------------------------------------------------------------------
+ * Move constructor. (disabled)
+ */
+ Tasks(Tasks && o) = delete;
+
+ /* --------------------------------------------------------------------------------------------
+ * Destructor. (disabled)
+ */
+ ~Tasks() = delete;
+
+ /* --------------------------------------------------------------------------------------------
+ * Copy assignment operator. (disabled)
+ */
+ Tasks & operator = (const Tasks & o) = delete;
+
+ /* --------------------------------------------------------------------------------------------
+ * Move assignment operator. (disabled)
+ */
+ Tasks & operator = (Tasks && o) = delete;
+
+public:
+
+ /* --------------------------------------------------------------------------------------------
+ * Process all active tasks and update elapsed time.
+ */
+ static void Process();
+
+ /* --------------------------------------------------------------------------------------------
+ * Initialize all resources and prepare for startup.
+ */
+ static void Initialize();
+
+ /* --------------------------------------------------------------------------------------------
+ * Release all resources and prepare for shutdown.
+ */
+ static void Deinitialize();
+
+protected:
+
+ /* --------------------------------------------------------------------------------------------
+ * Retrieve the instance of the specified entity.
+ */
+ static Object & FindEntity(Int32 id, Int32 type);
+
+ /* --------------------------------------------------------------------------------------------
+ * Find an unoccupied task slot.
+ */
+ static SQInteger FindUnused();
+
+ /* --------------------------------------------------------------------------------------------
+ * Locate the first task with the specified parameters.
+ */
+ static SQInteger Find(Int32 id, Int32 type, SQInteger & pos, HSQUIRRELVM vm);
+
+ /* --------------------------------------------------------------------------------------------
+ * Attempt to create a task with the specified parameters.
+ */
+ static SQInteger Create(Int32 id, Int32 type, HSQUIRRELVM vm);
+
+ /* --------------------------------------------------------------------------------------------
+ * Attempt to remove the task with the specified parameters.
+ */
+ static SQInteger Remove(Int32 id, Int32 type, HSQUIRRELVM vm);
+
+ /* --------------------------------------------------------------------------------------------
+ * See if a task with the specified parameters exists.
+ */
+ static SQInteger Exists(Int32 id, Int32 type, HSQUIRRELVM vm);
+
+public:
+
+ /* --------------------------------------------------------------------------------------------
+ * Cleanup all tasks associated with the specified entity.
+ */
+ static void Cleanup(Int32 id, Int32 type);
+
+ /* --------------------------------------------------------------------------------------------
+ * Forwards calls to create tasks.
+ */
+ template < typename Entity, Int32 Type > static SQInteger MakeTask(HSQUIRRELVM vm)
+ {
+ // The entity instance
+ const Entity * inst = nullptr;
+ // Attempt to extract the instance
+ try
+ {
+ // Fetch the instance from the stack
+ inst = Var< const Entity * >(vm, 1).value;
+ // Do we have a valid instance?
+ if (!inst)
+ {
+ STHROWF("Invalid entity instance");
+ }
+ // Validate the actual entity instance
+ inst->Validate();
+ }
+ catch (const Sqrat::Exception & e)
+ {
+ return sq_throwerror(vm, e.what());
+ }
+ // Forward the call and return the result
+ return Create(inst->GetID(), Type, vm);
+ }
+
+ /* --------------------------------------------------------------------------------------------
+ * Forwards calls to remove tasks.
+ */
+ template < typename Entity, Int32 Type > static SQInteger DropTask(HSQUIRRELVM vm)
+ {
+ // The entity instance
+ const Entity * inst = nullptr;
+ // Attempt to extract the instance
+ try
+ {
+ // Fetch the instance from the stack
+ inst = Var< const Entity * >(vm, 1).value;
+ // Do we have a valid instance?
+ if (!inst)
+ {
+ STHROWF("Invalid entity instance");
+ }
+ // Validate the actual entity instance
+ inst->Validate();
+ }
+ catch (const Sqrat::Exception & e)
+ {
+ return sq_throwerror(vm, e.what());
+ }
+ // Forward the call and return the result
+ return Remove(inst->GetID(), Type, vm);
+ }
+
+ /* --------------------------------------------------------------------------------------------
+ * Forwards calls to check tasks.
+ */
+ template < typename Entity, Int32 Type > static SQInteger DoesTask(HSQUIRRELVM vm)
+ {
+ // The entity instance
+ const Entity * inst = nullptr;
+ // Attempt to extract the instance
+ try
+ {
+ // Fetch the instance from the stack
+ inst = Var< const Entity * >(vm, 1).value;
+ // Do we have a valid instance?
+ if (!inst)
+ {
+ STHROWF("Invalid entity instance");
+ }
+ // Validate the actual entity instance
+ inst->Validate();
+ }
+ catch (const Sqrat::Exception & e)
+ {
+ return sq_throwerror(vm, e.what());
+ }
+ // Forward the call and return the result
+ return Exists(inst->GetID(), Type, vm);
+ }
+};
+
+} // Namespace:: SqMod
+
+#endif // _TASKS_HPP_