1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2025-10-29 21:37:18 +01:00

Restructure some files.

This commit is contained in:
Sandu Liviu Catalin
2018-07-29 12:25:44 +03:00
parent 526538fdb9
commit 0ad290ca34
27 changed files with 96 additions and 132 deletions

476
source/Misc/Areas.cpp Normal file
View File

@@ -0,0 +1,476 @@
// ------------------------------------------------------------------------------------------------
#include "Areas.hpp"
// ------------------------------------------------------------------------------------------------
#include <algorithm>
// ------------------------------------------------------------------------------------------------
namespace SqMod {
// ------------------------------------------------------------------------------------------------
SQMODE_DECL_TYPENAME(AreaTypename, _SC("SqArea"))
// ------------------------------------------------------------------------------------------------
AreaManager AreaManager::s_Inst;
// ------------------------------------------------------------------------------------------------
void Area::AddArray(const Sqrat::Array & a)
{
float values[2];
a.Foreach([this, &values, n = int(0)](HSQUIRRELVM vm) mutable -> bool {
// Retrieve the type of the value
const SQObjectType type = sq_gettype(vm, -1);
// Are we dealing with a vector?
if (type == OT_INSTANCE)
{
// Next time, start again for floats
n = 0;
// Grab the instance from the stack
this->AddPoint(*ClassType< Vector2 >::GetInstance(vm, -1));
}
else if (type & SQOBJECT_NUMERIC)
{
// Retrieve the value from the stack
values[n] = Var< float >(vm, -1).value;
// Do we have enough to form a vector?
if (++n == 2)
{
this->AddPointEx(values[0], values[1]);
// Reset the counter
n = 0;
}
}
// Ignore anything else
return true;
});
}
// ------------------------------------------------------------------------------------------------
bool Area::Manage()
{
// Are we connected to any cells?
if (!mCells.empty())
{
STHROWF("The area is already managed");
}
// We expect this to be called only from the script so that the first parameter in the vm
// is the area instance
LightObj obj(1, DefaultVM::Get());
// Attempt to manage this area
AreaManager::Get().InsertArea(*this, obj);
// Return whether the area is now managed by any cells
return !mCells.empty();
}
// ------------------------------------------------------------------------------------------------
bool Area::Unmanage()
{
// Are we connected to any cells?
if (mCells.empty())
{
return true; // Already unmanaged
}
// Attempt to unmanage this area
AreaManager::Get().RemoveArea(*this);
// Return whether the area is not managed by any cells
return mCells.empty();
}
// ------------------------------------------------------------------------------------------------
bool Area::IsInside(float x, float y) const
{
// Is the an area to test?
if (mPoints.size() < 3)
{
return false; // Can't possibly be in an area that doesn't exist
}
// http://sidvind.com/wiki/Point-in-polygon:_Jordan_Curve_Theorem
// The points creating the polygon
float x1, x2;
// How many times the ray crosses a line segment
int crossings = 0;
// Iterate through each line
for (Uint32 i = 0, n = static_cast< Uint32 >(mPoints.size()); i < n; ++i)
{
Points::const_reference a = mPoints[i];
Points::const_reference b = mPoints[(i + 1) % n];
// This is done to ensure that we get the same result when
// the line goes from left to right and right to left.
if (a.x < b.x)
{
x1 = a.x;
x2 = b.x;
}
else
{
x1 = b.x;
x2 = a.x;
}
// First check if the ray is able to cross the line
if (x > x1 && x <= x2 && (y < a.y || y <= b.y))
{
// Calculate the equation of the line
const float dx = (b.x - a.x);
const float dy = (b.y - a.y);
float k;
if (fabs(dx) < 0.000001f)
{
k = 0xffffffff;
}
else
{
k = (dy / dx);
}
const float m = (a.y - k * a.x);
const float y2 = (k * x + m);
// Does the ray cross the line?
if (y <= y2)
{
++crossings;
}
}
}
// Return if the crossings are not even
return (crossings % 2 == 1);
}
// ------------------------------------------------------------------------------------------------
AreaManager::AreaManager(size_t sz)
: m_Queue(), m_ProcList(), m_Grid{}
{
// Negative half grid size (left)
int l = (-GRIDH * CELLD);
// Positive half grid size minus one cell (bottom)
int b = (abs(l) - CELLD);
// Negative half grid size minus one cell (right)
int r = (l + CELLD);
// Positive half grid size (top)
int t = abs(l);
// Initialize the grid cells
for (int y = 0; y < GRIDN; ++y)
{
for (int x = 0; x < GRIDN; ++x)
{
// Grab a reference to the cell
AreaCell & c = m_Grid[y][x];
// Configure the range of the cell
c.mL = static_cast< float >(l);
c.mB = static_cast< float >(b);
c.mR = static_cast< float >(r);
c.mT = static_cast< float >(t);
// Reserve area memory if requested
c.mAreas.reserve(sz);
// Reset the locks on this area
c.mLocks = 0;
// Advance the left side
l = r;
// Advance the right side
r += CELLD;
// Should we advance to the next row?
if (r > (GRIDH * CELLD))
{
// Reset the left side
l = (-GRIDH * CELLD);
// Reset the right side
r = (l + CELLD);
// Advance the bottom
b -= CELLD;
// Advance the top
t -= CELLD;
}
}
}
// Reserve some space in the queue
m_Queue.reserve(128);
m_ProcList.reserve(128);
}
// ------------------------------------------------------------------------------------------------
void AreaManager::Insert(AreaCell & c, Area & a, LightObj & obj)
{
// Is this cell currently locked?
if (c.mLocks)
{
m_Queue.emplace_back(c, a, obj); // Queue this request for now
}
else
{
c.mAreas.emplace_back(&a, obj);
}
// Associate the area with this cell so it can't be managed again (even while in the queue)
a.mCells.push_back(&c);
}
// ------------------------------------------------------------------------------------------------
void AreaManager::Remove(AreaCell & c, Area & a)
{
// Is this cell currently locked?
if (c.mLocks)
{
m_Queue.emplace_back(c, a); // Queue this request for now
}
else
{
// Attempt to locate this area in the cell
AreaCell::Areas::iterator itr = std::find_if(c.mAreas.begin(), c.mAreas.end(),
[&a](AreaCell::Areas::reference p) -> bool {
return (p.first == &a);
});
// Have we found it?
if (itr != c.mAreas.end())
{
c.mAreas.erase(itr); // Erase it
}
}
// Dissociate the area with this cell so it can be managed again (even while in the queue)
Area::Cells::iterator itr = std::find(a.mCells.begin(), a.mCells.end(), &c);
// Was is associated?
if (itr != a.mCells.end())
{
a.mCells.erase(itr); // Dissociate them
}
}
// ------------------------------------------------------------------------------------------------
void AreaManager::ProcQueue()
{
// Look for actions that can be completed
for (Queue::iterator itr = m_Queue.begin(); itr != m_Queue.end(); ++itr)
{
// Was this cell unlocked in the meantime?
if (itr->mCell->mLocks <= 0)
{
m_ProcList.push_back(itr);
}
}
// Process the actions that are ready
for (auto & itr : m_ProcList)
{
// Was this a remove request?
if (itr->mObj.IsNull())
{
Remove(*(itr->mCell), *(itr->mArea));
}
else
{
Insert(*(itr->mCell), *(itr->mArea), itr->mObj);
}
}
// Remove processed requests
for (auto & itr : m_ProcList)
{
m_Queue.erase(itr);
}
// Actions were processed
m_ProcList.clear();
}
// ------------------------------------------------------------------------------------------------
void AreaManager::Clear()
{
// Clear the cells
for (AreaCell (&row)[GRIDN] : m_Grid)
{
for (AreaCell & c : row)
{
c.mAreas.clear();
}
}
// Clear the queue as well
m_Queue.clear();
m_ProcList.clear();
}
// ------------------------------------------------------------------------------------------------
void AreaManager::InsertArea(Area & a, LightObj & obj)
{
// See if this area is already managed
if (!a.mCells.empty() || a.mPoints.empty())
{
return; // Already managed or nothing to manage
}
// Go through each cell and check if the area touches it
for (int y = 0; y < GRIDN; ++y)
{
for (int x = 0; x < GRIDN; ++x)
{
AreaCell & c = m_Grid[y][x];
// Does the bounding box of this cell intersect with the one of the area?
if (a.mL <= c.mR && c.mL <= a.mR && a.mB <= c.mT && c.mB <= a.mT)
{
Insert(c, a, obj); // Attempt to insert the area into this cell
}
}
}
}
// ------------------------------------------------------------------------------------------------
void AreaManager::RemoveArea(Area & a)
{
// Just remove the associated cells
for (auto c : a.mCells)
{
Remove(*c, a);
}
}
// ------------------------------------------------------------------------------------------------
Vector2i AreaManager::LocateCell(float x, float y)
{
// Transform the world coordinates into a cell coordinates
// and cast to integral after rounding the value
int xc = static_cast< int >(std::round(x / CELLD));
int yc = static_cast< int >(std::round(y / CELLD));
// Grab the absolute cell coordinates for range checking
const int xca = std::abs(xc);
const int yca = std::abs(yc);
// Make sure the cell coordinates are within range
if (xca > (GRIDH+1) || yca > (GRIDH+1))
{
return Vector2i(NOCELL, NOCELL); // Out of our scanning area
}
// Clamp the x coordinate if necessary
if (xca >= (GRIDH))
{
xc = xc < 0 ? -(GRIDH-1) : (GRIDH-1);
}
// Clamp the y coordinate if necessary
if (yca >= (GRIDH))
{
yc = xc < 0 ? -(GRIDH-1) : (GRIDH-1);
}
// Return the identified cell row and column
return Vector2i(GRIDH+xc, GRIDH-yc);
}
// ------------------------------------------------------------------------------------------------
static void Areas_TestPointEx(Object & env, Function & func, float x, float y)
{
// Is the function valid?
if (func.IsNull())
{
STHROWF("Invalid callback object");
}
// Should we use a custom environment?
else if (!env.IsNull())
{
func = Function(env.GetVM(), env, func.GetFunc());
}
// Begin testing
AreaManager::Get().TestPoint([&func](AreaCell::Areas::reference ap) -> void {
func.Execute(ap.second);
}, x, y);
}
// ------------------------------------------------------------------------------------------------
static void Areas_TestPoint(Object & env, Function & func, const Vector2 & v)
{
Areas_TestPointEx(env, func, v.x, v.y);
}
// ------------------------------------------------------------------------------------------------
static void Areas_TestPointOnEx(Object & ctx, Object & env, Function & func, float x, float y)
{
// Is the function valid?
if (func.IsNull())
{
STHROWF("Invalid callback object");
}
// Should we use a custom environment?
else if (!env.IsNull())
{
func = Function(env.GetVM(), env, func.GetFunc());
}
// Begin testing
AreaManager::Get().TestPoint([&ctx, &func](AreaCell::Areas::reference ap) -> void {
func.Execute(ctx, ap.second);
}, x, y);
}
// ------------------------------------------------------------------------------------------------
static void Areas_TestPointOn(Object & ctx, Object & env, Function & func, const Vector2 & v)
{
Areas_TestPointOnEx(ctx, env, func, v.x, v.y);
}
// ------------------------------------------------------------------------------------------------
static Vector2i Areas_LocatePointCell(const Vector2 & v)
{
return AreaManager::Get().LocateCell(v.x, v.y);
}
// ------------------------------------------------------------------------------------------------
static Vector2i Areas_LocatePointCellEx(float x, float y)
{
return AreaManager::Get().LocateCell(x, y);
}
// ------------------------------------------------------------------------------------------------
void TerminateAreas()
{
AreaManager::Get().Clear();
}
// ================================================================================================
void Register_Areas(HSQUIRRELVM vm)
{
RootTable(vm).Bind(_SC("SqArea"),
Class< Area >(vm, AreaTypename::Str)
// Constructors
.Ctor()
.FmtCtor()
.FmtCtor< SQInteger >()
.Ctor< const Vector2 &, const Vector2 &, const Vector2 & >()
.FmtCtor< const Vector2 &, const Vector2 &, const Vector2 & >()
.FmtCtor< const Vector2 &, const Vector2 &, const Vector2 &, SQInteger >()
.Ctor< float, float, float, float, float, float >()
.FmtCtor< float, float, float, float, float, float >()
.FmtCtor< float, float, float, float, float, float, SQInteger >()
// Meta-methods
.SquirrelFunc(_SC("_typename"), &AreaTypename::Fn)
.Func(_SC("_tostring"), &Area::ToString)
// Member Properties
.Prop(_SC("Name"), &Area::GetName, &Area::SetName)
.Prop(_SC("ID"), &Area::GetID, &Area::SetID)
.Prop(_SC("Locked"), &Area::IsLocked)
.Prop(_SC("IsLocked"), &Area::IsLocked)
.Prop(_SC("Center"), &Area::GetCenter)
.Prop(_SC("Box"), &Area::GetBoundingBox, &Area::SetBoundingBox)
.Prop(_SC("Empty"), &Area::Empty)
.Prop(_SC("Empty"), &Area::Empty)
.Prop(_SC("Size"), &Area::Size)
.Prop(_SC("Points"), &Area::Size)
.Prop(_SC("Capacity"), &Area::Capacity)
// Member Methods
.FmtFunc(_SC("SetName"), &Area::ApplyName)
.Func(_SC("SetID"), &Area::ApplyID)
.Func(_SC("Clear"), &Area::Clear)
.Func(_SC("Reserve"), &Area::Reserve)
.Func(_SC("SetBox"), &Area::SetBoundingBoxEx)
.Func(_SC("SetBoundingBox"), &Area::SetBoundingBoxEx)
.Func(_SC("Add"), &Area::AddPoint)
.Func(_SC("AddEx"), &Area::AddPointEx)
.Func(_SC("AddVirtual"), &Area::AddVirtualPoint)
.Func(_SC("AddVirtualEx"), &Area::AddVirtualPointEx)
.Func(_SC("AddFake"), &Area::AddVirtualPoint)
.Func(_SC("AddFakeEx"), &Area::AddVirtualPointEx)
.Func(_SC("AddArray"), &Area::AddArray)
.Func(_SC("Test"), &Area::Test)
.Func(_SC("TestEx"), &Area::TestEx)
.Func(_SC("Manage"), &Area::Manage)
.Func(_SC("Unmanage"), &Area::Unmanage)
// Static Functions
.StaticFunc(_SC("GlobalTest"), &Areas_TestPoint)
.StaticFunc(_SC("GlobalTestEx"), &Areas_TestPointEx)
.StaticFunc(_SC("GlobalTestOn"), &Areas_TestPointOn)
.StaticFunc(_SC("GlobalTestOnEx"), &Areas_TestPointOnEx)
.StaticFunc(_SC("LocatePointCell"), &Areas_LocatePointCell)
.StaticFunc(_SC("LocatePointCellEx"), &Areas_LocatePointCellEx)
.StaticFunc(_SC("UnmanageAll"), &TerminateAreas)
);
}
} // Namespace:: SqMod

716
source/Misc/Areas.hpp Normal file
View File

@@ -0,0 +1,716 @@
#ifndef _AREAS_HPP_
#define _AREAS_HPP_
// ------------------------------------------------------------------------------------------------
#include "Base/Shared.hpp"
#include "Base/Vector2.hpp"
#include "Base/Vector4.hpp"
#include "Base/Vector2i.hpp"
// ------------------------------------------------------------------------------------------------
#include <vector>
#include <utility>
// ------------------------------------------------------------------------------------------------
namespace SqMod {
/* ------------------------------------------------------------------------------------------------
* Various information associated with an area cell.
*/
struct AreaCell
{
// --------------------------------------------------------------------------------------------
typedef std::pair< Area *, LightObj > AreaPair; // A reference to an area object.
typedef std::vector< AreaPair > Areas; // A list of area objects.
// --------------------------------------------------------------------------------------------
float mL, mB, mR, mT; // Left-Bottom, Right-Top components of the cell bounding box.
// --------------------------------------------------------------------------------------------
Areas mAreas; // Areas that intersect with the cell.
// --------------------------------------------------------------------------------------------
int mLocks; // The amount of locks on the cell.
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
AreaCell()
: mL(0), mB(0), mR(0), mT(0), mAreas(0), mLocks(0)
{
//...
}
};
/* ------------------------------------------------------------------------------------------------
* Area implementation used to store area points.
*/
struct Area
{
typedef std::vector< Vector2 > Points;
typedef std::vector< AreaCell * > Cells;
// --------------------------------------------------------------------------------------------
static constexpr float DEF_L = std::numeric_limits< float >::infinity();
static constexpr float DEF_B = std::numeric_limits< float >::infinity();
static constexpr float DEF_R = -std::numeric_limits< float >::infinity();
static constexpr float DEF_T = -std::numeric_limits< float >::infinity();
// --------------------------------------------------------------------------------------------
float mL, mB, mR, mT; // Left-Bottom, Right-Top components of the bounding box.
// --------------------------------------------------------------------------------------------
Points mPoints; // Collection of points that make up the area.
// --------------------------------------------------------------------------------------------
SQInteger mID; // The user identifier given to this area.
// --------------------------------------------------------------------------------------------
Cells mCells; // The cells covered by this area.
// --------------------------------------------------------------------------------------------
String mName; // The user name given to this area.
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
Area()
: mL(DEF_L), mB(DEF_B), mR(DEF_R), mT(DEF_T), mPoints(), mID(0), mCells(), mName()
{
//...
}
/* --------------------------------------------------------------------------------------------
* Named constructor.
*/
Area(const StackStrF & name)
: Area(16, name)
{
//...
}
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
Area(SQInteger sz, const StackStrF & name)
: mL(DEF_L), mB(DEF_B), mR(DEF_R), mT(DEF_T), mPoints(), mID(0), mCells()
, mName(name.mPtr, name.mLen <= 0 ? 0 : name.mLen)
{
// Should we reserve some space for points in advance?
if (sz > 0)
{
mPoints.reserve(static_cast< size_t >(sz));
}
}
/* --------------------------------------------------------------------------------------------
* Vector2 constructor.
*/
Area(const Vector2 & a, const Vector2 & b, const Vector2 & c)
: Area(a.x, a.y, b.x, b.y, c.x, c.y, 16, StackStrF())
{
//...
}
/* --------------------------------------------------------------------------------------------
* Vector2 constructor with name.
*/
Area(const Vector2 & a, const Vector2 & b, const Vector2 & c, const StackStrF & name)
: Area(a.x, a.y, b.x, b.y, c.x, c.y, 16, name)
{
//...
}
/* --------------------------------------------------------------------------------------------
* Vector2 constructor with name and memory to reserve.
*/
Area(const Vector2 & a, const Vector2 & b, const Vector2 & c, SQInteger sz, const StackStrF & name)
: Area(a.x, a.y, b.x, b.y, c.x, c.y, sz, name)
{
//...
}
/* --------------------------------------------------------------------------------------------
* Extended constructor.
*/
Area(float ax, float ay, float bx, float by, float cx, float cy)
: Area(ax, ay, bx, by, cx, cy, 16, StackStrF())
{
//...
}
/* --------------------------------------------------------------------------------------------
* Extended constructor with name.
*/
Area(float ax, float ay, float bx, float by, float cx, float cy, const StackStrF & name)
: Area(ax, ay, bx, by, cx, cy, 16, name)
{
//...
}
/* --------------------------------------------------------------------------------------------
* Base constructor.
*/
Area(float ax, float ay, float bx, float by, float cx, float cy, SQInteger sz, const StackStrF & name)
: mL(DEF_L), mB(DEF_B), mR(DEF_R), mT(DEF_T), mPoints(), mID(0), mCells()
, mName(name.mPtr, name.mLen <= 0 ? 0 : name.mLen)
{
// Should we reserve some space for points in advance?
if (sz > 0)
{
mPoints.reserve(static_cast< size_t >(sz));
}
// Insert the given points
AddPointEx(ax, ay);
AddPointEx(bx, by);
AddPointEx(cx, cy);
}
/* --------------------------------------------------------------------------------------------
* Copy constructor.
*/
Area(const Area & o)
: mL(o.mL), mB(o.mB), mR(o.mR), mT(o.mT), mPoints(o.mPoints), mID(o.mID), mCells(0), mName(o.mName)
{
//...
}
/* --------------------------------------------------------------------------------------------
* Move constructor. (disabled)
*/
Area(Area && o) = delete;
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
~Area()
{
//...
}
/* --------------------------------------------------------------------------------------------
* Copy assignment operator. (disabled)
*/
Area & operator = (const Area & o) = delete;
/* --------------------------------------------------------------------------------------------
* Move assignment operator. (disabled)
*/
Area & operator = (Area && o) = delete;
/* --------------------------------------------------------------------------------------------
* Used by the script engine to convert this instance to a string.
*/
const String & ToString() const
{
return mName;
}
/* --------------------------------------------------------------------------------------------
* Checks if the area is locked from changes and throws an exception if it is.
*/
void CheckLock()
{
// Are we connected to any cells?
if (!mCells.empty())
{
STHROWF("The area cannot be modified while being managed");
}
}
/* --------------------------------------------------------------------------------------------
* Retrieve the name of this area.
*/
const String & GetName() const
{
return mName;
}
/* --------------------------------------------------------------------------------------------
* Modify the name of this area.
*/
void SetName(const StackStrF & name)
{
if (name.mLen <= 0)
{
mName.clear();
}
else
{
mName.assign(name.mPtr, static_cast< size_t >(name.mLen));
}
}
/* --------------------------------------------------------------------------------------------
* Modify the name of this area. (allows chaining function calls)
*/
Area & ApplyName(const StackStrF & name)
{
SetName(name);
return *this;
}
/* --------------------------------------------------------------------------------------------
* Retrieve the identifier of this area.
*/
SQInteger GetID() const
{
return mID;
}
/* --------------------------------------------------------------------------------------------
* Modify the identifier of this area.
*/
void SetID(SQInteger id)
{
mID = id;
}
/* --------------------------------------------------------------------------------------------
* Modify the identifier of this area. (allows chaining function calls)
*/
Area & ApplyID(SQInteger id)
{
mID = id;
return *this;
}
/* --------------------------------------------------------------------------------------------
* Check if the area is locked from changes
*/
bool IsLocked() const
{
return mCells.empty();
}
/* --------------------------------------------------------------------------------------------
* Retrieve the center of this area.
*/
Vector2 GetCenter() const
{
return Vector2((mL * 0.5f) + (mR * 0.5f), (mB * 0.5f) + (mT * 0.5f));
}
/* --------------------------------------------------------------------------------------------
* Retrieve the bounding box of this area.
*/
Vector4 GetBoundingBox() const
{
return Vector4(mL, mB, mR, mT);
}
/* --------------------------------------------------------------------------------------------
* Modify the bounding box of this area.
*/
void SetBoundingBox(const Vector4 & b)
{
CheckLock();
// Apply the given bounding box
mL = b.x;
mB = b.y;
mR = b.z;
mT = b.w;
}
/* --------------------------------------------------------------------------------------------
* Modify the bounding box of this area.
*/
void SetBoundingBoxEx(float l, float b, float r, float t)
{
CheckLock();
// Apply the given bounding box
mL = l;
mB = b;
mR = r;
mT = t;
}
/* --------------------------------------------------------------------------------------------
* See whether the area has no points.
*/
bool Empty() const
{
return mPoints.empty();
}
/* --------------------------------------------------------------------------------------------
* Retrieve the number of points in this area.
*/
SQInteger Size() const
{
return ConvTo< SQInteger >::From(mPoints.size());
}
/* --------------------------------------------------------------------------------------------
* Retrieve the number of points this area has allocated for.
*/
SQInteger Capacity() const
{
return ConvTo< SQInteger >::From(mPoints.capacity());
}
/* --------------------------------------------------------------------------------------------
* Clear all points in this area.
*/
void Clear()
{
CheckLock();
// Perform the requested action
mPoints.clear();
}
/* --------------------------------------------------------------------------------------------
* Clear all points in this area.
*/
void Reserve(SQInteger sz)
{
// Perform the requested action
if (sz > 0)
{
mPoints.reserve(static_cast< size_t >(sz));
}
}
/* --------------------------------------------------------------------------------------------
* Add a 2D vector to the point list.
*/
void AddPoint(const Vector2 & v)
{
CheckLock();
// Perform the requested action
mPoints.emplace_back(v);
// Update the bounding box
Expand(v.x, v.y);
}
/* --------------------------------------------------------------------------------------------
* Add a point to the point list.
*/
void AddPointEx(float x, float y)
{
CheckLock();
// Perform the requested action
mPoints.emplace_back(x, y);
// Update the bounding box
Expand(x, y);
}
/* --------------------------------------------------------------------------------------------
* Add a 2D vector to the bounding box only. Not stored in the list.
*/
void AddVirtualPoint(const Vector2 & v)
{
CheckLock();
// Update the bounding box
Expand(v.x, v.y);
}
/* --------------------------------------------------------------------------------------------
* Add a point to the bounding box only. Not stored in the list.
*/
void AddVirtualPointEx(float x, float y)
{
CheckLock();
// Update the bounding box
Expand(x, y);
}
/* --------------------------------------------------------------------------------------------
* Add an array of points to the point list.
*/
void AddArray(const Sqrat::Array & a);
/* --------------------------------------------------------------------------------------------
* Test if a point is inside the bounding box and then the area.
*/
bool Test(const Vector2 & v)
{
// Is the given point in this bounding box at least?
if (mL <= v.x && mR >= v.x && mB <= v.y && mT >= v.y)
{
return mPoints.empty() ? true : IsInside(v.x, v.y);
}
// Not in this area
return false;
}
/* --------------------------------------------------------------------------------------------
* Test if a point is inside the bounding box and then the area.
*/
bool TestEx(float x, float y)
{
// Is the given point in this bounding box at least?
if (mL <= x && mR >= x && mB <= y && mT >= y)
{
return mPoints.empty() ? true : IsInside(x, y);
}
// Not in this area
return false;
}
/* --------------------------------------------------------------------------------------------
* Add this area to the manager to be scanned. MUST BE CALLED ONLY FROM SCRIPT!
*/
bool Manage();
/* --------------------------------------------------------------------------------------------
* Remove this area from the manager to no longer be scanned.
*/
bool Unmanage();
protected:
/* --------------------------------------------------------------------------------------------
* Test if a point is inside the area.
*/
bool IsInside(float x, float y) const;
/* --------------------------------------------------------------------------------------------
* Expand the bounding box area to include the given point.
*/
void Expand(float x, float y)
{
mL = std::fmin(mL, x);
mB = std::fmin(mB, y);
mR = std::fmax(mR, x);
mT = std::fmax(mT, y);
}
};
/* ------------------------------------------------------------------------------------------------
* Manager responsible for storing and partitioning areas.
*/
class AreaManager
{
private:
// --------------------------------------------------------------------------------------------
static AreaManager s_Inst; // Manager instance.
/* --------------------------------------------------------------------------------------------
* Base constructor.
*/
AreaManager(size_t sz = 16);
/* --------------------------------------------------------------------------------------------
* Copy constructor. (disabled)
*/
AreaManager(const AreaManager & o) = delete;
/* --------------------------------------------------------------------------------------------
* Move constructor. (disabled)
*/
AreaManager(AreaManager && o) = delete;
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
~AreaManager()
{
//...
}
/* --------------------------------------------------------------------------------------------
* Copy assignment operator. (disabled)
*/
AreaManager & operator = (const AreaManager & o) = delete;
/* --------------------------------------------------------------------------------------------
* Move assignment operator. (disabled)
*/
AreaManager & operator = (AreaManager && o) = delete;
protected:
/* --------------------------------------------------------------------------------------------
* Helper used to make sure a cell is properly processed after leaving the scope.
*/
struct CellGuard
{
AreaCell & mCell;
/* ----------------------------------------------------------------------------------------
* Base constructor.
*/
CellGuard(AreaCell & cell)
: mCell(cell)
{
++(cell.mLocks); // Place a lock on the cell to prevent iterator invalidation
}
/* ----------------------------------------------------------------------------------------
* Destructor.
*/
~CellGuard()
{
// Remove the lock from the cell so it can be processed
--(mCell.mLocks);
// Process requested actions during the lock
AreaManager::Get().ProcQueue();
}
};
// --------------------------------------------------------------------------------------------
static constexpr int GRIDN = 16; // Number of horizontal and vertical number of cells.
static constexpr int GRIDH = GRIDN/2; // Half of the grid horizontal and vertical size.
static constexpr int CELLS = GRIDN*GRIDN; // Total number of cells in the grid.
static constexpr int CELLH = CELLS/2; // Half total number of cells in the grid.
static constexpr int CELLD = 256; // Area covered by a cell in the world.
static constexpr int NOCELL = std::numeric_limits< int >::max(); // Inexistent cell index.
/* --------------------------------------------------------------------------------------------
* Helper used to queue a certain action if the cell is locked.
*/
struct QueueElement
{
// ----------------------------------------------------------------------------------------
AreaCell * mCell; // The cell to be affected.
Area * mArea; // The area that made the request.
LightObj mObj; // Strong reference to the object.
/* ----------------------------------------------------------------------------------------
* Base constructor.
*/
QueueElement(AreaCell & cell, Area & area)
: mCell(&cell), mArea(&area), mObj()
{
//...
}
/* ----------------------------------------------------------------------------------------
* Base constructor.
*/
QueueElement(AreaCell & cell, Area & area, LightObj & obj)
: mCell(&cell), mArea(&area), mObj(obj)
{
//...
}
/* --------------------------------------------------------------------------------------------
* Copy constructor. (disabled)
*/
QueueElement(const QueueElement & o) = delete;
/* --------------------------------------------------------------------------------------------
* Move constructor.
*/
QueueElement(QueueElement && o)
: mCell(o.mCell), mArea(o.mArea), mObj(std::move(o.mObj))
{
// Take ownership
o.mCell = nullptr;
o.mArea = nullptr;
}
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
~QueueElement()
{
//...
}
/* --------------------------------------------------------------------------------------------
* Copy assignment operator. (disabled)
*/
QueueElement & operator = (const QueueElement & o) = delete;
/* --------------------------------------------------------------------------------------------
* Move assignment operator.
*/
QueueElement & operator = (QueueElement && o)
{
// Avoid self assignment
if (this != &o)
{
// Transfer values
mCell = o.mCell;
mArea = o.mArea;
mObj = std::move(o.mObj);
// Take ownership
o.mCell = nullptr;
o.mArea = nullptr;
}
return *this;
}
};
// --------------------------------------------------------------------------------------------
typedef std::vector< QueueElement > Queue; // Queued actions.
typedef std::vector< Queue::iterator > ProcList; // Elements in the queue redy to process.
/* --------------------------------------------------------------------------------------------
* Attempt to insert an area into a cell or queue the action if not possible.
*/
void Insert(AreaCell & c, Area & a, LightObj & obj);
/* --------------------------------------------------------------------------------------------
* Attempt to remove an area from a cell or queue the action if not possible.
*/
void Remove(AreaCell & c, Area & a);
private:
// --------------------------------------------------------------------------------------------
Queue m_Queue; // Actions currently queued.
ProcList m_ProcList; // Actions ready to be completed.
// --------------------------------------------------------------------------------------------
AreaCell m_Grid[GRIDN][GRIDN]; // A grid of area lists.
public:
/* --------------------------------------------------------------------------------------------
* Retrieve the core instance.
*/
static AreaManager & Get()
{
return s_Inst;
}
/* --------------------------------------------------------------------------------------------
* Attempt to process elements in the queue that can be completed.
*/
void ProcQueue();
/* --------------------------------------------------------------------------------------------
* Clear all cell lists and release any script references.
*/
void Clear();
/* --------------------------------------------------------------------------------------------
* Add an area to be managed.
*/
void InsertArea(Area & a, LightObj & obj);
/* --------------------------------------------------------------------------------------------
* Add an area to be managed.
*/
void RemoveArea(Area & a);
/* --------------------------------------------------------------------------------------------
* Clear all cell lists and release any script references.
*/
Vector2i LocateCell(float x, float y);
/* --------------------------------------------------------------------------------------------
* Test a point to see whether it intersects with any areas
*/
template < typename F > void TestPoint(F && f, float x, float y)
{
// Transform the world coordinates into a cell coordinates
const Vector2i cc(LocateCell(x, y));
// Were these coordinates valid?
if (cc.x == NOCELL)
{
return; // Not our problem
}
// Retrieve a reference to the identified cell
AreaCell & c = m_Grid[cc.y][cc.x];
// Is this cell empty?
if (c.mAreas.empty())
{
return; // Nothing to test
}
// Guard the cell while processing
const CellGuard cg(c);
// Finally, begin processing the areas in this cell
for (auto & a : c.mAreas)
{
if (a.first->TestEx(x, y))
{
f(a);
}
}
}
};
} // Namespace:: SqMod
#endif // _AREAS_HPP_

1178
source/Misc/Command.cpp Normal file

File diff suppressed because it is too large Load Diff

2003
source/Misc/Command.hpp Normal file

File diff suppressed because it is too large Load Diff

1163
source/Misc/Constants.cpp Normal file

File diff suppressed because it is too large Load Diff

926
source/Misc/Exports.cpp Normal file
View File

@@ -0,0 +1,926 @@
// ------------------------------------------------------------------------------------------------
#include "Core.hpp"
#include "Base/Buffer.hpp"
// ------------------------------------------------------------------------------------------------
#include "Library/Numeric/LongInt.hpp"
#include "Library/Chrono/Date.hpp"
#include "Library/Chrono/Time.hpp"
#include "Library/Chrono/Datetime.hpp"
#include "Library/Chrono/Timestamp.hpp"
#include "Library/Utils/Buffer.hpp"
// ------------------------------------------------------------------------------------------------
#include <cmath>
#include <cstdlib>
#include <cstring>
// ------------------------------------------------------------------------------------------------
#include <sqstdio.h>
#include <sqstdblob.h>
#include <sqstdstring.h>
#include <SqMod.h>
// ------------------------------------------------------------------------------------------------
namespace SqMod {
// ------------------------------------------------------------------------------------------------
static HSQUIRRELVM GetSquirrelVM()
{
return Core::Get().GetVM();
}
// ------------------------------------------------------------------------------------------------
static SQRESULT SqModImpl_LoadScript(const SQChar * filepath, SQBool delay)
{
// Attempt to add the specified script to the load queue
if (Core::Get().LoadScript(filepath, delay))
{
return SQ_OK; // The script as added or already existed
}
// The path was invalid or was unable to pool the script
return SQ_ERROR;
}
// ------------------------------------------------------------------------------------------------
static SQRESULT SqModImpl_GetSLongValue(HSQUIRRELVM vm, SQInteger idx, Int64 * num)
{
// Validate the specified number pointer and value type
if (!num)
{
return SQ_ERROR; // Nowhere to save!
}
// Is this an instance that we can treat as a SLongInt type?
else if (sq_gettype(vm, idx) == OT_INSTANCE)
{
// Attempt to obtain the long instance and it's value from the stack
try
{
*num = static_cast< Int64 >(Var< const SLongInt & >(vm, idx).value.GetNum());
}
catch (...)
{
return SQ_ERROR; // Unable to obtain the value!
}
}
// Is this a pure integer value?
else if(sq_gettype(vm, idx) == OT_INTEGER)
{
SQInteger val = 0;
// Attempt to get the value from the stack
sq_getinteger(vm, idx, &val);
// Save it into the specified memory location
*num = static_cast< Int64 >(val);
}
// Is this a pure floating point value?
else if(sq_gettype(vm, idx) == OT_FLOAT)
{
SQFloat val = 0.0;
// Attempt to get the value from the stack
sq_getfloat(vm, idx, &val);
// Save it into the specified memory location
*num = static_cast< Int64 >(std::llround(val));
}
// Is this a pure boolean value?
else if(sq_gettype(vm, idx) == OT_BOOL)
{
SQBool val = SQFalse;
// Attempt to get the value from the stack
sq_getbool(vm, idx, &val);
// Save it into the specified memory location
*num = static_cast< Int64 >(val);
}
// Unrecognized value
else
{
return SQ_ERROR;
}
// Value retrieved
return SQ_OK;
}
// ------------------------------------------------------------------------------------------------
static SQRESULT SqModImpl_PushSLongObject(HSQUIRRELVM vm, Int64 num)
{
// Attempt to push the requested instance
try
{
Var< const SLongInt & >::push(vm, SLongInt(num));
}
catch (...)
{
// Specify that we failed
return SQ_ERROR;
}
// Specify that we succeeded
return SQ_OK;
}
// ------------------------------------------------------------------------------------------------
static SQRESULT SqModImpl_GetULongValue(HSQUIRRELVM vm, SQInteger idx, Uint64 * num)
{
// Validate the specified number pointer and value type
if (!num)
{
return SQ_ERROR; // Nowhere to save
}
// Is this an instance that we can treat as a ULongInt type?
else if (sq_gettype(vm, idx) == OT_INSTANCE)
{
// Attempt to obtain the long instance and it's value from the stack
try
{
*num = static_cast< Uint64 >(Var< const ULongInt & >(vm, idx).value.GetNum());
}
catch (...)
{
return SQ_ERROR; // Unable to obtain the value!
}
}
// Is this a pure integer value?
else if(sq_gettype(vm, idx) == OT_INTEGER)
{
SQInteger val = 0;
// Attempt to get the value from the stack
sq_getinteger(vm, idx, &val);
// Save it into the specified memory location
*num = val ? static_cast< Uint64 >(val) : 0L;
}
// Is this a pure floating point value?
else if(sq_gettype(vm, idx) == OT_FLOAT)
{
SQFloat val = 0.0;
// Attempt to get the value from the stack
sq_getfloat(vm, idx, &val);
// Save it into the specified memory location
*num = EpsLt(val, SQFloat(0.0)) ? 0L : static_cast< Uint64 >(std::llround(val));
}
// Is this a pure boolean value?
else if(sq_gettype(vm, idx) == OT_BOOL)
{
SQBool val = SQFalse;
// Attempt to get the value from the stack
sq_getbool(vm, idx, &val);
// Save it into the specified memory location
*num = static_cast< Uint64 >(val);
}
// Unrecognized value
else
{
return SQ_ERROR;
}
// Value retrieved
return SQ_OK;
}
// ------------------------------------------------------------------------------------------------
static SQRESULT SqModImpl_PushULongObject(HSQUIRRELVM vm, Uint64 num)
{
// Attempt to push the requested instance
try
{
Var< const ULongInt & >::push(vm, ULongInt(num));
}
catch (...)
{
// Specify that we failed
return SQ_ERROR;
}
// Specify that we succeeded
return SQ_OK;
}
// ------------------------------------------------------------------------------------------------
SQBool SqModImpl_ValidDate(uint16_t year, uint8_t month, uint8_t day)
{
return Chrono::ValidDate(year, month, day);
}
// ------------------------------------------------------------------------------------------------
SQBool SqModImpl_IsLeapYear(uint16_t year)
{
return Chrono::IsLeapYear(year);
}
// ------------------------------------------------------------------------------------------------
static SQRESULT SqModImpl_GetTimestamp(HSQUIRRELVM vm, SQInteger idx, Int64 * num)
{
// Validate the specified number pointer and value type
if (!num)
{
return SQ_ERROR; // Nowhere to save
}
// Is this an instance that we can treat as a Time-stamp type?
else if (sq_gettype(vm, idx) == OT_INSTANCE)
{
// Attempt to obtain the time-stamp and it's value from the stack
try
{
*num = static_cast< Int64 >(Var< const Timestamp & >(vm, idx).value.GetNum());
}
catch (...)
{
return SQ_ERROR; // Unable to obtain the value!
}
}
// Is this a pure integer value?
else if(sq_gettype(vm, idx) == OT_INTEGER)
{
SQInteger val = 0;
// Attempt to get the value from the stack
sq_getinteger(vm, idx, &val);
// Save it into the specified memory location
*num = static_cast< Int64 >(val);
}
// Is this a pure floating point value?
else if(sq_gettype(vm, idx) == OT_FLOAT)
{
SQFloat val = 0.0;
// Attempt to get the value from the stack
sq_getfloat(vm, idx, &val);
// Save it into the specified memory location
*num = static_cast< Int64 >(std::llround(val));
}
// Is this a pure boolean value?
else if(sq_gettype(vm, idx) == OT_BOOL)
{
SQBool val = SQFalse;
// Attempt to get the value from the stack
sq_getbool(vm, idx, &val);
// Save it into the specified memory location
*num = static_cast< Int64 >(val);
}
// Unrecognized value
else
{
return SQ_ERROR;
}
// Value retrieved
return SQ_OK;
}
// ------------------------------------------------------------------------------------------------
static SQRESULT SqModImpl_PushTimestamp(HSQUIRRELVM vm, Int64 num)
{
// Attempt to push the requested instance
try
{
Var< const Timestamp & >::push(vm, Timestamp(num));
}
catch (...)
{
// Specify that we failed
return SQ_ERROR;
}
// Specify that we succeeded
return SQ_OK;
}
// ------------------------------------------------------------------------------------------------
SQRESULT SqModImpl_GetDate(HSQUIRRELVM vm, SQInteger idx, uint16_t * year, uint8_t * month, uint8_t * day)
{
// Is this an instance that we can treat as a Date type?
if (sq_gettype(vm, idx) == OT_INSTANCE)
{
// Attempt to obtain the time-stamp and it's value from the stack
try
{
// Attempt to retrieve the instance
Var< Date * > var(vm, idx);
// Assign the year
if (year != nullptr)
{
*year = var.value->GetYear();
}
// Assign the month
if (month != nullptr)
{
*month = var.value->GetMonth();
}
// Assign the day
if (day != nullptr)
{
*day = var.value->GetDay();
}
}
catch (...)
{
return SQ_ERROR; // Unable to obtain the value!
}
}
// Unrecognized value
else
{
return SQ_ERROR;
}
// Value retrieved
return SQ_OK;
}
// ------------------------------------------------------------------------------------------------
SQRESULT SqModImpl_PushDate(HSQUIRRELVM vm, uint16_t year, uint8_t month, uint8_t day)
{
// Attempt to push the requested instance
try
{
Var< const Date & >::push(vm, Date(year, month, day));
}
catch (...)
{
// Specify that we failed
return SQ_ERROR;
}
// Specify that we succeeded
return SQ_OK;
}
// ------------------------------------------------------------------------------------------------
SQRESULT SqModImpl_GetTime(HSQUIRRELVM vm, SQInteger idx, uint8_t * hour, uint8_t * minute,
uint8_t * second, uint16_t * millisecond)
{
// Is this an instance that we can treat as a Time type?
if (sq_gettype(vm, idx) == OT_INSTANCE)
{
// Attempt to obtain the time-stamp and it's value from the stack
try
{
// Attempt to retrieve the instance
Var< Time * > var(vm, idx);
// Assign the hour
if (hour != nullptr)
{
*hour = var.value->GetHour();
}
// Assign the minute
if (minute != nullptr)
{
*minute = var.value->GetMinute();
}
// Assign the second
if (second != nullptr)
{
*second = var.value->GetSecond();
}
// Assign the millisecond
if (millisecond != nullptr)
{
*millisecond = var.value->GetMillisecond();
}
}
catch (...)
{
return SQ_ERROR; // Unable to obtain the value!
}
}
// Unrecognized value
else
{
return SQ_ERROR;
}
// Value retrieved
return SQ_OK;
}
// ------------------------------------------------------------------------------------------------
SQRESULT SqModImpl_PushTime(HSQUIRRELVM vm, uint8_t hour, uint8_t minute, uint8_t second,
uint16_t millisecond)
{
// Attempt to push the requested instance
try
{
Var< const Time & >::push(vm, Time(hour, minute, second, millisecond));
}
catch (...)
{
// Specify that we failed
return SQ_ERROR;
}
// Specify that we succeeded
return SQ_OK;
}
// ------------------------------------------------------------------------------------------------
SQRESULT SqModImpl_GetDatetime(HSQUIRRELVM vm, SQInteger idx, uint16_t * year, uint8_t * month, uint8_t * day,
uint8_t * hour, uint8_t * minute, uint8_t * second, uint16_t * millisecond)
{
// Is this an instance that we can treat as a Date-time type?
if (sq_gettype(vm, idx) == OT_INSTANCE)
{
// Attempt to obtain the time-stamp and it's value from the stack
try
{
// Attempt to retrieve the instance
Var< Datetime * > var(vm, idx);
// Assign the year
if (year != nullptr)
{
*year = var.value->GetYear();
}
// Assign the month
if (month != nullptr)
{
*month = var.value->GetMonth();
}
// Assign the day
if (day != nullptr)
{
*day = var.value->GetDay();
}
// Assign the hour
if (hour != nullptr)
{
*hour = var.value->GetHour();
}
// Assign the minute
if (minute != nullptr)
{
*minute = var.value->GetMinute();
}
// Assign the second
if (second != nullptr)
{
*second = var.value->GetSecond();
}
// Assign the millisecond
if (millisecond != nullptr)
{
*millisecond = var.value->GetMillisecond();
}
}
catch (...)
{
return SQ_ERROR; // Unable to obtain the value!
}
}
// Unrecognized value
else
{
return SQ_ERROR;
}
// Value retrieved
return SQ_OK;
}
// ------------------------------------------------------------------------------------------------
SQRESULT SqModImpl_PushDatetime(HSQUIRRELVM vm, uint16_t year, uint8_t month, uint8_t day,
uint8_t hour, uint8_t minute, uint8_t second, uint16_t millisecond)
{
// Attempt to push the requested instance
try
{
Var< const Datetime & >::push(vm, Datetime(year, month, day, hour, minute, second, millisecond));
}
catch (...)
{
// Specify that we failed
return SQ_ERROR;
}
// Specify that we succeeded
return SQ_OK;
}
// ------------------------------------------------------------------------------------------------
SQRESULT SqModImpl_PushBuffer(HSQUIRRELVM vm, SQInteger size, SQInteger cursor)
{
// Attempt to push the requested instance
try
{
Var< const SqBuffer & >::push(vm, SqBuffer(size, cursor));
}
catch (...)
{
// Specify that we failed
return SQ_ERROR;
}
// Specify that we succeeded
return SQ_OK;
}
// ------------------------------------------------------------------------------------------------
SQRESULT SqModImpl_PushBufferData(HSQUIRRELVM vm, const char * data, SQInteger size, SQInteger cursor)
{
// Attempt to push the requested instance
try
{
Var< const SqBuffer & >::push(vm, SqBuffer(data, size, cursor));
}
catch (...)
{
// Specify that we failed
return SQ_ERROR;
}
// Specify that we succeeded
return SQ_OK;
}
// ------------------------------------------------------------------------------------------------
SQRESULT SqModImpl_GetBufferInfo(HSQUIRRELVM vm, SQInteger idx, const char ** ptr, SQInteger * size, SQInteger * cursor)
{
// Attempt to obtain the requested information
try
{
// Attempt to retrieve the instance
Var< SqBuffer * > var(vm, idx);
// Validate the obtained buffer
if (!(var.value) || !(var.value->GetRef()) || !(*var.value->GetRef()))
{
// Should we obtain the buffer contents?
if (ptr)
{
*ptr = nullptr; // Default to null data
}
// Should we obtain the buffer length?
if (size)
{
*size = 0; // Default to 0 length
}
// Should we obtain the cursor position?
if (cursor)
{
*cursor = 0; // Default to position 0
}
}
// Grab the internal buffer
const Buffer & b = *var.value->GetRef();
// Should we obtain the buffer contents?
if (ptr)
{
*ptr = b.Data();
}
// Should we obtain the buffer length?
if (size)
{
*size = ConvTo< SQInteger >::From(b.Capacity());
}
// Should we obtain the cursor position?
if (cursor)
{
*cursor = ConvTo< SQInteger >::From(b.Position());
}
}
catch (...)
{
// Specify that we failed
return SQ_ERROR;
}
// Specify that we succeeded
return SQ_OK;
}
// ------------------------------------------------------------------------------------------------
const char * SqModImpl_GetBufferData(HSQUIRRELVM vm, SQInteger idx)
{
// Attempt to obtain the requested information
try
{
// Attempt to retrieve the instance
Var< SqBuffer * > var(vm, idx);
// Validate the obtained buffer and return the requested information
if ((var.value) && (var.value->GetRef()) && (*var.value->GetRef()))
{
return var.value->GetRef()->Data();
}
}
catch (...)
{
// Just ignore it...
}
// Specify that we failed
return nullptr;
}
// ------------------------------------------------------------------------------------------------
SQInteger SqModImpl_GetBufferSize(HSQUIRRELVM vm, SQInteger idx)
{
// Attempt to obtain the requested information
try
{
// Attempt to retrieve the instance
Var< SqBuffer * > var(vm, idx);
// Validate the obtained buffer and return the requested information
if ((var.value) && (var.value->GetRef()) && (*var.value->GetRef()))
{
return ConvTo< SQInteger >::From(var.value->GetRef()->Capacity());
}
}
catch (...)
{
// Just ignore it...
}
// Specify that we failed
return -1;
}
// ------------------------------------------------------------------------------------------------
SQInteger SqModImpl_GetBufferCursor(HSQUIRRELVM vm, SQInteger idx)
{
// Attempt to obtain the requested information
try
{
// Attempt to retrieve the instance
Var< SqBuffer * > var(vm, idx);
// Validate the obtained buffer and return the requested information
if ((var.value) && (var.value->GetRef()) && (*var.value->GetRef()))
{
return ConvTo< SQInteger >::From(var.value->GetRef()->Position());
}
}
catch (...)
{
// Just ignore it...
}
// Specify that we failed
return -1;
}
// ------------------------------------------------------------------------------------------------
static int32_t SqExport_PopulateModuleAPI(HSQMODAPI api, size_t size)
{
if (!api)
{
return 0; // Nothing to populate!
}
else if (size != sizeof(SQMODAPI))
{
return -1; // Incompatible API!
}
//primitive functions
api->GetSquirrelVM = GetSquirrelVM;
//logging utilities
api->LogDbg = LogDbg;
api->LogUsr = LogUsr;
api->LogScs = LogScs;
api->LogInf = LogInf;
api->LogWrn = LogWrn;
api->LogErr = LogErr;
api->LogFtl = LogFtl;
api->LogSDbg = LogSDbg;
api->LogSUsr = LogSUsr;
api->LogSScs = LogSScs;
api->LogSInf = LogSInf;
api->LogSWrn = LogSWrn;
api->LogSErr = LogSErr;
api->LogSFtl = LogSFtl;
//script loading
api->LoadScript = SqModImpl_LoadScript;
//numeric utilities
api->GetSLongValue = SqModImpl_GetSLongValue;
api->PushSLongObject = SqModImpl_PushSLongObject;
api->GetULongValue = SqModImpl_GetULongValue;
api->PushULongObject = SqModImpl_PushULongObject;
//time utilities
api->GetCurrentSysTime = Chrono::GetCurrentSysTime;
api->GetEpochTimeMicro = Chrono::GetEpochTimeMicro;
api->GetEpochTimeMilli = Chrono::GetEpochTimeMilli;
api->ValidDate = SqModImpl_ValidDate;
api->IsLeapYear = SqModImpl_IsLeapYear;
api->DaysInYear = Chrono::DaysInYear;
api->DaysInMonth = Chrono::DaysInMonth;
api->DayOfYear = Chrono::DayOfYear;
api->DateRangeToSeconds = Chrono::DateRangeToSeconds;
api->GetTimestamp = SqModImpl_GetTimestamp;
api->PushTimestamp = SqModImpl_PushTimestamp;
api->GetDate = SqModImpl_GetDate;
api->PushDate = SqModImpl_PushDate;
api->GetTime = SqModImpl_GetTime;
api->PushTime = SqModImpl_PushTime;
api->GetDatetime = SqModImpl_GetDatetime;
api->PushDatetime = SqModImpl_PushDatetime;
//stack utilities
api->PopStackInteger = PopStackInteger;
api->PopStackFloat = PopStackFloat;
api->PopStackSLong = PopStackSLong;
api->PopStackULong = PopStackULong;
//buffer utilities
api->PushBuffer = SqModImpl_PushBuffer;
api->PushBufferData = SqModImpl_PushBufferData;
api->GetBufferInfo = SqModImpl_GetBufferInfo;
api->GetBufferData = SqModImpl_GetBufferData;
api->GetBufferSize = SqModImpl_GetBufferSize;
api->GetBufferCursor = SqModImpl_GetBufferCursor;
return 1; // Successfully populated!
}
// ------------------------------------------------------------------------------------------------
static int32_t SqExport_PopulateSquirrelAPI(HSQLIBAPI api, size_t size)
{
if (!api)
{
return 0; // Nothing to populate!
}
else if (size != sizeof(SQLIBAPI))
{
return -1; // Incompatible API!
}
//vm
api->open = sq_open;
api->newthread = sq_newthread;
api->seterrorhandler = sq_seterrorhandler;
api->close = sq_close;
api->setforeignptr = sq_setforeignptr;
api->getforeignptr = sq_getforeignptr;
api->setsharedforeignptr = sq_setsharedforeignptr;
api->getsharedforeignptr = sq_getsharedforeignptr;
api->setvmreleasehook = sq_setvmreleasehook;
api->getvmreleasehook = sq_getvmreleasehook;
api->setsharedreleasehook = sq_setsharedreleasehook;
api->getsharedreleasehook = sq_getsharedreleasehook;
api->setprintfunc = sq_setprintfunc;
api->getprintfunc = sq_getprintfunc;
api->geterrorfunc = sq_geterrorfunc;
api->suspendvm = sq_suspendvm;
api->wakeupvm = sq_wakeupvm;
api->getvmstate = sq_getvmstate;
api->getversion = sq_getversion;
//compiler
api->compile = sq_compile;
api->compilebuffer = sq_compilebuffer;
api->enabledebuginfo = sq_enabledebuginfo;
api->notifyallexceptions = sq_notifyallexceptions;
api->setcompilererrorhandler = sq_setcompilererrorhandler;
//stack operations
api->push = sq_push;
api->pop = sq_pop;
api->poptop = sq_poptop;
api->remove = sq_remove;
api->gettop = sq_gettop;
api->settop = sq_settop;
api->reservestack = sq_reservestack;
api->cmp = sq_cmp;
api->move = sq_move;
//object creation handling
api->newuserdata = sq_newuserdata;
api->newtable = sq_newtable;
api->newtableex = sq_newtableex;
api->newarray = sq_newarray;
api->newclosure = sq_newclosure;
api->setparamscheck = sq_setparamscheck;
api->bindenv = sq_bindenv;
api->setclosureroot = sq_setclosureroot;
api->getclosureroot = sq_getclosureroot;
api->pushstring = sq_pushstring;
api->pushfloat = sq_pushfloat;
api->pushinteger = sq_pushinteger;
api->pushbool = sq_pushbool;
api->pushuserpointer = sq_pushuserpointer;
api->pushnull = sq_pushnull;
api->pushthread = sq_pushthread;
api->gettype = sq_gettype;
api->typeof_ = sq_typeof;
api->getsize = sq_getsize;
api->gethash = sq_gethash;
api->getbase = sq_getbase;
api->instanceof = sq_instanceof;
api->tostring = sq_tostring;
api->tobool = sq_tobool;
api->getstringandsize = sq_getstringandsize;
api->getstring = sq_getstring;
api->getinteger = sq_getinteger;
api->getfloat = sq_getfloat;
api->getbool = sq_getbool;
api->getthread = sq_getthread;
api->getuserpointer = sq_getuserpointer;
api->getuserdata = sq_getuserdata;
api->settypetag = sq_settypetag;
api->gettypetag = sq_gettypetag;
api->setreleasehook = sq_setreleasehook;
api->getreleasehook = sq_getreleasehook;
api->getscratchpad = sq_getscratchpad;
api->getfunctioninfo = sq_getfunctioninfo;
api->getclosureinfo = sq_getclosureinfo;
api->getclosurename = sq_getclosurename;
api->setnativeclosurename = sq_setnativeclosurename;
api->setinstanceup = sq_setinstanceup;
api->getinstanceup = sq_getinstanceup;
api->setclassudsize = sq_setclassudsize;
api->newclass = sq_newclass;
api->createinstance = sq_createinstance;
api->setattributes = sq_setattributes;
api->getattributes = sq_getattributes;
api->getclass = sq_getclass;
api->weakref = sq_weakref;
api->getdefaultdelegate = sq_getdefaultdelegate;
api->getmemberhandle = sq_getmemberhandle;
api->getbyhandle = sq_getbyhandle;
api->setbyhandle = sq_setbyhandle;
//object manipulation
api->pushroottable = sq_pushroottable;
api->pushregistrytable = sq_pushregistrytable;
api->pushconsttable = sq_pushconsttable;
api->setroottable = sq_setroottable;
api->setconsttable = sq_setconsttable;
api->newslot = sq_newslot;
api->deleteslot = sq_deleteslot;
api->set = sq_set;
api->get = sq_get;
api->rawget = sq_rawget;
api->rawset = sq_rawset;
api->rawdeleteslot = sq_rawdeleteslot;
api->newmember = sq_newmember;
api->rawnewmember = sq_rawnewmember;
api->arrayappend = sq_arrayappend;
api->arraypop = sq_arraypop;
api->arrayresize = sq_arrayresize;
api->arrayreverse = sq_arrayreverse;
api->arrayremove = sq_arrayremove;
api->arrayinsert = sq_arrayinsert;
api->setdelegate = sq_setdelegate;
api->getdelegate = sq_getdelegate;
api->clone = sq_clone;
api->setfreevariable = sq_setfreevariable;
api->next = sq_next;
api->getweakrefval = sq_getweakrefval;
api->clear = sq_clear;
//calls
api->call = sq_call;
api->resume = sq_resume;
api->getlocal = sq_getlocal;
api->getcallee = sq_getcallee;
api->getfreevariable = sq_getfreevariable;
api->throwerror = sq_throwerror;
api->throwobject = sq_throwobject;
api->reseterror = sq_reseterror;
api->getlasterror = sq_getlasterror;
//raw object handling
api->getstackobj = sq_getstackobj;
api->pushobject = sq_pushobject;
api->addref = sq_addref;
api->release = sq_release;
api->getrefcount = sq_getrefcount;
api->resetobject = sq_resetobject;
api->objtostring = sq_objtostring;
api->objtobool = sq_objtobool;
api->objtointeger = sq_objtointeger;
api->objtofloat = sq_objtofloat;
api->objtouserpointer = sq_objtouserpointer;
api->getobjtypetag = sq_getobjtypetag;
api->getvmrefcount = sq_getvmrefcount;
//GC
api->collectgarbage = sq_collectgarbage;
api->resurrectunreachable = sq_resurrectunreachable;
//serialization
api->writeclosure = sq_writeclosure;
api->readclosure = sq_readclosure;
//mem allocation
api->malloc = sq_malloc;
api->realloc = sq_realloc;
api->free = sq_free;
//debug
api->stackinfos = sq_stackinfos;
api->setdebughook = sq_setdebughook;
api->setnativedebughook = sq_setnativedebughook;
//compiler helpers
api->loadfile = sqstd_loadfile;
api->dofile = sqstd_dofile;
api->writeclosuretofile = sqstd_writeclosuretofile;
//blob
api->createblob = sqstd_createblob;
api->getblob = sqstd_getblob;
api->getblobsize = sqstd_getblobsize;
//string
api->format = sqstd_format;
return 1; // Successfully populated!
}
// ------------------------------------------------------------------------------------------------
static const SQMODEXPORTS g_SqModExports{
sizeof(SQMODEXPORTS),
SqExport_PopulateModuleAPI,
SqExport_PopulateSquirrelAPI
};
// ------------------------------------------------------------------------------------------------
void InitExports()
{
// The server needs a pointer to a pointer, and a persistent one
static const SQMODEXPORTS * sqmodexports = &g_SqModExports;
// Tell the server about the pointer to the exports structure
_Func->ExportFunctions(_Info->pluginId, reinterpret_cast< const void ** >(&sqmodexports),
sizeof(HSQMODEXPORTS));
}
} // Namespace:: SqMod

307
source/Misc/Routine.cpp Normal file
View File

@@ -0,0 +1,307 @@
// ------------------------------------------------------------------------------------------------
#include "Routine.hpp"
#include "Library/Chrono.hpp"
// ------------------------------------------------------------------------------------------------
#include <cstdio>
#include <cstdlib>
#include <cstring>
// ------------------------------------------------------------------------------------------------
#include <utility>
// ------------------------------------------------------------------------------------------------
namespace SqMod {
// ------------------------------------------------------------------------------------------------
SQMODE_DECL_TYPENAME(Typename, _SC("SqRoutineBase"))
// ------------------------------------------------------------------------------------------------
Routine::Time Routine::s_Last = 0;
Routine::Time Routine::s_Prev = 0;
Routine::Interval Routine::s_Intervals[SQMOD_MAX_ROUTINES];
Routine::Instance Routine::s_Instances[SQMOD_MAX_ROUTINES];
// ------------------------------------------------------------------------------------------------
void Routine::Process()
{
// Is this the first call?
if (s_Last == 0)
{
s_Last = Chrono::GetCurrentSysTime();
// We'll do it text time
return;
}
// Backup the last known time-stamp
s_Prev = s_Last;
// Get the current time-stamp
s_Last = Chrono::GetCurrentSysTime();
// Calculate the elapsed time
const Int32 delta = Int32((s_Last - s_Prev) / 1000L);
// Process all active routines
for (Interval * itr = s_Intervals; itr != (s_Intervals + SQMOD_MAX_ROUTINES); ++itr)
{
// Is this routine valid?
if (*itr)
{
// Decrease the elapsed time
(*itr) -= delta;
// Have we completed the routine interval?
if ((*itr) <= 0)
{
// Execute and reset the elapsed time
(*itr) = s_Instances[itr - s_Intervals].Execute();
}
}
}
}
// ------------------------------------------------------------------------------------------------
void Routine::Initialize()
{
std::memset(s_Intervals, 0, sizeof(s_Intervals));
}
// ------------------------------------------------------------------------------------------------
void Routine::Deinitialize()
{
// Release any script resources that the routines might store
for (auto & r : s_Instances)
{
r.Terminate();
}
}
// ------------------------------------------------------------------------------------------------
SQInteger Routine::Create(HSQUIRRELVM vm)
{
// Locate the identifier of a free slot
const SQInteger slot = FindUnused();
// See if we have where to store this routine
if (slot < 0)
{
return sq_throwerror(vm, "Reached the maximum number of active routines");
}
// Grab the top of the stack
const SQInteger top = sq_gettop(vm);
// See if too many arguments were specified
if (top >= 20) /* 5 base + 14 parameters = 19 */
{
return sq_throwerror(vm, "Too many parameters specified");
}
// Was there was an environment specified?
else if (top <= 1)
{
return sq_throwerror(vm, "Missing routine environment");
}
// Was there was a callback specified?
else if (top <= 2)
{
return sq_throwerror(vm, "Missing routine callback");
}
// Validate the callback type
else if (sq_gettype(vm, 3) != OT_CLOSURE && sq_gettype(vm, 3) != OT_NATIVECLOSURE)
{
return sq_throwerror(vm, "Invalid callback type");
}
SQRESULT res = SQ_OK;
// Prepare an object for the environment
HSQOBJECT env;
// Is the specified environment a null value?
if (sq_gettype(vm, 2) == OT_NULL)
{
// Preserve the stack state
const StackGuard sg(vm);
// Push the root table on the stack
sq_pushroottable(vm);
// Attempt to retrieve the table object
res = sq_getstackobj(vm, -1, &env);
}
else
{
sq_getstackobj(vm, 2, &env); // Just retrieve the specified environment
}
// Validate the result
if (SQ_FAILED(res))
{
return res; // Propagate the error
}
// Prepare an object for the function
HSQOBJECT func;
// Fetch the specified callback object
res = sq_getstackobj(vm, 3, &func);
// Validate the result
if (SQ_FAILED(res))
{
return res; // Propagate the error
}
// The number of iterations and interval to execute the routine
SQInteger intrv = 0, itr = 0;
// Was there an interval specified?
if (top > 3)
{
// Grab the interval from the stack
res = sq_getinteger(vm, 4, &intrv);
// Validate the result
if (SQ_FAILED(res))
{
return res; // Propagate the error
}
}
// Was there a number of iterations specified?
if (top > 4)
{
// Grab the iterations from the stack
res = sq_getinteger(vm, 5, &itr);
// Validate the result
if (SQ_FAILED(res))
{
return res; // Propagate the error
}
}
// Attempt to create a routine instance
try
{
ClassType< Routine >::PushInstance(vm, new Routine());
}
catch (const Sqrat::Exception & e)
{
return sq_throwerror(vm, "Unable to create the routine instance");
}
// Prepare an object for the routine
HSQOBJECT obj;
// Fetch the created routine object
res = sq_getstackobj(vm, -1, &obj);
// Validate the result
if (SQ_FAILED(res))
{
return res; // Propagate the error
}
// At this point we can grab a reference to our slot
Instance & inst = s_Instances[slot];
// Were there any arguments specified?
if (top > 5)
{
// Grab a pointer to the arguments array
Argument * args = inst.mArgv;
// Reset the argument counter
inst.mArgc = 0;
// Grab the specified arguments from the stack
for (SQInteger i = 6; i <= top; ++i)
{
res = sq_getstackobj(vm, i, &(args[inst.mArgc].mObj));
// Validate the result
if (SQ_FAILED(res))
{
// Clear previous arguments
inst.Clear();
// Propagate the error
return res;
}
// Keep a strong reference to the argument
sq_addref(vm, &(args[inst.mArgc].mObj));
// Increase the argument counter
++inst.mArgc;
}
}
// Attempt to retrieve the routine from the stack and associate it with the slot
try
{
Var< Routine * >(vm, -1).value->m_Slot = ConvTo< Uint32 >::From(slot);
}
catch (const Sqrat::Exception & e)
{
// Clear extracted arguments
inst.Clear();
// Now it's safe to throw the error
return sq_throwerror(vm, "Unable to create the routine instance");
}
// Alright, at this point we can initialize the slot
inst.Init(env, func, obj, intrv, itr);
// Now initialize the timer
s_Intervals[slot] = intrv;
// We have the created routine on the stack, so let's return it
return 1;
}
// ------------------------------------------------------------------------------------------------
bool Routine::IsWithTag(const StackStrF & tag)
{
// Is the specified tag valid?
if (tag.mPtr != nullptr)
{
// Iterate routine list
for (const auto & r : s_Instances)
{
if (!r.mInst.IsNull() && r.mTag.compare(tag.mPtr) == 0)
{
return true; // Yup, we're doing this
}
}
}
// Unable to find such routine
return false;
}
/* ------------------------------------------------------------------------------------------------
* Forward the call to process routines.
*/
void ProcessRoutines()
{
Routine::Process();
}
/* ------------------------------------------------------------------------------------------------
* Forward the call to initialize routines.
*/
void InitializeRoutines()
{
Routine::Initialize();
}
/* ------------------------------------------------------------------------------------------------
* Forward the call to terminate routines.
*/
void TerminateRoutines()
{
Routine::Deinitialize();
}
// ================================================================================================
void Register_Routine(HSQUIRRELVM vm)
{
RootTable(vm).Bind(Typename::Str,
Class< Routine, NoConstructor< Routine > >(vm, Typename::Str)
// Meta-methods
.SquirrelFunc(_SC("_typename"), &Typename::Fn)
.Func(_SC("_tostring"), &Routine::ToString)
// Properties
.Prop(_SC("Tag"), &Routine::GetTag, &Routine::SetTag)
.Prop(_SC("Env"), &Routine::GetEnv, &Routine::SetEnv)
.Prop(_SC("Func"), &Routine::GetFunc, &Routine::SetFunc)
.Prop(_SC("Data"), &Routine::GetData, &Routine::SetData)
.Prop(_SC("Interval"), &Routine::GetInterval, &Routine::SetInterval)
.Prop(_SC("Iterations"), &Routine::GetIterations, &Routine::SetIterations)
.Prop(_SC("Suspended"), &Routine::GetSuspended, &Routine::SetSuspended)
.Prop(_SC("Quiet"), &Routine::GetQuiet, &Routine::SetQuiet)
.Prop(_SC("Endure"), &Routine::GetEndure, &Routine::SetEndure)
.Prop(_SC("Arguments"), &Routine::GetArguments)
// Member Methods
.FmtFunc(_SC("SetTag"), &Routine::SetTag)
.Func(_SC("Terminate"), &Routine::Terminate)
.Func(_SC("GetArgument"), &Routine::GetArgument)
);
// Global functions
RootTable(vm).SquirrelFunc(_SC("SqRoutine"), &Routine::Create);
RootTable(vm).FmtFunc(_SC("SqFindRoutineByTag"), &Routine::FindByTag);
RootTable(vm).FmtFunc(_SC("SqIsRoutineWithTag"), &Routine::IsWithTag);
}
} // Namespace:: SqMod

553
source/Misc/Routine.hpp Normal file
View File

@@ -0,0 +1,553 @@
#ifndef _ROUTINE_HPP_
#define _ROUTINE_HPP_
// ------------------------------------------------------------------------------------------------
#include "Base/Shared.hpp"
// ------------------------------------------------------------------------------------------------
namespace SqMod {
/* ------------------------------------------------------------------------------------------------
* Execute callbacks after specific intervals of time.
*/
class Routine
{
public:
/* --------------------------------------------------------------------------------------------
* Simplify future changes to a single point of change.
*/
typedef Int64 Time;
typedef SQInteger Interval;
typedef Uint32 Iterator;
typedef LightObj Argument;
private:
/* --------------------------------------------------------------------------------------------
* Structure that represents an active routine and keeps track of the routine information.
*/
struct Instance
{
// ----------------------------------------------------------------------------------------
LightObj mEnv; // A reference to the managed environment object.
LightObj mFunc; // A reference to the managed function object.
LightObj mInst; // Reference to the routine associated with this instance.
LightObj mData; // A reference to the arbitrary data associated with this instance.
String mTag; // An arbitrary string which represents the tag.
Iterator mIterations; // Number of iterations before self destruct.
Interval mInterval; // Interval between routine invocations.
bool mSuspended; // Whether this instance is allowed to receive calls.
bool mQuiet; // Whether this instance is allowed to handle errors.
bool mEndure; // Whether this instance is allowed to terminate itself on errors.
Uint8 mArgc; // The number of arguments that the routine must forward.
Argument mArgv[14]; // The arguments that the routine must forward.
/* ----------------------------------------------------------------------------------------
* Default constructor.
*/
Instance()
: mEnv()
, mFunc()
, mInst()
, mData()
, mTag()
, mIterations(0)
, mInterval(0)
, mSuspended(false)
, mQuiet(ErrorHandling::IsEnabled())
, mEndure(false)
, mArgc(0)
, mArgv()
{
/* ... */
}
/* ----------------------------------------------------------------------------------------
* Copy constructor. (disabled)
*/
Instance(const Instance & o) = delete;
/* ----------------------------------------------------------------------------------------
* Move constructor. (disabled)
*/
Instance(Instance && o) = delete;
/* ----------------------------------------------------------------------------------------
* Destructor.
*/
~Instance()
{
Terminate();
}
/* ----------------------------------------------------------------------------------------
* Copy assignment operator. (disabled)
*/
Instance & operator = (const Instance & o) = delete;
/* ----------------------------------------------------------------------------------------
* Move assignment operator. (disabled)
*/
Instance & operator = (Instance && o) = delete;
/* ----------------------------------------------------------------------------------------
* Initializes the routine parameters. (assumes previous values are already released)
*/
void Init(HSQOBJECT & env, HSQOBJECT & func, HSQOBJECT & inst, Interval intrv, Iterator itr)
{
// Initialize the callback objects
mEnv = LightObj(env);
mFunc = LightObj(func);
// Associate with the routine instance
mInst = LightObj(inst);
// Initialize the routine options
mIterations = itr;
mInterval = intrv;
}
/* ----------------------------------------------------------------------------------------
* Release managed script resources.
*/
void Release()
{
mEnv.Release();
mFunc.Release();
mInst.Release();
mData.Release();
mIterations = 0;
mInterval = 0;
mTag.clear();
}
/* ----------------------------------------------------------------------------------------
* Execute the managed routine.
*/
Interval Execute()
{
// is this even a valid routine?
if (mInst.IsNull())
{
return 0; // Dunno how we got here but it ends now
}
// Are we allowed to forward calls?
else if (!mSuspended)
{
// Grab the virtual machine once
HSQUIRRELVM vm = DefaultVM::Get();
// Push the function on the stack
sq_pushobject(vm, mFunc);
// Push the environment on the stack
sq_pushobject(vm, mEnv);
// Push function parameters, if any
for (Uint32 n = 0; n < mArgc; ++n)
{
sq_pushobject(vm, mArgv[n].mObj);
}
// Make the function call and store the result
const SQRESULT res = sq_call(vm, mArgc + 1, false, !mQuiet);
// Pop the callback object from the stack
sq_pop(vm, 1);
// Validate the result
if (SQ_FAILED(res))
{
// Should we endure the errors?
if (!mEndure)
{
Terminate(); // Destroy ourself on error
}
}
}
// Decrease the number of iterations if necessary
if (mIterations && (--mIterations) == 0)
{
Terminate(); // This routine reached the end of it's life
}
// Return the current interval
return mInterval;
}
/* ----------------------------------------------------------------------------------------
* Clear the arguments.
*/
void Clear()
{
// Now release the arguments
for (auto & a : mArgv)
{
a.Release();
}
// Reset the counter
mArgc = 0;
}
/* ----------------------------------------------------------------------------------------
* Terminate the routine.
*/
void Terminate()
{
Release();
Clear();
}
};
private:
// --------------------------------------------------------------------------------------------
static Time s_Last; // Last time point.
static Time s_Prev; // Previous time point.
static Interval s_Intervals[SQMOD_MAX_ROUTINES]; // List of intervals to be processed.
static Instance s_Instances[SQMOD_MAX_ROUTINES]; // List of routines to be executed.
private:
/* --------------------------------------------------------------------------------------------
* The index of the slot in the pool of active routines.
*/
Uint32 m_Slot;
protected:
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
Routine()
: m_Slot(SQMOD_MAX_ROUTINES)
{
/* ... */
}
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
Routine(Uint32 slot)
: m_Slot(slot)
{
/* ... */
}
/* --------------------------------------------------------------------------------------------
* Copy constructor. (disabled)
*/
Routine(const Routine & o) = delete;
/* --------------------------------------------------------------------------------------------
* Move constructor. (disabled)
*/
Routine(Routine && o) = delete;
/* --------------------------------------------------------------------------------------------
* Copy assignment operator. (disabled)
*/
Routine & operator = (const Routine & o) = delete;
/* --------------------------------------------------------------------------------------------
* Move assignment operator. (disabled)
*/
Routine & operator = (Routine && o) = delete;
/* --------------------------------------------------------------------------------------------
* Find an unoccupied routine slot.
*/
static SQInteger FindUnused()
{
for (const auto & r : s_Instances)
{
if (r.mInst.IsNull())
{
return (&r - s_Instances); // Return the index of this element
}
}
// No available slot
return -1;
}
public:
/* --------------------------------------------------------------------------------------------
* Retrieve the number of used routine slots.
*/
static SQInteger GetUsed()
{
SQInteger n = 0;
// Iterate routine list
for (const auto & r : s_Instances)
{
if (!r.mInst.IsNull())
{
++n;
}
}
// Return the final count
return n;
}
/* --------------------------------------------------------------------------------------------
* Retrieve the number of used routine slots.
*/
static const LightObj & FindByTag(const StackStrF & tag)
{
// Is the specified tag valid?
if (!tag.mPtr)
{
STHROWF("Invalid routine tag");
}
// Iterate routine list
for (const auto & r : s_Instances)
{
if (!r.mInst.IsNull() && r.mTag.compare(tag.mPtr) == 0)
{
return r.mInst; // Return this routine instance
}
}
// Unable to find such routine
STHROWF("Unable to find a routine with tag (%s)", tag.mPtr);
// Should not reach this point but if it did, we have to return something
return s_Instances[SQMOD_MAX_ROUTINES].mInst; // Intentional Buffer overflow!
}
/* --------------------------------------------------------------------------------------------
* Check if a routine with a certain tag exists.
*/
static bool IsWithTag(const StackStrF & tag);
/* --------------------------------------------------------------------------------------------
* Process all active routines and update elapsed time.
*/
static void Process();
/* --------------------------------------------------------------------------------------------
* Initialize all resources and prepare for startup.
*/
static void Initialize();
/* --------------------------------------------------------------------------------------------
* Release all resources and prepare for shutdown.
*/
static void Deinitialize();
/* --------------------------------------------------------------------------------------------
* Create a routine with the specified parameters.
*/
static SQInteger Create(HSQUIRRELVM vm);
protected:
/* --------------------------------------------------------------------------------------------
* See whether this routine is valid otherwise throw an exception.
*/
void Validate() const
{
if (m_Slot >= SQMOD_MAX_ROUTINES)
{
STHROWF("This instance does not reference a valid routine");
}
}
/* --------------------------------------------------------------------------------------------
* See whether this routine is valid otherwise throw an exception.
*/
Instance & GetValid() const
{
if (m_Slot >= SQMOD_MAX_ROUTINES)
{
STHROWF("This instance does not reference a valid routine");
}
// We know it's valid so let's return it
return s_Instances[m_Slot];
}
public:
/* --------------------------------------------------------------------------------------------
* Used by the script engine to convert an instance of this type to a string.
*/
const String & ToString() const
{
return (m_Slot >= SQMOD_MAX_ROUTINES) ? NullString() : s_Instances[m_Slot].mTag;
}
/* --------------------------------------------------------------------------------------------
* Terminate the routine.
*/
void Terminate()
{
GetValid().Terminate();
}
/* --------------------------------------------------------------------------------------------
* Retrieve the associated user tag.
*/
const String & GetTag() const
{
return GetValid().mTag;
}
/* --------------------------------------------------------------------------------------------
* Modify the associated user tag.
*/
void SetTag(const StackStrF & tag)
{
GetValid().mTag.assign(tag.mPtr, ClampMin(tag.mLen, 0));
}
/* --------------------------------------------------------------------------------------------
* Retrieve the environment object.
*/
const LightObj & GetEnv() const
{
return GetValid().mEnv;
}
/* --------------------------------------------------------------------------------------------
* Modify the environment object.
*/
void SetEnv(const LightObj & env)
{
GetValid().mEnv = env.IsNull() ? LightObj(RootTable().GetObject()) : env;
}
/* --------------------------------------------------------------------------------------------
* Retrieve the function object.
*/
const LightObj & GetFunc() const
{
return GetValid().mFunc;
}
/* --------------------------------------------------------------------------------------------
* Modify the function object.
*/
void SetFunc(const Function & func)
{
// Validate the specified
if (!sq_isclosure(func.GetFunc()) && !sq_isnativeclosure(func.GetFunc()))
{
STHROWF("Invalid callback type %s", SqTypeName(GetValid().mFunc.GetType()));
}
// Store the function without the environment
GetValid().mFunc = LightObj(func.GetFunc());
}
/* --------------------------------------------------------------------------------------------
* Retrieve the arbitrary user data object.
*/
const LightObj & GetData() const
{
return GetValid().mData;
}
/* --------------------------------------------------------------------------------------------
* Modify the arbitrary user data object.
*/
void SetData(const LightObj & data)
{
GetValid().mData = data;
}
/* --------------------------------------------------------------------------------------------
* Retrieve the execution interval.
*/
SQInteger GetInterval() const
{
return ConvTo< SQInteger >::From(GetValid().mInterval);
}
/* --------------------------------------------------------------------------------------------
* Modify the execution interval.
*/
void SetInterval(SQInteger itr)
{
GetValid().mInterval = ClampMin(ConvTo< Interval >::From(itr), static_cast< Interval >(0));
}
/* --------------------------------------------------------------------------------------------
* Retrieve the number of iterations.
*/
SQInteger GetIterations() const
{
return ConvTo< SQInteger >::From(GetValid().mIterations);
}
/* --------------------------------------------------------------------------------------------
* Modify the number of iterations.
*/
void SetIterations(SQInteger itr)
{
GetValid().mIterations = ConvTo< Iterator >::From(itr);
}
/* --------------------------------------------------------------------------------------------
* See whether the routine is suspended.
*/
bool GetSuspended() const
{
return GetValid().mSuspended;
}
/* --------------------------------------------------------------------------------------------
* Set whether the routine should be suspended.
*/
void SetSuspended(bool toggle)
{
GetValid().mSuspended = toggle;
}
/* --------------------------------------------------------------------------------------------
* See whether the routine is quite.
*/
bool GetQuiet() const
{
return GetValid().mQuiet;
}
/* --------------------------------------------------------------------------------------------
* Set whether the routine should be quiet.
*/
void SetQuiet(bool toggle)
{
GetValid().mQuiet = toggle;
}
/* --------------------------------------------------------------------------------------------
* See whether the routine endures.
*/
bool GetEndure() const
{
return GetValid().mEndure;
}
/* --------------------------------------------------------------------------------------------
* Set whether the routine should endure.
*/
void SetEndure(bool toggle)
{
GetValid().mEndure = toggle;
}
/* --------------------------------------------------------------------------------------------
* Retrieve the number of arguments to be forwarded.
*/
SQInteger GetArguments() const
{
return ConvTo< SQInteger >::From(GetValid().mArgc);
}
/* --------------------------------------------------------------------------------------------
* Retrieve a certain argument.
*/
const Argument & GetArgument(SQInteger arg) const
{
// Cast the index to the proper value
Uint8 idx = ConvTo< Uint8 >::From(arg);
// Validate the specified index
if (idx >= 14)
{
STHROWF("The specified index is out of range: %u >= %u", idx, 14);
}
// Return the requested argument
return GetValid().mArgv[idx];
}
};
} // Namespace:: SqMod
#endif // _ROUTINE_HPP_

1716
source/Misc/Signal.cpp Normal file

File diff suppressed because it is too large Load Diff

767
source/Misc/Signal.hpp Normal file
View File

@@ -0,0 +1,767 @@
#ifndef _SIGNAL_HPP_
#define _SIGNAL_HPP_
// ------------------------------------------------------------------------------------------------
#include "Base/Shared.hpp"
// ------------------------------------------------------------------------------------------------
#include <vector>
#include <utility>
#include <algorithm>
#include <functional>
// ------------------------------------------------------------------------------------------------
namespace SqMod {
// ------------------------------------------------------------------------------------------------
struct Signal;
struct SignalWrapper;
/* ------------------------------------------------------------------------------------------------
* Class used to deliver events to one or more listeners.
*/
struct Signal
{
friend class SignalWrapper;
// --------------------------------------------------------------------------------------------
typedef unsigned int SizeType; // Type of value used to represent sizes and/or indexes.
// --------------------------------------------------------------------------------------------
enum { SMB_SIZE = 8 };
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
Signal();
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
explicit Signal(const char * name)
: Signal(String(name))
{
//...
}
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
explicit Signal(const String & name)
: Signal(String(name))
{
//...
}
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
Signal(String && name);
/* --------------------------------------------------------------------------------------------
* Copy constructor (disabled).
*/
Signal(const Signal & o) = delete;
/* --------------------------------------------------------------------------------------------
* Move constructor (disabled).
*/
Signal(Signal && o) = delete;
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
~Signal();
/* --------------------------------------------------------------------------------------------
* Copy assignment operator (disabled).
*/
Signal & operator = (const Signal & o) = delete;
/* --------------------------------------------------------------------------------------------
* Move assignment operator (disabled).
*/
Signal & operator = (Signal && o) = delete;
protected:
/* --------------------------------------------------------------------------------------------
* Adjust the internal buffer size if necessary.
*/
bool AdjustSlots(SizeType capacity);
/* --------------------------------------------------------------------------------------------
* Structure responsible for storing information about a slot.
*/
struct Slot {
SQHash mThisHash; // The hash of the specified environment.
SQHash mFuncHash; // The hash of the specified callback.
HSQOBJECT mThisRef; // The specified script environment.
HSQOBJECT mFuncRef; // The specified script callback.
/* ----------------------------------------------------------------------------------------
* Default constructor.
*/
Slot()
: mThisHash(0)
, mFuncHash(0)
, mThisRef()
, mFuncRef()
{
sq_resetobject(&mThisRef);
sq_resetobject(&mFuncRef);
}
/* ----------------------------------------------------------------------------------------
* Forwarding constructor.
*/
Slot(Object & env, Function & func)
: Slot(env.GetObject(), func.GetFunc())
{
/* ... */
}
/* ----------------------------------------------------------------------------------------
* Base constructor.
*/
Slot(HSQOBJECT & env, HSQOBJECT & func)
: mThisHash(0)
, mFuncHash(0)
, mThisRef(env)
, mFuncRef(func)
{
HSQUIRRELVM vm = DefaultVM::Get();
// Remember the current stack size
const StackGuard sg(vm);
// Is there an explicit environment?
if (!sq_isnull(mThisRef))
{
// Keep a reference to this environment
sq_addref(vm, &mThisRef);
// Push the environment on the stack
sq_pushobject(vm, mThisRef);
// Grab the hash of the environment object
mThisHash = sq_gethash(vm, -1);
}
// Is there an explicit function?
if (!sq_isnull(mFuncRef))
{
// Keep a reference to this function
sq_addref(vm, &mFuncRef);
// Push the callback on the stack
sq_pushobject(vm, mFuncRef);
// Grab the hash of the callback object
mFuncHash = sq_gethash(vm, -1);
}
}
/* ----------------------------------------------------------------------------------------
* Base constructor.
*/
Slot(HSQOBJECT & env, HSQOBJECT & func, SQHash envh, SQHash funch)
: mThisHash(envh)
, mFuncHash(funch)
, mThisRef(env)
, mFuncRef(func)
{
}
/* ----------------------------------------------------------------------------------------
* Copy constructor.
*/
Slot(const Slot & o)
: mThisHash(o.mThisHash)
, mFuncHash(o.mFuncHash)
, mThisRef(o.mThisRef)
, mFuncRef(o.mFuncRef)
{
// Track reference
if (mFuncHash != 0)
{
sq_addref(DefaultVM::Get(), &mThisRef);
sq_addref(DefaultVM::Get(), &mFuncRef);
}
}
/* ----------------------------------------------------------------------------------------
* Move constructor.
*/
Slot(Slot && o)
: mThisHash(o.mThisHash)
, mFuncHash(o.mFuncHash)
, mThisRef(o.mThisRef)
, mFuncRef(o.mFuncRef)
{
// Take ownership
sq_resetobject(&o.mThisRef);
sq_resetobject(&o.mFuncRef);
}
/* ----------------------------------------------------------------------------------------
* Destructor.
*/
~Slot()
{
Release();
}
/* ----------------------------------------------------------------------------------------
* Copy assignment operator. (disabled)
*/
Slot & operator = (const Slot & o)
{
if (this != &o)
{
// Release current resources, if any
Release();
// Replicate data
mThisHash = o.mThisHash;
mFuncHash = o.mFuncHash;
mThisRef = o.mThisRef;
mFuncRef = o.mFuncRef;
// Track reference
sq_addref(DefaultVM::Get(), &const_cast< HSQOBJECT & >(o.mThisRef));
sq_addref(DefaultVM::Get(), &const_cast< HSQOBJECT & >(o.mFuncRef));
}
return *this;
}
/* ----------------------------------------------------------------------------------------
* Move assignment operator.
*/
Slot & operator = (Slot && o)
{
if (this != &o)
{
// Release current resources, if any
Release();
// Replicate data
mThisHash = o.mThisHash;
mFuncHash = o.mFuncHash;
mThisRef = o.mThisRef;
mFuncRef = o.mFuncRef;
// Take ownership
sq_resetobject(&o.mThisRef);
sq_resetobject(&o.mFuncRef);
}
return *this;
}
/* ----------------------------------------------------------------------------------------
* Equality comparison operator.
*/
bool operator == (const Slot & o) const
{
return (mThisHash == o.mThisHash) && (mFuncHash == o.mFuncHash);
}
/* ----------------------------------------------------------------------------------------
* Inequality comparison operator.
*/
bool operator != (const Slot & o) const
{
return (mThisHash != o.mThisHash) || (mFuncHash != o.mFuncHash);
}
/* ----------------------------------------------------------------------------------------
* Release managed script resources.
*/
bool Available() const
{
return (mFuncHash == 0);
}
/* ----------------------------------------------------------------------------------------
* Release managed script resources.
*/
void Release()
{
// Should we release any environment object?
if (mThisHash != 0)
{
sq_release(DefaultVM::Get(), &mThisRef);
sq_resetobject(&mThisRef);
// Also reset the hash
mThisHash = 0;
}
// Should we release any callback object?
if (mFuncHash != 0)
{
sq_release(DefaultVM::Get(), &mFuncRef);
sq_resetobject(&mFuncRef);
// Also reset the hash
mFuncHash = 0;
}
}
/* ----------------------------------------------------------------------------------------
* Swap the values of two slots.
*/
void Swap(Slot & s)
{
// Swap the environment hash
SQHash h = mThisHash;
mThisHash = s.mThisHash;
s.mThisHash = h;
// Swap the callback hash
h = mFuncHash;
mFuncHash = s.mFuncHash;
s.mFuncHash = h;
// Swap the environment object
HSQOBJECT o = mThisRef;
mThisRef = s.mThisRef;
s.mThisRef = o;
// Swap the callback object
o = mFuncRef;
mFuncRef = s.mFuncRef;
s.mFuncRef = o;
}
};
// --------------------------------------------------------------------------------------------
typedef Slot ValueType; // Value type used to represent a slot.
typedef ValueType & Reference; // Reference to the stored value type
typedef const ValueType & ConstReference; // Constant reference to the stored value type.
typedef ValueType * Pointer; // Pointer to the stored value type
typedef const ValueType * ConstPointer; // Constant pointer to the stored value type.
// --------------------------------------------------------------------------------------------
/// Execution scope used to adjust iterators when removing slots or adjusting the buffer.
struct Scope {
// ----------------------------------------------------------------------------------------
Pointer mItr; ///< Currently executed slot.
Pointer mEnd; ///< Where the execution ends.
Scope * mParent; ///< Previous execution scope.
Scope * mChild; ///< Next execution scope.
// ----------------------------------------------------------------------------------------
/// Default constructor.
Scope(Scope * parent, Pointer begin, Pointer end)
: mItr(begin), mEnd(end), mParent(parent), mChild(nullptr)
{
if (mParent != nullptr) mParent->mChild = this;
}
// ----------------------------------------------------------------------------------------
/// Destructor.
~Scope() {
if (mParent != nullptr) mParent->mChild = nullptr;
}
// ----------------------------------------------------------------------------------------
/// Adjust the iterators to account for the fact that the specified slot was removed.
void Descend(Pointer ptr);
/// Adjust the iterators to account for the fact that the specified slot is now leading.
void Lead(Pointer ptr);
/// Adjust the iterators to account for the fact that the specified slot is now tailing.
void Tail(Pointer ptr);
/// Adjust the iterators to finish the execution abruptly.
void Finish();
};
private:
// --------------------------------------------------------------------------------------------
SizeType m_Used; // The number of stored slots that are valid.
SizeType m_Size; // The size of the memory allocated for slots.
Pointer m_Slots; // Pointer to the memory containing the slots.
// --------------------------------------------------------------------------------------------
Scope * m_Scope; // Current execution state.
// --------------------------------------------------------------------------------------------
String m_Name; // The name that identifies this signal.
LightObj m_Data; // User data associated with this instance.
// --------------------------------------------------------------------------------------------
ValueType m_SMB[SMB_SIZE]{}; // Small buffer optimization.
public:
/* --------------------------------------------------------------------------------------------
* Used by the script engine to convert an instance of this type to a string.
*/
const String & ToString() const
{
return m_Name;
}
/* --------------------------------------------------------------------------------------------
* Retrieve the associated user data.
*/
LightObj & GetData()
{
return m_Data;
}
/* --------------------------------------------------------------------------------------------
* Modify the associated user data.
*/
void SetData(LightObj & data)
{
m_Data = data;
}
/* --------------------------------------------------------------------------------------------
* The number of slots connected to the signal.
*/
SQInteger GetUsed() const
{
return static_cast< SQInteger >(m_Used);
}
/* --------------------------------------------------------------------------------------------
* Clear all slots connected to the signal.
*/
void ClearSlots();
/* --------------------------------------------------------------------------------------------
* See if there are any slots connected.
*/
bool IsEmpty() const
{
return (m_Used == 0);
}
protected:
/* --------------------------------------------------------------------------------------------
* Connect the specified slot to the signal.
*/
SQInteger Connect(SignalWrapper & w);
/* --------------------------------------------------------------------------------------------
* Connect the specified slot but not before disconnecting all other occurrences.
*/
SQInteger ConnectOnce(SignalWrapper & w);
/* --------------------------------------------------------------------------------------------
* Disconnect all occurrences of the specified slot from the signal.
*/
SQInteger Disconnect(SignalWrapper & w);
/* --------------------------------------------------------------------------------------------
* See if the specified slot is connected to the signal.
*/
SQInteger Exists(SignalWrapper & w);
/* --------------------------------------------------------------------------------------------
* See if the specified slot environment is connected to the signal.
*/
SQInteger ExistsThis(SignalWrapper & w);
/* --------------------------------------------------------------------------------------------
* See if the specified slot callback is connected to the signal.
*/
SQInteger ExistsFunc(SignalWrapper & w);
/* --------------------------------------------------------------------------------------------
* Count all occurrences of the specified slot.
*/
SQInteger Count(SignalWrapper & w);
/* --------------------------------------------------------------------------------------------
* Count all occurrences of the specified slot environment.
*/
SQInteger CountThis(SignalWrapper & w);
/* --------------------------------------------------------------------------------------------
* Count all occurrences of the specified slot callback.
*/
SQInteger CountFunc(SignalWrapper & w);
/* --------------------------------------------------------------------------------------------
* Move all occurrences of the specified slot to the front.
*/
SQInteger Lead(SignalWrapper & w);
/* --------------------------------------------------------------------------------------------
* Move all occurrences of the specified slot environment to the front.
*/
SQInteger LeadThis(SignalWrapper & w);
/* --------------------------------------------------------------------------------------------
* Move all occurrences of the specified slot callback to the front.
*/
SQInteger LeadFunc(SignalWrapper & w);
/* --------------------------------------------------------------------------------------------
* Move all occurrences of the specified slot to the back.
*/
SQInteger Tail(SignalWrapper & w);
/* --------------------------------------------------------------------------------------------
* Move all occurrences of the specified slot environment to the back.
*/
SQInteger TailThis(SignalWrapper & w);
/* --------------------------------------------------------------------------------------------
* Move all occurrences of the specified slot callback to the back.
*/
SQInteger TailFunc(SignalWrapper & w);
/* --------------------------------------------------------------------------------------------
* Remove all occurrences of the specified slot.
*/
SQInteger Eliminate(SignalWrapper & w);
/* --------------------------------------------------------------------------------------------
* Remove all occurrences of the specified slot environment.
*/
SQInteger EliminateThis(SignalWrapper & w);
/* --------------------------------------------------------------------------------------------
* Remove all occurrences of the specified slot callback.
*/
SQInteger EliminateFunc(SignalWrapper & w);
public:
/* --------------------------------------------------------------------------------------------
* Squirrel wrapper for the `Connect` method of this class.
*/
static SQInteger SqConnect(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Squirrel wrapper for the `ConnectOnce` method of this class.
*/
static SQInteger SqConnectOnce(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Squirrel wrapper for the `Exists` method of this class.
*/
static SQInteger SqExists(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Squirrel wrapper for the `Disconnect` method of this class.
*/
static SQInteger SqDisconnect(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Squirrel wrapper for the `ExistsThis` method of this class.
*/
static SQInteger SqExistsThis(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Squirrel wrapper for the `ExistsFunc` method of this class.
*/
static SQInteger SqExistsFunc(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Squirrel wrapper for the `Count` method of this class.
*/
static SQInteger SqCount(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Squirrel wrapper for the `CountThis` method of this class.
*/
static SQInteger SqCountThis(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Squirrel wrapper for the `CountFunc` method of this class.
*/
static SQInteger SqCountFunc(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Squirrel wrapper for the `Lead` method of this class.
*/
static SQInteger SqLead(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Squirrel wrapper for the `LeadThis` method of this class.
*/
static SQInteger SqLeadThis(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Squirrel wrapper for the `LeadFunc` method of this class.
*/
static SQInteger SqLeadFunc(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Squirrel wrapper for the `Tail` method of this class.
*/
static SQInteger SqTail(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Squirrel wrapper for the `TailThis` method of this class.
*/
static SQInteger SqTailThis(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Squirrel wrapper for the `TailFunc` method of this class.
*/
static SQInteger SqTailFunc(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Squirrel wrapper for the `Eliminate` method of this class.
*/
static SQInteger SqEliminate(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Squirrel wrapper for the `EliminateThis` method of this class.
*/
static SQInteger SqEliminateThis(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Squirrel wrapper for the `EliminateFunc` method of this class.
*/
static SQInteger SqEliminateFunc(HSQUIRRELVM vm);
protected:
/* --------------------------------------------------------------------------------------------
* Emit the event to the connected slots.
*/
SQInteger Emit(HSQUIRRELVM vm, SQInteger top);
/* --------------------------------------------------------------------------------------------
* Emit the event to the connected slots and collect returned values.
*/
SQInteger Query(HSQUIRRELVM vm, SQInteger top);
/* --------------------------------------------------------------------------------------------
* Emit the event to the connected slots and see if they consume it.
*/
SQInteger Consume(HSQUIRRELVM vm, SQInteger top);
/* --------------------------------------------------------------------------------------------
* Emit the event to the connected slots and see if they approve it.
*/
SQInteger Approve(HSQUIRRELVM vm, SQInteger top);
/* --------------------------------------------------------------------------------------------
* Emit the event to the connected slots and see if they return something.
*/
SQInteger Request(HSQUIRRELVM vm, SQInteger top);
public:
/* --------------------------------------------------------------------------------------------
* Squirrel wrapper for the `Emit` method of this class.
*/
static SQInteger SqEmit(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Squirrel wrapper for the `Query` method of this class.
*/
static SQInteger SqQuery(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Squirrel wrapper for the `Consume` method of this class.
*/
static SQInteger SqConsume(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Squirrel wrapper for the `Approve` method of this class.
*/
static SQInteger SqApprove(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Squirrel wrapper for the `Request` method of this class.
*/
static SQInteger SqRequest(HSQUIRRELVM vm);
protected:
// --------------------------------------------------------------------------------------------
typedef std::pair< std::size_t, SignalPair > SignalElement;
typedef std::vector< SignalElement > SignalPool;
typedef std::vector< Signal * > FreeSignals;
// --------------------------------------------------------------------------------------------
static SignalPool s_Signals; // List of all created signals.
static FreeSignals s_FreeSignals; // List of signals without a name.
/* --------------------------------------------------------------------------------------------
* Specialization for when there are no arguments given.
*/
void PushParameters()
{
//...
}
/* --------------------------------------------------------------------------------------------
* Specialization for when there's only one argument given/remaining.
*/
template < typename T > void PushParameters(T v)
{
Var< T >::push(DefaultVM::Get(), v);
}
/* --------------------------------------------------------------------------------------------
* Specialization for when there's more than one argument given.
*/
template < typename T, typename... Args > void PushParameters(T v, Args... args)
{
Var< T >::push(DefaultVM::Get(), v);
PushParameters(args...);
}
public:
/* --------------------------------------------------------------------------------------------
* Terminate all signal instances and release any script resources.
*/
static void Terminate();
/* --------------------------------------------------------------------------------------------
* Create a free signal without a specific name.
*/
static LightObj CreateFree();
/* --------------------------------------------------------------------------------------------
* Create a new signal with the specified name.
*/
static LightObj Create(const StackStrF & name);
/* --------------------------------------------------------------------------------------------
* Remove the signal with the specified name.
*/
static void Remove(const StackStrF & name);
/* --------------------------------------------------------------------------------------------
* Retrieve the signal with the specified name.
*/
static const LightObj & Fetch(const StackStrF & name);
/* --------------------------------------------------------------------------------------------
* Emit a signal from the module.
*/
template < typename... Args > void operator () (Args&&... args)
{
// Are there any slots connected?
if (!m_Used) return;
// Enter a new execution scope
Scope scope(m_Scope, m_Slots, m_Slots + m_Used);
// Activate the current scope and create a guard to restore it
const AutoAssign< Scope * > aa(m_Scope, scope.mParent, &scope);
// Grab the default virtual machine
HSQUIRRELVM vm = DefaultVM::Get();
// Process the slots from this scope
while (scope.mItr != scope.mEnd)
{
// Grab a reference to the current slot
const Slot & slot = *(scope.mItr++);
// Push the callback object
sq_pushobject(vm, slot.mFuncRef);
// Is there an explicit environment?
if (slot.mThisHash == 0)
{
sq_pushroottable(vm);
}
else
{
sq_pushobject(vm, slot.mThisRef);
}
// Push the given parameters on the stack
PushParameters(args...);
// Make the function call and store the result
const SQRESULT res = sq_call(vm, 1 + sizeof...(Args), false, ErrorHandling::IsEnabled());
// Pop the callback object from the stack
sq_pop(vm, 1);
// Validate the result
if (SQ_FAILED(res))
{
SQTHROW(vm, LastErrorString(vm)); // Stop emitting signals
}
}
}
};
} // Namespace:: SqMod
#endif // _SIGNAL_HPP_

524
source/Misc/Tasks.cpp Normal file
View File

@@ -0,0 +1,524 @@
// ------------------------------------------------------------------------------------------------
#include "Tasks.hpp"
#include "Core.hpp"
#include "Library/Chrono.hpp"
// ------------------------------------------------------------------------------------------------
#include <cstdio>
#include <cstdlib>
#include <cstring>
// ------------------------------------------------------------------------------------------------
#include <utility>
// ------------------------------------------------------------------------------------------------
namespace SqMod {
// ------------------------------------------------------------------------------------------------
SQMODE_DECL_TYPENAME(Typename, _SC("SqTask"))
// ------------------------------------------------------------------------------------------------
Tasks::Time Tasks::s_Last = 0;
Tasks::Time Tasks::s_Prev = 0;
Tasks::Interval Tasks::s_Intervals[SQMOD_MAX_TASKS];
Tasks::Task Tasks::s_Tasks[SQMOD_MAX_TASKS];
// ------------------------------------------------------------------------------------------------
void Tasks::Task::Init(HSQOBJECT & func, HSQOBJECT & inst, Interval intrv, Iterator itr, Int32 id, Int32 type)
{
// Initialize the callback hash
mHash = 0;
// Initialize the callback objects
mFunc = LightObj(func);
mInst = LightObj(inst);
// Initialize the task options
mIterations = itr;
mInterval = intrv;
// Initialize the entity information
mEntity = ConvTo< Int16 >::From(id);
mType = ConvTo< Uint8 >::From(type);
// Grab the virtual machine once
HSQUIRRELVM vm = DefaultVM::Get();
// Remember the current stack size
const StackGuard sg(vm);
// Is there a valid function?
if (!mFunc.IsNull())
{
// Push the callback on the stack
sq_pushobject(vm, mFunc);
// Grab the hash of the callback object
mHash = sq_gethash(vm, -1);
}
}
// ------------------------------------------------------------------------------------------------
void Tasks::Task::Release()
{
mHash = 0;
mTag.clear();
mFunc.Release();
mInst.Release();
mData.Release();
mIterations = 0;
mInterval = 0;
mEntity = -1;
mType = -1;
}
// ------------------------------------------------------------------------------------------------
Tasks::Interval Tasks::Task::Execute()
{
// Are we even a valid task?
if (INVALID_ENTITY(mEntity))
{
return 0; // Dunno how we got here but it ends now
}
// Grab the virtual machine once
HSQUIRRELVM vm = DefaultVM::Get();
// Push the function on the stack
sq_pushobject(vm, mFunc);
// Push the environment on the stack
sq_pushobject(vm, mSelf);
// Push function parameters, if any
for (Uint32 n = 0; n < mArgc; ++n)
{
sq_pushobject(vm, mArgv[n].mObj);
}
// Make the function call and store the result
const SQRESULT res = sq_call(vm, mArgc + 1, false, ErrorHandling::IsEnabled());
// Pop the callback object from the stack
sq_pop(vm, 1);
// Validate the result
if (SQ_FAILED(res))
{
Terminate(); // Destroy ourself on error
}
// Decrease the number of iterations if necessary
if (mIterations && (--mIterations) == 0)
{
Terminate(); // This routine reached the end of it's life
}
// Return the current interval
return mInterval;
}
// ------------------------------------------------------------------------------------------------
void Tasks::Process()
{
// Is this the first call?
if (s_Last == 0)
{
s_Last = Chrono::GetCurrentSysTime();
// We'll do it text time
return;
}
// Backup the last known time-stamp
s_Prev = s_Last;
// Get the current time-stamp
s_Last = Chrono::GetCurrentSysTime();
// Calculate the elapsed time
const Int32 delta = Int32((s_Last - s_Prev) / 1000L);
// Process all active tasks
for (Interval * itr = s_Intervals; itr != (s_Intervals + SQMOD_MAX_TASKS); ++itr)
{
// Is this task valid?
if (*itr)
{
// Decrease the elapsed time
(*itr) -= delta;
// Have we completed the routine interval?
if ((*itr) <= 0)
{
// Execute and reset the elapsed time
(*itr) = s_Tasks[itr - s_Intervals].Execute();
}
}
}
}
// ------------------------------------------------------------------------------------------------
void Tasks::Initialize()
{
std::memset(s_Intervals, 0, sizeof(s_Intervals));
// Transform all task instances to script objects
for (auto & t : s_Tasks)
{
// This is fine because they'll always outlive the virtual machine
t.mSelf = LightObj(&t);
}
}
// ------------------------------------------------------------------------------------------------
void Tasks::Register(HSQUIRRELVM vm)
{
RootTable(vm).Bind(Typename::Str,
Class< Task, NoDestructor< Task > >(vm, Typename::Str)
// Meta-methods
.SquirrelFunc(_SC("_typename"), &Typename::Fn)
.Func(_SC("_tostring"), &Task::ToString)
// Properties
.Prop(_SC("Tag"), &Task::GetTag, &Task::SetTag)
.Prop(_SC("Entity"), &Task::GetInst)
.Prop(_SC("Func"), &Task::GetFunc, &Task::SetFunc)
.Prop(_SC("Data"), &Task::GetData, &Task::SetData)
.Prop(_SC("Interval"), &Task::GetInterval, &Task::SetInterval)
.Prop(_SC("Iterations"), &Task::GetIterations, &Task::SetIterations)
.Prop(_SC("Arguments"), &Task::GetArguments)
.Prop(_SC("Inst"), &Task::GetInst)
// Member Methods
.FmtFunc(_SC("SetTag"), &Task::SetTag)
.Func(_SC("Terminate"), &Task::Terminate)
.Func(_SC("GetArgument"), &Task::GetArgument)
// Static functions
.StaticFunc(_SC("Used"), &Tasks::GetUsed)
);
}
// ------------------------------------------------------------------------------------------------
void Tasks::Deinitialize()
{
// Release any script resources that the tasks might store
for (auto & t : s_Tasks)
{
t.Terminate();
t.mSelf.Release();
}
}
// ------------------------------------------------------------------------------------------------
LightObj & Tasks::FindEntity(Int32 id, Int32 type)
{
switch (type)
{
case ENT_BLIP: return Core::Get().GetBlip(id).mObj;
case ENT_CHECKPOINT: return Core::Get().GetCheckpoint(id).mObj;
case ENT_KEYBIND: return Core::Get().GetKeybind(id).mObj;
case ENT_OBJECT: return Core::Get().GetObject(id).mObj;
case ENT_PICKUP: return Core::Get().GetPickup(id).mObj;
case ENT_PLAYER: return Core::Get().GetPlayer(id).mObj;
case ENT_VEHICLE: return Core::Get().GetVehicle(id).mObj;
default: return NullLightObj();
}
}
// ------------------------------------------------------------------------------------------------
SQInteger Tasks::FindUnused()
{
for (const auto & t : s_Tasks)
{
if (INVALID_ENTITY(t.mEntity))
{
return (&t - s_Tasks); // Return the index of this element
}
}
// No available slot
return -1;
}
// ------------------------------------------------------------------------------------------------
SQInteger Tasks::Create(Int32 id, Int32 type, HSQUIRRELVM vm)
{
// Locate the identifier of a free slot
const SQInteger slot = FindUnused();
// See if we have where to store this task
if (slot < 0)
{
return sq_throwerror(vm, "Reached the maximum number of tasks");
}
// Grab the top of the stack
const SQInteger top = sq_gettop(vm);
// See if too many arguments were specified
if (top > 12) /* 4 base + 8 parameters = 12 */
{
return sq_throwerror(vm, "Too many parameters specified");
}
// Was there was a callback specified?
else if (top <= 1)
{
return sq_throwerror(vm, "Missing task callback");
}
// Validate the callback type
else if (sq_gettype(vm, 2) != OT_CLOSURE && sq_gettype(vm, 2) != OT_NATIVECLOSURE)
{
return sq_throwerror(vm, "Invalid callback type");
}
// Prepare an entity instance object
HSQOBJECT inst;
// Attempt to retrieve the entity instance
try
{
inst = FindEntity(id, type).GetObject();
}
catch (const std::exception & e)
{
return sq_throwerror(vm, e.what());
}
// Prepare the function object
HSQOBJECT func;
// Fetch the specified callback
SQRESULT res = sq_getstackobj(vm, 2, &func);
// Validate the result
if (SQ_FAILED(res))
{
return res; // Propagate the error
}
// The number of iterations and interval to execute the task
SQInteger intrv = 0, itr = 0;
// Was there an interval specified?
if (top > 2)
{
// Grab the interval from the stack
res = sq_getinteger(vm, 3, &intrv);
// Validate the result
if (SQ_FAILED(res))
{
return res; // Propagate the error
}
}
// Was there a number of iterations specified?
if (top > 3)
{
// Grab the iterations from the stack
res = sq_getinteger(vm, 4, &itr);
// Validate the result
if (SQ_FAILED(res))
{
return res; // Propagate the error
}
}
// At this point we can grab a reference to our slot
Task & task = s_Tasks[slot];
// Were there any arguments specified?
if (top > 4)
{
// Grab a pointer to the arguments array
Argument * args = task.mArgv;
// Reset the argument counter
task.mArgc = 0;
// Grab the specified arguments from the stack
for (SQInteger i = 5; i <= top; ++i)
{
res = sq_getstackobj(vm, i, &(args[task.mArgc].mObj));
// Validate the result
if (SQ_FAILED(res))
{
// Clear previous arguments
task.Clear();
// Propagate the error
return res;
}
// Keep a strong reference to the argument
sq_addref(vm, &(args[task.mArgc].mObj));
// Increase the argument counter
++task.mArgc;
}
}
// Alright, at this point we can initialize the slot
task.Init(func, inst, intrv, itr, id, type);
// Now initialize the timer
s_Intervals[slot] = intrv;
// Push the tag instance on the stack
sq_pushobject(vm, task.mSelf);
// Specify that this function returns a value
return 1;
}
// ------------------------------------------------------------------------------------------------
SQInteger Tasks::Find(Int32 id, Int32 type, SQInteger & pos, HSQUIRRELVM vm)
{
// Grab the top of the stack
const SQInteger top = sq_gettop(vm);
// Was there a callback specified?
if (top <= 1)
{
return sq_throwerror(vm, "Missing task callback");
}
SQRESULT res = SQ_OK;
// Grab the hash of the callback object
const SQHash chash = sq_gethash(vm, 2);
// Should we include the iterations in the criteria?
if (top > 3)
{
SQInteger intrv = 0;
// Grab the interval from the stack
res = sq_getinteger(vm, 3, &intrv);
// Validate the result
if (SQ_FAILED(res))
{
return res; // Propagate the error
}
// Attempt to find the requested task
for (const auto & t : s_Tasks)
{
if (t.mHash == chash && t.mEntity == id && t.mType == type && t.mInterval == intrv)
{
pos = static_cast< SQInteger >(&t - s_Tasks); // Store the index of this element
}
}
}
// Should we include the interval in the criteria?
else if (top > 2)
{
SQInteger intrv = 0, sqitr = 0;
// Grab the interval from the stack
res = sq_getinteger(vm, 3, &intrv);
// Validate the result
if (SQ_FAILED(res))
{
return res; // Propagate the error
}
// Grab the iterations from the stack
res = sq_getinteger(vm, 4, &sqitr);
// Validate the result
if (SQ_FAILED(res))
{
return res; // Propagate the error
}
// Cast iterations to the right type
const Iterator itr = ConvTo< Iterator >::From(sqitr);
// Attempt to find the requested task
for (const auto & t : s_Tasks)
{
if (t.mHash == chash && t.mEntity == id && t.mType == type && t.mInterval == intrv && t.mIterations == itr)
{
pos = static_cast< SQInteger >(&t - s_Tasks); // Store the index of this element
}
}
}
else
{
// Attempt to find the requested task
for (const auto & t : s_Tasks)
{
if (t.mHash == chash && t.mEntity == id && t.mType == type)
{
pos = static_cast< SQInteger >(&t - s_Tasks); // Store the index of this element
}
}
}
// We could not find such task
return res;
}
// ------------------------------------------------------------------------------------------------
SQInteger Tasks::Remove(Int32 id, Int32 type, HSQUIRRELVM vm)
{
// Default to not found
SQInteger pos = -1;
// Perform a search
SQRESULT res = Find(id, type, pos, vm);
// Did the search failed?
if (SQ_FAILED(res))
{
return res; // Propagate the error
}
// Did we find anything?
else if (pos < 0)
{
return sq_throwerror(vm, "Unable to locate such task");
}
else
{
// Release task resources
s_Tasks[pos].Terminate();
// Reset the timer
s_Intervals[pos] = 0;
}
// Specify that we don't return anything
return 0;
}
// ------------------------------------------------------------------------------------------------
SQInteger Tasks::Exists(Int32 id, Int32 type, HSQUIRRELVM vm)
{
// Default to not found
SQInteger pos = -1;
// Perform a search
SQRESULT res = Find(id, type, pos, vm);
// Did the search failed?
if (SQ_FAILED(res))
{
return res; // Propagate the error
}
// Push a boolean on whether this task was found
sq_pushbool(vm, pos >= 0);
// Specify that we're returning a value
return 1;
}
// ------------------------------------------------------------------------------------------------
const Tasks::Task & Tasks::FindByTag(Int32 id, Int32 type, const StackStrF & tag)
{
// Attempt to find the requested task
for (const auto & t : s_Tasks)
{
if (t.mEntity == id && t.mType == type && t.mTag.compare(tag.mPtr) == 0)
{
return t; // Return this task instance
}
}
// Unable to find such task
STHROWF("Unable to find a task with tag (%s)", tag.mPtr);
// Should not reach this point but if it did, we have to return something
return s_Tasks[SQMOD_MAX_TASKS]; // Intentional Buffer overflow!
}
// ------------------------------------------------------------------------------------------------
void Tasks::Cleanup(Int32 id, Int32 type)
{
for (auto & t : s_Tasks)
{
if (t.mEntity == id && t.mType == type)
{
t.Terminate();
// Also disable the timer
s_Intervals[&t - s_Tasks] = 0;
}
}
}
/* ------------------------------------------------------------------------------------------------
* Forward the call to process tasks.
*/
void ProcessTasks()
{
Tasks::Process();
}
/* ------------------------------------------------------------------------------------------------
* Forward the call to initialize tasks.
*/
void InitializeTasks()
{
Tasks::Initialize();
}
/* ------------------------------------------------------------------------------------------------
* Forward the call to register tasks.
*/
void RegisterTask(HSQUIRRELVM vm)
{
Tasks::Register(vm);
}
/* ------------------------------------------------------------------------------------------------
* Forward the call to terminate tasks.
*/
void TerminateTasks()
{
Tasks::Deinitialize();
}
/* ------------------------------------------------------------------------------------------------
* Forward the call to cleanup certain tasks.
*/
void CleanupTasks(Int32 id, Int32 type)
{
Tasks::Cleanup(id, type);
}
} // Namespace:: SqMod

527
source/Misc/Tasks.hpp Normal file
View File

@@ -0,0 +1,527 @@
#ifndef _TASKS_HPP_
#define _TASKS_HPP_
// ------------------------------------------------------------------------------------------------
#include "Base/Shared.hpp"
// ------------------------------------------------------------------------------------------------
namespace SqMod {
/* ------------------------------------------------------------------------------------------------
* Execute callbacks for specific entities after specific intervals of time.
*/
class Tasks
{
public:
/* --------------------------------------------------------------------------------------------
* Simplify future changes to a single point of change.
*/
typedef Int64 Time;
typedef SQInteger Interval;
typedef Uint32 Iterator;
typedef LightObj Argument;
private:
/* --------------------------------------------------------------------------------------------
* Structure that represents a task and keeps track of the task information.
*/
struct Task
{
// ----------------------------------------------------------------------------------------
SQHash mHash; // The hash of the referenced function object.
String mTag; // An arbitrary string which represents the tag.
LightObj mSelf; // A reference to `this`as a script object.
LightObj mFunc; // A reference to the managed function object.
LightObj mInst; // A reference to the associated entity object.
LightObj mData; // A reference to the arbitrary data associated with this instance.
Iterator mIterations; // Number of iterations before self destruct.
Interval mInterval; // Interval between task invocations.
Int16 mEntity; // The identifier of the entity to which is belongs.
Uint8 mType; // The type of the entity to which is belongs.
Uint8 mArgc; // The number of arguments that the task must forward.
Argument mArgv[8]; // The arguments that the task must forward.
/* ----------------------------------------------------------------------------------------
* Default constructor.
*/
Task()
: mHash(0)
, mTag()
, mSelf()
, mFunc()
, mInst()
, mData()
, mIterations(0)
, mInterval(0)
, mEntity(-1)
, mType(-1)
, mArgc(0)
, mArgv()
{
/* ... */
}
/* ----------------------------------------------------------------------------------------
* Copy constructor. (disabled)
*/
Task(const Task & o) = delete;
/* ----------------------------------------------------------------------------------------
* Move constructor. (disabled)
*/
Task(Task && o) = delete;
/* ----------------------------------------------------------------------------------------
* Release managed script resources.
*/
~Task()
{
Terminate();
}
/* ----------------------------------------------------------------------------------------
* Copy assignment operator. (disabled)
*/
Task & operator = (const Task & o) = delete;
/* ----------------------------------------------------------------------------------------
* Move assignment operator. (disabled)
*/
Task & operator = (Task && o) = delete;
/* ----------------------------------------------------------------------------------------
* Used by the script engine to convert an instance of this type to a string.
*/
const String & ToString() const
{
return mTag;
}
/* ----------------------------------------------------------------------------------------
* Initializes the task parameters. (assumes previous values are already released)
*/
void Init(HSQOBJECT & inst, HSQOBJECT & func, Interval intrv, Iterator itr, Int32 id, Int32 type);
/* ----------------------------------------------------------------------------------------
* Release managed script resources.
*/
void Release();
/* ----------------------------------------------------------------------------------------
* Execute the managed task.
*/
Interval Execute();
/* ----------------------------------------------------------------------------------------
* Clear the arguments.
*/
void Clear()
{
// Now release the arguments
for (auto & a : mArgv)
{
a.Release();
}
// Reset the counter
mArgc = 0;
}
/* ----------------------------------------------------------------------------------------
* Terminate the task.
*/
void Terminate()
{
Release();
Clear();
}
/* ----------------------------------------------------------------------------------------
* Retrieve the associated user tag.
*/
const String & GetTag() const
{
return mTag;
}
/* ----------------------------------------------------------------------------------------
* Modify the associated user tag.
*/
void SetTag(const StackStrF & tag)
{
mTag.assign(tag.mPtr, ClampMin(tag.mLen, 0));
}
/* ----------------------------------------------------------------------------------------
* Retrieve the instance to entity instance.
*/
const LightObj & GetInst() const
{
return mInst;
}
/* ----------------------------------------------------------------------------------------
* Retrieve the function object.
*/
const LightObj & GetFunc() const
{
return mFunc;
}
/* ----------------------------------------------------------------------------------------
* Modify the function object.
*/
void SetFunc(const Function & func)
{
// Validate the specified
if (!sq_isclosure(func.GetFunc()) && !sq_isnativeclosure(func.GetFunc()))
{
STHROWF("Invalid callback type %s", SqTypeName(mFunc.GetType()));
}
// Grab the virtual machine once
HSQUIRRELVM vm = DefaultVM::Get();
// Remember the current stack size
const StackGuard sg(vm);
// Push the callback on the stack
sq_pushobject(vm, func.GetFunc());
// Grab the hash of the callback object
mHash = sq_gethash(vm, -1);
// Now store the function without the environment
mFunc = LightObj(func.GetFunc());
}
/* ----------------------------------------------------------------------------------------
* Retrieve the arbitrary user data object.
*/
const LightObj & GetData() const
{
return mData;
}
/* ----------------------------------------------------------------------------------------
* Modify the arbitrary user data object.
*/
void SetData(const LightObj & data)
{
mData = data;
}
/* ----------------------------------------------------------------------------------------
* Retrieve the execution interval.
*/
SQInteger GetInterval() const
{
return ConvTo< SQInteger >::From(mInterval);
}
/* ----------------------------------------------------------------------------------------
* Modify the execution interval.
*/
void SetInterval(SQInteger itr)
{
mInterval = ClampMin(ConvTo< Interval >::From(itr), static_cast< Interval >(0));
}
/* ----------------------------------------------------------------------------------------
* Retrieve the number of iterations.
*/
SQInteger GetIterations() const
{
return ConvTo< SQInteger >::From(mIterations);
}
/* ----------------------------------------------------------------------------------------
* Modify the number of iterations.
*/
void SetIterations(SQInteger itr)
{
mIterations = ConvTo< Iterator >::From(itr);
}
/* ----------------------------------------------------------------------------------------
* Retrieve the number of arguments to be forwarded.
*/
SQInteger GetArguments() const
{
return ConvTo< SQInteger >::From(mArgc);
}
/* ----------------------------------------------------------------------------------------
* Retrieve a certain argument.
*/
const Argument & GetArgument(SQInteger arg) const
{
constexpr Uint32 argvn = (sizeof(mArgv) / sizeof(mArgv[0]));
// Cast the index to the proper value
Uint8 idx = ConvTo< Uint8 >::From(arg);
// Validate the specified index
if (idx >= argvn)
{
STHROWF("The specified index is out of range: %u >= %u", idx, argvn);
}
// Return the requested argument
return mArgv[idx];
}
};
// --------------------------------------------------------------------------------------------
static Time s_Last; // Last time point.
static Time s_Prev; // Previous time point.
static Interval s_Intervals[SQMOD_MAX_TASKS]; // List of intervals to be processed.
static Task s_Tasks[SQMOD_MAX_TASKS]; // List of tasks to be executed.
/* --------------------------------------------------------------------------------------------
* Default constructor. (disabled)
*/
Tasks() = delete;
/* --------------------------------------------------------------------------------------------
* Copy constructor. (disabled)
*/
Tasks(const Tasks & o) = delete;
/* --------------------------------------------------------------------------------------------
* Move constructor. (disabled)
*/
Tasks(Tasks && o) = delete;
/* --------------------------------------------------------------------------------------------
* Destructor. (disabled)
*/
~Tasks() = delete;
/* --------------------------------------------------------------------------------------------
* Copy assignment operator. (disabled)
*/
Tasks & operator = (const Tasks & o) = delete;
/* --------------------------------------------------------------------------------------------
* Move assignment operator. (disabled)
*/
Tasks & operator = (Tasks && o) = delete;
public:
/* --------------------------------------------------------------------------------------------
* Process all active tasks and update elapsed time.
*/
static void Process();
/* --------------------------------------------------------------------------------------------
* Initialize all resources and prepare for startup.
*/
static void Initialize();
/* --------------------------------------------------------------------------------------------
* Register the task class.
*/
static void Register(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Release all resources and prepare for shutdown.
*/
static void Deinitialize();
protected:
/* --------------------------------------------------------------------------------------------
* Retrieve the instance of the specified entity.
*/
static LightObj & FindEntity(Int32 id, Int32 type);
/* --------------------------------------------------------------------------------------------
* Find an unoccupied task slot.
*/
static SQInteger FindUnused();
/* --------------------------------------------------------------------------------------------
* Locate the first task with the specified parameters.
*/
static SQInteger Find(Int32 id, Int32 type, SQInteger & pos, HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Attempt to create a task with the specified parameters.
*/
static SQInteger Create(Int32 id, Int32 type, HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Attempt to remove the task with the specified parameters.
*/
static SQInteger Remove(Int32 id, Int32 type, HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* See if a task with the specified parameters exists.
*/
static SQInteger Exists(Int32 id, Int32 type, HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Cleanup all tasks associated with the specified entity.
*/
static const Task & FindByTag(Int32 id, Int32 type, const StackStrF & tag);
public:
/* --------------------------------------------------------------------------------------------
* Retrieve the number of used tasks slots.
*/
static SQInteger GetUsed()
{
SQInteger n = 0;
// Iterate task list
for (const auto & t : s_Tasks)
{
if (VALID_ENTITY(t.mEntity))
{
++n;
}
}
// Return the final count
return n;
}
/* --------------------------------------------------------------------------------------------
* Cleanup all tasks associated with the specified entity.
*/
static void Cleanup(Int32 id, Int32 type);
/* --------------------------------------------------------------------------------------------
* Forwards calls to create tasks.
*/
template < typename Entity, Int32 Type > static SQInteger MakeTask(HSQUIRRELVM vm)
{
// The entity instance
const Entity * inst = nullptr;
// Attempt to extract the instance
try
{
// Fetch the instance from the stack
inst = Var< const Entity * >(vm, 1).value;
// Do we have a valid instance?
if (!inst)
{
STHROWF("Invalid entity instance");
}
// Validate the actual entity instance
inst->Validate();
}
catch (const Sqrat::Exception & e)
{
return sq_throwerror(vm, e.what());
}
// Forward the call and return the result
return Create(inst->GetID(), Type, vm);
}
/* --------------------------------------------------------------------------------------------
* Forwards calls to remove tasks.
*/
template < typename Entity, Int32 Type > static SQInteger DropTask(HSQUIRRELVM vm)
{
// The entity instance
const Entity * inst = nullptr;
// Attempt to extract the instance
try
{
// Fetch the instance from the stack
inst = Var< const Entity * >(vm, 1).value;
// Do we have a valid instance?
if (!inst)
{
STHROWF("Invalid entity instance");
}
// Validate the actual entity instance
inst->Validate();
}
catch (const Sqrat::Exception & e)
{
return sq_throwerror(vm, e.what());
}
// Forward the call and return the result
return Remove(inst->GetID(), Type, vm);
}
/* --------------------------------------------------------------------------------------------
* Forwards calls to check tasks.
*/
template < typename Entity, Int32 Type > static SQInteger DoesTask(HSQUIRRELVM vm)
{
// The entity instance
const Entity * inst = nullptr;
// Attempt to extract the instance
try
{
// Fetch the instance from the stack
inst = Var< const Entity * >(vm, 1).value;
// Do we have a valid instance?
if (!inst)
{
STHROWF("Invalid entity instance");
}
// Validate the actual entity instance
inst->Validate();
}
catch (const Sqrat::Exception & e)
{
return sq_throwerror(vm, e.what());
}
// Forward the call and return the result
return Exists(inst->GetID(), Type, vm);
}
/* --------------------------------------------------------------------------------------------
* Forwards calls to find tasks.
*/
template < typename Entity, Int32 Type > static SQInteger FindTask(HSQUIRRELVM vm)
{
// Was the tag string specified?
if (sq_gettop(vm) <= 1)
{
return sq_throwerror(vm, "Missing tag string");
}
// The entity instance
const Entity * inst = nullptr;
// Attempt to extract the instance
try
{
// Fetch the instance from the stack
inst = Var< const Entity * >(vm, 1).value;
// Do we have a valid instance?
if (!inst)
{
STHROWF("Invalid entity instance");
}
// Validate the actual entity instance
inst->Validate();
}
catch (const Sqrat::Exception & e)
{
return sq_throwerror(vm, e.what());
}
// Attempt to generate the string value
const StackStrF tag(vm, 2, true);
// Have we failed to retrieve the string?
if (SQ_FAILED(tag.mRes))
{
return tag.mRes; // Propagate the error!
}
// Attempt to find the specified task
try
{
// Perform the search
const Task & task = FindByTag(inst->GetID(), Type, tag);
// Now push the instance on the stack
sq_pushobject(vm, task.mSelf.mObj);
}
catch (const Sqrat::Exception & e)
{
return sq_throwerror(vm, e.what());
}
// Specify that this function returns a value
return 1;
}
};
} // Namespace:: SqMod
#endif // _TASKS_HPP_