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
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
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_
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+926
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
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
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_
File diff suppressed because it is too large Load Diff
+767
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
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
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_