mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2024-11-08 00:37:15 +01:00
Initial implementation of entity tasks.
This commit is contained in:
parent
cb9581786e
commit
2d07ed67b9
@ -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 />
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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 >)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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 >)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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 >)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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 >)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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 >)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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 >)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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 >)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
481
source/Tasks.cpp
Normal 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
397
source/Tasks.hpp
Normal 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_
|
Loading…
Reference in New Issue
Block a user