1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-08 08:47:17 +01:00

Initial implementation of entity tasks.

This commit is contained in:
Sandu Liviu Catalin 2016-11-17 00:23:59 +02:00
parent cb9581786e
commit 2d07ed67b9
13 changed files with 935 additions and 3 deletions

View File

@ -550,6 +550,8 @@
<Unit filename="../source/Routine.cpp" />
<Unit filename="../source/Routine.hpp" />
<Unit filename="../source/SqBase.hpp" />
<Unit filename="../source/Tasks.cpp" />
<Unit filename="../source/Tasks.hpp" />
<Extensions>
<code_completion />
<envvars />

View File

@ -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))
{

View File

@ -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 >)
);
}

View File

@ -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 >)
);
}

View File

@ -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 >)
);
}

View File

@ -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 >)
);
}

View File

@ -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 >)
);
}

View File

@ -6,6 +6,7 @@
#include "Base/Vector3.hpp"
#include "Library/Utils/Buffer.hpp"
#include "Core.hpp"
#include "Tasks.hpp"
// ------------------------------------------------------------------------------------------------
#include <cstring>
@ -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 >)
);
}

View File

@ -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 >)
);
}

View File

@ -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)
}

View File

@ -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

481
source/Tasks.cpp Normal file
View File

@ -0,0 +1,481 @@
// ------------------------------------------------------------------------------------------------
#include "Tasks.hpp"
#include "Core.hpp"
#include "Library/Chrono.hpp"
// ------------------------------------------------------------------------------------------------
#include <cstdio>
#include <cstdlib>
#include <cstring>
// ------------------------------------------------------------------------------------------------
#include <utility>
// ------------------------------------------------------------------------------------------------
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

397
source/Tasks.hpp Normal file
View File

@ -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_