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

Catch exceptions during server callbacks.

Allow routines to safely terminate routines during execution.
Various other fixes and improvements.
This commit is contained in:
Sandu Liviu Catalin 2016-03-12 08:47:50 +02:00
parent d58099461a
commit d2f3da1747
4 changed files with 1021 additions and 264 deletions

View File

@ -51,6 +51,15 @@ template <> struct NumLimit< long double > { static const long double Min, Max;
*/
struct StackGuard
{
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
StackGuard()
: m_Top(sq_gettop(DefaultVM::Get())), m_VM(DefaultVM::Get())
{
/* ... */
}
/* --------------------------------------------------------------------------------------------
* Base constructor.
*/

View File

@ -109,351 +109,617 @@ SQMOD_API_EXPORT unsigned int VcmpPluginInit(PluginFuncs * funcs, PluginCallback
return SQMOD_SUCCESS;
}
#define SQMOD_CATCH_EVENT_EXCEPTION(ev) /*
*/ catch (const Sqrat::Exception & e) /*
*/ { /*
*/ LogErr("Squirrel exception caught during (" #ev ") event"); /*
*/ LogInf("Message: %s", e.Message().c_str()); /*
*/ } /*
*/ catch (const std::exception & e) /*
*/ { /*
*/ LogErr("Program exception caught during (" #ev ") event"); /*
*/ LogInf("Message: %s", e.what()); /*
*/ } /*
*/ catch (...) /*
*/ { /*
*/ LogErr("Unknown exception caught during (" #ev ") event"); /*
*/ } /*
*/
// --------------------------------------------------------------------------------------------
static int VC_InitServer(void)
{
// Don't even try to initialize if there's no core instance
if (!_Core)
{
return SQMOD_FAILURE;
}
// Mark the initialization as successful by default
_Core->SetState(1);
// Obtain the API version as a string
String apiver(ToStrF("%d", SQMOD_API_VER));
// Signal outside plugins to do fetch our proxies
_Func->SendCustomCommand(0xDABBAD00, apiver.c_str());
// Attempt to load the module core
if (_Core->Load())
_Core->EmitServerStartup();
else
LogFtl("Unable to load the plugin resources properly");
// Attempt to forward the event
try
{
// Obtain the API version as a string
String apiver(ToStrF("%d", SQMOD_API_VER));
// Signal outside plug-ins to do fetch our proxies
_Func->SendCustomCommand(0xDABBAD00, apiver.c_str());
// Attempt to load the module core
if (_Core->Load())
{
_Core->EmitServerStartup();
}
else
{
LogFtl("Unable to load the plugin resources properly");
}
}
SQMOD_CATCH_EVENT_EXCEPTION(InitServer)
// Return the last known plug-in state
return _Core->GetState();
}
static void VC_ShutdownServer(void)
{
// Don't even try to de-initialize if there's no core instance
if (!_Core)
{
return;
_Core->EmitServerShutdown();
// Deallocate and release everything obtained at startup
_Core->Terminate();
// The server still triggers callbacks and we deallocated everything!
UnbindCallbacks();
// Destroy components
DestroyComponents();
}
// Attempt to forward the event
try
{
_Core->EmitServerShutdown();
// Deallocate and release everything obtained at startup
_Core->Terminate();
// The server still triggers callbacks and we deallocated everything!
UnbindCallbacks();
// Destroy components
DestroyComponents();
}
SQMOD_CATCH_EVENT_EXCEPTION(ShutdownServer)
}
static void VC_Frame(float delta)
{
_Core->EmitServerFrame(delta);
// Attempt to forward the event
try
{
_Core->EmitServerFrame(delta);
}
SQMOD_CATCH_EVENT_EXCEPTION(Frame)
}
static void VC_PlayerConnect(int player)
{
_Core->ConnectPlayer(player, SQMOD_CREATE_AUTOMATIC, NullObject());
// Attempt to forward the event
try
{
_Core->ConnectPlayer(player, SQMOD_CREATE_AUTOMATIC, NullObject());
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerConnect)
}
static void VC_PlayerDisconnect(int player, int reason)
{
_Core->DisconnectPlayer(player, reason, NullObject());
// Attempt to forward the event
try
{
_Core->DisconnectPlayer(player, reason, NullObject());
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerDisconnect)
}
static void VC_PlayerBeginTyping(int player)
{
_Core->EmitPlayerStartTyping(player);
// Attempt to forward the event
try
{
_Core->EmitPlayerStartTyping(player);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerBeginTyping)
}
static void VC_PlayerEndTyping(int player)
{
_Core->EmitPlayerStopTyping(player);
// Attempt to forward the event
try
{
_Core->EmitPlayerStopTyping(player);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerEndTyping)
}
static int VC_PlayerRequestClass(int player, int offset)
{
// Mark the initialization as successful by default
_Core->SetState(SQMOD_SUCCESS);
_Core->EmitPlayerRequestClass(player, offset);
// Attempt to forward the event
try
{
_Core->EmitPlayerRequestClass(player, offset);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerEndTyping)
// Return the last known plug-in state
return _Core->GetState();
}
static int VC_PlayerRequestSpawn(int player)
{
// Mark the initialization as successful by default
_Core->SetState(SQMOD_SUCCESS);
_Core->EmitPlayerRequestSpawn(player);
// Attempt to forward the event
try
{
_Core->EmitPlayerRequestSpawn(player);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerRequestSpawn)
// Return the last known plug-in state
return _Core->GetState();
}
static void VC_PlayerSpawn(int player)
{
_Core->EmitPlayerSpawn(player);
// Attempt to forward the event
try
{
_Core->EmitPlayerSpawn(player);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerSpawn)
}
static void VC_PlayerDeath(int player, int killer, int reason, int body_part)
{
if (_Func->IsPlayerConnected(killer))
_Core->EmitPlayerKilled(player, killer, reason, body_part);
else
_Core->EmitPlayerWasted(player, reason);
// Attempt to forward the event
try
{
if (_Func->IsPlayerConnected(killer))
{
_Core->EmitPlayerKilled(player, killer, reason, body_part);
}
else
{
_Core->EmitPlayerWasted(player, reason);
}
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerDeath)
}
static void VC_PlayerUpdate(int player, int type)
{
_Core->EmitPlayerUpdate(player, type);
// Attempt to forward the event
try
{
_Core->EmitPlayerUpdate(player, type);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerUpdate)
}
static int VC_PlayerRequestEnter(int player, int vehicle, int slot)
{
// Mark the initialization as successful by default
_Core->SetState(SQMOD_SUCCESS);
_Core->EmitPlayerEmbarking(player, vehicle, slot);
// Attempt to forward the event
try
{
_Core->EmitPlayerEmbarking(player, vehicle, slot);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerRequestEnter)
// Return the last known plug-in state
return _Core->GetState();
}
static void VC_PlayerEnterVehicle(int player, int vehicle, int slot)
{
_Core->EmitPlayerEmbarked(player, vehicle, slot);
// Attempt to forward the event
try
{
_Core->EmitPlayerEmbarked(player, vehicle, slot);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerEnterVehicle)
}
static void VC_PlayerExitVehicle(int player, int vehicle)
{
_Core->EmitPlayerDisembark(player, vehicle);
// Attempt to forward the event
try
{
_Core->EmitPlayerDisembark(player, vehicle);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerExitVehicle)
}
static int VC_PickupClaimPicked(int pickup, int player)
{
// Mark the initialization as successful by default
_Core->SetState(SQMOD_SUCCESS);
_Core->EmitPickupClaimed(player, pickup);
// Attempt to forward the event
try
{
_Core->EmitPickupClaimed(player, pickup);
}
SQMOD_CATCH_EVENT_EXCEPTION(PickupClaimPicked)
// Return the last known plug-in state
return _Core->GetState();
}
static void VC_PickupPickedUp(int pickup, int player)
{
_Core->EmitPickupCollected(player, pickup);
// Attempt to forward the event
try
{
_Core->EmitPickupCollected(player, pickup);
}
SQMOD_CATCH_EVENT_EXCEPTION(PickupPickedUp)
}
static void VC_PickupRespawn(int pickup)
{
_Core->EmitPickupRespawn(pickup);
// Attempt to forward the event
try
{
_Core->EmitPickupRespawn(pickup);
}
SQMOD_CATCH_EVENT_EXCEPTION(PickupRespawn)
}
static void VC_VehicleUpdate(int vehicle, int type)
{
_Core->EmitVehicleUpdate(vehicle, type);
// Attempt to forward the event
try
{
_Core->EmitVehicleUpdate(vehicle, type);
}
SQMOD_CATCH_EVENT_EXCEPTION(VehicleUpdate)
}
static void VC_VehicleExplode(int vehicle)
{
_Core->EmitVehicleExplode(vehicle);
// Attempt to forward the event
try
{
_Core->EmitVehicleExplode(vehicle);
}
SQMOD_CATCH_EVENT_EXCEPTION(VehicleExplode)
}
static void VC_VehicleRespawn(int vehicle)
{
_Core->EmitVehicleRespawn(vehicle);
// Attempt to forward the event
try
{
_Core->EmitVehicleRespawn(vehicle);
}
SQMOD_CATCH_EVENT_EXCEPTION(VehicleRespawn)
}
static void VC_ObjectShot(int object, int player, int weapon)
{
_Core->EmitObjectShot(player, object, weapon);
// Attempt to forward the event
try
{
_Core->EmitObjectShot(player, object, weapon);
}
SQMOD_CATCH_EVENT_EXCEPTION(ObjectShot)
}
static void VC_ObjectBump(int object, int player)
{
_Core->EmitObjectBump(player, object);
// Attempt to forward the event
try
{
_Core->EmitObjectBump(player, object);
}
SQMOD_CATCH_EVENT_EXCEPTION(ObjectBump)
}
static int VC_PublicMessage(int player, const char * text)
{
// Mark the initialization as successful by default
_Core->SetState(SQMOD_SUCCESS);
_Core->EmitPlayerChat(player, text);
// Attempt to forward the event
try
{
_Core->EmitPlayerChat(player, text);
}
SQMOD_CATCH_EVENT_EXCEPTION(PublicMessage)
// Return the last known plug-in state
return _Core->GetState();
}
static int VC_CommandMessage(int player, const char * text)
{
// Mark the initialization as successful by default
_Core->SetState(SQMOD_SUCCESS);
_Core->EmitPlayerCommand(player, text);
// Attempt to forward the event
try
{
_Core->EmitPlayerCommand(player, text);
}
SQMOD_CATCH_EVENT_EXCEPTION(CommandMessage)
// Return the last known plug-in state
return _Core->GetState();
}
static int VC_PrivateMessage(int player, int target, const char * text)
{
// Mark the initialization as successful by default
_Core->SetState(SQMOD_SUCCESS);
_Core->EmitPlayerMessage(player, target, text);
// Attempt to forward the event
try
{
_Core->EmitPlayerMessage(player, target, text);
}
SQMOD_CATCH_EVENT_EXCEPTION(PrivateMessage)
// Return the last known plug-in state
return _Core->GetState();
}
static int VC_InternalCommand(unsigned int type, const char * text)
{
// Mark the initialization as successful by default
_Core->SetState(SQMOD_SUCCESS);
_Core->EmitInternalCommand(type, text);
// Attempt to forward the event
try
{
_Core->EmitInternalCommand(type, text);
}
SQMOD_CATCH_EVENT_EXCEPTION(InternalCommand)
// Return the last known plug-in state
return _Core->GetState();
}
static int VC_LoginAttempt(char * name, const char * passwd, const char * address)
{
// Mark the initialization as successful by default
_Core->SetState(SQMOD_SUCCESS);
_Core->EmitLoginAttempt(name, passwd, address);
// Attempt to forward the event
try
{
_Core->EmitLoginAttempt(name, passwd, address);
}
SQMOD_CATCH_EVENT_EXCEPTION(LoginAttempt)
// Return the last known plug-in state
return _Core->GetState();
}
static void VC_EntityPool(int type, int id, unsigned int deleted)
{
_Core->EmitEntityPool(type, id, static_cast<bool>(deleted));
// Attempt to forward the event
try
{
_Core->EmitEntityPool(type, id, static_cast< bool >(deleted));
}
SQMOD_CATCH_EVENT_EXCEPTION(EntityPool)
}
static void VC_KeyBindDown(int player, int bind)
{
_Core->EmitPlayerKeyPress(player, bind);
// Attempt to forward the event
try
{
_Core->EmitPlayerKeyPress(player, bind);
}
SQMOD_CATCH_EVENT_EXCEPTION(KeyBindDown)
}
static void VC_KeyBindUp(int player, int bind)
{
_Core->EmitPlayerKeyRelease(player, bind);
// Attempt to forward the event
try
{
_Core->EmitPlayerKeyRelease(player, bind);
}
SQMOD_CATCH_EVENT_EXCEPTION(KeyBindUp)
}
static void VC_PlayerAway(int player, unsigned int status)
{
_Core->EmitPlayerAway(player, static_cast<bool>(status));
// Attempt to forward the event
try
{
_Core->EmitPlayerAway(player, static_cast< bool >(status));
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerAway)
}
static void VC_PlayerSpectate(int player, int target)
{
_Core->EmitPlayerSpectate(player, target);
// Attempt to forward the event
try
{
_Core->EmitPlayerSpectate(player, target);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerSpectate)
}
static void VC_PlayerCrashReport(int player, const char * report)
{
_Core->EmitPlayerCrashreport(player, report);
// Attempt to forward the event
try
{
_Core->EmitPlayerCrashreport(player, report);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerCrashReport)
}
static void VC_ServerPerformanceReport(int count, const char ** description, unsigned long long * millis)
static void VC_ServerPerformanceReport(int /*count*/, const char ** /*description*/, unsigned long long * /*millis*/)
{
// Ignored for now...
SQMOD_UNUSED_VAR(count);
SQMOD_UNUSED_VAR(description);
SQMOD_UNUSED_VAR(millis);
}
static void VC_PlayerName(int player, const char * previous, const char * current)
{
_Core->EmitPlayerRename(player, previous, current);
// Attempt to forward the event
try
{
_Core->EmitPlayerRename(player, previous, current);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerName)
}
static void VC_PlayerState(int player, int previous, int current)
{
_Core->EmitPlayerState(player, previous, current);
switch (current)
// Attempt to forward the event
try
{
case SQMOD_PLAYER_STATE_NONE:
_Core->EmitStateNone(player, previous);
break;
case SQMOD_PLAYER_STATE_NORMAL:
_Core->EmitStateNormal(player, previous);
break;
case SQMOD_PLAYER_STATE_SHOOTING:
_Core->EmitStateShooting(player, previous);
break;
case SQMOD_PLAYER_STATE_DRIVER:
_Core->EmitStateDriver(player, previous);
break;
case SQMOD_PLAYER_STATE_PASSENGER:
_Core->EmitStatePassenger(player, previous);
break;
case SQMOD_PLAYER_STATE_ENTERING_AS_DRIVER:
_Core->EmitStateEnterDriver(player, previous);
break;
case SQMOD_PLAYER_STATE_ENTERING_AS_PASSENGER:
_Core->EmitStateEnterPassenger(player, previous);
break;
case SQMOD_PLAYER_STATE_EXITING_VEHICLE:
_Core->EmitStateExitVehicle(player, previous);
break;
case SQMOD_PLAYER_STATE_UNSPAWNED:
_Core->EmitStateUnspawned(player, previous);
break;
_Core->EmitPlayerState(player, previous, current);
// Identify the current state and trigger the listeners specific to that
switch (current)
{
case SQMOD_PLAYER_STATE_NONE:
_Core->EmitStateNone(player, previous);
break;
case SQMOD_PLAYER_STATE_NORMAL:
_Core->EmitStateNormal(player, previous);
break;
case SQMOD_PLAYER_STATE_SHOOTING:
_Core->EmitStateShooting(player, previous);
break;
case SQMOD_PLAYER_STATE_DRIVER:
_Core->EmitStateDriver(player, previous);
break;
case SQMOD_PLAYER_STATE_PASSENGER:
_Core->EmitStatePassenger(player, previous);
break;
case SQMOD_PLAYER_STATE_ENTERING_AS_DRIVER:
_Core->EmitStateEnterDriver(player, previous);
break;
case SQMOD_PLAYER_STATE_ENTERING_AS_PASSENGER:
_Core->EmitStateEnterPassenger(player, previous);
break;
case SQMOD_PLAYER_STATE_EXITING_VEHICLE:
_Core->EmitStateExitVehicle(player, previous);
break;
case SQMOD_PLAYER_STATE_UNSPAWNED:
_Core->EmitStateUnspawned(player, previous);
break;
}
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerState)
}
static void VC_PlayerAction(int player, int previous, int current)
{
_Core->EmitPlayerAction(player, previous, current);
switch (current)
// Attempt to forward the event
try
{
case SQMOD_PLAYER_ACTION_NONE:
_Core->EmitActionNone(player, previous);
break;
case SQMOD_PLAYER_ACTION_NORMAL:
_Core->EmitActionNormal(player, previous);
break;
case SQMOD_PLAYER_ACTION_AIMING:
_Core->EmitActionAiming(player, previous);
break;
case SQMOD_PLAYER_ACTION_SHOOTING:
_Core->EmitActionShooting(player, previous);
break;
case SQMOD_PLAYER_ACTION_JUMPING:
_Core->EmitActionJumping(player, previous);
break;
case SQMOD_PLAYER_ACTION_LYING_ON_GROUND:
_Core->EmitActionLieDown(player, previous);
break;
case SQMOD_PLAYER_ACTION_GETTING_UP:
_Core->EmitActionGettingUp(player, previous);
break;
case SQMOD_PLAYER_ACTION_JUMPING_FROM_VEHICLE:
_Core->EmitActionJumpVehicle(player, previous);
break;
case SQMOD_PLAYER_ACTION_DRIVING:
_Core->EmitActionDriving(player, previous);
break;
case SQMOD_PLAYER_ACTION_DYING:
_Core->EmitActionDying(player, previous);
break;
case SQMOD_PLAYER_ACTION_WASTED:
_Core->EmitActionWasted(player, previous);
break;
case SQMOD_PLAYER_ACTION_ENTERING_VEHICLE:
_Core->EmitActionEmbarking(player, previous);
break;
case SQMOD_PLAYER_ACTION_EXITING_VEHICLE:
_Core->EmitActionDisembarking(player, previous);
break;
_Core->EmitPlayerAction(player, previous, current);
// Identify the current action and trigger the listeners specific to that
switch (current)
{
case SQMOD_PLAYER_ACTION_NONE:
_Core->EmitActionNone(player, previous);
break;
case SQMOD_PLAYER_ACTION_NORMAL:
_Core->EmitActionNormal(player, previous);
break;
case SQMOD_PLAYER_ACTION_AIMING:
_Core->EmitActionAiming(player, previous);
break;
case SQMOD_PLAYER_ACTION_SHOOTING:
_Core->EmitActionShooting(player, previous);
break;
case SQMOD_PLAYER_ACTION_JUMPING:
_Core->EmitActionJumping(player, previous);
break;
case SQMOD_PLAYER_ACTION_LYING_ON_GROUND:
_Core->EmitActionLieDown(player, previous);
break;
case SQMOD_PLAYER_ACTION_GETTING_UP:
_Core->EmitActionGettingUp(player, previous);
break;
case SQMOD_PLAYER_ACTION_JUMPING_FROM_VEHICLE:
_Core->EmitActionJumpVehicle(player, previous);
break;
case SQMOD_PLAYER_ACTION_DRIVING:
_Core->EmitActionDriving(player, previous);
break;
case SQMOD_PLAYER_ACTION_DYING:
_Core->EmitActionDying(player, previous);
break;
case SQMOD_PLAYER_ACTION_WASTED:
_Core->EmitActionWasted(player, previous);
break;
case SQMOD_PLAYER_ACTION_ENTERING_VEHICLE:
_Core->EmitActionEmbarking(player, previous);
break;
case SQMOD_PLAYER_ACTION_EXITING_VEHICLE:
_Core->EmitActionDisembarking(player, previous);
break;
}
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerAction)
}
static void VC_PlayerOnFire(int player, unsigned int state)
{
_Core->EmitPlayerBurning(player, static_cast<bool>(state));
// Attempt to forward the event
try
{
_Core->EmitPlayerBurning(player, static_cast< bool >(state));
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerOnFire)
}
static void VC_PlayerCrouch(int player, unsigned int state)
{
_Core->EmitPlayerCrouching(player, static_cast<bool>(state));
// Attempt to forward the event
try
{
_Core->EmitPlayerCrouching(player, static_cast< bool >(state));
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerCrouch)
}
static void VC_PlayerGameKeys(int player, int previous, int current)
{
_Core->EmitPlayerGameKeys(player, previous, current);
// Attempt to forward the event
try
{
_Core->EmitPlayerGameKeys(player, previous, current);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerGameKeys)
}
static void VC_OnCheckpointEntered(int checkpoint, int player)
{
_Core->EmitCheckpointEntered(player, checkpoint);
// Attempt to forward the event
try
{
_Core->EmitCheckpointEntered(player, checkpoint);
}
SQMOD_CATCH_EVENT_EXCEPTION(CheckpointEntered)
}
static void VC_OnCheckpointExited(int checkpoint, int player)
{
_Core->EmitCheckpointExited(player, checkpoint);
// Attempt to forward the event
try
{
_Core->EmitCheckpointExited(player, checkpoint);
}
SQMOD_CATCH_EVENT_EXCEPTION(CheckpointExited)
}
static void VC_OnSphereEntered(int sphere, int player)
{
_Core->EmitForcefieldEntered(player, sphere);
// Attempt to forward the event
try
{
_Core->EmitForcefieldEntered(player, sphere);
}
SQMOD_CATCH_EVENT_EXCEPTION(SphereEntered)
}
static void VC_OnSphereExited(int sphere, int player)
{
_Core->EmitForcefieldExited(player, sphere);
// Attempt to forward the event
try
{
_Core->EmitForcefieldExited(player, sphere);
}
SQMOD_CATCH_EVENT_EXCEPTION(SphereExited)
}
// ------------------------------------------------------------------------------------------------

View File

@ -14,13 +14,24 @@ Routine::Time Routine::s_Last = 0;
Routine::Time Routine::s_Prev = 0;
Routine::Queue Routine::s_Queue;
Routine::Buckets Routine::s_Buckets;
Routine::Objects Routine::s_Objects;
// ------------------------------------------------------------------------------------------------
SQInteger Routine::Typename(HSQUIRRELVM vm)
{
static SQChar name[] = _SC("SqRoutine");
sq_pushstring(vm, name, sizeof(name));
return 1;
}
// ------------------------------------------------------------------------------------------------
void Routine::Attach(Routine * routine, Interval interval)
{
// Do we have a valid routine and interval bucket to attach?
if (!routine || ! interval)
{
return;
}
// Attempt to locate the bucket with the specified interval
Buckets::iterator itr = std::find_if(s_Buckets.begin(), s_Buckets.end(), IntrvFunc(interval));
// Does this bucket exist?
@ -33,8 +44,9 @@ void Routine::Attach(Routine * routine, Interval interval)
}
// Is this routine already attached to this bucket?
else if (std::find(itr->mRoutines.begin(), itr->mRoutines.end(), routine) != itr->mRoutines.end())
// Then let's attach it now
itr->mRoutines.push_back(routine);
{
itr->mRoutines.push_back(routine); // Then let's attach it now
}
}
// ------------------------------------------------------------------------------------------------
@ -42,22 +54,89 @@ void Routine::Detach(Routine * routine, Interval interval)
{
// Do we have a valid routine and interval to detach?
if (!routine || ! interval)
{
return;
}
// Attempt to locate the bucket with this interval
Buckets::iterator bitr = std::find_if(s_Buckets.begin(), s_Buckets.end(), IntrvFunc(interval));
// Was there a bucket with this interval?
if (bitr == s_Buckets.end())
{
return; // Nothing to detach from!
}
// Attempt to find this routine in the associated bucket
Routines::iterator ritr = std::find(bitr->mRoutines.begin(), bitr->mRoutines.end(), routine);
// Was this routine even attached?
if (ritr != bitr->mRoutines.end())
// Then erase it and move on
bitr->mRoutines.erase(ritr);
{
bitr->mRoutines.erase(ritr); // Then erase it and move on
}
// Any reason to keep this bucket?
if (bitr->mRoutines.empty())
// Remove the bucket as well
s_Buckets.erase(bitr);
{
s_Buckets.erase(bitr); // Remove the bucket as well
}
}
// ------------------------------------------------------------------------------------------------
Object Routine::Associate(Routine * routine)
{
// Attempt to see if this instance already exists in the pool
Objects::iterator itr = s_Objects.find(routine);
// Is this routine remembered for the first time?
if (itr == s_Objects.end())
{
// Obtain the initial stack size
const StackGuard sg;
// Push this instance on the stack
ClassType< Routine >::PushInstance(DefaultVM::Get(), routine);
// Initialize this element into the pool
itr = s_Objects.emplace(routine, Var< Object >(DefaultVM::Get(), -1).value).first;
// If the iterator still points to a null element then we failed
if (itr == s_Objects.end())
{
SqThrowF("Unable to remember routine instance");
}
}
// Does this routine still keep a strong reference to it self?
else if (itr->second.IsNull())
{
// Obtain the initial stack size
const StackGuard sg;
// Push this instance on the stack
ClassType< Routine >::PushInstance(DefaultVM::Get(), routine);
// Obtain a strong reference to it
itr->second = Var< Object >(DefaultVM::Get(), -1).value;
}
// Return the object that we have
return itr->second;
}
// ------------------------------------------------------------------------------------------------
void Routine::Dissociate(Routine * routine)
{
// Attempt to see if this instance already exists in the pool
Objects::iterator itr = s_Objects.find(routine);
// Was this routine even stored in the pool?
if (itr != s_Objects.end() && !itr->second.IsNull())
{
itr->second.Release(); // Release the reference to self
}
}
// ------------------------------------------------------------------------------------------------
bool Routine::Associated(Routine * routine)
{
// Attempt to see if this instance already exists in the pool
Objects::iterator itr = s_Objects.find(routine);
// Return whether this routine is pooled and references itself
return (itr != s_Objects.end() && !itr->second.IsNull());
}
// ------------------------------------------------------------------------------------------------
void Routine::Forget(Routine * routine)
{
s_Objects.erase(routine);
}
// ------------------------------------------------------------------------------------------------
@ -65,16 +144,28 @@ void Routine::ProcQueue()
{
// Do we have any queued commands that must be performed when unlocked?
if (s_Queue.empty() || s_Lock)
{
return; // We're done here!
}
// Process all commands in the queue
for (const auto & cmd : s_Queue)
{
// Are we supposed to detach the associated routine?
if (cmd.mCommand == CMD_DETACH)
{
// Detach the routine from it's associated bucket first
Detach(cmd.mRoutine, cmd.mInterval);
// Break association to allow the instance to be destroyed
Dissociate(cmd.mRoutine);
}
// Are we supposed to attach the associated routine?
else if (cmd.mCommand == CMD_ATTACH)
{
// Attach the routine to it's associated bucket first
Attach(cmd.mRoutine, cmd.mInterval);
// Prevent destruction of this routine while buckets are locked
Associate(cmd.mRoutine);
}
}
// Clear processed commands
s_Queue.clear();
@ -109,10 +200,14 @@ void Routine::Process()
bucket.mElapsed += delta;
// Have we completed the bucket interval?
if (bucket.mElapsed < bucket.mInterval)
{
continue; // Move to the next one
}
// Attempt to execute bucket routines, if any
for (auto & routine : bucket.mRoutines)
{
routine->Execute();
}
// Reset the bucket elapsed time
bucket.mElapsed = 0;
}
@ -141,6 +236,8 @@ void Routine::TerminateAll()
}
// Clear all references to routines
s_Buckets.clear();
// Clear all routine instance associations
s_Objects.clear();
// Clear the last time-stamp in case of a reload
s_Last = 0;
}
@ -153,6 +250,8 @@ Routine::Routine(Object & env, Function & func, Interval interval)
, m_Suspended(false)
, m_Terminated(false)
, m_Callback(env.GetVM(), env, func.GetFunc())
, m_Tag()
, m_Data()
{
Create();
}
@ -164,6 +263,8 @@ Routine::Routine(Object & env, Function & func, Interval interval, Iterate itera
, m_Suspended(false)
, m_Terminated(false)
, m_Callback(env.GetVM(), env, func.GetFunc())
, m_Tag()
, m_Data()
{
Create();
}
@ -177,6 +278,8 @@ Routine::Routine(Object & env, Function & func, Interval interval, Iterate itera
, m_Suspended(false)
, m_Terminated(false)
, m_Callback(env.GetVM(), env, func.GetFunc())
, m_Tag()
, m_Data()
, m_Arg1(a1)
{
Create();
@ -191,6 +294,8 @@ Routine::Routine(Object & env, Function & func, Interval interval, Iterate itera
, m_Suspended(false)
, m_Terminated(false)
, m_Callback(env.GetVM(), env, func.GetFunc())
, m_Tag()
, m_Data()
, m_Arg1(a1), m_Arg2(a2)
{
Create();
@ -205,6 +310,8 @@ Routine::Routine(Object & env, Function & func, Interval interval, Iterate itera
, m_Suspended(false)
, m_Terminated(false)
, m_Callback(env.GetVM(), env, func.GetFunc())
, m_Tag()
, m_Data()
, m_Arg1(a1), m_Arg2(a2), m_Arg3(a3)
{
Create();
@ -219,6 +326,8 @@ Routine::Routine(Object & env, Function & func, Interval interval, Iterate itera
, m_Suspended(false)
, m_Terminated(false)
, m_Callback(env.GetVM(), env, func.GetFunc())
, m_Tag()
, m_Data()
, m_Arg1(a1), m_Arg2(a2), m_Arg3(a3), m_Arg4(a4)
{
Create();
@ -233,6 +342,8 @@ Routine::Routine(Object & env, Function & func, Interval interval, Iterate itera
, m_Suspended(false)
, m_Terminated(false)
, m_Callback(env.GetVM(), env, func.GetFunc())
, m_Tag()
, m_Data()
, m_Arg1(a1), m_Arg2(a2), m_Arg3(a3), m_Arg4(a4), m_Arg5(a5)
{
Create();
@ -241,9 +352,13 @@ Routine::Routine(Object & env, Function & func, Interval interval, Iterate itera
// ------------------------------------------------------------------------------------------------
Routine::~Routine()
{
// Remove this instance from the pool
Forget(this);
// Was the routine already terminated?
if (m_Terminated)
return;
{
return; // Nothing to release!
}
// Detach from the associated bucket
Detach();
// Release script resources
@ -267,12 +382,63 @@ CSStr Routine::ToString() const
return ToStrF(_PRINT_INT_FMT, m_Interval);
}
// ------------------------------------------------------------------------------------------------
const String & Routine::GetTag() const
{
return m_Tag;
}
// ------------------------------------------------------------------------------------------------
void Routine::SetTag(CSStr tag)
{
m_Tag.assign(tag);
}
// ------------------------------------------------------------------------------------------------
Object & Routine::GetData()
{
// Validate the routine lifetime
Validate();
// Return the requested information
return m_Data;
}
// ------------------------------------------------------------------------------------------------
void Routine::SetData(Object & data)
{
// Validate the routine lifetime
Validate();
// Apply the specified value
m_Data = data;
}
// ------------------------------------------------------------------------------------------------
Routine & Routine::ApplyTag(CSStr tag)
{
m_Tag.assign(tag);
// Allow chaining
return *this;
}
// ------------------------------------------------------------------------------------------------
Routine & Routine::ApplyData(Object & data)
{
// Validate the routine lifetime
Validate();
// Apply the specified value
m_Data = data;
// Allow chaining
return *this;
}
// ------------------------------------------------------------------------------------------------
void Routine::Terminate()
{
// Was the routine already terminated?
if (m_Terminated)
SqThrowF("Routine was already terminated");
{
SqThrowF("Routine [%s] => Was already terminated", m_Tag.c_str());
}
// Detach from the associated bucket
Detach();
// Release script resources and mark it as terminated
@ -280,11 +446,10 @@ void Routine::Terminate()
}
// ------------------------------------------------------------------------------------------------
void Routine::SetArg(Uint8 num, Object & val)
Routine & Routine::SetArg(Uint8 num, Object & val)
{
// Was the routine terminated?
if (m_Terminated)
SqThrowF("Routine was terminated");
// Validate the routine lifetime
Validate();
// Identify which argument was requested
switch (num)
{
@ -302,15 +467,17 @@ void Routine::SetArg(Uint8 num, Object & val)
case 12: m_Arg12 = val; break;
case 13: m_Arg13 = val; break;
case 14: m_Arg14 = val; break;
default: SqThrowF("Argument is out of range: %d", num);
default: SqThrowF("Routine [%s] => Argument is out of range: %d", m_Tag.c_str(), num);
}
// Allow chaining
return *this;
}
// ------------------------------------------------------------------------------------------------
Object & Routine::GetArg(Uint8 num)
{
// Was the routine terminated?
if (m_Terminated)
SqThrowF("Routine was terminated");
// Validate the routine lifetime
Validate();
// Identify which argument was requested
switch (num)
{
@ -328,7 +495,7 @@ Object & Routine::GetArg(Uint8 num)
case 12: return m_Arg12;
case 13: return m_Arg13;
case 14: return m_Arg14;
default: SqThrowF("Argument is out of range: %d", num);
default: SqThrowF("Routine [%s] => Argument is out of range: %d", m_Tag.c_str(), num);
}
// Shouldn't really reach this point
return NullObject();
@ -340,14 +507,16 @@ Routine::Interval Routine::GetInterval() const
return m_Interval;
}
// ------------------------------------------------------------------------------------------------
void Routine::SetInterval(Interval interval)
{
// Was the routine terminated?
if (m_Terminated)
SqThrowF("Routine was terminated");
// Validate the routine lifetime
Validate();
// Is the specified interval valid?
else if (!interval)
SqThrowF("Invalid routine interval");
if (!interval)
{
SqThrowF("Routine [%s] => Invalid interval", m_Tag.c_str());
}
// Detach from the current bucket
Detach();
// Update the interval
@ -362,11 +531,11 @@ Routine::Iterate Routine::GetIterations() const
return m_Iterations;
}
// ------------------------------------------------------------------------------------------------
void Routine::SetIterations(Iterate iterations)
{
// Was the routine terminated?
if (m_Terminated)
SqThrowF("Routine was terminated");
// Validate the routine lifetime
Validate();
// Perform the requested operation
m_Iterations = iterations;
}
@ -379,12 +548,13 @@ Uint8 Routine::GetArguments() const
void Routine::SetArguments(Uint8 num)
{
// Was the routine terminated?
if (m_Terminated)
SqThrowF("Routine was terminated");
// Validate the routine lifetime
Validate();
// Is the specified argument count valid?
else if (num > 14)
SqThrowF("Argument is out of range: %d", num);
if (num > 14)
{
SqThrowF("Routine [%s] => Argument is out of range: %d", m_Tag.c_str(), num);
}
// Perform the requested operation
m_Arguments = num;
}
@ -395,11 +565,11 @@ bool Routine::GetSuspended() const
return m_Suspended;
}
// ------------------------------------------------------------------------------------------------
void Routine::SetSuspended(bool toggle)
{
// Was the routine terminated?
if (m_Terminated)
SqThrowF("Routine was terminated");
// Validate the routine lifetime
Validate();
// Perform the requested operation
m_Suspended = toggle;
}
@ -413,18 +583,17 @@ bool Routine::GetTerminated() const
// ------------------------------------------------------------------------------------------------
Function & Routine::GetCallback()
{
// Was the routine terminated?
if (m_Terminated)
SqThrowF("Routine was terminated");
// Validate the routine lifetime
Validate();
// Return the requested information
return m_Callback;
}
// ------------------------------------------------------------------------------------------------
void Routine::SetCallback(Object & env, Function & func)
{
// Was the routine terminated?
if (m_Terminated)
SqThrowF("Routine was terminated");
// Validate the routine lifetime
Validate();
// Perform the requested operation
m_Callback = Function(env.GetVM(), env, func.GetFunc());
}
@ -432,9 +601,11 @@ void Routine::SetCallback(Object & env, Function & func)
// ------------------------------------------------------------------------------------------------
void Routine::Release()
{
// Was the routine terminated?
// Was the routine already terminated?
if (m_Terminated)
return;
{
return; // Nothing to release!
}
// Mark it as terminated
m_Terminated = true;
// Release the callback
@ -459,14 +630,18 @@ void Routine::Release()
// ------------------------------------------------------------------------------------------------
void Routine::Create()
{
// Was the routine terminated?
// Do we even have a valid interval?
if (!m_Interval)
SqThrowF("Invalid routine interval");
// Is the specified callback valid?
{
SqThrowF("Routine [%s] => Invalid interval", m_Tag.c_str());
}
// Is the specified callback even valid?
else if (m_Callback.IsNull())
SqThrowF("Invalid routine callback");
// Attempt to attach the routine
Attach();
{
SqThrowF("Routine [%s] => Invalid callback", m_Tag.c_str());
}
// Always use the command queue to attach the routine when created
s_Queue.emplace_back(this, m_Interval, CMD_ATTACH);
}
// ------------------------------------------------------------------------------------------------
@ -474,14 +649,23 @@ void Routine::Attach()
{
// Do we have a valid interval?
if (!m_Interval)
{
return; // Nothing to attach to!
}
// Are the buckets locked?
else if (s_Lock)
{
// Queue a command to attach this routine when the bucket is unlocked
s_Queue.emplace_back(this, m_Interval, CMD_ATTACH);
}
// Attempt to attach the the routine now
else
{
// Attach to the associated bucket
Attach(this, m_Interval);
// Associate the instance with it's script object to prevent unexpected release
Associate(this);
}
}
// ------------------------------------------------------------------------------------------------
@ -489,14 +673,23 @@ void Routine::Detach()
{
// Do we have a valid interval?
if (!m_Interval)
{
return; // Nothing to detach from!
}
// Are the buckets locked?
else if (s_Lock)
{
// Queue a command to detach this routine when the bucket is unlocked
s_Queue.emplace_back(this, m_Interval, CMD_DETACH);
}
// Attempt to detach the the routine now
else
{
// Detach from the associated bucket
Detach(this, m_Interval);
// Break association to allow the instance to be released
Dissociate(this);
}
}
// ------------------------------------------------------------------------------------------------
@ -504,53 +697,91 @@ void Routine::Execute()
{
// Is this routine suspended or has nothing to call?
if (m_Suspended || m_Callback.IsNull())
return; // We're done here!
// Attempt to identify how many arguments should be passed
switch (m_Arguments)
{
case 0: m_Callback.Execute();
break;
case 1: m_Callback.Execute(m_Arg1);
break;
case 2: m_Callback.Execute(m_Arg1, m_Arg2);
break;
case 3: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3);
break;
case 4: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4);
break;
case 5: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5);
break;
case 6: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6);
break;
case 7: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7);
break;
case 8: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7,
return; // We're done here!
}
// Make sure that we have a known number of arguments
else if (m_Arguments > 14)
{
SqThrowF("Routine [%s] => Out of range argument count: %d", m_Tag.c_str(), m_Arguments);
}
// Attempt to forward the call
try
{
switch (m_Arguments)
{
// Attempt to identify how many arguments should be passed
case 0:
m_Callback.Execute();
break;
case 1:
m_Callback.Execute(m_Arg1);
break;
case 2:
m_Callback.Execute(m_Arg1, m_Arg2);
break;
case 3:
m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3);
break;
case 4:
m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4);
break;
case 5:
m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5);
break;
case 6:
m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6);
break;
case 7:
m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7);
break;
case 8:
m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7,
m_Arg8);
break;
case 9: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7,
break;
case 9:
m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7,
m_Arg8, m_Arg9);
break;
case 10: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7,
break;
case 10:
m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7,
m_Arg8, m_Arg9, m_Arg10);
break;
case 11: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7,
break;
case 11:
m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7,
m_Arg8, m_Arg9, m_Arg10, m_Arg11);
break;
case 12: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7,
break;
case 12:
m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7,
m_Arg8, m_Arg9, m_Arg10, m_Arg11, m_Arg12);
break;
case 13: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7,
break;
case 13:
m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7,
m_Arg8, m_Arg9, m_Arg10, m_Arg11, m_Arg12, m_Arg13);
break;
case 14: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7,
break;
case 14:
m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7,
m_Arg8, m_Arg9, m_Arg10, m_Arg11, m_Arg12, m_Arg13, m_Arg14);
break;
default:
SqThrowF("Unknown argument count: %d", m_Arguments);
break;
}
}
catch (const Sqrat::Exception & e)
{
SqThrowF("Routine [%s] => Squirrel error: %s", m_Tag.c_str(), e.Message().c_str());
}
catch (const std::exception & e)
{
SqThrowF("Routine [%s] => Program error: %s", m_Tag.c_str(), e.what());
}
catch (...)
{
SqThrowF("Routine [%s] => Unknown error", m_Tag.c_str());
}
// Decrease the number of iterations if necessary
if (m_Iterations && (--m_Iterations) == 0)
{
Terminate(); // This routine reached the end of it's life
}
}
/* ------------------------------------------------------------------------------------------------
@ -569,28 +800,103 @@ void TerminateRoutine()
Routine::TerminateAll();
}
// ------------------------------------------------------------------------------------------------
Object Routine::Create(Object & env, Function & func, Interval interval)
{
return Associate(new Routine(env, func, interval));
}
// ------------------------------------------------------------------------------------------------
Object Routine::Create(Object & env, Function & func, Interval interval, Iterate iterations)
{
return Associate(new Routine(env, func, interval, iterations));
}
// ------------------------------------------------------------------------------------------------
Object Routine::Create(Object & env, Function & func, Interval interval, Iterate iterations
, Object & a1)
{
return Associate(new Routine(env, func, interval, iterations, a1));
}
// ------------------------------------------------------------------------------------------------
Object Routine::Create(Object & env, Function & func, Interval interval, Iterate iterations
, Object & a1, Object & a2)
{
return Associate(new Routine(env, func, interval, iterations, a1, a2));
}
// ------------------------------------------------------------------------------------------------
Object Routine::Create(Object & env, Function & func, Interval interval, Iterate iterations
, Object & a1, Object & a2, Object & a3)
{
return Associate(new Routine(env, func, interval, iterations, a1, a2, a3));
}
// ------------------------------------------------------------------------------------------------
Object Routine::Create(Object & env, Function & func, Interval interval, Iterate iterations
, Object & a1, Object & a2, Object & a3, Object & a4)
{
return Associate(new Routine(env, func, interval, iterations, a1, a2, a3, a4));
}
// ------------------------------------------------------------------------------------------------
Object Routine::Create(Object & env, Function & func, Interval interval, Iterate iterations
, Object & a1, Object & a2, Object & a3, Object & a4, Object & a5)
{
return Associate(new Routine(env, func, interval, iterations, a1, a2, a3, a4, a5));
}
// ------------------------------------------------------------------------------------------------
Uint32 Routine::Count()
{
return static_cast< Uint32 >(s_Objects.size());
}
// ------------------------------------------------------------------------------------------------
Object Routine::FindByTag(CSStr tag)
{
// Perform a validity check on the specified tag
if (!tag || *tag == '\0')
{
SqThrowF("The specified routine tag is invalid: null/empty");
}
// Process each routine in the pool
for (const auto & elem : s_Objects)
{
// Is this routine valid (should always be) and does the tag match the specified one?
if (elem.first != nullptr && elem.first->m_Tag.compare(tag) == 0)
{
// Do we need to obtain the script object again?
if (elem.second.IsNull())
{
// Obtain the initial stack size
const StackGuard sg;
// Push this instance on the stack
ClassType< Routine >::PushInstance(DefaultVM::Get(), elem.first);
// Obtain a strong reference to it and return it
return Var< Object >(DefaultVM::Get(), -1).value;
}
// Stop searching and return this routine
return elem.second;
}
}
// Unable to locate a routine matching the specified tag
return NullObject();
}
// ================================================================================================
void Register_Routine(HSQUIRRELVM vm)
{
RootTable(vm).Bind(_SC("SqRoutine"),
Class< Routine, NoCopy< Routine > >(vm, _SC("SqRoutine"))
/* Constructors */
.Ctor< Object &, Function &, Routine::Interval >()
.Ctor< Object &, Function &, Routine::Interval, Routine::Iterate >()
.Ctor< Object &, Function &, Routine::Interval, Routine::Iterate
, Object & >()
.Ctor< Object &, Function &, Routine::Interval, Routine::Iterate
, Object &, Object & >()
.Ctor< Object &, Function &, Routine::Interval, Routine::Iterate
, Object &, Object &, Object & >()
.Ctor< Object &, Function &, Routine::Interval, Routine::Iterate
, Object &, Object &, Object &, Object & >()
.Ctor< Object &, Function &, Routine::Interval, Routine::Iterate
, Object &, Object &, Object &, Object &, Object & >()
Class< Routine, NoConstructor< Routine > >(vm, _SC("SqRoutine"))
/* Metamethods */
.Func(_SC("_cmp"), &Routine::Cmp)
.SquirrelFunc(_SC("_typename"), &Routine::Typename)
.Func(_SC("_tostring"), &Routine::ToString)
/* Properties */
.Prop(_SC("Tag"), &Routine::GetTag, &Routine::SetTag)
.Prop(_SC("Data"), &Routine::GetData, &Routine::SetData)
.Prop(_SC("Interval"), &Routine::GetInterval, &Routine::SetInterval)
.Prop(_SC("Iterations"), &Routine::GetIterations, &Routine::SetIterations)
.Prop(_SC("Arguments"), &Routine::GetArguments, &Routine::SetArguments)
@ -598,10 +904,35 @@ void Register_Routine(HSQUIRRELVM vm)
.Prop(_SC("Terminated"), &Routine::GetTerminated)
.Prop(_SC("Callback"), &Routine::GetCallback)
/* Functions */
.Func(_SC("SetTag"), &Routine::ApplyTag)
.Func(_SC("SetData"), &Routine::ApplyData)
.Func(_SC("Terminate"), &Routine::Terminate)
.Func(_SC("Bind"), &Routine::SetCallback)
.Func(_SC("GetArg"), &Routine::GetArg)
.Func(_SC("SetArg"), &Routine::SetArg)
// Static Functions
.StaticFunc(_SC("Count"), &Routine::Count)
.StaticFunc(_SC("FindByTag"), &Routine::FindByTag)
// Static Overloads
.StaticOverload< Object (*)(Object &, Function &, Routine::Interval) >
(_SC("Create"), &Routine::Create)
.StaticOverload< Object (*)(Object &, Function &, Routine::Interval, Routine::Iterate) >
(_SC("Create"), &Routine::Create)
.StaticOverload< Object (*)(Object &, Function &, Routine::Interval, Routine::Iterate,
Object &) >
(_SC("Create"), &Routine::Create)
.StaticOverload< Object (*)(Object &, Function &, Routine::Interval, Routine::Iterate,
Object &, Object &) >
(_SC("Create"), &Routine::Create)
.StaticOverload< Object (*)(Object &, Function &, Routine::Interval, Routine::Iterate,
Object &, Object &, Object &) >
(_SC("Create"), &Routine::Create)
.StaticOverload< Object (*)(Object &, Function &, Routine::Interval, Routine::Iterate,
Object &, Object &, Object &, Object &) >
(_SC("Create"), &Routine::Create)
.StaticOverload< Object (*)(Object &, Function &, Routine::Interval, Routine::Iterate,
Object &, Object &, Object &, Object &, Object &) >
(_SC("Create"), &Routine::Create)
);
}

View File

@ -138,9 +138,10 @@ protected:
};
// --------------------------------------------------------------------------------------------
typedef Int64 Time;
typedef std::vector< Cmd > Queue;
typedef std::vector< Bucket > Buckets;
typedef Int64 Time;
typedef std::vector< Cmd > Queue;
typedef std::vector< Bucket > Buckets;
typedef std::unordered_map< Routine *, Object > Objects;
/* --------------------------------------------------------------------------------------------
* Functor used to search for buckets with a certain interval.
@ -186,6 +187,7 @@ protected:
static Time s_Prev; /* Previous time point. */
static Queue s_Queue; /* Actions to be performed when the buckets aren't locked */
static Buckets s_Buckets; /* Buckets of routines grouped by similar intervals. */
static Objects s_Objects; /* List of existing routines and their associated object. */
/* --------------------------------------------------------------------------------------------
* Attach a routine to a certain bucket.
@ -197,43 +199,44 @@ protected:
*/
static void Detach(Routine * routine, Interval interval);
/* --------------------------------------------------------------------------------------------
* Create or locate the object for the specified routine and keep a strong reference to it.
*/
static Object Associate(Routine * routine);
/* --------------------------------------------------------------------------------------------
* Release the strong reference associated with the specified routine so it can be destroyed.
*/
static void Dissociate(Routine * routine);
/* --------------------------------------------------------------------------------------------
* See whether the specified routine exists in the pool and references itself.
*/
static bool Associated(Routine * routine);
/* --------------------------------------------------------------------------------------------
* Remove the specified routine from the pool and any associated reference, if any.
*/
static void Forget(Routine * routine);
/* --------------------------------------------------------------------------------------------
* Process queue commands.
*/
static void ProcQueue();
private:
/* --------------------------------------------------------------------------------------------
* Copy constructor. (disabled)
* See whether this routine is valid otherwise throw an exception.
*/
Routine(const Routine &);
/* --------------------------------------------------------------------------------------------
* Copy assignment operator. (disabled)
*/
Routine & operator = (const Routine &);
void Validate() const
{
if (m_Terminated)
{
SqThrowF("Routine [%s] => Was terminated", m_Tag.c_str());
}
}
private:
// --------------------------------------------------------------------------------------------
Iterate m_Iterations; /* Number of iterations before self destruct. */
Interval m_Interval; /* Interval between calls. */
Uint8 m_Arguments; /* Number of arguments to forward. */
bool m_Suspended; /* Whether calls should be ignored. */
bool m_Terminated; /* Whether the routine was terminated. */
// --------------------------------------------------------------------------------------------
Function m_Callback; /* The callback to be executed when triggered. */
/* --------------------------------------------------------------------------------------------
* Arguments to be forwarded to the callback.
*/
Object m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7,
m_Arg8, m_Arg9, m_Arg10, m_Arg11, m_Arg12, m_Arg13, m_Arg14;
public:
/* --------------------------------------------------------------------------------------------
* Constructor with just an interval.
*/
@ -274,6 +277,66 @@ public:
Routine(Object & env, Function & func, Interval interval, Iterate iterations
, Object & a1, Object & a2, Object & a3, Object & a4, Object & a5);
/* --------------------------------------------------------------------------------------------
* Copy constructor. (disabled)
*/
Routine(const Routine &);
/* --------------------------------------------------------------------------------------------
* Copy assignment operator. (disabled)
*/
Routine & operator = (const Routine &);
private:
/* --------------------------------------------------------------------------------------------
* Number of iterations before self destruct.
*/
Iterate m_Iterations;
/* --------------------------------------------------------------------------------------------
* Interval between calls.
*/
Interval m_Interval;
/* --------------------------------------------------------------------------------------------
* Number of arguments to forward.
*/
Uint8 m_Arguments;
/* --------------------------------------------------------------------------------------------
* Whether calls should be ignored.
*/
bool m_Suspended;
/* --------------------------------------------------------------------------------------------
* Whether the routine was terminated.
*/
bool m_Terminated;
/* --------------------------------------------------------------------------------------------
* The callback to be executed when triggered.
*/
Function m_Callback;
/* --------------------------------------------------------------------------------------------
* User tag associated with this instance.
*/
String m_Tag;
/* --------------------------------------------------------------------------------------------
* User data associated with this instance.
*/
Object m_Data;
/* --------------------------------------------------------------------------------------------
* Arguments to be forwarded to the callback.
*/
Object m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7,
m_Arg8, m_Arg9, m_Arg10, m_Arg11, m_Arg12, m_Arg13, m_Arg14;
public:
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
@ -289,6 +352,41 @@ public:
*/
CSStr ToString() const;
/* --------------------------------------------------------------------------------------------
* Used by the script engine to retrieve the name from instances of this type.
*/
static SQInteger Typename(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Retrieve the associated user tag.
*/
const String & GetTag() const;
/* --------------------------------------------------------------------------------------------
* Modify the associated user tag.
*/
void SetTag(CSStr tag);
/* --------------------------------------------------------------------------------------------
* Retrieve the associated user data.
*/
Object & GetData();
/* --------------------------------------------------------------------------------------------
* Modify the associated user data.
*/
void SetData(Object & data);
/* --------------------------------------------------------------------------------------------
* Modify the associated user tag and allow chaining of operations.
*/
Routine & ApplyTag(CSStr tag);
/* --------------------------------------------------------------------------------------------
* Modify the associated user data and allow chaining of operations.
*/
Routine & ApplyData(Object & data);
/* --------------------------------------------------------------------------------------------
* Terminate this routine by releasing all resources and scheduling it for detachment.
*/
@ -297,7 +395,7 @@ public:
/* --------------------------------------------------------------------------------------------
* Modify an explicit value to be passed as the specified argument.
*/
void SetArg(Uint8 num, Object & val);
Routine & SetArg(Uint8 num, Object & val);
/* --------------------------------------------------------------------------------------------
* Retrieve the value that is passed as the specified argument.
@ -385,6 +483,59 @@ protected:
* Execute the binded callback.
*/
void Execute();
public:
/* --------------------------------------------------------------------------------------------
* Create a routine with just an interval.
*/
static Object Create(Object & env, Function & func, Interval interval);
/* --------------------------------------------------------------------------------------------
* Create a routine with just an interval and explicit iterations.
*/
static Object Create(Object & env, Function & func, Interval interval, Iterate iterations);
/* --------------------------------------------------------------------------------------------
* Create a routine with just an interval, explicit iterations and arguments.
*/
static Object Create(Object & env, Function & func, Interval interval, Iterate iterations
, Object & a1);
/* --------------------------------------------------------------------------------------------
* Create a routine with just an interval, explicit iterations and arguments.
*/
static Object Create(Object & env, Function & func, Interval interval, Iterate iterations
, Object & a1, Object & a2);
/* --------------------------------------------------------------------------------------------
* Create a routine with just an interval, explicit iterations and arguments.
*/
static Object Create(Object & env, Function & func, Interval interval, Iterate iterations
, Object & a1, Object & a2, Object & a3);
/* --------------------------------------------------------------------------------------------
* Create a routine with just an interval, explicit iterations and arguments.
*/
static Object Create(Object & env, Function & func, Interval interval, Iterate iterations
, Object & a1, Object & a2, Object & a3, Object & a4);
/* --------------------------------------------------------------------------------------------
* Create a routine with just an interval, explicit iterations and arguments.
*/
static Object Create(Object & env, Function & func, Interval interval, Iterate iterations
, Object & a1, Object & a2, Object & a3, Object & a4, Object & a5);
/* --------------------------------------------------------------------------------------------
* Return the number of known routines.
*/
static Uint32 Count();
/* --------------------------------------------------------------------------------------------
* Attempt to find a certain routine by its associated tag.
*/
static Object FindByTag(CSStr tag);
};
} // Namespace:: SqMod