1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2025-05-03 01:27:13 +02: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 struct StackGuard
{ {
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
StackGuard()
: m_Top(sq_gettop(DefaultVM::Get())), m_VM(DefaultVM::Get())
{
/* ... */
}
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Base constructor. * Base constructor.
*/ */

View File

@ -109,31 +109,66 @@ SQMOD_API_EXPORT unsigned int VcmpPluginInit(PluginFuncs * funcs, PluginCallback
return SQMOD_SUCCESS; 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) static int VC_InitServer(void)
{ {
// Don't even try to initialize if there's no core instance
if (!_Core) if (!_Core)
{
return SQMOD_FAILURE; return SQMOD_FAILURE;
}
// Mark the initialization as successful by default
_Core->SetState(1); _Core->SetState(1);
// Attempt to forward the event
try
{
// Obtain the API version as a string // Obtain the API version as a string
String apiver(ToStrF("%d", SQMOD_API_VER)); String apiver(ToStrF("%d", SQMOD_API_VER));
// Signal outside plugins to do fetch our proxies // Signal outside plug-ins to do fetch our proxies
_Func->SendCustomCommand(0xDABBAD00, apiver.c_str()); _Func->SendCustomCommand(0xDABBAD00, apiver.c_str());
// Attempt to load the module core // Attempt to load the module core
if (_Core->Load()) if (_Core->Load())
{
_Core->EmitServerStartup(); _Core->EmitServerStartup();
}
else else
{
LogFtl("Unable to load the plugin resources properly"); LogFtl("Unable to load the plugin resources properly");
}
}
SQMOD_CATCH_EVENT_EXCEPTION(InitServer)
// Return the last known plug-in state
return _Core->GetState(); return _Core->GetState();
} }
static void VC_ShutdownServer(void) static void VC_ShutdownServer(void)
{ {
// Don't even try to de-initialize if there's no core instance
if (!_Core) if (!_Core)
{
return; return;
}
// Attempt to forward the event
try
{
_Core->EmitServerShutdown(); _Core->EmitServerShutdown();
// Deallocate and release everything obtained at startup // Deallocate and release everything obtained at startup
_Core->Terminate(); _Core->Terminate();
@ -141,206 +176,395 @@ static void VC_ShutdownServer(void)
UnbindCallbacks(); UnbindCallbacks();
// Destroy components // Destroy components
DestroyComponents(); DestroyComponents();
}
SQMOD_CATCH_EVENT_EXCEPTION(ShutdownServer)
} }
static void VC_Frame(float delta) static void VC_Frame(float delta)
{ {
// Attempt to forward the event
try
{
_Core->EmitServerFrame(delta); _Core->EmitServerFrame(delta);
}
SQMOD_CATCH_EVENT_EXCEPTION(Frame)
} }
static void VC_PlayerConnect(int player) static void VC_PlayerConnect(int player)
{ {
// Attempt to forward the event
try
{
_Core->ConnectPlayer(player, SQMOD_CREATE_AUTOMATIC, NullObject()); _Core->ConnectPlayer(player, SQMOD_CREATE_AUTOMATIC, NullObject());
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerConnect)
} }
static void VC_PlayerDisconnect(int player, int reason) static void VC_PlayerDisconnect(int player, int reason)
{ {
// Attempt to forward the event
try
{
_Core->DisconnectPlayer(player, reason, NullObject()); _Core->DisconnectPlayer(player, reason, NullObject());
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerDisconnect)
} }
static void VC_PlayerBeginTyping(int player) static void VC_PlayerBeginTyping(int player)
{ {
// Attempt to forward the event
try
{
_Core->EmitPlayerStartTyping(player); _Core->EmitPlayerStartTyping(player);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerBeginTyping)
} }
static void VC_PlayerEndTyping(int player) static void VC_PlayerEndTyping(int player)
{ {
// Attempt to forward the event
try
{
_Core->EmitPlayerStopTyping(player); _Core->EmitPlayerStopTyping(player);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerEndTyping)
} }
static int VC_PlayerRequestClass(int player, int offset) static int VC_PlayerRequestClass(int player, int offset)
{ {
// Mark the initialization as successful by default
_Core->SetState(SQMOD_SUCCESS); _Core->SetState(SQMOD_SUCCESS);
// Attempt to forward the event
try
{
_Core->EmitPlayerRequestClass(player, offset); _Core->EmitPlayerRequestClass(player, offset);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerEndTyping)
// Return the last known plug-in state
return _Core->GetState(); return _Core->GetState();
} }
static int VC_PlayerRequestSpawn(int player) static int VC_PlayerRequestSpawn(int player)
{ {
// Mark the initialization as successful by default
_Core->SetState(SQMOD_SUCCESS); _Core->SetState(SQMOD_SUCCESS);
// Attempt to forward the event
try
{
_Core->EmitPlayerRequestSpawn(player); _Core->EmitPlayerRequestSpawn(player);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerRequestSpawn)
// Return the last known plug-in state
return _Core->GetState(); return _Core->GetState();
} }
static void VC_PlayerSpawn(int player) static void VC_PlayerSpawn(int player)
{ {
// Attempt to forward the event
try
{
_Core->EmitPlayerSpawn(player); _Core->EmitPlayerSpawn(player);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerSpawn)
} }
static void VC_PlayerDeath(int player, int killer, int reason, int body_part) static void VC_PlayerDeath(int player, int killer, int reason, int body_part)
{ {
// Attempt to forward the event
try
{
if (_Func->IsPlayerConnected(killer)) if (_Func->IsPlayerConnected(killer))
{
_Core->EmitPlayerKilled(player, killer, reason, body_part); _Core->EmitPlayerKilled(player, killer, reason, body_part);
}
else else
{
_Core->EmitPlayerWasted(player, reason); _Core->EmitPlayerWasted(player, reason);
}
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerDeath)
} }
static void VC_PlayerUpdate(int player, int type) static void VC_PlayerUpdate(int player, int type)
{ {
// Attempt to forward the event
try
{
_Core->EmitPlayerUpdate(player, type); _Core->EmitPlayerUpdate(player, type);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerUpdate)
} }
static int VC_PlayerRequestEnter(int player, int vehicle, int slot) static int VC_PlayerRequestEnter(int player, int vehicle, int slot)
{ {
// Mark the initialization as successful by default
_Core->SetState(SQMOD_SUCCESS); _Core->SetState(SQMOD_SUCCESS);
// Attempt to forward the event
try
{
_Core->EmitPlayerEmbarking(player, vehicle, slot); _Core->EmitPlayerEmbarking(player, vehicle, slot);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerRequestEnter)
// Return the last known plug-in state
return _Core->GetState(); return _Core->GetState();
} }
static void VC_PlayerEnterVehicle(int player, int vehicle, int slot) static void VC_PlayerEnterVehicle(int player, int vehicle, int slot)
{ {
// Attempt to forward the event
try
{
_Core->EmitPlayerEmbarked(player, vehicle, slot); _Core->EmitPlayerEmbarked(player, vehicle, slot);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerEnterVehicle)
} }
static void VC_PlayerExitVehicle(int player, int vehicle) static void VC_PlayerExitVehicle(int player, int vehicle)
{ {
// Attempt to forward the event
try
{
_Core->EmitPlayerDisembark(player, vehicle); _Core->EmitPlayerDisembark(player, vehicle);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerExitVehicle)
} }
static int VC_PickupClaimPicked(int pickup, int player) static int VC_PickupClaimPicked(int pickup, int player)
{ {
// Mark the initialization as successful by default
_Core->SetState(SQMOD_SUCCESS); _Core->SetState(SQMOD_SUCCESS);
// Attempt to forward the event
try
{
_Core->EmitPickupClaimed(player, pickup); _Core->EmitPickupClaimed(player, pickup);
}
SQMOD_CATCH_EVENT_EXCEPTION(PickupClaimPicked)
// Return the last known plug-in state
return _Core->GetState(); return _Core->GetState();
} }
static void VC_PickupPickedUp(int pickup, int player) static void VC_PickupPickedUp(int pickup, int player)
{ {
// Attempt to forward the event
try
{
_Core->EmitPickupCollected(player, pickup); _Core->EmitPickupCollected(player, pickup);
}
SQMOD_CATCH_EVENT_EXCEPTION(PickupPickedUp)
} }
static void VC_PickupRespawn(int pickup) static void VC_PickupRespawn(int pickup)
{ {
// Attempt to forward the event
try
{
_Core->EmitPickupRespawn(pickup); _Core->EmitPickupRespawn(pickup);
}
SQMOD_CATCH_EVENT_EXCEPTION(PickupRespawn)
} }
static void VC_VehicleUpdate(int vehicle, int type) static void VC_VehicleUpdate(int vehicle, int type)
{ {
// Attempt to forward the event
try
{
_Core->EmitVehicleUpdate(vehicle, type); _Core->EmitVehicleUpdate(vehicle, type);
}
SQMOD_CATCH_EVENT_EXCEPTION(VehicleUpdate)
} }
static void VC_VehicleExplode(int vehicle) static void VC_VehicleExplode(int vehicle)
{ {
// Attempt to forward the event
try
{
_Core->EmitVehicleExplode(vehicle); _Core->EmitVehicleExplode(vehicle);
}
SQMOD_CATCH_EVENT_EXCEPTION(VehicleExplode)
} }
static void VC_VehicleRespawn(int vehicle) static void VC_VehicleRespawn(int vehicle)
{ {
// Attempt to forward the event
try
{
_Core->EmitVehicleRespawn(vehicle); _Core->EmitVehicleRespawn(vehicle);
}
SQMOD_CATCH_EVENT_EXCEPTION(VehicleRespawn)
} }
static void VC_ObjectShot(int object, int player, int weapon) static void VC_ObjectShot(int object, int player, int weapon)
{ {
// Attempt to forward the event
try
{
_Core->EmitObjectShot(player, object, weapon); _Core->EmitObjectShot(player, object, weapon);
}
SQMOD_CATCH_EVENT_EXCEPTION(ObjectShot)
} }
static void VC_ObjectBump(int object, int player) static void VC_ObjectBump(int object, int player)
{ {
// Attempt to forward the event
try
{
_Core->EmitObjectBump(player, object); _Core->EmitObjectBump(player, object);
}
SQMOD_CATCH_EVENT_EXCEPTION(ObjectBump)
} }
static int VC_PublicMessage(int player, const char * text) static int VC_PublicMessage(int player, const char * text)
{ {
// Mark the initialization as successful by default
_Core->SetState(SQMOD_SUCCESS); _Core->SetState(SQMOD_SUCCESS);
// Attempt to forward the event
try
{
_Core->EmitPlayerChat(player, text); _Core->EmitPlayerChat(player, text);
}
SQMOD_CATCH_EVENT_EXCEPTION(PublicMessage)
// Return the last known plug-in state
return _Core->GetState(); return _Core->GetState();
} }
static int VC_CommandMessage(int player, const char * text) static int VC_CommandMessage(int player, const char * text)
{ {
// Mark the initialization as successful by default
_Core->SetState(SQMOD_SUCCESS); _Core->SetState(SQMOD_SUCCESS);
// Attempt to forward the event
try
{
_Core->EmitPlayerCommand(player, text); _Core->EmitPlayerCommand(player, text);
}
SQMOD_CATCH_EVENT_EXCEPTION(CommandMessage)
// Return the last known plug-in state
return _Core->GetState(); return _Core->GetState();
} }
static int VC_PrivateMessage(int player, int target, const char * text) static int VC_PrivateMessage(int player, int target, const char * text)
{ {
// Mark the initialization as successful by default
_Core->SetState(SQMOD_SUCCESS); _Core->SetState(SQMOD_SUCCESS);
// Attempt to forward the event
try
{
_Core->EmitPlayerMessage(player, target, text); _Core->EmitPlayerMessage(player, target, text);
}
SQMOD_CATCH_EVENT_EXCEPTION(PrivateMessage)
// Return the last known plug-in state
return _Core->GetState(); return _Core->GetState();
} }
static int VC_InternalCommand(unsigned int type, const char * text) static int VC_InternalCommand(unsigned int type, const char * text)
{ {
// Mark the initialization as successful by default
_Core->SetState(SQMOD_SUCCESS); _Core->SetState(SQMOD_SUCCESS);
// Attempt to forward the event
try
{
_Core->EmitInternalCommand(type, text); _Core->EmitInternalCommand(type, text);
}
SQMOD_CATCH_EVENT_EXCEPTION(InternalCommand)
// Return the last known plug-in state
return _Core->GetState(); return _Core->GetState();
} }
static int VC_LoginAttempt(char * name, const char * passwd, const char * address) static int VC_LoginAttempt(char * name, const char * passwd, const char * address)
{ {
// Mark the initialization as successful by default
_Core->SetState(SQMOD_SUCCESS); _Core->SetState(SQMOD_SUCCESS);
// Attempt to forward the event
try
{
_Core->EmitLoginAttempt(name, passwd, address); _Core->EmitLoginAttempt(name, passwd, address);
}
SQMOD_CATCH_EVENT_EXCEPTION(LoginAttempt)
// Return the last known plug-in state
return _Core->GetState(); return _Core->GetState();
} }
static void VC_EntityPool(int type, int id, unsigned int deleted) 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) static void VC_KeyBindDown(int player, int bind)
{ {
// Attempt to forward the event
try
{
_Core->EmitPlayerKeyPress(player, bind); _Core->EmitPlayerKeyPress(player, bind);
}
SQMOD_CATCH_EVENT_EXCEPTION(KeyBindDown)
} }
static void VC_KeyBindUp(int player, int bind) static void VC_KeyBindUp(int player, int bind)
{ {
// Attempt to forward the event
try
{
_Core->EmitPlayerKeyRelease(player, bind); _Core->EmitPlayerKeyRelease(player, bind);
}
SQMOD_CATCH_EVENT_EXCEPTION(KeyBindUp)
} }
static void VC_PlayerAway(int player, unsigned int status) 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) static void VC_PlayerSpectate(int player, int target)
{ {
// Attempt to forward the event
try
{
_Core->EmitPlayerSpectate(player, target); _Core->EmitPlayerSpectate(player, target);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerSpectate)
} }
static void VC_PlayerCrashReport(int player, const char * report) static void VC_PlayerCrashReport(int player, const char * report)
{ {
// Attempt to forward the event
try
{
_Core->EmitPlayerCrashreport(player, report); _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... // 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) static void VC_PlayerName(int player, const char * previous, const char * current)
{ {
// Attempt to forward the event
try
{
_Core->EmitPlayerRename(player, previous, current); _Core->EmitPlayerRename(player, previous, current);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerName)
} }
static void VC_PlayerState(int player, int previous, int current) static void VC_PlayerState(int player, int previous, int current)
{ {
// Attempt to forward the event
try
{
_Core->EmitPlayerState(player, previous, current); _Core->EmitPlayerState(player, previous, current);
// Identify the current state and trigger the listeners specific to that
switch (current) switch (current)
{ {
case SQMOD_PLAYER_STATE_NONE: case SQMOD_PLAYER_STATE_NONE:
@ -371,12 +595,17 @@ static void VC_PlayerState(int player, int previous, int current)
_Core->EmitStateUnspawned(player, previous); _Core->EmitStateUnspawned(player, previous);
break; break;
} }
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerState)
} }
static void VC_PlayerAction(int player, int previous, int current) static void VC_PlayerAction(int player, int previous, int current)
{ {
// Attempt to forward the event
try
{
_Core->EmitPlayerAction(player, previous, current); _Core->EmitPlayerAction(player, previous, current);
// Identify the current action and trigger the listeners specific to that
switch (current) switch (current)
{ {
case SQMOD_PLAYER_ACTION_NONE: case SQMOD_PLAYER_ACTION_NONE:
@ -419,41 +648,78 @@ static void VC_PlayerAction(int player, int previous, int current)
_Core->EmitActionDisembarking(player, previous); _Core->EmitActionDisembarking(player, previous);
break; break;
} }
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerAction)
} }
static void VC_PlayerOnFire(int player, unsigned int state) 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) 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) static void VC_PlayerGameKeys(int player, int previous, int current)
{ {
// Attempt to forward the event
try
{
_Core->EmitPlayerGameKeys(player, previous, current); _Core->EmitPlayerGameKeys(player, previous, current);
}
SQMOD_CATCH_EVENT_EXCEPTION(PlayerGameKeys)
} }
static void VC_OnCheckpointEntered(int checkpoint, int player) static void VC_OnCheckpointEntered(int checkpoint, int player)
{ {
// Attempt to forward the event
try
{
_Core->EmitCheckpointEntered(player, checkpoint); _Core->EmitCheckpointEntered(player, checkpoint);
}
SQMOD_CATCH_EVENT_EXCEPTION(CheckpointEntered)
} }
static void VC_OnCheckpointExited(int checkpoint, int player) static void VC_OnCheckpointExited(int checkpoint, int player)
{ {
// Attempt to forward the event
try
{
_Core->EmitCheckpointExited(player, checkpoint); _Core->EmitCheckpointExited(player, checkpoint);
}
SQMOD_CATCH_EVENT_EXCEPTION(CheckpointExited)
} }
static void VC_OnSphereEntered(int sphere, int player) static void VC_OnSphereEntered(int sphere, int player)
{ {
// Attempt to forward the event
try
{
_Core->EmitForcefieldEntered(player, sphere); _Core->EmitForcefieldEntered(player, sphere);
}
SQMOD_CATCH_EVENT_EXCEPTION(SphereEntered)
} }
static void VC_OnSphereExited(int sphere, int player) static void VC_OnSphereExited(int sphere, int player)
{ {
// Attempt to forward the event
try
{
_Core->EmitForcefieldExited(player, sphere); _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::Time Routine::s_Prev = 0;
Routine::Queue Routine::s_Queue; Routine::Queue Routine::s_Queue;
Routine::Buckets Routine::s_Buckets; 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) void Routine::Attach(Routine * routine, Interval interval)
{ {
// Do we have a valid routine and interval bucket to attach? // Do we have a valid routine and interval bucket to attach?
if (!routine || ! interval) if (!routine || ! interval)
{
return; return;
}
// Attempt to locate the bucket with the specified interval // Attempt to locate the bucket with the specified interval
Buckets::iterator itr = std::find_if(s_Buckets.begin(), s_Buckets.end(), IntrvFunc(interval)); Buckets::iterator itr = std::find_if(s_Buckets.begin(), s_Buckets.end(), IntrvFunc(interval));
// Does this bucket exist? // Does this bucket exist?
@ -33,8 +44,9 @@ void Routine::Attach(Routine * routine, Interval interval)
} }
// Is this routine already attached to this bucket? // Is this routine already attached to this bucket?
else if (std::find(itr->mRoutines.begin(), itr->mRoutines.end(), routine) != itr->mRoutines.end()) 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? // Do we have a valid routine and interval to detach?
if (!routine || ! interval) if (!routine || ! interval)
{
return; return;
}
// Attempt to locate the bucket with this interval // Attempt to locate the bucket with this interval
Buckets::iterator bitr = std::find_if(s_Buckets.begin(), s_Buckets.end(), IntrvFunc(interval)); Buckets::iterator bitr = std::find_if(s_Buckets.begin(), s_Buckets.end(), IntrvFunc(interval));
// Was there a bucket with this interval? // Was there a bucket with this interval?
if (bitr == s_Buckets.end()) if (bitr == s_Buckets.end())
{
return; // Nothing to detach from! return; // Nothing to detach from!
}
// Attempt to find this routine in the associated bucket // Attempt to find this routine in the associated bucket
Routines::iterator ritr = std::find(bitr->mRoutines.begin(), bitr->mRoutines.end(), routine); Routines::iterator ritr = std::find(bitr->mRoutines.begin(), bitr->mRoutines.end(), routine);
// Was this routine even attached? // Was this routine even attached?
if (ritr != bitr->mRoutines.end()) 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? // Any reason to keep this bucket?
if (bitr->mRoutines.empty()) 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? // Do we have any queued commands that must be performed when unlocked?
if (s_Queue.empty() || s_Lock) if (s_Queue.empty() || s_Lock)
{
return; // We're done here! return; // We're done here!
}
// Process all commands in the queue // Process all commands in the queue
for (const auto & cmd : s_Queue) for (const auto & cmd : s_Queue)
{ {
// Are we supposed to detach the associated routine? // Are we supposed to detach the associated routine?
if (cmd.mCommand == CMD_DETACH) if (cmd.mCommand == CMD_DETACH)
{
// Detach the routine from it's associated bucket first
Detach(cmd.mRoutine, cmd.mInterval); 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? // Are we supposed to attach the associated routine?
else if (cmd.mCommand == CMD_ATTACH) else if (cmd.mCommand == CMD_ATTACH)
{
// Attach the routine to it's associated bucket first
Attach(cmd.mRoutine, cmd.mInterval); Attach(cmd.mRoutine, cmd.mInterval);
// Prevent destruction of this routine while buckets are locked
Associate(cmd.mRoutine);
}
} }
// Clear processed commands // Clear processed commands
s_Queue.clear(); s_Queue.clear();
@ -109,10 +200,14 @@ void Routine::Process()
bucket.mElapsed += delta; bucket.mElapsed += delta;
// Have we completed the bucket interval? // Have we completed the bucket interval?
if (bucket.mElapsed < bucket.mInterval) if (bucket.mElapsed < bucket.mInterval)
{
continue; // Move to the next one continue; // Move to the next one
}
// Attempt to execute bucket routines, if any // Attempt to execute bucket routines, if any
for (auto & routine : bucket.mRoutines) for (auto & routine : bucket.mRoutines)
{
routine->Execute(); routine->Execute();
}
// Reset the bucket elapsed time // Reset the bucket elapsed time
bucket.mElapsed = 0; bucket.mElapsed = 0;
} }
@ -141,6 +236,8 @@ void Routine::TerminateAll()
} }
// Clear all references to routines // Clear all references to routines
s_Buckets.clear(); s_Buckets.clear();
// Clear all routine instance associations
s_Objects.clear();
// Clear the last time-stamp in case of a reload // Clear the last time-stamp in case of a reload
s_Last = 0; s_Last = 0;
} }
@ -153,6 +250,8 @@ Routine::Routine(Object & env, Function & func, Interval interval)
, m_Suspended(false) , m_Suspended(false)
, m_Terminated(false) , m_Terminated(false)
, m_Callback(env.GetVM(), env, func.GetFunc()) , m_Callback(env.GetVM(), env, func.GetFunc())
, m_Tag()
, m_Data()
{ {
Create(); Create();
} }
@ -164,6 +263,8 @@ Routine::Routine(Object & env, Function & func, Interval interval, Iterate itera
, m_Suspended(false) , m_Suspended(false)
, m_Terminated(false) , m_Terminated(false)
, m_Callback(env.GetVM(), env, func.GetFunc()) , m_Callback(env.GetVM(), env, func.GetFunc())
, m_Tag()
, m_Data()
{ {
Create(); Create();
} }
@ -177,6 +278,8 @@ Routine::Routine(Object & env, Function & func, Interval interval, Iterate itera
, m_Suspended(false) , m_Suspended(false)
, m_Terminated(false) , m_Terminated(false)
, m_Callback(env.GetVM(), env, func.GetFunc()) , m_Callback(env.GetVM(), env, func.GetFunc())
, m_Tag()
, m_Data()
, m_Arg1(a1) , m_Arg1(a1)
{ {
Create(); Create();
@ -191,6 +294,8 @@ Routine::Routine(Object & env, Function & func, Interval interval, Iterate itera
, m_Suspended(false) , m_Suspended(false)
, m_Terminated(false) , m_Terminated(false)
, m_Callback(env.GetVM(), env, func.GetFunc()) , m_Callback(env.GetVM(), env, func.GetFunc())
, m_Tag()
, m_Data()
, m_Arg1(a1), m_Arg2(a2) , m_Arg1(a1), m_Arg2(a2)
{ {
Create(); Create();
@ -205,6 +310,8 @@ Routine::Routine(Object & env, Function & func, Interval interval, Iterate itera
, m_Suspended(false) , m_Suspended(false)
, m_Terminated(false) , m_Terminated(false)
, m_Callback(env.GetVM(), env, func.GetFunc()) , m_Callback(env.GetVM(), env, func.GetFunc())
, m_Tag()
, m_Data()
, m_Arg1(a1), m_Arg2(a2), m_Arg3(a3) , m_Arg1(a1), m_Arg2(a2), m_Arg3(a3)
{ {
Create(); Create();
@ -219,6 +326,8 @@ Routine::Routine(Object & env, Function & func, Interval interval, Iterate itera
, m_Suspended(false) , m_Suspended(false)
, m_Terminated(false) , m_Terminated(false)
, m_Callback(env.GetVM(), env, func.GetFunc()) , m_Callback(env.GetVM(), env, func.GetFunc())
, m_Tag()
, m_Data()
, m_Arg1(a1), m_Arg2(a2), m_Arg3(a3), m_Arg4(a4) , m_Arg1(a1), m_Arg2(a2), m_Arg3(a3), m_Arg4(a4)
{ {
Create(); Create();
@ -233,6 +342,8 @@ Routine::Routine(Object & env, Function & func, Interval interval, Iterate itera
, m_Suspended(false) , m_Suspended(false)
, m_Terminated(false) , m_Terminated(false)
, m_Callback(env.GetVM(), env, func.GetFunc()) , 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) , m_Arg1(a1), m_Arg2(a2), m_Arg3(a3), m_Arg4(a4), m_Arg5(a5)
{ {
Create(); Create();
@ -241,9 +352,13 @@ Routine::Routine(Object & env, Function & func, Interval interval, Iterate itera
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Routine::~Routine() Routine::~Routine()
{ {
// Remove this instance from the pool
Forget(this);
// Was the routine already terminated? // Was the routine already terminated?
if (m_Terminated) if (m_Terminated)
return; {
return; // Nothing to release!
}
// Detach from the associated bucket // Detach from the associated bucket
Detach(); Detach();
// Release script resources // Release script resources
@ -267,12 +382,63 @@ CSStr Routine::ToString() const
return ToStrF(_PRINT_INT_FMT, m_Interval); 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() void Routine::Terminate()
{ {
// Was the routine already terminated? // Was the routine already terminated?
if (m_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 from the associated bucket
Detach(); Detach();
// Release script resources and mark it as terminated // 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? // Validate the routine lifetime
if (m_Terminated) Validate();
SqThrowF("Routine was terminated");
// Identify which argument was requested // Identify which argument was requested
switch (num) switch (num)
{ {
@ -302,15 +467,17 @@ void Routine::SetArg(Uint8 num, Object & val)
case 12: m_Arg12 = val; break; case 12: m_Arg12 = val; break;
case 13: m_Arg13 = val; break; case 13: m_Arg13 = val; break;
case 14: m_Arg14 = 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) Object & Routine::GetArg(Uint8 num)
{ {
// Was the routine terminated? // Validate the routine lifetime
if (m_Terminated) Validate();
SqThrowF("Routine was terminated");
// Identify which argument was requested // Identify which argument was requested
switch (num) switch (num)
{ {
@ -328,7 +495,7 @@ Object & Routine::GetArg(Uint8 num)
case 12: return m_Arg12; case 12: return m_Arg12;
case 13: return m_Arg13; case 13: return m_Arg13;
case 14: return m_Arg14; 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 // Shouldn't really reach this point
return NullObject(); return NullObject();
@ -340,14 +507,16 @@ Routine::Interval Routine::GetInterval() const
return m_Interval; return m_Interval;
} }
// ------------------------------------------------------------------------------------------------
void Routine::SetInterval(Interval interval) void Routine::SetInterval(Interval interval)
{ {
// Was the routine terminated? // Validate the routine lifetime
if (m_Terminated) Validate();
SqThrowF("Routine was terminated");
// Is the specified interval valid? // Is the specified interval valid?
else if (!interval) if (!interval)
SqThrowF("Invalid routine interval"); {
SqThrowF("Routine [%s] => Invalid interval", m_Tag.c_str());
}
// Detach from the current bucket // Detach from the current bucket
Detach(); Detach();
// Update the interval // Update the interval
@ -362,11 +531,11 @@ Routine::Iterate Routine::GetIterations() const
return m_Iterations; return m_Iterations;
} }
// ------------------------------------------------------------------------------------------------
void Routine::SetIterations(Iterate iterations) void Routine::SetIterations(Iterate iterations)
{ {
// Was the routine terminated? // Validate the routine lifetime
if (m_Terminated) Validate();
SqThrowF("Routine was terminated");
// Perform the requested operation // Perform the requested operation
m_Iterations = iterations; m_Iterations = iterations;
} }
@ -379,12 +548,13 @@ Uint8 Routine::GetArguments() const
void Routine::SetArguments(Uint8 num) void Routine::SetArguments(Uint8 num)
{ {
// Was the routine terminated? // Validate the routine lifetime
if (m_Terminated) Validate();
SqThrowF("Routine was terminated");
// Is the specified argument count valid? // Is the specified argument count valid?
else if (num > 14) if (num > 14)
SqThrowF("Argument is out of range: %d", num); {
SqThrowF("Routine [%s] => Argument is out of range: %d", m_Tag.c_str(), num);
}
// Perform the requested operation // Perform the requested operation
m_Arguments = num; m_Arguments = num;
} }
@ -395,11 +565,11 @@ bool Routine::GetSuspended() const
return m_Suspended; return m_Suspended;
} }
// ------------------------------------------------------------------------------------------------
void Routine::SetSuspended(bool toggle) void Routine::SetSuspended(bool toggle)
{ {
// Was the routine terminated? // Validate the routine lifetime
if (m_Terminated) Validate();
SqThrowF("Routine was terminated");
// Perform the requested operation // Perform the requested operation
m_Suspended = toggle; m_Suspended = toggle;
} }
@ -413,18 +583,17 @@ bool Routine::GetTerminated() const
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Function & Routine::GetCallback() Function & Routine::GetCallback()
{ {
// Was the routine terminated? // Validate the routine lifetime
if (m_Terminated) Validate();
SqThrowF("Routine was terminated");
// Return the requested information // Return the requested information
return m_Callback; return m_Callback;
} }
// ------------------------------------------------------------------------------------------------
void Routine::SetCallback(Object & env, Function & func) void Routine::SetCallback(Object & env, Function & func)
{ {
// Was the routine terminated? // Validate the routine lifetime
if (m_Terminated) Validate();
SqThrowF("Routine was terminated");
// Perform the requested operation // Perform the requested operation
m_Callback = Function(env.GetVM(), env, func.GetFunc()); m_Callback = Function(env.GetVM(), env, func.GetFunc());
} }
@ -432,9 +601,11 @@ void Routine::SetCallback(Object & env, Function & func)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Routine::Release() void Routine::Release()
{ {
// Was the routine terminated? // Was the routine already terminated?
if (m_Terminated) if (m_Terminated)
return; {
return; // Nothing to release!
}
// Mark it as terminated // Mark it as terminated
m_Terminated = true; m_Terminated = true;
// Release the callback // Release the callback
@ -459,14 +630,18 @@ void Routine::Release()
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Routine::Create() void Routine::Create()
{ {
// Was the routine terminated? // Do we even have a valid interval?
if (!m_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()) else if (m_Callback.IsNull())
SqThrowF("Invalid routine callback"); {
// Attempt to attach the routine SqThrowF("Routine [%s] => Invalid callback", m_Tag.c_str());
Attach(); }
// 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? // Do we have a valid interval?
if (!m_Interval) if (!m_Interval)
{
return; // Nothing to attach to! return; // Nothing to attach to!
}
// Are the buckets locked? // Are the buckets locked?
else if (s_Lock) else if (s_Lock)
{
// Queue a command to attach this routine when the bucket is unlocked // Queue a command to attach this routine when the bucket is unlocked
s_Queue.emplace_back(this, m_Interval, CMD_ATTACH); s_Queue.emplace_back(this, m_Interval, CMD_ATTACH);
}
// Attempt to attach the the routine now // Attempt to attach the the routine now
else else
{
// Attach to the associated bucket
Attach(this, m_Interval); 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? // Do we have a valid interval?
if (!m_Interval) if (!m_Interval)
{
return; // Nothing to detach from! return; // Nothing to detach from!
}
// Are the buckets locked? // Are the buckets locked?
else if (s_Lock) else if (s_Lock)
{
// Queue a command to detach this routine when the bucket is unlocked // Queue a command to detach this routine when the bucket is unlocked
s_Queue.emplace_back(this, m_Interval, CMD_DETACH); s_Queue.emplace_back(this, m_Interval, CMD_DETACH);
}
// Attempt to detach the the routine now // Attempt to detach the the routine now
else else
{
// Detach from the associated bucket
Detach(this, m_Interval); 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? // Is this routine suspended or has nothing to call?
if (m_Suspended || m_Callback.IsNull()) if (m_Suspended || m_Callback.IsNull())
{
return; // We're done here! return; // We're done here!
// Attempt to identify how many arguments should be passed }
// 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) switch (m_Arguments)
{ {
case 0: m_Callback.Execute(); // Attempt to identify how many arguments should be passed
case 0:
m_Callback.Execute();
break; break;
case 1: m_Callback.Execute(m_Arg1); case 1:
m_Callback.Execute(m_Arg1);
break; break;
case 2: m_Callback.Execute(m_Arg1, m_Arg2); case 2:
m_Callback.Execute(m_Arg1, m_Arg2);
break; break;
case 3: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3); case 3:
m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3);
break; break;
case 4: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4); case 4:
m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4);
break; break;
case 5: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5); case 5:
m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5);
break; break;
case 6: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6); case 6:
m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6);
break; break;
case 7: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7); case 7:
m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7);
break; break;
case 8: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7, case 8:
m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7,
m_Arg8); m_Arg8);
break; break;
case 9: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7, case 9:
m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7,
m_Arg8, m_Arg9); m_Arg8, m_Arg9);
break; break;
case 10: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7, 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); m_Arg8, m_Arg9, m_Arg10);
break; break;
case 11: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7, 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); m_Arg8, m_Arg9, m_Arg10, m_Arg11);
break; break;
case 12: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7, 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); m_Arg8, m_Arg9, m_Arg10, m_Arg11, m_Arg12);
break; break;
case 13: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7, 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); m_Arg8, m_Arg9, m_Arg10, m_Arg11, m_Arg12, m_Arg13);
break; break;
case 14: m_Callback.Execute(m_Arg1, m_Arg2, m_Arg3, m_Arg4, m_Arg5, m_Arg6, m_Arg7, 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); m_Arg8, m_Arg9, m_Arg10, m_Arg11, m_Arg12, m_Arg13, m_Arg14);
break; break;
default: }
SqThrowF("Unknown argument count: %d", m_Arguments); }
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 // Decrease the number of iterations if necessary
if (m_Iterations && (--m_Iterations) == 0) if (m_Iterations && (--m_Iterations) == 0)
{
Terminate(); // This routine reached the end of it's life Terminate(); // This routine reached the end of it's life
}
} }
/* ------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------
@ -569,28 +800,103 @@ void TerminateRoutine()
Routine::TerminateAll(); 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) void Register_Routine(HSQUIRRELVM vm)
{ {
RootTable(vm).Bind(_SC("SqRoutine"), RootTable(vm).Bind(_SC("SqRoutine"),
Class< Routine, NoCopy< Routine > >(vm, _SC("SqRoutine")) Class< Routine, NoConstructor< 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 & >()
/* Metamethods */ /* Metamethods */
.Func(_SC("_cmp"), &Routine::Cmp) .Func(_SC("_cmp"), &Routine::Cmp)
.SquirrelFunc(_SC("_typename"), &Routine::Typename)
.Func(_SC("_tostring"), &Routine::ToString) .Func(_SC("_tostring"), &Routine::ToString)
/* Properties */ /* Properties */
.Prop(_SC("Tag"), &Routine::GetTag, &Routine::SetTag)
.Prop(_SC("Data"), &Routine::GetData, &Routine::SetData)
.Prop(_SC("Interval"), &Routine::GetInterval, &Routine::SetInterval) .Prop(_SC("Interval"), &Routine::GetInterval, &Routine::SetInterval)
.Prop(_SC("Iterations"), &Routine::GetIterations, &Routine::SetIterations) .Prop(_SC("Iterations"), &Routine::GetIterations, &Routine::SetIterations)
.Prop(_SC("Arguments"), &Routine::GetArguments, &Routine::SetArguments) .Prop(_SC("Arguments"), &Routine::GetArguments, &Routine::SetArguments)
@ -598,10 +904,35 @@ void Register_Routine(HSQUIRRELVM vm)
.Prop(_SC("Terminated"), &Routine::GetTerminated) .Prop(_SC("Terminated"), &Routine::GetTerminated)
.Prop(_SC("Callback"), &Routine::GetCallback) .Prop(_SC("Callback"), &Routine::GetCallback)
/* Functions */ /* Functions */
.Func(_SC("SetTag"), &Routine::ApplyTag)
.Func(_SC("SetData"), &Routine::ApplyData)
.Func(_SC("Terminate"), &Routine::Terminate) .Func(_SC("Terminate"), &Routine::Terminate)
.Func(_SC("Bind"), &Routine::SetCallback) .Func(_SC("Bind"), &Routine::SetCallback)
.Func(_SC("GetArg"), &Routine::GetArg) .Func(_SC("GetArg"), &Routine::GetArg)
.Func(_SC("SetArg"), &Routine::SetArg) .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

@ -141,6 +141,7 @@ protected:
typedef Int64 Time; typedef Int64 Time;
typedef std::vector< Cmd > Queue; typedef std::vector< Cmd > Queue;
typedef std::vector< Bucket > Buckets; typedef std::vector< Bucket > Buckets;
typedef std::unordered_map< Routine *, Object > Objects;
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Functor used to search for buckets with a certain interval. * Functor used to search for buckets with a certain interval.
@ -186,6 +187,7 @@ protected:
static Time s_Prev; /* Previous time point. */ static Time s_Prev; /* Previous time point. */
static Queue s_Queue; /* Actions to be performed when the buckets aren't locked */ 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 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. * Attach a routine to a certain bucket.
@ -197,43 +199,44 @@ protected:
*/ */
static void Detach(Routine * routine, Interval interval); 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. * Process queue commands.
*/ */
static void ProcQueue(); static void ProcQueue();
private:
/* -------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------
* Copy constructor. (disabled) * See whether this routine is valid otherwise throw an exception.
*/ */
Routine(const Routine &); void Validate() const
{
/* -------------------------------------------------------------------------------------------- if (m_Terminated)
* Copy assignment operator. (disabled) {
*/ SqThrowF("Routine [%s] => Was terminated", m_Tag.c_str());
Routine & operator = (const Routine &); }
}
private: 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. * Constructor with just an interval.
*/ */
@ -274,6 +277,66 @@ public:
Routine(Object & env, Function & func, Interval interval, Iterate iterations Routine(Object & env, Function & func, Interval interval, Iterate iterations
, Object & a1, Object & a2, Object & a3, Object & a4, Object & a5); , 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. * Destructor.
*/ */
@ -289,6 +352,41 @@ public:
*/ */
CSStr ToString() const; 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. * 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. * 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. * Retrieve the value that is passed as the specified argument.
@ -385,6 +483,59 @@ protected:
* Execute the binded callback. * Execute the binded callback.
*/ */
void Execute(); 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 } // Namespace:: SqMod