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

361 lines
12 KiB
C++
Raw Normal View History

2016-02-21 08:25:46 +01:00
// ------------------------------------------------------------------------------------------------
#include "Misc/Routine.hpp"
#include "Library/Chrono.hpp"
2016-02-21 08:25:46 +01:00
// ------------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
#include <cstring>
// ------------------------------------------------------------------------------------------------
2016-02-21 08:25:46 +01:00
// ------------------------------------------------------------------------------------------------
namespace SqMod {
// ------------------------------------------------------------------------------------------------
SQMODE_DECL_TYPENAME(Typename, _SC("SqRoutineInstance"))
2016-02-21 08:25:46 +01:00
// ------------------------------------------------------------------------------------------------
Routine::Time Routine::s_Last = 0;
Routine::Time Routine::s_Prev = 0;
2016-11-17 22:10:31 +01:00
Routine::Interval Routine::s_Intervals[SQMOD_MAX_ROUTINES];
Routine::Instance Routine::s_Instances[SQMOD_MAX_ROUTINES];
// ------------------------------------------------------------------------------------------------
void Routine::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
2020-03-22 05:53:04 +01:00
const auto delta = Int32((s_Last - s_Prev) / 1000L);
// Process all active routines
for (Interval * itr = s_Intervals; itr != (s_Intervals + SQMOD_MAX_ROUTINES); ++itr)
{
// Is this routine valid?
2016-11-17 22:10:31 +01:00
if (*itr)
{
2016-11-17 22:10:31 +01:00
// Decrease the elapsed time
(*itr) -= delta;
// Have we completed the routine interval?
if ((*itr) <= 0)
{
// Execute and reset the elapsed time
(*itr) = s_Instances[itr - s_Intervals].Execute();
}
}
}
}
// ------------------------------------------------------------------------------------------------
void Routine::Initialize()
{
2016-11-17 22:10:31 +01:00
std::memset(s_Intervals, 0, sizeof(s_Intervals));
}
// ------------------------------------------------------------------------------------------------
void Routine::Deinitialize()
{
2016-11-17 22:10:31 +01:00
// Release any script resources that the routines might store
for (auto & r : s_Instances)
{
2016-11-17 22:10:31 +01:00
r.Terminate();
}
}
// ------------------------------------------------------------------------------------------------
2016-11-17 22:10:31 +01:00
SQInteger Routine::Create(HSQUIRRELVM vm)
{
2016-11-17 22:10:31 +01:00
// Locate the identifier of a free slot
const SQInteger slot = FindUnused();
// See if we have where to store this routine
if (slot < 0)
{
2016-11-17 22:10:31 +01:00
return sq_throwerror(vm, "Reached the maximum number of active routines");
}
2016-11-17 22:10:31 +01:00
// Grab the top of the stack
const SQInteger top = sq_gettop(vm);
// See if too many arguments were specified
if (top >= 20) /* 5 base + 14 parameters = 19 */
{
2016-11-17 22:10:31 +01:00
return sq_throwerror(vm, "Too many parameters specified");
}
2016-11-17 22:10:31 +01:00
// Was there was an environment specified?
else if (top <= 1)
{
2016-11-17 22:10:31 +01:00
return sq_throwerror(vm, "Missing routine environment");
}
2016-11-17 22:10:31 +01:00
// Was there was a callback specified?
else if (top <= 2)
{
2016-11-17 22:10:31 +01:00
return sq_throwerror(vm, "Missing routine callback");
}
2016-11-17 22:10:31 +01:00
// Validate the callback type
else if (sq_gettype(vm, 3) != OT_CLOSURE && sq_gettype(vm, 3) != OT_NATIVECLOSURE)
{
2016-11-17 22:10:31 +01:00
return sq_throwerror(vm, "Invalid callback type");
}
2016-11-17 22:10:31 +01:00
SQRESULT res = SQ_OK;
// Prepare an object for the environment
HSQOBJECT env;
// Get the type of the environment object
const SQObjectType etype = sq_gettype(vm, 2);
// Whether to default to the root table
bool use_root = etype == OT_NULL;
// Is the specified environment a boolean (true) value?
if (etype == OT_STRING)
{
// Attempt to generate the string value
StackStrF val(vm, 2);
// Have we failed to retrieve the string?
if (SQ_FAILED(val.Proc()))
{
return val.mRes; // Propagate the error!
}
// If the string is empty or "root" then we use the root table
else if (!val.mLen || sqmod_stricmp(val.mPtr, "root") == 0)
{
use_root = true;
}
// If the string is "self" then we leave it null and default to self
else if (sqmod_stricmp(val.mPtr, "self") == 0)
{
sq_resetobject(&env); // Make sure environment is null
use_root = false; // Just in case
}
}
2016-11-17 22:10:31 +01:00
// Is the specified environment a null value?
if (use_root)
{
2016-11-17 22:10:31 +01:00
// Preserve the stack state
const StackGuard sg(vm);
// Push the root table on the stack
sq_pushroottable(vm);
// Attempt to retrieve the table object
res = sq_getstackobj(vm, -1, &env);
}
// Should we treat it as a valid environment object?
else if (etype != OT_STRING)
{
2016-11-17 22:10:31 +01:00
sq_getstackobj(vm, 2, &env); // Just retrieve the specified environment
}
2016-11-17 22:10:31 +01:00
// Validate the result
if (SQ_FAILED(res))
{
2016-11-17 22:10:31 +01:00
return res; // Propagate the error
}
2016-02-21 08:25:46 +01:00
2016-11-17 22:10:31 +01:00
// Prepare an object for the function
HSQOBJECT func;
// Fetch the specified callback object
res = sq_getstackobj(vm, 3, &func);
// Validate the result
if (SQ_FAILED(res))
{
2016-11-17 22:10:31 +01:00
return res; // Propagate the error
}
2016-02-21 08:25:46 +01:00
2016-11-17 22:10:31 +01:00
// The number of iterations and interval to execute the routine
SQInteger intrv = 0, itr = 0;
// Was there an interval specified?
if (top > 3)
{
2016-11-17 22:10:31 +01:00
// Grab the interval from the stack
res = sq_getinteger(vm, 4, &intrv);
// Validate the result
if (SQ_FAILED(res))
{
2016-11-17 22:10:31 +01:00
return res; // Propagate the error
}
}
2016-11-17 22:10:31 +01:00
// Was there a number of iterations specified?
if (top > 4)
{
2016-11-17 22:10:31 +01:00
// Grab the iterations from the stack
res = sq_getinteger(vm, 5, &itr);
// Validate the result
if (SQ_FAILED(res))
{
2016-11-17 22:10:31 +01:00
return res; // Propagate the error
}
}
2016-02-21 08:25:46 +01:00
2016-11-17 22:10:31 +01:00
// Attempt to create a routine instance
try
{
2016-11-17 22:10:31 +01:00
ClassType< Routine >::PushInstance(vm, new Routine());
}
2016-11-17 22:10:31 +01:00
catch (const Sqrat::Exception & e)
{
2016-11-17 22:10:31 +01:00
return sq_throwerror(vm, "Unable to create the routine instance");
}
2016-11-17 22:10:31 +01:00
// Prepare an object for the routine
HSQOBJECT obj;
// Fetch the created routine object
res = sq_getstackobj(vm, -1, &obj);
// Validate the result
if (SQ_FAILED(res))
{
2016-11-17 22:10:31 +01:00
return res; // Propagate the error
}
2016-02-21 08:25:46 +01:00
2016-11-17 22:10:31 +01:00
// At this point we can grab a reference to our slot
Instance & inst = s_Instances[slot];
// Were there any arguments specified?
if (top > 5)
{
2016-11-17 22:10:31 +01:00
// Grab a pointer to the arguments array
Argument * args = inst.mArgv;
// Reset the argument counter
inst.mArgc = 0;
// Grab the specified arguments from the stack
for (SQInteger i = 6; i <= top; ++i)
{
2016-11-17 22:10:31 +01:00
res = sq_getstackobj(vm, i, &(args[inst.mArgc].mObj));
// Validate the result
if (SQ_FAILED(res))
{
// Clear previous arguments
inst.Clear();
// Propagate the error
return res;
}
// Keep a strong reference to the argument
sq_addref(vm, &(args[inst.mArgc].mObj));
// Increase the argument counter
++inst.mArgc;
}
}
2016-02-21 08:25:46 +01:00
2016-11-17 22:10:31 +01:00
// Attempt to retrieve the routine from the stack and associate it with the slot
try
{
2016-11-17 22:10:31 +01:00
Var< Routine * >(vm, -1).value->m_Slot = ConvTo< Uint32 >::From(slot);
}
2016-11-17 22:10:31 +01:00
catch (const Sqrat::Exception & e)
{
2016-11-17 22:10:31 +01:00
// Clear extracted arguments
inst.Clear();
// Now it's safe to throw the error
return sq_throwerror(vm, "Unable to create the routine instance");
}
2016-02-21 08:25:46 +01:00
2016-11-17 22:10:31 +01:00
// Alright, at this point we can initialize the slot
2020-03-22 05:53:04 +01:00
inst.Init(env, func, obj, intrv, static_cast< Iterator >(itr));
2016-11-17 22:10:31 +01:00
// Now initialize the timer
s_Intervals[slot] = intrv;
// We have the created routine on the stack, so let's return it
return 1;
}
// ------------------------------------------------------------------------------------------------
bool Routine::IsWithTag(StackStrF & tag)
{
// Is the specified tag valid?
if (tag.mPtr != nullptr)
{
// Iterate routine list
for (const auto & r : s_Instances)
{
2020-03-22 05:53:04 +01:00
if (!r.mInst.IsNull() && r.mTag == tag.mPtr)
{
return true; // Yup, we're doing this
}
}
}
// Unable to find such routine
return false;
}
// ------------------------------------------------------------------------------------------------
bool Routine::TerminateWithTag(StackStrF & tag)
{
// Is the specified tag valid?
if (tag.mPtr != nullptr)
{
// Iterate routine list
for (auto & r : s_Instances)
{
2020-03-22 05:53:04 +01:00
if (!r.mInst.IsNull() && r.mTag == tag.mPtr)
{
r.Terminate(); // Yup, we're doing this
return true; // A routine was terminated
}
}
}
// Unable to find such routine
return false;
}
/* ------------------------------------------------------------------------------------------------
* Forward the call to process routines.
*/
void ProcessRoutines()
{
Routine::Process();
}
/* ------------------------------------------------------------------------------------------------
* Forward the call to initialize routines.
*/
void InitializeRoutines()
{
Routine::Initialize();
}
/* ------------------------------------------------------------------------------------------------
* Forward the call to terminate routines.
*/
void TerminateRoutines()
{
Routine::Deinitialize();
}
2016-02-21 08:25:46 +01:00
// ================================================================================================
void Register_Routine(HSQUIRRELVM vm)
{
RootTable(vm).Bind(Typename::Str,
Class< Routine, NoConstructor< Routine > >(vm, Typename::Str)
// Meta-methods
.SquirrelFunc(_SC("_typename"), &Typename::Fn)
.Func(_SC("_tostring"), &Routine::ToString)
// Properties
.Prop(_SC("Tag"), &Routine::GetTag, &Routine::SetTag)
2016-11-17 22:10:31 +01:00
.Prop(_SC("Env"), &Routine::GetEnv, &Routine::SetEnv)
.Prop(_SC("Func"), &Routine::GetFunc, &Routine::SetFunc)
.Prop(_SC("Data"), &Routine::GetData, &Routine::SetData)
2016-02-21 08:25:46 +01:00
.Prop(_SC("Interval"), &Routine::GetInterval, &Routine::SetInterval)
.Prop(_SC("Iterations"), &Routine::GetIterations, &Routine::SetIterations)
.Prop(_SC("Suspended"), &Routine::GetSuspended, &Routine::SetSuspended)
.Prop(_SC("Quiet"), &Routine::GetQuiet, &Routine::SetQuiet)
.Prop(_SC("Endure"), &Routine::GetEndure, &Routine::SetEndure)
2016-11-17 22:10:31 +01:00
.Prop(_SC("Arguments"), &Routine::GetArguments)
// Member Methods
.FmtFunc(_SC("SetTag"), &Routine::ApplyTag)
.Func(_SC("SetData"), &Routine::ApplyData)
.Func(_SC("SetInterval"), &Routine::ApplyInterval)
.Func(_SC("SetIterations"), &Routine::ApplyIterations)
.Func(_SC("SetSuspended"), &Routine::ApplySuspended)
.Func(_SC("SetQuiet"), &Routine::AppplyQuiet)
.Func(_SC("SetEndure"), &Routine::ApplyEndure)
2016-02-21 08:25:46 +01:00
.Func(_SC("Terminate"), &Routine::Terminate)
2016-11-17 22:10:31 +01:00
.Func(_SC("GetArgument"), &Routine::GetArgument)
.Func(_SC("DropEnv"), &Routine::DropEnv)
.StaticFunc(_SC("ActiveCount"), &Routine::GetUsed)
2016-02-21 08:25:46 +01:00
);
2016-11-17 22:10:31 +01:00
// Global functions
RootTable(vm).SquirrelFunc(_SC("SqRoutine"), &Routine::Create);
RootTable(vm).FmtFunc(_SC("SqFindRoutineByTag"), &Routine::FindByTag);
RootTable(vm).FmtFunc(_SC("SqIsRoutineWithTag"), &Routine::IsWithTag);
RootTable(vm).FmtFunc(_SC("SqTerminateRoutineWithTag"), &Routine::TerminateWithTag);
2016-02-21 08:25:46 +01:00
}
} // Namespace:: SqMod