mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2024-11-08 00:37:15 +01:00
Implement timers for official plug-in compatibility layer.
This commit is contained in:
parent
ed07b17c22
commit
1297635b89
@ -72,54 +72,101 @@ void Routine::Deinitialize()
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQInteger Routine::Create(HSQUIRRELVM vm)
|
||||
struct RoutineBuilder
|
||||
{
|
||||
// Locate the identifier of a free slot
|
||||
const SQInteger slot = FindUnused();
|
||||
// See if we have where to store this routine
|
||||
if (slot < 0)
|
||||
HSQUIRRELVM mVM{nullptr};
|
||||
SQRESULT mRes{SQ_OK};
|
||||
SQInteger mTop{0};
|
||||
SQInteger mSlot{-1};
|
||||
HSQOBJECT mEnv{};
|
||||
HSQOBJECT mFunc{};
|
||||
HSQOBJECT mInst{};
|
||||
SQInteger mInterval{0};
|
||||
SQInteger mIterations{0};
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Base constructor.
|
||||
*/
|
||||
RoutineBuilder(HSQUIRRELVM vm, SQInteger slot)
|
||||
: mVM(vm), mRes(), mTop(sq_gettop(vm)), mSlot(slot), mEnv(), mFunc(), mInst(), mInterval(0), mIterations(0)
|
||||
{
|
||||
return sq_throwerror(vm, "Reached the maximum number of active routines");
|
||||
}
|
||||
// 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 */
|
||||
{
|
||||
return sq_throwerror(vm, "Too many parameters specified");
|
||||
}
|
||||
// Was there was an environment specified?
|
||||
else if (top <= 1)
|
||||
{
|
||||
return sq_throwerror(vm, "Missing routine environment");
|
||||
}
|
||||
// Was there was a callback specified?
|
||||
else if (top <= 2)
|
||||
{
|
||||
return sq_throwerror(vm, "Missing routine callback");
|
||||
}
|
||||
// Validate the callback type
|
||||
else if (sq_gettype(vm, 3) != OT_CLOSURE && sq_gettype(vm, 3) != OT_NATIVECLOSURE)
|
||||
{
|
||||
return sq_throwerror(vm, "Invalid callback type");
|
||||
sq_resetobject(&mEnv);
|
||||
sq_resetobject(&mFunc);
|
||||
sq_resetobject(&mInst);
|
||||
}
|
||||
|
||||
SQRESULT res = SQ_OK;
|
||||
// Prepare an object for the environment
|
||||
HSQOBJECT env;
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copy/Move constructor (disabled).
|
||||
*/
|
||||
RoutineBuilder(const RoutineBuilder &) = delete;
|
||||
RoutineBuilder(RoutineBuilder &&) = delete;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Destructor.
|
||||
*/
|
||||
~RoutineBuilder() = default;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copy/Move assignment operator (disabled).
|
||||
*/
|
||||
RoutineBuilder & operator = (const RoutineBuilder &) = delete;
|
||||
RoutineBuilder & operator = (RoutineBuilder &&) = delete;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Initialize routine creation.
|
||||
*/
|
||||
SQRESULT Begin()
|
||||
{
|
||||
mRes = SQ_OK;
|
||||
// See if we have where to store this routine
|
||||
if (mSlot < 0)
|
||||
{
|
||||
mRes = sq_throwerror(mVM, "Reached the maximum number of active routines");
|
||||
}
|
||||
// See if too many arguments were specified
|
||||
else if (mTop >= 20) /* 5 base + 14 parameters = 19 */
|
||||
{
|
||||
mRes = sq_throwerror(mVM, "Too many parameters specified");
|
||||
}
|
||||
// Was there was an environment specified?
|
||||
else if (mTop <= 1)
|
||||
{
|
||||
mRes = sq_throwerror(mVM, "Missing routine environment");
|
||||
}
|
||||
// Was there was a callback specified?
|
||||
else if (mTop <= 2)
|
||||
{
|
||||
mRes = sq_throwerror(mVM, "Missing routine callback");
|
||||
}
|
||||
// Validate the callback type
|
||||
else if (sq_gettype(mVM, 3) != OT_CLOSURE && sq_gettype(mVM, 3) != OT_NATIVECLOSURE)
|
||||
{
|
||||
mRes = sq_throwerror(mVM, "Invalid callback type");
|
||||
}
|
||||
// Return the result
|
||||
return mRes;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Extract target.
|
||||
*/
|
||||
SQRESULT Target(SQInteger env, SQInteger func)
|
||||
{
|
||||
// Get the type of the environment object
|
||||
const SQObjectType etype = sq_gettype(vm, 2);
|
||||
const SQObjectType type = sq_gettype(mVM, env);
|
||||
// Whether to default to the root table
|
||||
bool use_root = etype == OT_NULL;
|
||||
bool use_root = (type == OT_NULL);
|
||||
|
||||
// Is the specified environment a boolean (true) value?
|
||||
if (etype == OT_STRING)
|
||||
if (type == OT_STRING)
|
||||
{
|
||||
StackStrF val(mVM, env);
|
||||
// Attempt to generate the string value
|
||||
StackStrF val(vm, 2);
|
||||
mRes = val.Proc(false);
|
||||
// Have we failed to retrieve the string?
|
||||
if (SQ_FAILED(val.Proc()))
|
||||
if (SQ_FAILED(mRes))
|
||||
{
|
||||
return val.mRes; // Propagate the error!
|
||||
return 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)
|
||||
@ -129,136 +176,288 @@ SQInteger Routine::Create(HSQUIRRELVM vm)
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// Is the specified environment a null value?
|
||||
if (use_root)
|
||||
{
|
||||
// Push the root table on the stack
|
||||
sq_pushroottable(vm);
|
||||
sq_pushroottable(mVM);
|
||||
// Attempt to retrieve the table object
|
||||
res = sq_getstackobj(vm, -1, &env);
|
||||
mRes = sq_getstackobj(mVM, -1, &mEnv);
|
||||
// Preserve the stack state
|
||||
sq_poptop(vm);
|
||||
sq_poptop(mVM);
|
||||
}
|
||||
// Should we treat it as a valid environment object?
|
||||
else if (etype != OT_STRING)
|
||||
else if (type != OT_STRING)
|
||||
{
|
||||
sq_getstackobj(vm, 2, &env); // Just retrieve the specified environment
|
||||
mRes = sq_getstackobj(mVM, env, &mEnv); // Just retrieve the specified environment
|
||||
}
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
// Can we attempt to retrieve the callback?
|
||||
if (SQ_SUCCEEDED(mRes))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
|
||||
// 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))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
mRes = sq_getstackobj(mVM, func, &mFunc);
|
||||
}
|
||||
// Return result
|
||||
return mRes;
|
||||
}
|
||||
|
||||
// The number of iterations and interval to execute the routine
|
||||
SQInteger intrv = 0, itr = 0;
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Extract options.
|
||||
*/
|
||||
SQRESULT Opts(SQInteger iv, SQInteger it)
|
||||
{
|
||||
// Was there an interval specified?
|
||||
if (top > 3)
|
||||
if (mTop >= iv)
|
||||
{
|
||||
// Grab the interval from the stack
|
||||
res = sq_getinteger(vm, 4, &intrv);
|
||||
mRes = sq_getinteger(mVM, iv, &mInterval);
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
if (SQ_FAILED(mRes))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
}
|
||||
// Was there a number of iterations specified?
|
||||
if (top > 4)
|
||||
{
|
||||
// Grab the iterations from the stack
|
||||
res = sq_getinteger(vm, 5, &itr);
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
return mRes; // Propagate the error
|
||||
}
|
||||
}
|
||||
|
||||
// Was there a number of iterations specified?
|
||||
if (mTop >= it)
|
||||
{
|
||||
// Grab the iterations from the stack
|
||||
mRes = sq_getinteger(mVM, it, &mIterations);
|
||||
// Validate the result
|
||||
if (SQ_FAILED(mRes))
|
||||
{
|
||||
return mRes; // Propagate the error
|
||||
}
|
||||
}
|
||||
|
||||
// Successfully processed
|
||||
return SQ_OK;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Create instance.
|
||||
*/
|
||||
SQRESULT Inst()
|
||||
{
|
||||
// Attempt to create a routine instance
|
||||
try
|
||||
{
|
||||
DeleteGuard< Routine > dg(new Routine());
|
||||
ClassType< Routine >::PushInstance(vm, dg.Get());
|
||||
ClassType< Routine >::PushInstance(mVM, dg.Get());
|
||||
dg.Release();
|
||||
}
|
||||
catch (const std::exception & e)
|
||||
{
|
||||
return sq_throwerrorf(vm, "Unable to create the routine instance: %s", e.what());
|
||||
}
|
||||
// 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))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
return (mRes = sq_throwerrorf(mVM, "Unable to create the routine instance: %s", e.what()));
|
||||
}
|
||||
|
||||
// Fetch the created routine object
|
||||
mRes = sq_getstackobj(mVM, -1, &mInst);
|
||||
// Validate the result
|
||||
if (SQ_FAILED(mRes))
|
||||
{
|
||||
return mRes; // Propagate the error
|
||||
}
|
||||
|
||||
// Successfully created
|
||||
return SQ_OK;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Extract arguments.
|
||||
*/
|
||||
SQRESULT Args(SQInteger idx)
|
||||
{
|
||||
// At this point we can grab a reference to our slot
|
||||
Instance & inst = s_Instances[slot];
|
||||
Routine::Instance & inst = Routine::s_Instances[mSlot];
|
||||
// Were there any arguments specified?
|
||||
if (top > 5)
|
||||
if (mTop >= idx)
|
||||
{
|
||||
// Grab a pointer to the arguments array
|
||||
Argument * args = inst.mArgv;
|
||||
Routine::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)
|
||||
for (SQInteger i = idx; i <= mTop; ++i)
|
||||
{
|
||||
res = sq_getstackobj(vm, i, &(args[inst.mArgc].mObj));
|
||||
mRes = sq_getstackobj(mVM, i, &(args[inst.mArgc].mObj));
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
if (SQ_FAILED(mRes))
|
||||
{
|
||||
// Clear previous arguments
|
||||
inst.Clear();
|
||||
// Propagate the error
|
||||
return res;
|
||||
return mRes;
|
||||
}
|
||||
// Keep a strong reference to the argument
|
||||
sq_addref(vm, &(args[inst.mArgc].mObj));
|
||||
sq_addref(mVM, &(args[inst.mArgc].mObj));
|
||||
// Increase the argument counter
|
||||
++inst.mArgc;
|
||||
}
|
||||
}
|
||||
|
||||
// Successfully processed
|
||||
return SQ_OK;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Finish routine creation.
|
||||
*/
|
||||
#ifdef VCMP_ENABLE_OFFICIAL
|
||||
SQRESULT Finish(bool refs = false)
|
||||
#else
|
||||
SQRESULT Finish()
|
||||
#endif
|
||||
{
|
||||
// Grab a reference to our slot
|
||||
Routine::Instance & inst = Routine::s_Instances[mSlot];
|
||||
// Attempt to retrieve the routine from the stack and associate it with the slot
|
||||
try
|
||||
{
|
||||
Var< Routine * >(vm, -1).value->m_Slot = ConvTo< uint32_t >::From(slot);
|
||||
Var< Routine * >(mVM, -1).value->m_Slot = ConvTo< uint32_t >::From(mSlot);
|
||||
}
|
||||
catch (const std::exception & e)
|
||||
{
|
||||
// Clear extracted arguments
|
||||
inst.Clear();
|
||||
// Now it's safe to throw the error
|
||||
return sq_throwerrorf(vm, "Unable to create the routine instance: %s", e.what());
|
||||
return (mRes = sq_throwerrorf(mVM, "Unable to create the routine instance: %s", e.what()));
|
||||
}
|
||||
|
||||
// Alright, at this point we can initialize the slot
|
||||
inst.Init(env, func, obj, intrv, static_cast< Iterator >(itr));
|
||||
inst.Init(mEnv, mFunc, mInst, mInterval, static_cast< Routine::Iterator >(mIterations));
|
||||
// Now initialize the timer
|
||||
s_Intervals[slot] = intrv;
|
||||
Routine::s_Intervals[mSlot] = mInterval;
|
||||
#ifdef VCMP_ENABLE_OFFICIAL
|
||||
// Drop the temporary callback reference
|
||||
if (refs)
|
||||
{
|
||||
sq_release(mVM, &mFunc);
|
||||
}
|
||||
#endif
|
||||
// Successfully finished
|
||||
return SQ_OK;
|
||||
}
|
||||
#ifdef VCMP_ENABLE_OFFICIAL
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Initialize timer creation.
|
||||
*/
|
||||
SQRESULT BeginComp()
|
||||
{
|
||||
mRes = SQ_OK;
|
||||
// See if we have where to store this timers
|
||||
if (mSlot < 0)
|
||||
{
|
||||
mRes = sq_throwerror(mVM, "Reached the maximum number of active routines");
|
||||
}
|
||||
// See if too many arguments were specified
|
||||
else if (mTop >= 19) /* 4 base + 14 parameters = 18 */
|
||||
{
|
||||
mRes = sq_throwerror(mVM, "Too many parameters specified");
|
||||
}
|
||||
// Was there was a callback specified?
|
||||
else if (mTop <= 1)
|
||||
{
|
||||
mRes = sq_throwerror(mVM, "Missing timer callback");
|
||||
}
|
||||
// Validate the callback type
|
||||
else if (sq_gettype(mVM, 2) != OT_STRING)
|
||||
{
|
||||
mRes = sq_throwerror(mVM, "Invalid callback type");
|
||||
}
|
||||
// Return the result
|
||||
return mRes;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Extract target.
|
||||
*/
|
||||
SQRESULT TargetComp(SQInteger func)
|
||||
{
|
||||
StackStrF val(mVM, func);
|
||||
// Attempt to generate the string value
|
||||
mRes = val.Proc(false);
|
||||
// Have we failed to retrieve the string?
|
||||
if (SQ_FAILED(mRes))
|
||||
{
|
||||
return mRes; // Propagate the error!
|
||||
}
|
||||
// Push the root table on the stack
|
||||
sq_pushroottable(mVM);
|
||||
// Attempt to retrieve the table object
|
||||
mRes = sq_getstackobj(mVM, -1, &mEnv);
|
||||
// Have we failed to retrieve the string?
|
||||
if (SQ_FAILED(mRes))
|
||||
{
|
||||
return mRes; // Propagate the error!
|
||||
}
|
||||
// Push the callback name on the stack
|
||||
sq_pushobject(mVM, val.mObj);
|
||||
// Attempt to retrieve the value from the root table
|
||||
mRes = sq_get(mVM, -2);
|
||||
// Do we have the callback on the stack?
|
||||
if (SQ_SUCCEEDED(mRes))
|
||||
{
|
||||
// Attempt to retrieve the function object
|
||||
mRes = sq_getstackobj(mVM, -1, &mFunc);
|
||||
// Keep a reference to it for now
|
||||
if (SQ_SUCCEEDED(mRes))
|
||||
{
|
||||
sq_addref(mVM, &mFunc);
|
||||
}
|
||||
// Pop the callback and root table
|
||||
sq_pop(mVM, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
sq_poptop(mVM); // Pop the root table
|
||||
}
|
||||
// Return the result
|
||||
return mRes;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQInteger Routine::Create(HSQUIRRELVM vm)
|
||||
{
|
||||
RoutineBuilder b(vm, FindUnused());
|
||||
// Initialize routine
|
||||
if (SQ_FAILED(b.Begin()) ||
|
||||
SQ_FAILED(b.Target(2, 3)) ||
|
||||
SQ_FAILED(b.Opts(4, 5)) ||
|
||||
SQ_FAILED(b.Args(6)) ||
|
||||
SQ_FAILED(b.Inst()) ||
|
||||
SQ_FAILED(b.Finish()))
|
||||
{
|
||||
return b.mRes; // Propagate result
|
||||
}
|
||||
// We have the created routine on the stack, so let's return it
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef VCMP_ENABLE_OFFICIAL
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQInteger Routine::CreateOfficial(HSQUIRRELVM vm)
|
||||
{
|
||||
RoutineBuilder b(vm, FindUnused());
|
||||
// Initialize routine
|
||||
if (SQ_FAILED(b.BeginComp()) ||
|
||||
SQ_FAILED(b.TargetComp(2)) ||
|
||||
SQ_FAILED(b.Opts(3, 4)) ||
|
||||
SQ_FAILED(b.Args(5)) ||
|
||||
SQ_FAILED(b.Inst()) ||
|
||||
SQ_FAILED(b.Finish(true)))
|
||||
{
|
||||
return b.mRes; // Propagate result
|
||||
}
|
||||
// We have the created routine on the stack, so let's return it
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
bool Routine::IsWithTag(StackStrF & tag)
|
||||
{
|
||||
@ -361,6 +560,9 @@ void Register_Routine(HSQUIRRELVM vm)
|
||||
RootTable(vm).FmtFunc(_SC("SqFindRoutineByTag"), &Routine::FindByTag);
|
||||
RootTable(vm).FmtFunc(_SC("SqIsRoutineWithTag"), &Routine::IsWithTag);
|
||||
RootTable(vm).FmtFunc(_SC("SqTerminateRoutineWithTag"), &Routine::TerminateWithTag);
|
||||
#ifdef VCMP_ENABLE_OFFICIAL
|
||||
RootTable(vm).SquirrelFunc(_SC("NewTimer"), &Routine::CreateOfficial);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // Namespace:: SqMod
|
||||
|
@ -6,11 +6,15 @@
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
namespace SqMod {
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
struct RoutineBuilder;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Execute callbacks after specific intervals of time.
|
||||
*/
|
||||
class Routine
|
||||
{
|
||||
friend struct RoutineBuilder;
|
||||
public:
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
@ -329,6 +333,7 @@ public:
|
||||
}
|
||||
// Unable to find such routine
|
||||
STHROWF("Unable to find a routine with tag ({})", tag.mPtr);
|
||||
SQ_UNREACHABLE;
|
||||
// Should not reach this point but if it did, we have to return something
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
@ -366,7 +371,12 @@ public:
|
||||
* Create a routine with the specified parameters.
|
||||
*/
|
||||
static SQInteger Create(HSQUIRRELVM vm);
|
||||
|
||||
#ifdef VCMP_ENABLE_OFFICIAL
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Create a routine with the specified parameters using the official compatibility layer.
|
||||
*/
|
||||
static SQInteger CreateOfficial(HSQUIRRELVM vm);
|
||||
#endif
|
||||
protected:
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
|
Loading…
Reference in New Issue
Block a user