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_