mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2025-10-29 21:37:18 +01:00
Restructure some files.
This commit is contained in:
476
source/Misc/Areas.cpp
Normal file
476
source/Misc/Areas.cpp
Normal file
@@ -0,0 +1,476 @@
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
#include "Areas.hpp"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
#include <algorithm>
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
namespace SqMod {
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQMODE_DECL_TYPENAME(AreaTypename, _SC("SqArea"))
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
AreaManager AreaManager::s_Inst;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Area::AddArray(const Sqrat::Array & a)
|
||||
{
|
||||
float values[2];
|
||||
|
||||
a.Foreach([this, &values, n = int(0)](HSQUIRRELVM vm) mutable -> bool {
|
||||
// Retrieve the type of the value
|
||||
const SQObjectType type = sq_gettype(vm, -1);
|
||||
// Are we dealing with a vector?
|
||||
if (type == OT_INSTANCE)
|
||||
{
|
||||
// Next time, start again for floats
|
||||
n = 0;
|
||||
// Grab the instance from the stack
|
||||
this->AddPoint(*ClassType< Vector2 >::GetInstance(vm, -1));
|
||||
}
|
||||
else if (type & SQOBJECT_NUMERIC)
|
||||
{
|
||||
// Retrieve the value from the stack
|
||||
values[n] = Var< float >(vm, -1).value;
|
||||
// Do we have enough to form a vector?
|
||||
if (++n == 2)
|
||||
{
|
||||
this->AddPointEx(values[0], values[1]);
|
||||
// Reset the counter
|
||||
n = 0;
|
||||
}
|
||||
}
|
||||
// Ignore anything else
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
bool Area::Manage()
|
||||
{
|
||||
// Are we connected to any cells?
|
||||
if (!mCells.empty())
|
||||
{
|
||||
STHROWF("The area is already managed");
|
||||
}
|
||||
// We expect this to be called only from the script so that the first parameter in the vm
|
||||
// is the area instance
|
||||
LightObj obj(1, DefaultVM::Get());
|
||||
// Attempt to manage this area
|
||||
AreaManager::Get().InsertArea(*this, obj);
|
||||
// Return whether the area is now managed by any cells
|
||||
return !mCells.empty();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
bool Area::Unmanage()
|
||||
{
|
||||
// Are we connected to any cells?
|
||||
if (mCells.empty())
|
||||
{
|
||||
return true; // Already unmanaged
|
||||
}
|
||||
// Attempt to unmanage this area
|
||||
AreaManager::Get().RemoveArea(*this);
|
||||
// Return whether the area is not managed by any cells
|
||||
return mCells.empty();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
bool Area::IsInside(float x, float y) const
|
||||
{
|
||||
// Is the an area to test?
|
||||
if (mPoints.size() < 3)
|
||||
{
|
||||
return false; // Can't possibly be in an area that doesn't exist
|
||||
}
|
||||
// http://sidvind.com/wiki/Point-in-polygon:_Jordan_Curve_Theorem
|
||||
// The points creating the polygon
|
||||
float x1, x2;
|
||||
// How many times the ray crosses a line segment
|
||||
int crossings = 0;
|
||||
// Iterate through each line
|
||||
for (Uint32 i = 0, n = static_cast< Uint32 >(mPoints.size()); i < n; ++i)
|
||||
{
|
||||
Points::const_reference a = mPoints[i];
|
||||
Points::const_reference b = mPoints[(i + 1) % n];
|
||||
// This is done to ensure that we get the same result when
|
||||
// the line goes from left to right and right to left.
|
||||
if (a.x < b.x)
|
||||
{
|
||||
x1 = a.x;
|
||||
x2 = b.x;
|
||||
}
|
||||
else
|
||||
{
|
||||
x1 = b.x;
|
||||
x2 = a.x;
|
||||
}
|
||||
// First check if the ray is able to cross the line
|
||||
if (x > x1 && x <= x2 && (y < a.y || y <= b.y))
|
||||
{
|
||||
// Calculate the equation of the line
|
||||
const float dx = (b.x - a.x);
|
||||
const float dy = (b.y - a.y);
|
||||
float k;
|
||||
|
||||
if (fabs(dx) < 0.000001f)
|
||||
{
|
||||
k = 0xffffffff;
|
||||
}
|
||||
else
|
||||
{
|
||||
k = (dy / dx);
|
||||
}
|
||||
|
||||
const float m = (a.y - k * a.x);
|
||||
const float y2 = (k * x + m);
|
||||
// Does the ray cross the line?
|
||||
if (y <= y2)
|
||||
{
|
||||
++crossings;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Return if the crossings are not even
|
||||
return (crossings % 2 == 1);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
AreaManager::AreaManager(size_t sz)
|
||||
: m_Queue(), m_ProcList(), m_Grid{}
|
||||
{
|
||||
// Negative half grid size (left)
|
||||
int l = (-GRIDH * CELLD);
|
||||
// Positive half grid size minus one cell (bottom)
|
||||
int b = (abs(l) - CELLD);
|
||||
// Negative half grid size minus one cell (right)
|
||||
int r = (l + CELLD);
|
||||
// Positive half grid size (top)
|
||||
int t = abs(l);
|
||||
// Initialize the grid cells
|
||||
for (int y = 0; y < GRIDN; ++y)
|
||||
{
|
||||
for (int x = 0; x < GRIDN; ++x)
|
||||
{
|
||||
// Grab a reference to the cell
|
||||
AreaCell & c = m_Grid[y][x];
|
||||
// Configure the range of the cell
|
||||
c.mL = static_cast< float >(l);
|
||||
c.mB = static_cast< float >(b);
|
||||
c.mR = static_cast< float >(r);
|
||||
c.mT = static_cast< float >(t);
|
||||
// Reserve area memory if requested
|
||||
c.mAreas.reserve(sz);
|
||||
// Reset the locks on this area
|
||||
c.mLocks = 0;
|
||||
// Advance the left side
|
||||
l = r;
|
||||
// Advance the right side
|
||||
r += CELLD;
|
||||
// Should we advance to the next row?
|
||||
if (r > (GRIDH * CELLD))
|
||||
{
|
||||
// Reset the left side
|
||||
l = (-GRIDH * CELLD);
|
||||
// Reset the right side
|
||||
r = (l + CELLD);
|
||||
// Advance the bottom
|
||||
b -= CELLD;
|
||||
// Advance the top
|
||||
t -= CELLD;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Reserve some space in the queue
|
||||
m_Queue.reserve(128);
|
||||
m_ProcList.reserve(128);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void AreaManager::Insert(AreaCell & c, Area & a, LightObj & obj)
|
||||
{
|
||||
// Is this cell currently locked?
|
||||
if (c.mLocks)
|
||||
{
|
||||
m_Queue.emplace_back(c, a, obj); // Queue this request for now
|
||||
}
|
||||
else
|
||||
{
|
||||
c.mAreas.emplace_back(&a, obj);
|
||||
}
|
||||
// Associate the area with this cell so it can't be managed again (even while in the queue)
|
||||
a.mCells.push_back(&c);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void AreaManager::Remove(AreaCell & c, Area & a)
|
||||
{
|
||||
// Is this cell currently locked?
|
||||
if (c.mLocks)
|
||||
{
|
||||
m_Queue.emplace_back(c, a); // Queue this request for now
|
||||
}
|
||||
else
|
||||
{
|
||||
// Attempt to locate this area in the cell
|
||||
AreaCell::Areas::iterator itr = std::find_if(c.mAreas.begin(), c.mAreas.end(),
|
||||
[&a](AreaCell::Areas::reference p) -> bool {
|
||||
return (p.first == &a);
|
||||
});
|
||||
// Have we found it?
|
||||
if (itr != c.mAreas.end())
|
||||
{
|
||||
c.mAreas.erase(itr); // Erase it
|
||||
}
|
||||
}
|
||||
// Dissociate the area with this cell so it can be managed again (even while in the queue)
|
||||
Area::Cells::iterator itr = std::find(a.mCells.begin(), a.mCells.end(), &c);
|
||||
// Was is associated?
|
||||
if (itr != a.mCells.end())
|
||||
{
|
||||
a.mCells.erase(itr); // Dissociate them
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void AreaManager::ProcQueue()
|
||||
{
|
||||
// Look for actions that can be completed
|
||||
for (Queue::iterator itr = m_Queue.begin(); itr != m_Queue.end(); ++itr)
|
||||
{
|
||||
// Was this cell unlocked in the meantime?
|
||||
if (itr->mCell->mLocks <= 0)
|
||||
{
|
||||
m_ProcList.push_back(itr);
|
||||
}
|
||||
}
|
||||
// Process the actions that are ready
|
||||
for (auto & itr : m_ProcList)
|
||||
{
|
||||
// Was this a remove request?
|
||||
if (itr->mObj.IsNull())
|
||||
{
|
||||
Remove(*(itr->mCell), *(itr->mArea));
|
||||
}
|
||||
else
|
||||
{
|
||||
Insert(*(itr->mCell), *(itr->mArea), itr->mObj);
|
||||
}
|
||||
}
|
||||
// Remove processed requests
|
||||
for (auto & itr : m_ProcList)
|
||||
{
|
||||
m_Queue.erase(itr);
|
||||
}
|
||||
// Actions were processed
|
||||
m_ProcList.clear();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void AreaManager::Clear()
|
||||
{
|
||||
// Clear the cells
|
||||
for (AreaCell (&row)[GRIDN] : m_Grid)
|
||||
{
|
||||
for (AreaCell & c : row)
|
||||
{
|
||||
c.mAreas.clear();
|
||||
}
|
||||
}
|
||||
// Clear the queue as well
|
||||
m_Queue.clear();
|
||||
m_ProcList.clear();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void AreaManager::InsertArea(Area & a, LightObj & obj)
|
||||
{
|
||||
// See if this area is already managed
|
||||
if (!a.mCells.empty() || a.mPoints.empty())
|
||||
{
|
||||
return; // Already managed or nothing to manage
|
||||
}
|
||||
// Go through each cell and check if the area touches it
|
||||
for (int y = 0; y < GRIDN; ++y)
|
||||
{
|
||||
for (int x = 0; x < GRIDN; ++x)
|
||||
{
|
||||
AreaCell & c = m_Grid[y][x];
|
||||
// Does the bounding box of this cell intersect with the one of the area?
|
||||
if (a.mL <= c.mR && c.mL <= a.mR && a.mB <= c.mT && c.mB <= a.mT)
|
||||
{
|
||||
Insert(c, a, obj); // Attempt to insert the area into this cell
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void AreaManager::RemoveArea(Area & a)
|
||||
{
|
||||
// Just remove the associated cells
|
||||
for (auto c : a.mCells)
|
||||
{
|
||||
Remove(*c, a);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Vector2i AreaManager::LocateCell(float x, float y)
|
||||
{
|
||||
// Transform the world coordinates into a cell coordinates
|
||||
// and cast to integral after rounding the value
|
||||
int xc = static_cast< int >(std::round(x / CELLD));
|
||||
int yc = static_cast< int >(std::round(y / CELLD));
|
||||
// Grab the absolute cell coordinates for range checking
|
||||
const int xca = std::abs(xc);
|
||||
const int yca = std::abs(yc);
|
||||
// Make sure the cell coordinates are within range
|
||||
if (xca > (GRIDH+1) || yca > (GRIDH+1))
|
||||
{
|
||||
return Vector2i(NOCELL, NOCELL); // Out of our scanning area
|
||||
}
|
||||
// Clamp the x coordinate if necessary
|
||||
if (xca >= (GRIDH))
|
||||
{
|
||||
xc = xc < 0 ? -(GRIDH-1) : (GRIDH-1);
|
||||
}
|
||||
// Clamp the y coordinate if necessary
|
||||
if (yca >= (GRIDH))
|
||||
{
|
||||
yc = xc < 0 ? -(GRIDH-1) : (GRIDH-1);
|
||||
}
|
||||
// Return the identified cell row and column
|
||||
return Vector2i(GRIDH+xc, GRIDH-yc);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
static void Areas_TestPointEx(Object & env, Function & func, float x, float y)
|
||||
{
|
||||
// Is the function valid?
|
||||
if (func.IsNull())
|
||||
{
|
||||
STHROWF("Invalid callback object");
|
||||
}
|
||||
// Should we use a custom environment?
|
||||
else if (!env.IsNull())
|
||||
{
|
||||
func = Function(env.GetVM(), env, func.GetFunc());
|
||||
}
|
||||
// Begin testing
|
||||
AreaManager::Get().TestPoint([&func](AreaCell::Areas::reference ap) -> void {
|
||||
func.Execute(ap.second);
|
||||
}, x, y);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
static void Areas_TestPoint(Object & env, Function & func, const Vector2 & v)
|
||||
{
|
||||
Areas_TestPointEx(env, func, v.x, v.y);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
static void Areas_TestPointOnEx(Object & ctx, Object & env, Function & func, float x, float y)
|
||||
{
|
||||
// Is the function valid?
|
||||
if (func.IsNull())
|
||||
{
|
||||
STHROWF("Invalid callback object");
|
||||
}
|
||||
// Should we use a custom environment?
|
||||
else if (!env.IsNull())
|
||||
{
|
||||
func = Function(env.GetVM(), env, func.GetFunc());
|
||||
}
|
||||
// Begin testing
|
||||
AreaManager::Get().TestPoint([&ctx, &func](AreaCell::Areas::reference ap) -> void {
|
||||
func.Execute(ctx, ap.second);
|
||||
}, x, y);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
static void Areas_TestPointOn(Object & ctx, Object & env, Function & func, const Vector2 & v)
|
||||
{
|
||||
Areas_TestPointOnEx(ctx, env, func, v.x, v.y);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
static Vector2i Areas_LocatePointCell(const Vector2 & v)
|
||||
{
|
||||
return AreaManager::Get().LocateCell(v.x, v.y);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
static Vector2i Areas_LocatePointCellEx(float x, float y)
|
||||
{
|
||||
return AreaManager::Get().LocateCell(x, y);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TerminateAreas()
|
||||
{
|
||||
AreaManager::Get().Clear();
|
||||
}
|
||||
|
||||
// ================================================================================================
|
||||
void Register_Areas(HSQUIRRELVM vm)
|
||||
{
|
||||
RootTable(vm).Bind(_SC("SqArea"),
|
||||
Class< Area >(vm, AreaTypename::Str)
|
||||
// Constructors
|
||||
.Ctor()
|
||||
.FmtCtor()
|
||||
.FmtCtor< SQInteger >()
|
||||
.Ctor< const Vector2 &, const Vector2 &, const Vector2 & >()
|
||||
.FmtCtor< const Vector2 &, const Vector2 &, const Vector2 & >()
|
||||
.FmtCtor< const Vector2 &, const Vector2 &, const Vector2 &, SQInteger >()
|
||||
.Ctor< float, float, float, float, float, float >()
|
||||
.FmtCtor< float, float, float, float, float, float >()
|
||||
.FmtCtor< float, float, float, float, float, float, SQInteger >()
|
||||
// Meta-methods
|
||||
.SquirrelFunc(_SC("_typename"), &AreaTypename::Fn)
|
||||
.Func(_SC("_tostring"), &Area::ToString)
|
||||
// Member Properties
|
||||
.Prop(_SC("Name"), &Area::GetName, &Area::SetName)
|
||||
.Prop(_SC("ID"), &Area::GetID, &Area::SetID)
|
||||
.Prop(_SC("Locked"), &Area::IsLocked)
|
||||
.Prop(_SC("IsLocked"), &Area::IsLocked)
|
||||
.Prop(_SC("Center"), &Area::GetCenter)
|
||||
.Prop(_SC("Box"), &Area::GetBoundingBox, &Area::SetBoundingBox)
|
||||
.Prop(_SC("Empty"), &Area::Empty)
|
||||
.Prop(_SC("Empty"), &Area::Empty)
|
||||
.Prop(_SC("Size"), &Area::Size)
|
||||
.Prop(_SC("Points"), &Area::Size)
|
||||
.Prop(_SC("Capacity"), &Area::Capacity)
|
||||
// Member Methods
|
||||
.FmtFunc(_SC("SetName"), &Area::ApplyName)
|
||||
.Func(_SC("SetID"), &Area::ApplyID)
|
||||
.Func(_SC("Clear"), &Area::Clear)
|
||||
.Func(_SC("Reserve"), &Area::Reserve)
|
||||
.Func(_SC("SetBox"), &Area::SetBoundingBoxEx)
|
||||
.Func(_SC("SetBoundingBox"), &Area::SetBoundingBoxEx)
|
||||
.Func(_SC("Add"), &Area::AddPoint)
|
||||
.Func(_SC("AddEx"), &Area::AddPointEx)
|
||||
.Func(_SC("AddVirtual"), &Area::AddVirtualPoint)
|
||||
.Func(_SC("AddVirtualEx"), &Area::AddVirtualPointEx)
|
||||
.Func(_SC("AddFake"), &Area::AddVirtualPoint)
|
||||
.Func(_SC("AddFakeEx"), &Area::AddVirtualPointEx)
|
||||
.Func(_SC("AddArray"), &Area::AddArray)
|
||||
.Func(_SC("Test"), &Area::Test)
|
||||
.Func(_SC("TestEx"), &Area::TestEx)
|
||||
.Func(_SC("Manage"), &Area::Manage)
|
||||
.Func(_SC("Unmanage"), &Area::Unmanage)
|
||||
// Static Functions
|
||||
.StaticFunc(_SC("GlobalTest"), &Areas_TestPoint)
|
||||
.StaticFunc(_SC("GlobalTestEx"), &Areas_TestPointEx)
|
||||
.StaticFunc(_SC("GlobalTestOn"), &Areas_TestPointOn)
|
||||
.StaticFunc(_SC("GlobalTestOnEx"), &Areas_TestPointOnEx)
|
||||
.StaticFunc(_SC("LocatePointCell"), &Areas_LocatePointCell)
|
||||
.StaticFunc(_SC("LocatePointCellEx"), &Areas_LocatePointCellEx)
|
||||
.StaticFunc(_SC("UnmanageAll"), &TerminateAreas)
|
||||
);
|
||||
}
|
||||
|
||||
} // Namespace:: SqMod
|
||||
716
source/Misc/Areas.hpp
Normal file
716
source/Misc/Areas.hpp
Normal file
@@ -0,0 +1,716 @@
|
||||
#ifndef _AREAS_HPP_
|
||||
#define _AREAS_HPP_
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
#include "Base/Shared.hpp"
|
||||
#include "Base/Vector2.hpp"
|
||||
#include "Base/Vector4.hpp"
|
||||
#include "Base/Vector2i.hpp"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
namespace SqMod {
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Various information associated with an area cell.
|
||||
*/
|
||||
struct AreaCell
|
||||
{
|
||||
// --------------------------------------------------------------------------------------------
|
||||
typedef std::pair< Area *, LightObj > AreaPair; // A reference to an area object.
|
||||
typedef std::vector< AreaPair > Areas; // A list of area objects.
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
float mL, mB, mR, mT; // Left-Bottom, Right-Top components of the cell bounding box.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
Areas mAreas; // Areas that intersect with the cell.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
int mLocks; // The amount of locks on the cell.
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Default constructor.
|
||||
*/
|
||||
AreaCell()
|
||||
: mL(0), mB(0), mR(0), mT(0), mAreas(0), mLocks(0)
|
||||
{
|
||||
//...
|
||||
}
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Area implementation used to store area points.
|
||||
*/
|
||||
struct Area
|
||||
{
|
||||
typedef std::vector< Vector2 > Points;
|
||||
typedef std::vector< AreaCell * > Cells;
|
||||
// --------------------------------------------------------------------------------------------
|
||||
static constexpr float DEF_L = std::numeric_limits< float >::infinity();
|
||||
static constexpr float DEF_B = std::numeric_limits< float >::infinity();
|
||||
static constexpr float DEF_R = -std::numeric_limits< float >::infinity();
|
||||
static constexpr float DEF_T = -std::numeric_limits< float >::infinity();
|
||||
// --------------------------------------------------------------------------------------------
|
||||
float mL, mB, mR, mT; // Left-Bottom, Right-Top components of the bounding box.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
Points mPoints; // Collection of points that make up the area.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
SQInteger mID; // The user identifier given to this area.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
Cells mCells; // The cells covered by this area.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
String mName; // The user name given to this area.
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Default constructor.
|
||||
*/
|
||||
Area()
|
||||
: mL(DEF_L), mB(DEF_B), mR(DEF_R), mT(DEF_T), mPoints(), mID(0), mCells(), mName()
|
||||
{
|
||||
//...
|
||||
}
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Named constructor.
|
||||
*/
|
||||
Area(const StackStrF & name)
|
||||
: Area(16, name)
|
||||
{
|
||||
//...
|
||||
}
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Default constructor.
|
||||
*/
|
||||
Area(SQInteger sz, const StackStrF & name)
|
||||
: mL(DEF_L), mB(DEF_B), mR(DEF_R), mT(DEF_T), mPoints(), mID(0), mCells()
|
||||
, mName(name.mPtr, name.mLen <= 0 ? 0 : name.mLen)
|
||||
|
||||
{
|
||||
// Should we reserve some space for points in advance?
|
||||
if (sz > 0)
|
||||
{
|
||||
mPoints.reserve(static_cast< size_t >(sz));
|
||||
}
|
||||
}
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Vector2 constructor.
|
||||
*/
|
||||
Area(const Vector2 & a, const Vector2 & b, const Vector2 & c)
|
||||
: Area(a.x, a.y, b.x, b.y, c.x, c.y, 16, StackStrF())
|
||||
{
|
||||
//...
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Vector2 constructor with name.
|
||||
*/
|
||||
Area(const Vector2 & a, const Vector2 & b, const Vector2 & c, const StackStrF & name)
|
||||
: Area(a.x, a.y, b.x, b.y, c.x, c.y, 16, name)
|
||||
{
|
||||
//...
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Vector2 constructor with name and memory to reserve.
|
||||
*/
|
||||
Area(const Vector2 & a, const Vector2 & b, const Vector2 & c, SQInteger sz, const StackStrF & name)
|
||||
: Area(a.x, a.y, b.x, b.y, c.x, c.y, sz, name)
|
||||
{
|
||||
//...
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Extended constructor.
|
||||
*/
|
||||
Area(float ax, float ay, float bx, float by, float cx, float cy)
|
||||
: Area(ax, ay, bx, by, cx, cy, 16, StackStrF())
|
||||
{
|
||||
//...
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Extended constructor with name.
|
||||
*/
|
||||
Area(float ax, float ay, float bx, float by, float cx, float cy, const StackStrF & name)
|
||||
: Area(ax, ay, bx, by, cx, cy, 16, name)
|
||||
{
|
||||
//...
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Base constructor.
|
||||
*/
|
||||
Area(float ax, float ay, float bx, float by, float cx, float cy, SQInteger sz, const StackStrF & name)
|
||||
: mL(DEF_L), mB(DEF_B), mR(DEF_R), mT(DEF_T), mPoints(), mID(0), mCells()
|
||||
, mName(name.mPtr, name.mLen <= 0 ? 0 : name.mLen)
|
||||
{
|
||||
// Should we reserve some space for points in advance?
|
||||
if (sz > 0)
|
||||
{
|
||||
mPoints.reserve(static_cast< size_t >(sz));
|
||||
}
|
||||
// Insert the given points
|
||||
AddPointEx(ax, ay);
|
||||
AddPointEx(bx, by);
|
||||
AddPointEx(cx, cy);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copy constructor.
|
||||
*/
|
||||
Area(const Area & o)
|
||||
: mL(o.mL), mB(o.mB), mR(o.mR), mT(o.mT), mPoints(o.mPoints), mID(o.mID), mCells(0), mName(o.mName)
|
||||
{
|
||||
//...
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Move constructor. (disabled)
|
||||
*/
|
||||
Area(Area && o) = delete;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Destructor.
|
||||
*/
|
||||
~Area()
|
||||
{
|
||||
//...
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copy assignment operator. (disabled)
|
||||
*/
|
||||
Area & operator = (const Area & o) = delete;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Move assignment operator. (disabled)
|
||||
*/
|
||||
Area & operator = (Area && o) = delete;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Used by the script engine to convert this instance to a string.
|
||||
*/
|
||||
const String & ToString() const
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Checks if the area is locked from changes and throws an exception if it is.
|
||||
*/
|
||||
void CheckLock()
|
||||
{
|
||||
// Are we connected to any cells?
|
||||
if (!mCells.empty())
|
||||
{
|
||||
STHROWF("The area cannot be modified while being managed");
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Retrieve the name of this area.
|
||||
*/
|
||||
const String & GetName() const
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Modify the name of this area.
|
||||
*/
|
||||
void SetName(const StackStrF & name)
|
||||
{
|
||||
if (name.mLen <= 0)
|
||||
{
|
||||
mName.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
mName.assign(name.mPtr, static_cast< size_t >(name.mLen));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Modify the name of this area. (allows chaining function calls)
|
||||
*/
|
||||
Area & ApplyName(const StackStrF & name)
|
||||
{
|
||||
SetName(name);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Retrieve the identifier of this area.
|
||||
*/
|
||||
SQInteger GetID() const
|
||||
{
|
||||
return mID;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Modify the identifier of this area.
|
||||
*/
|
||||
void SetID(SQInteger id)
|
||||
{
|
||||
mID = id;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Modify the identifier of this area. (allows chaining function calls)
|
||||
*/
|
||||
Area & ApplyID(SQInteger id)
|
||||
{
|
||||
mID = id;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Check if the area is locked from changes
|
||||
*/
|
||||
bool IsLocked() const
|
||||
{
|
||||
return mCells.empty();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Retrieve the center of this area.
|
||||
*/
|
||||
Vector2 GetCenter() const
|
||||
{
|
||||
return Vector2((mL * 0.5f) + (mR * 0.5f), (mB * 0.5f) + (mT * 0.5f));
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Retrieve the bounding box of this area.
|
||||
*/
|
||||
Vector4 GetBoundingBox() const
|
||||
{
|
||||
return Vector4(mL, mB, mR, mT);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Modify the bounding box of this area.
|
||||
*/
|
||||
void SetBoundingBox(const Vector4 & b)
|
||||
{
|
||||
CheckLock();
|
||||
// Apply the given bounding box
|
||||
mL = b.x;
|
||||
mB = b.y;
|
||||
mR = b.z;
|
||||
mT = b.w;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Modify the bounding box of this area.
|
||||
*/
|
||||
void SetBoundingBoxEx(float l, float b, float r, float t)
|
||||
{
|
||||
CheckLock();
|
||||
// Apply the given bounding box
|
||||
mL = l;
|
||||
mB = b;
|
||||
mR = r;
|
||||
mT = t;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* See whether the area has no points.
|
||||
*/
|
||||
bool Empty() const
|
||||
{
|
||||
return mPoints.empty();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Retrieve the number of points in this area.
|
||||
*/
|
||||
SQInteger Size() const
|
||||
{
|
||||
return ConvTo< SQInteger >::From(mPoints.size());
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Retrieve the number of points this area has allocated for.
|
||||
*/
|
||||
SQInteger Capacity() const
|
||||
{
|
||||
return ConvTo< SQInteger >::From(mPoints.capacity());
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Clear all points in this area.
|
||||
*/
|
||||
void Clear()
|
||||
{
|
||||
CheckLock();
|
||||
// Perform the requested action
|
||||
mPoints.clear();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Clear all points in this area.
|
||||
*/
|
||||
void Reserve(SQInteger sz)
|
||||
{
|
||||
// Perform the requested action
|
||||
if (sz > 0)
|
||||
{
|
||||
mPoints.reserve(static_cast< size_t >(sz));
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Add a 2D vector to the point list.
|
||||
*/
|
||||
void AddPoint(const Vector2 & v)
|
||||
{
|
||||
CheckLock();
|
||||
// Perform the requested action
|
||||
mPoints.emplace_back(v);
|
||||
// Update the bounding box
|
||||
Expand(v.x, v.y);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Add a point to the point list.
|
||||
*/
|
||||
void AddPointEx(float x, float y)
|
||||
{
|
||||
CheckLock();
|
||||
// Perform the requested action
|
||||
mPoints.emplace_back(x, y);
|
||||
// Update the bounding box
|
||||
Expand(x, y);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Add a 2D vector to the bounding box only. Not stored in the list.
|
||||
*/
|
||||
void AddVirtualPoint(const Vector2 & v)
|
||||
{
|
||||
CheckLock();
|
||||
// Update the bounding box
|
||||
Expand(v.x, v.y);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Add a point to the bounding box only. Not stored in the list.
|
||||
*/
|
||||
void AddVirtualPointEx(float x, float y)
|
||||
{
|
||||
CheckLock();
|
||||
// Update the bounding box
|
||||
Expand(x, y);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Add an array of points to the point list.
|
||||
*/
|
||||
void AddArray(const Sqrat::Array & a);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Test if a point is inside the bounding box and then the area.
|
||||
*/
|
||||
bool Test(const Vector2 & v)
|
||||
{
|
||||
// Is the given point in this bounding box at least?
|
||||
if (mL <= v.x && mR >= v.x && mB <= v.y && mT >= v.y)
|
||||
{
|
||||
return mPoints.empty() ? true : IsInside(v.x, v.y);
|
||||
}
|
||||
// Not in this area
|
||||
return false;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Test if a point is inside the bounding box and then the area.
|
||||
*/
|
||||
bool TestEx(float x, float y)
|
||||
{
|
||||
// Is the given point in this bounding box at least?
|
||||
if (mL <= x && mR >= x && mB <= y && mT >= y)
|
||||
{
|
||||
return mPoints.empty() ? true : IsInside(x, y);
|
||||
}
|
||||
// Not in this area
|
||||
return false;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Add this area to the manager to be scanned. MUST BE CALLED ONLY FROM SCRIPT!
|
||||
*/
|
||||
bool Manage();
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Remove this area from the manager to no longer be scanned.
|
||||
*/
|
||||
bool Unmanage();
|
||||
|
||||
protected:
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Test if a point is inside the area.
|
||||
*/
|
||||
bool IsInside(float x, float y) const;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Expand the bounding box area to include the given point.
|
||||
*/
|
||||
void Expand(float x, float y)
|
||||
{
|
||||
mL = std::fmin(mL, x);
|
||||
mB = std::fmin(mB, y);
|
||||
mR = std::fmax(mR, x);
|
||||
mT = std::fmax(mT, y);
|
||||
}
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Manager responsible for storing and partitioning areas.
|
||||
*/
|
||||
class AreaManager
|
||||
{
|
||||
private:
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
static AreaManager s_Inst; // Manager instance.
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Base constructor.
|
||||
*/
|
||||
AreaManager(size_t sz = 16);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copy constructor. (disabled)
|
||||
*/
|
||||
AreaManager(const AreaManager & o) = delete;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Move constructor. (disabled)
|
||||
*/
|
||||
AreaManager(AreaManager && o) = delete;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Destructor.
|
||||
*/
|
||||
~AreaManager()
|
||||
{
|
||||
//...
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copy assignment operator. (disabled)
|
||||
*/
|
||||
AreaManager & operator = (const AreaManager & o) = delete;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Move assignment operator. (disabled)
|
||||
*/
|
||||
AreaManager & operator = (AreaManager && o) = delete;
|
||||
|
||||
protected:
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Helper used to make sure a cell is properly processed after leaving the scope.
|
||||
*/
|
||||
struct CellGuard
|
||||
{
|
||||
AreaCell & mCell;
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Base constructor.
|
||||
*/
|
||||
CellGuard(AreaCell & cell)
|
||||
: mCell(cell)
|
||||
{
|
||||
++(cell.mLocks); // Place a lock on the cell to prevent iterator invalidation
|
||||
}
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Destructor.
|
||||
*/
|
||||
~CellGuard()
|
||||
{
|
||||
// Remove the lock from the cell so it can be processed
|
||||
--(mCell.mLocks);
|
||||
// Process requested actions during the lock
|
||||
AreaManager::Get().ProcQueue();
|
||||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
static constexpr int GRIDN = 16; // Number of horizontal and vertical number of cells.
|
||||
static constexpr int GRIDH = GRIDN/2; // Half of the grid horizontal and vertical size.
|
||||
static constexpr int CELLS = GRIDN*GRIDN; // Total number of cells in the grid.
|
||||
static constexpr int CELLH = CELLS/2; // Half total number of cells in the grid.
|
||||
static constexpr int CELLD = 256; // Area covered by a cell in the world.
|
||||
static constexpr int NOCELL = std::numeric_limits< int >::max(); // Inexistent cell index.
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Helper used to queue a certain action if the cell is locked.
|
||||
*/
|
||||
struct QueueElement
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------
|
||||
AreaCell * mCell; // The cell to be affected.
|
||||
Area * mArea; // The area that made the request.
|
||||
LightObj mObj; // Strong reference to the object.
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Base constructor.
|
||||
*/
|
||||
QueueElement(AreaCell & cell, Area & area)
|
||||
: mCell(&cell), mArea(&area), mObj()
|
||||
{
|
||||
//...
|
||||
}
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Base constructor.
|
||||
*/
|
||||
QueueElement(AreaCell & cell, Area & area, LightObj & obj)
|
||||
: mCell(&cell), mArea(&area), mObj(obj)
|
||||
{
|
||||
//...
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copy constructor. (disabled)
|
||||
*/
|
||||
QueueElement(const QueueElement & o) = delete;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Move constructor.
|
||||
*/
|
||||
QueueElement(QueueElement && o)
|
||||
: mCell(o.mCell), mArea(o.mArea), mObj(std::move(o.mObj))
|
||||
{
|
||||
// Take ownership
|
||||
o.mCell = nullptr;
|
||||
o.mArea = nullptr;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Destructor.
|
||||
*/
|
||||
~QueueElement()
|
||||
{
|
||||
//...
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copy assignment operator. (disabled)
|
||||
*/
|
||||
QueueElement & operator = (const QueueElement & o) = delete;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Move assignment operator.
|
||||
*/
|
||||
QueueElement & operator = (QueueElement && o)
|
||||
{
|
||||
// Avoid self assignment
|
||||
if (this != &o)
|
||||
{
|
||||
// Transfer values
|
||||
mCell = o.mCell;
|
||||
mArea = o.mArea;
|
||||
mObj = std::move(o.mObj);
|
||||
// Take ownership
|
||||
o.mCell = nullptr;
|
||||
o.mArea = nullptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
// --------------------------------------------------------------------------------------------
|
||||
typedef std::vector< QueueElement > Queue; // Queued actions.
|
||||
typedef std::vector< Queue::iterator > ProcList; // Elements in the queue redy to process.
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Attempt to insert an area into a cell or queue the action if not possible.
|
||||
*/
|
||||
void Insert(AreaCell & c, Area & a, LightObj & obj);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Attempt to remove an area from a cell or queue the action if not possible.
|
||||
*/
|
||||
void Remove(AreaCell & c, Area & a);
|
||||
|
||||
private:
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
Queue m_Queue; // Actions currently queued.
|
||||
ProcList m_ProcList; // Actions ready to be completed.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
AreaCell m_Grid[GRIDN][GRIDN]; // A grid of area lists.
|
||||
|
||||
public:
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Retrieve the core instance.
|
||||
*/
|
||||
static AreaManager & Get()
|
||||
{
|
||||
return s_Inst;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Attempt to process elements in the queue that can be completed.
|
||||
*/
|
||||
void ProcQueue();
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Clear all cell lists and release any script references.
|
||||
*/
|
||||
void Clear();
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Add an area to be managed.
|
||||
*/
|
||||
void InsertArea(Area & a, LightObj & obj);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Add an area to be managed.
|
||||
*/
|
||||
void RemoveArea(Area & a);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Clear all cell lists and release any script references.
|
||||
*/
|
||||
Vector2i LocateCell(float x, float y);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Test a point to see whether it intersects with any areas
|
||||
*/
|
||||
template < typename F > void TestPoint(F && f, float x, float y)
|
||||
{
|
||||
// Transform the world coordinates into a cell coordinates
|
||||
const Vector2i cc(LocateCell(x, y));
|
||||
// Were these coordinates valid?
|
||||
if (cc.x == NOCELL)
|
||||
{
|
||||
return; // Not our problem
|
||||
}
|
||||
// Retrieve a reference to the identified cell
|
||||
AreaCell & c = m_Grid[cc.y][cc.x];
|
||||
// Is this cell empty?
|
||||
if (c.mAreas.empty())
|
||||
{
|
||||
return; // Nothing to test
|
||||
}
|
||||
// Guard the cell while processing
|
||||
const CellGuard cg(c);
|
||||
// Finally, begin processing the areas in this cell
|
||||
for (auto & a : c.mAreas)
|
||||
{
|
||||
if (a.first->TestEx(x, y))
|
||||
{
|
||||
f(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // Namespace:: SqMod
|
||||
|
||||
#endif // _AREAS_HPP_
|
||||
1178
source/Misc/Command.cpp
Normal file
1178
source/Misc/Command.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2003
source/Misc/Command.hpp
Normal file
2003
source/Misc/Command.hpp
Normal file
File diff suppressed because it is too large
Load Diff
1163
source/Misc/Constants.cpp
Normal file
1163
source/Misc/Constants.cpp
Normal file
File diff suppressed because it is too large
Load Diff
926
source/Misc/Exports.cpp
Normal file
926
source/Misc/Exports.cpp
Normal file
@@ -0,0 +1,926 @@
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
#include "Core.hpp"
|
||||
#include "Base/Buffer.hpp"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
#include "Library/Numeric/LongInt.hpp"
|
||||
#include "Library/Chrono/Date.hpp"
|
||||
#include "Library/Chrono/Time.hpp"
|
||||
#include "Library/Chrono/Datetime.hpp"
|
||||
#include "Library/Chrono/Timestamp.hpp"
|
||||
#include "Library/Utils/Buffer.hpp"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
#include <sqstdio.h>
|
||||
#include <sqstdblob.h>
|
||||
#include <sqstdstring.h>
|
||||
#include <SqMod.h>
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
namespace SqMod {
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
static HSQUIRRELVM GetSquirrelVM()
|
||||
{
|
||||
return Core::Get().GetVM();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
static SQRESULT SqModImpl_LoadScript(const SQChar * filepath, SQBool delay)
|
||||
{
|
||||
// Attempt to add the specified script to the load queue
|
||||
if (Core::Get().LoadScript(filepath, delay))
|
||||
{
|
||||
return SQ_OK; // The script as added or already existed
|
||||
}
|
||||
// The path was invalid or was unable to pool the script
|
||||
return SQ_ERROR;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
static SQRESULT SqModImpl_GetSLongValue(HSQUIRRELVM vm, SQInteger idx, Int64 * num)
|
||||
{
|
||||
// Validate the specified number pointer and value type
|
||||
if (!num)
|
||||
{
|
||||
return SQ_ERROR; // Nowhere to save!
|
||||
}
|
||||
// Is this an instance that we can treat as a SLongInt type?
|
||||
else if (sq_gettype(vm, idx) == OT_INSTANCE)
|
||||
{
|
||||
// Attempt to obtain the long instance and it's value from the stack
|
||||
try
|
||||
{
|
||||
*num = static_cast< Int64 >(Var< const SLongInt & >(vm, idx).value.GetNum());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return SQ_ERROR; // Unable to obtain the value!
|
||||
}
|
||||
}
|
||||
// Is this a pure integer value?
|
||||
else if(sq_gettype(vm, idx) == OT_INTEGER)
|
||||
{
|
||||
SQInteger val = 0;
|
||||
// Attempt to get the value from the stack
|
||||
sq_getinteger(vm, idx, &val);
|
||||
// Save it into the specified memory location
|
||||
*num = static_cast< Int64 >(val);
|
||||
}
|
||||
// Is this a pure floating point value?
|
||||
else if(sq_gettype(vm, idx) == OT_FLOAT)
|
||||
{
|
||||
SQFloat val = 0.0;
|
||||
// Attempt to get the value from the stack
|
||||
sq_getfloat(vm, idx, &val);
|
||||
// Save it into the specified memory location
|
||||
*num = static_cast< Int64 >(std::llround(val));
|
||||
}
|
||||
// Is this a pure boolean value?
|
||||
else if(sq_gettype(vm, idx) == OT_BOOL)
|
||||
{
|
||||
SQBool val = SQFalse;
|
||||
// Attempt to get the value from the stack
|
||||
sq_getbool(vm, idx, &val);
|
||||
// Save it into the specified memory location
|
||||
*num = static_cast< Int64 >(val);
|
||||
}
|
||||
// Unrecognized value
|
||||
else
|
||||
{
|
||||
return SQ_ERROR;
|
||||
}
|
||||
// Value retrieved
|
||||
return SQ_OK;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
static SQRESULT SqModImpl_PushSLongObject(HSQUIRRELVM vm, Int64 num)
|
||||
{
|
||||
// Attempt to push the requested instance
|
||||
try
|
||||
{
|
||||
Var< const SLongInt & >::push(vm, SLongInt(num));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Specify that we failed
|
||||
return SQ_ERROR;
|
||||
}
|
||||
// Specify that we succeeded
|
||||
return SQ_OK;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
static SQRESULT SqModImpl_GetULongValue(HSQUIRRELVM vm, SQInteger idx, Uint64 * num)
|
||||
{
|
||||
// Validate the specified number pointer and value type
|
||||
if (!num)
|
||||
{
|
||||
return SQ_ERROR; // Nowhere to save
|
||||
}
|
||||
// Is this an instance that we can treat as a ULongInt type?
|
||||
else if (sq_gettype(vm, idx) == OT_INSTANCE)
|
||||
{
|
||||
// Attempt to obtain the long instance and it's value from the stack
|
||||
try
|
||||
{
|
||||
*num = static_cast< Uint64 >(Var< const ULongInt & >(vm, idx).value.GetNum());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return SQ_ERROR; // Unable to obtain the value!
|
||||
}
|
||||
}
|
||||
// Is this a pure integer value?
|
||||
else if(sq_gettype(vm, idx) == OT_INTEGER)
|
||||
{
|
||||
SQInteger val = 0;
|
||||
// Attempt to get the value from the stack
|
||||
sq_getinteger(vm, idx, &val);
|
||||
// Save it into the specified memory location
|
||||
*num = val ? static_cast< Uint64 >(val) : 0L;
|
||||
}
|
||||
// Is this a pure floating point value?
|
||||
else if(sq_gettype(vm, idx) == OT_FLOAT)
|
||||
{
|
||||
SQFloat val = 0.0;
|
||||
// Attempt to get the value from the stack
|
||||
sq_getfloat(vm, idx, &val);
|
||||
// Save it into the specified memory location
|
||||
*num = EpsLt(val, SQFloat(0.0)) ? 0L : static_cast< Uint64 >(std::llround(val));
|
||||
}
|
||||
// Is this a pure boolean value?
|
||||
else if(sq_gettype(vm, idx) == OT_BOOL)
|
||||
{
|
||||
SQBool val = SQFalse;
|
||||
// Attempt to get the value from the stack
|
||||
sq_getbool(vm, idx, &val);
|
||||
// Save it into the specified memory location
|
||||
*num = static_cast< Uint64 >(val);
|
||||
}
|
||||
// Unrecognized value
|
||||
else
|
||||
{
|
||||
return SQ_ERROR;
|
||||
}
|
||||
// Value retrieved
|
||||
return SQ_OK;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
static SQRESULT SqModImpl_PushULongObject(HSQUIRRELVM vm, Uint64 num)
|
||||
{
|
||||
// Attempt to push the requested instance
|
||||
try
|
||||
{
|
||||
Var< const ULongInt & >::push(vm, ULongInt(num));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Specify that we failed
|
||||
return SQ_ERROR;
|
||||
}
|
||||
// Specify that we succeeded
|
||||
return SQ_OK;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQBool SqModImpl_ValidDate(uint16_t year, uint8_t month, uint8_t day)
|
||||
{
|
||||
return Chrono::ValidDate(year, month, day);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQBool SqModImpl_IsLeapYear(uint16_t year)
|
||||
{
|
||||
return Chrono::IsLeapYear(year);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
static SQRESULT SqModImpl_GetTimestamp(HSQUIRRELVM vm, SQInteger idx, Int64 * num)
|
||||
{
|
||||
// Validate the specified number pointer and value type
|
||||
if (!num)
|
||||
{
|
||||
return SQ_ERROR; // Nowhere to save
|
||||
}
|
||||
// Is this an instance that we can treat as a Time-stamp type?
|
||||
else if (sq_gettype(vm, idx) == OT_INSTANCE)
|
||||
{
|
||||
// Attempt to obtain the time-stamp and it's value from the stack
|
||||
try
|
||||
{
|
||||
*num = static_cast< Int64 >(Var< const Timestamp & >(vm, idx).value.GetNum());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return SQ_ERROR; // Unable to obtain the value!
|
||||
}
|
||||
}
|
||||
// Is this a pure integer value?
|
||||
else if(sq_gettype(vm, idx) == OT_INTEGER)
|
||||
{
|
||||
SQInteger val = 0;
|
||||
// Attempt to get the value from the stack
|
||||
sq_getinteger(vm, idx, &val);
|
||||
// Save it into the specified memory location
|
||||
*num = static_cast< Int64 >(val);
|
||||
}
|
||||
// Is this a pure floating point value?
|
||||
else if(sq_gettype(vm, idx) == OT_FLOAT)
|
||||
{
|
||||
SQFloat val = 0.0;
|
||||
// Attempt to get the value from the stack
|
||||
sq_getfloat(vm, idx, &val);
|
||||
// Save it into the specified memory location
|
||||
*num = static_cast< Int64 >(std::llround(val));
|
||||
}
|
||||
// Is this a pure boolean value?
|
||||
else if(sq_gettype(vm, idx) == OT_BOOL)
|
||||
{
|
||||
SQBool val = SQFalse;
|
||||
// Attempt to get the value from the stack
|
||||
sq_getbool(vm, idx, &val);
|
||||
// Save it into the specified memory location
|
||||
*num = static_cast< Int64 >(val);
|
||||
}
|
||||
// Unrecognized value
|
||||
else
|
||||
{
|
||||
return SQ_ERROR;
|
||||
}
|
||||
// Value retrieved
|
||||
return SQ_OK;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
static SQRESULT SqModImpl_PushTimestamp(HSQUIRRELVM vm, Int64 num)
|
||||
{
|
||||
// Attempt to push the requested instance
|
||||
try
|
||||
{
|
||||
Var< const Timestamp & >::push(vm, Timestamp(num));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Specify that we failed
|
||||
return SQ_ERROR;
|
||||
}
|
||||
// Specify that we succeeded
|
||||
return SQ_OK;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQRESULT SqModImpl_GetDate(HSQUIRRELVM vm, SQInteger idx, uint16_t * year, uint8_t * month, uint8_t * day)
|
||||
{
|
||||
// Is this an instance that we can treat as a Date type?
|
||||
if (sq_gettype(vm, idx) == OT_INSTANCE)
|
||||
{
|
||||
// Attempt to obtain the time-stamp and it's value from the stack
|
||||
try
|
||||
{
|
||||
// Attempt to retrieve the instance
|
||||
Var< Date * > var(vm, idx);
|
||||
// Assign the year
|
||||
if (year != nullptr)
|
||||
{
|
||||
*year = var.value->GetYear();
|
||||
}
|
||||
// Assign the month
|
||||
if (month != nullptr)
|
||||
{
|
||||
*month = var.value->GetMonth();
|
||||
}
|
||||
// Assign the day
|
||||
if (day != nullptr)
|
||||
{
|
||||
*day = var.value->GetDay();
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return SQ_ERROR; // Unable to obtain the value!
|
||||
}
|
||||
}
|
||||
// Unrecognized value
|
||||
else
|
||||
{
|
||||
return SQ_ERROR;
|
||||
}
|
||||
// Value retrieved
|
||||
return SQ_OK;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQRESULT SqModImpl_PushDate(HSQUIRRELVM vm, uint16_t year, uint8_t month, uint8_t day)
|
||||
{
|
||||
// Attempt to push the requested instance
|
||||
try
|
||||
{
|
||||
Var< const Date & >::push(vm, Date(year, month, day));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Specify that we failed
|
||||
return SQ_ERROR;
|
||||
}
|
||||
// Specify that we succeeded
|
||||
return SQ_OK;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQRESULT SqModImpl_GetTime(HSQUIRRELVM vm, SQInteger idx, uint8_t * hour, uint8_t * minute,
|
||||
uint8_t * second, uint16_t * millisecond)
|
||||
{
|
||||
// Is this an instance that we can treat as a Time type?
|
||||
if (sq_gettype(vm, idx) == OT_INSTANCE)
|
||||
{
|
||||
// Attempt to obtain the time-stamp and it's value from the stack
|
||||
try
|
||||
{
|
||||
// Attempt to retrieve the instance
|
||||
Var< Time * > var(vm, idx);
|
||||
// Assign the hour
|
||||
if (hour != nullptr)
|
||||
{
|
||||
*hour = var.value->GetHour();
|
||||
}
|
||||
// Assign the minute
|
||||
if (minute != nullptr)
|
||||
{
|
||||
*minute = var.value->GetMinute();
|
||||
}
|
||||
// Assign the second
|
||||
if (second != nullptr)
|
||||
{
|
||||
*second = var.value->GetSecond();
|
||||
}
|
||||
// Assign the millisecond
|
||||
if (millisecond != nullptr)
|
||||
{
|
||||
*millisecond = var.value->GetMillisecond();
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return SQ_ERROR; // Unable to obtain the value!
|
||||
}
|
||||
}
|
||||
// Unrecognized value
|
||||
else
|
||||
{
|
||||
return SQ_ERROR;
|
||||
}
|
||||
// Value retrieved
|
||||
return SQ_OK;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQRESULT SqModImpl_PushTime(HSQUIRRELVM vm, uint8_t hour, uint8_t minute, uint8_t second,
|
||||
uint16_t millisecond)
|
||||
{
|
||||
// Attempt to push the requested instance
|
||||
try
|
||||
{
|
||||
Var< const Time & >::push(vm, Time(hour, minute, second, millisecond));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Specify that we failed
|
||||
return SQ_ERROR;
|
||||
}
|
||||
// Specify that we succeeded
|
||||
return SQ_OK;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQRESULT SqModImpl_GetDatetime(HSQUIRRELVM vm, SQInteger idx, uint16_t * year, uint8_t * month, uint8_t * day,
|
||||
uint8_t * hour, uint8_t * minute, uint8_t * second, uint16_t * millisecond)
|
||||
{
|
||||
// Is this an instance that we can treat as a Date-time type?
|
||||
if (sq_gettype(vm, idx) == OT_INSTANCE)
|
||||
{
|
||||
// Attempt to obtain the time-stamp and it's value from the stack
|
||||
try
|
||||
{
|
||||
// Attempt to retrieve the instance
|
||||
Var< Datetime * > var(vm, idx);
|
||||
// Assign the year
|
||||
if (year != nullptr)
|
||||
{
|
||||
*year = var.value->GetYear();
|
||||
}
|
||||
// Assign the month
|
||||
if (month != nullptr)
|
||||
{
|
||||
*month = var.value->GetMonth();
|
||||
}
|
||||
// Assign the day
|
||||
if (day != nullptr)
|
||||
{
|
||||
*day = var.value->GetDay();
|
||||
}
|
||||
// Assign the hour
|
||||
if (hour != nullptr)
|
||||
{
|
||||
*hour = var.value->GetHour();
|
||||
}
|
||||
// Assign the minute
|
||||
if (minute != nullptr)
|
||||
{
|
||||
*minute = var.value->GetMinute();
|
||||
}
|
||||
// Assign the second
|
||||
if (second != nullptr)
|
||||
{
|
||||
*second = var.value->GetSecond();
|
||||
}
|
||||
// Assign the millisecond
|
||||
if (millisecond != nullptr)
|
||||
{
|
||||
*millisecond = var.value->GetMillisecond();
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return SQ_ERROR; // Unable to obtain the value!
|
||||
}
|
||||
}
|
||||
// Unrecognized value
|
||||
else
|
||||
{
|
||||
return SQ_ERROR;
|
||||
}
|
||||
// Value retrieved
|
||||
return SQ_OK;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQRESULT SqModImpl_PushDatetime(HSQUIRRELVM vm, uint16_t year, uint8_t month, uint8_t day,
|
||||
uint8_t hour, uint8_t minute, uint8_t second, uint16_t millisecond)
|
||||
{
|
||||
// Attempt to push the requested instance
|
||||
try
|
||||
{
|
||||
Var< const Datetime & >::push(vm, Datetime(year, month, day, hour, minute, second, millisecond));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Specify that we failed
|
||||
return SQ_ERROR;
|
||||
}
|
||||
// Specify that we succeeded
|
||||
return SQ_OK;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQRESULT SqModImpl_PushBuffer(HSQUIRRELVM vm, SQInteger size, SQInteger cursor)
|
||||
{
|
||||
// Attempt to push the requested instance
|
||||
try
|
||||
{
|
||||
Var< const SqBuffer & >::push(vm, SqBuffer(size, cursor));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Specify that we failed
|
||||
return SQ_ERROR;
|
||||
}
|
||||
// Specify that we succeeded
|
||||
return SQ_OK;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQRESULT SqModImpl_PushBufferData(HSQUIRRELVM vm, const char * data, SQInteger size, SQInteger cursor)
|
||||
{
|
||||
// Attempt to push the requested instance
|
||||
try
|
||||
{
|
||||
Var< const SqBuffer & >::push(vm, SqBuffer(data, size, cursor));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Specify that we failed
|
||||
return SQ_ERROR;
|
||||
}
|
||||
// Specify that we succeeded
|
||||
return SQ_OK;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQRESULT SqModImpl_GetBufferInfo(HSQUIRRELVM vm, SQInteger idx, const char ** ptr, SQInteger * size, SQInteger * cursor)
|
||||
{
|
||||
// Attempt to obtain the requested information
|
||||
try
|
||||
{
|
||||
// Attempt to retrieve the instance
|
||||
Var< SqBuffer * > var(vm, idx);
|
||||
// Validate the obtained buffer
|
||||
if (!(var.value) || !(var.value->GetRef()) || !(*var.value->GetRef()))
|
||||
{
|
||||
// Should we obtain the buffer contents?
|
||||
if (ptr)
|
||||
{
|
||||
*ptr = nullptr; // Default to null data
|
||||
}
|
||||
// Should we obtain the buffer length?
|
||||
if (size)
|
||||
{
|
||||
*size = 0; // Default to 0 length
|
||||
}
|
||||
// Should we obtain the cursor position?
|
||||
if (cursor)
|
||||
{
|
||||
*cursor = 0; // Default to position 0
|
||||
}
|
||||
}
|
||||
// Grab the internal buffer
|
||||
const Buffer & b = *var.value->GetRef();
|
||||
// Should we obtain the buffer contents?
|
||||
if (ptr)
|
||||
{
|
||||
*ptr = b.Data();
|
||||
}
|
||||
// Should we obtain the buffer length?
|
||||
if (size)
|
||||
{
|
||||
*size = ConvTo< SQInteger >::From(b.Capacity());
|
||||
}
|
||||
// Should we obtain the cursor position?
|
||||
if (cursor)
|
||||
{
|
||||
*cursor = ConvTo< SQInteger >::From(b.Position());
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Specify that we failed
|
||||
return SQ_ERROR;
|
||||
}
|
||||
// Specify that we succeeded
|
||||
return SQ_OK;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
const char * SqModImpl_GetBufferData(HSQUIRRELVM vm, SQInteger idx)
|
||||
{
|
||||
// Attempt to obtain the requested information
|
||||
try
|
||||
{
|
||||
// Attempt to retrieve the instance
|
||||
Var< SqBuffer * > var(vm, idx);
|
||||
// Validate the obtained buffer and return the requested information
|
||||
if ((var.value) && (var.value->GetRef()) && (*var.value->GetRef()))
|
||||
{
|
||||
return var.value->GetRef()->Data();
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Just ignore it...
|
||||
}
|
||||
// Specify that we failed
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQInteger SqModImpl_GetBufferSize(HSQUIRRELVM vm, SQInteger idx)
|
||||
{
|
||||
// Attempt to obtain the requested information
|
||||
try
|
||||
{
|
||||
// Attempt to retrieve the instance
|
||||
Var< SqBuffer * > var(vm, idx);
|
||||
// Validate the obtained buffer and return the requested information
|
||||
if ((var.value) && (var.value->GetRef()) && (*var.value->GetRef()))
|
||||
{
|
||||
return ConvTo< SQInteger >::From(var.value->GetRef()->Capacity());
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Just ignore it...
|
||||
}
|
||||
// Specify that we failed
|
||||
return -1;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQInteger SqModImpl_GetBufferCursor(HSQUIRRELVM vm, SQInteger idx)
|
||||
{
|
||||
// Attempt to obtain the requested information
|
||||
try
|
||||
{
|
||||
// Attempt to retrieve the instance
|
||||
Var< SqBuffer * > var(vm, idx);
|
||||
// Validate the obtained buffer and return the requested information
|
||||
if ((var.value) && (var.value->GetRef()) && (*var.value->GetRef()))
|
||||
{
|
||||
return ConvTo< SQInteger >::From(var.value->GetRef()->Position());
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Just ignore it...
|
||||
}
|
||||
// Specify that we failed
|
||||
return -1;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
static int32_t SqExport_PopulateModuleAPI(HSQMODAPI api, size_t size)
|
||||
{
|
||||
if (!api)
|
||||
{
|
||||
return 0; // Nothing to populate!
|
||||
}
|
||||
else if (size != sizeof(SQMODAPI))
|
||||
{
|
||||
return -1; // Incompatible API!
|
||||
}
|
||||
|
||||
//primitive functions
|
||||
api->GetSquirrelVM = GetSquirrelVM;
|
||||
|
||||
//logging utilities
|
||||
api->LogDbg = LogDbg;
|
||||
api->LogUsr = LogUsr;
|
||||
api->LogScs = LogScs;
|
||||
api->LogInf = LogInf;
|
||||
api->LogWrn = LogWrn;
|
||||
api->LogErr = LogErr;
|
||||
api->LogFtl = LogFtl;
|
||||
api->LogSDbg = LogSDbg;
|
||||
api->LogSUsr = LogSUsr;
|
||||
api->LogSScs = LogSScs;
|
||||
api->LogSInf = LogSInf;
|
||||
api->LogSWrn = LogSWrn;
|
||||
api->LogSErr = LogSErr;
|
||||
api->LogSFtl = LogSFtl;
|
||||
|
||||
//script loading
|
||||
api->LoadScript = SqModImpl_LoadScript;
|
||||
|
||||
//numeric utilities
|
||||
api->GetSLongValue = SqModImpl_GetSLongValue;
|
||||
api->PushSLongObject = SqModImpl_PushSLongObject;
|
||||
api->GetULongValue = SqModImpl_GetULongValue;
|
||||
api->PushULongObject = SqModImpl_PushULongObject;
|
||||
|
||||
//time utilities
|
||||
api->GetCurrentSysTime = Chrono::GetCurrentSysTime;
|
||||
api->GetEpochTimeMicro = Chrono::GetEpochTimeMicro;
|
||||
api->GetEpochTimeMilli = Chrono::GetEpochTimeMilli;
|
||||
api->ValidDate = SqModImpl_ValidDate;
|
||||
api->IsLeapYear = SqModImpl_IsLeapYear;
|
||||
api->DaysInYear = Chrono::DaysInYear;
|
||||
api->DaysInMonth = Chrono::DaysInMonth;
|
||||
api->DayOfYear = Chrono::DayOfYear;
|
||||
api->DateRangeToSeconds = Chrono::DateRangeToSeconds;
|
||||
api->GetTimestamp = SqModImpl_GetTimestamp;
|
||||
api->PushTimestamp = SqModImpl_PushTimestamp;
|
||||
api->GetDate = SqModImpl_GetDate;
|
||||
api->PushDate = SqModImpl_PushDate;
|
||||
api->GetTime = SqModImpl_GetTime;
|
||||
api->PushTime = SqModImpl_PushTime;
|
||||
api->GetDatetime = SqModImpl_GetDatetime;
|
||||
api->PushDatetime = SqModImpl_PushDatetime;
|
||||
|
||||
//stack utilities
|
||||
api->PopStackInteger = PopStackInteger;
|
||||
api->PopStackFloat = PopStackFloat;
|
||||
api->PopStackSLong = PopStackSLong;
|
||||
api->PopStackULong = PopStackULong;
|
||||
|
||||
//buffer utilities
|
||||
api->PushBuffer = SqModImpl_PushBuffer;
|
||||
api->PushBufferData = SqModImpl_PushBufferData;
|
||||
api->GetBufferInfo = SqModImpl_GetBufferInfo;
|
||||
api->GetBufferData = SqModImpl_GetBufferData;
|
||||
api->GetBufferSize = SqModImpl_GetBufferSize;
|
||||
api->GetBufferCursor = SqModImpl_GetBufferCursor;
|
||||
|
||||
return 1; // Successfully populated!
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
static int32_t SqExport_PopulateSquirrelAPI(HSQLIBAPI api, size_t size)
|
||||
{
|
||||
if (!api)
|
||||
{
|
||||
return 0; // Nothing to populate!
|
||||
}
|
||||
else if (size != sizeof(SQLIBAPI))
|
||||
{
|
||||
return -1; // Incompatible API!
|
||||
}
|
||||
|
||||
//vm
|
||||
api->open = sq_open;
|
||||
api->newthread = sq_newthread;
|
||||
api->seterrorhandler = sq_seterrorhandler;
|
||||
api->close = sq_close;
|
||||
api->setforeignptr = sq_setforeignptr;
|
||||
api->getforeignptr = sq_getforeignptr;
|
||||
api->setsharedforeignptr = sq_setsharedforeignptr;
|
||||
api->getsharedforeignptr = sq_getsharedforeignptr;
|
||||
api->setvmreleasehook = sq_setvmreleasehook;
|
||||
api->getvmreleasehook = sq_getvmreleasehook;
|
||||
api->setsharedreleasehook = sq_setsharedreleasehook;
|
||||
api->getsharedreleasehook = sq_getsharedreleasehook;
|
||||
api->setprintfunc = sq_setprintfunc;
|
||||
api->getprintfunc = sq_getprintfunc;
|
||||
api->geterrorfunc = sq_geterrorfunc;
|
||||
api->suspendvm = sq_suspendvm;
|
||||
api->wakeupvm = sq_wakeupvm;
|
||||
api->getvmstate = sq_getvmstate;
|
||||
api->getversion = sq_getversion;
|
||||
|
||||
//compiler
|
||||
api->compile = sq_compile;
|
||||
api->compilebuffer = sq_compilebuffer;
|
||||
api->enabledebuginfo = sq_enabledebuginfo;
|
||||
api->notifyallexceptions = sq_notifyallexceptions;
|
||||
api->setcompilererrorhandler = sq_setcompilererrorhandler;
|
||||
|
||||
//stack operations
|
||||
api->push = sq_push;
|
||||
api->pop = sq_pop;
|
||||
api->poptop = sq_poptop;
|
||||
api->remove = sq_remove;
|
||||
api->gettop = sq_gettop;
|
||||
api->settop = sq_settop;
|
||||
api->reservestack = sq_reservestack;
|
||||
api->cmp = sq_cmp;
|
||||
api->move = sq_move;
|
||||
|
||||
//object creation handling
|
||||
api->newuserdata = sq_newuserdata;
|
||||
api->newtable = sq_newtable;
|
||||
api->newtableex = sq_newtableex;
|
||||
api->newarray = sq_newarray;
|
||||
api->newclosure = sq_newclosure;
|
||||
api->setparamscheck = sq_setparamscheck;
|
||||
api->bindenv = sq_bindenv;
|
||||
api->setclosureroot = sq_setclosureroot;
|
||||
api->getclosureroot = sq_getclosureroot;
|
||||
api->pushstring = sq_pushstring;
|
||||
api->pushfloat = sq_pushfloat;
|
||||
api->pushinteger = sq_pushinteger;
|
||||
api->pushbool = sq_pushbool;
|
||||
api->pushuserpointer = sq_pushuserpointer;
|
||||
api->pushnull = sq_pushnull;
|
||||
api->pushthread = sq_pushthread;
|
||||
api->gettype = sq_gettype;
|
||||
api->typeof_ = sq_typeof;
|
||||
api->getsize = sq_getsize;
|
||||
api->gethash = sq_gethash;
|
||||
api->getbase = sq_getbase;
|
||||
api->instanceof = sq_instanceof;
|
||||
api->tostring = sq_tostring;
|
||||
api->tobool = sq_tobool;
|
||||
api->getstringandsize = sq_getstringandsize;
|
||||
api->getstring = sq_getstring;
|
||||
api->getinteger = sq_getinteger;
|
||||
api->getfloat = sq_getfloat;
|
||||
api->getbool = sq_getbool;
|
||||
api->getthread = sq_getthread;
|
||||
api->getuserpointer = sq_getuserpointer;
|
||||
api->getuserdata = sq_getuserdata;
|
||||
api->settypetag = sq_settypetag;
|
||||
api->gettypetag = sq_gettypetag;
|
||||
api->setreleasehook = sq_setreleasehook;
|
||||
api->getreleasehook = sq_getreleasehook;
|
||||
api->getscratchpad = sq_getscratchpad;
|
||||
api->getfunctioninfo = sq_getfunctioninfo;
|
||||
api->getclosureinfo = sq_getclosureinfo;
|
||||
api->getclosurename = sq_getclosurename;
|
||||
api->setnativeclosurename = sq_setnativeclosurename;
|
||||
api->setinstanceup = sq_setinstanceup;
|
||||
api->getinstanceup = sq_getinstanceup;
|
||||
api->setclassudsize = sq_setclassudsize;
|
||||
api->newclass = sq_newclass;
|
||||
api->createinstance = sq_createinstance;
|
||||
api->setattributes = sq_setattributes;
|
||||
api->getattributes = sq_getattributes;
|
||||
api->getclass = sq_getclass;
|
||||
api->weakref = sq_weakref;
|
||||
api->getdefaultdelegate = sq_getdefaultdelegate;
|
||||
api->getmemberhandle = sq_getmemberhandle;
|
||||
api->getbyhandle = sq_getbyhandle;
|
||||
api->setbyhandle = sq_setbyhandle;
|
||||
|
||||
//object manipulation
|
||||
api->pushroottable = sq_pushroottable;
|
||||
api->pushregistrytable = sq_pushregistrytable;
|
||||
api->pushconsttable = sq_pushconsttable;
|
||||
api->setroottable = sq_setroottable;
|
||||
api->setconsttable = sq_setconsttable;
|
||||
api->newslot = sq_newslot;
|
||||
api->deleteslot = sq_deleteslot;
|
||||
api->set = sq_set;
|
||||
api->get = sq_get;
|
||||
api->rawget = sq_rawget;
|
||||
api->rawset = sq_rawset;
|
||||
api->rawdeleteslot = sq_rawdeleteslot;
|
||||
api->newmember = sq_newmember;
|
||||
api->rawnewmember = sq_rawnewmember;
|
||||
api->arrayappend = sq_arrayappend;
|
||||
api->arraypop = sq_arraypop;
|
||||
api->arrayresize = sq_arrayresize;
|
||||
api->arrayreverse = sq_arrayreverse;
|
||||
api->arrayremove = sq_arrayremove;
|
||||
api->arrayinsert = sq_arrayinsert;
|
||||
api->setdelegate = sq_setdelegate;
|
||||
api->getdelegate = sq_getdelegate;
|
||||
api->clone = sq_clone;
|
||||
api->setfreevariable = sq_setfreevariable;
|
||||
api->next = sq_next;
|
||||
api->getweakrefval = sq_getweakrefval;
|
||||
api->clear = sq_clear;
|
||||
|
||||
//calls
|
||||
api->call = sq_call;
|
||||
api->resume = sq_resume;
|
||||
api->getlocal = sq_getlocal;
|
||||
api->getcallee = sq_getcallee;
|
||||
api->getfreevariable = sq_getfreevariable;
|
||||
api->throwerror = sq_throwerror;
|
||||
api->throwobject = sq_throwobject;
|
||||
api->reseterror = sq_reseterror;
|
||||
api->getlasterror = sq_getlasterror;
|
||||
|
||||
//raw object handling
|
||||
api->getstackobj = sq_getstackobj;
|
||||
api->pushobject = sq_pushobject;
|
||||
api->addref = sq_addref;
|
||||
api->release = sq_release;
|
||||
api->getrefcount = sq_getrefcount;
|
||||
api->resetobject = sq_resetobject;
|
||||
api->objtostring = sq_objtostring;
|
||||
api->objtobool = sq_objtobool;
|
||||
api->objtointeger = sq_objtointeger;
|
||||
api->objtofloat = sq_objtofloat;
|
||||
api->objtouserpointer = sq_objtouserpointer;
|
||||
api->getobjtypetag = sq_getobjtypetag;
|
||||
api->getvmrefcount = sq_getvmrefcount;
|
||||
|
||||
//GC
|
||||
api->collectgarbage = sq_collectgarbage;
|
||||
api->resurrectunreachable = sq_resurrectunreachable;
|
||||
|
||||
//serialization
|
||||
api->writeclosure = sq_writeclosure;
|
||||
api->readclosure = sq_readclosure;
|
||||
|
||||
//mem allocation
|
||||
api->malloc = sq_malloc;
|
||||
api->realloc = sq_realloc;
|
||||
api->free = sq_free;
|
||||
|
||||
//debug
|
||||
api->stackinfos = sq_stackinfos;
|
||||
api->setdebughook = sq_setdebughook;
|
||||
api->setnativedebughook = sq_setnativedebughook;
|
||||
|
||||
//compiler helpers
|
||||
api->loadfile = sqstd_loadfile;
|
||||
api->dofile = sqstd_dofile;
|
||||
api->writeclosuretofile = sqstd_writeclosuretofile;
|
||||
|
||||
//blob
|
||||
api->createblob = sqstd_createblob;
|
||||
api->getblob = sqstd_getblob;
|
||||
api->getblobsize = sqstd_getblobsize;
|
||||
|
||||
//string
|
||||
api->format = sqstd_format;
|
||||
|
||||
return 1; // Successfully populated!
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
static const SQMODEXPORTS g_SqModExports{
|
||||
sizeof(SQMODEXPORTS),
|
||||
SqExport_PopulateModuleAPI,
|
||||
SqExport_PopulateSquirrelAPI
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void InitExports()
|
||||
{
|
||||
// The server needs a pointer to a pointer, and a persistent one
|
||||
static const SQMODEXPORTS * sqmodexports = &g_SqModExports;
|
||||
|
||||
// Tell the server about the pointer to the exports structure
|
||||
_Func->ExportFunctions(_Info->pluginId, reinterpret_cast< const void ** >(&sqmodexports),
|
||||
sizeof(HSQMODEXPORTS));
|
||||
}
|
||||
|
||||
} // Namespace:: SqMod
|
||||
307
source/Misc/Routine.cpp
Normal file
307
source/Misc/Routine.cpp
Normal file
@@ -0,0 +1,307 @@
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
#include "Routine.hpp"
|
||||
#include "Library/Chrono.hpp"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
#include <utility>
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
namespace SqMod {
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQMODE_DECL_TYPENAME(Typename, _SC("SqRoutineBase"))
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Routine::Time Routine::s_Last = 0;
|
||||
Routine::Time Routine::s_Prev = 0;
|
||||
Routine::Interval Routine::s_Intervals[SQMOD_MAX_ROUTINES];
|
||||
Routine::Instance Routine::s_Instances[SQMOD_MAX_ROUTINES];
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Routine::Process()
|
||||
{
|
||||
// Is this the first call?
|
||||
if (s_Last == 0)
|
||||
{
|
||||
s_Last = Chrono::GetCurrentSysTime();
|
||||
// We'll do it text time
|
||||
return;
|
||||
}
|
||||
// Backup the last known time-stamp
|
||||
s_Prev = s_Last;
|
||||
// Get the current time-stamp
|
||||
s_Last = Chrono::GetCurrentSysTime();
|
||||
// Calculate the elapsed time
|
||||
const Int32 delta = Int32((s_Last - s_Prev) / 1000L);
|
||||
// Process all active routines
|
||||
for (Interval * itr = s_Intervals; itr != (s_Intervals + SQMOD_MAX_ROUTINES); ++itr)
|
||||
{
|
||||
// Is this routine valid?
|
||||
if (*itr)
|
||||
{
|
||||
// Decrease the elapsed time
|
||||
(*itr) -= delta;
|
||||
// Have we completed the routine interval?
|
||||
if ((*itr) <= 0)
|
||||
{
|
||||
// Execute and reset the elapsed time
|
||||
(*itr) = s_Instances[itr - s_Intervals].Execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Routine::Initialize()
|
||||
{
|
||||
std::memset(s_Intervals, 0, sizeof(s_Intervals));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Routine::Deinitialize()
|
||||
{
|
||||
// Release any script resources that the routines might store
|
||||
for (auto & r : s_Instances)
|
||||
{
|
||||
r.Terminate();
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQInteger Routine::Create(HSQUIRRELVM vm)
|
||||
{
|
||||
// Locate the identifier of a free slot
|
||||
const SQInteger slot = FindUnused();
|
||||
// See if we have where to store this routine
|
||||
if (slot < 0)
|
||||
{
|
||||
return sq_throwerror(vm, "Reached the maximum number of active routines");
|
||||
}
|
||||
// Grab the top of the stack
|
||||
const SQInteger top = sq_gettop(vm);
|
||||
// See if too many arguments were specified
|
||||
if (top >= 20) /* 5 base + 14 parameters = 19 */
|
||||
{
|
||||
return sq_throwerror(vm, "Too many parameters specified");
|
||||
}
|
||||
// Was there was an environment specified?
|
||||
else if (top <= 1)
|
||||
{
|
||||
return sq_throwerror(vm, "Missing routine environment");
|
||||
}
|
||||
// Was there was a callback specified?
|
||||
else if (top <= 2)
|
||||
{
|
||||
return sq_throwerror(vm, "Missing routine callback");
|
||||
}
|
||||
// Validate the callback type
|
||||
else if (sq_gettype(vm, 3) != OT_CLOSURE && sq_gettype(vm, 3) != OT_NATIVECLOSURE)
|
||||
{
|
||||
return sq_throwerror(vm, "Invalid callback type");
|
||||
}
|
||||
|
||||
SQRESULT res = SQ_OK;
|
||||
// Prepare an object for the environment
|
||||
HSQOBJECT env;
|
||||
// Is the specified environment a null value?
|
||||
if (sq_gettype(vm, 2) == OT_NULL)
|
||||
{
|
||||
// Preserve the stack state
|
||||
const StackGuard sg(vm);
|
||||
// Push the root table on the stack
|
||||
sq_pushroottable(vm);
|
||||
// Attempt to retrieve the table object
|
||||
res = sq_getstackobj(vm, -1, &env);
|
||||
}
|
||||
else
|
||||
{
|
||||
sq_getstackobj(vm, 2, &env); // Just retrieve the specified environment
|
||||
}
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
|
||||
// Prepare an object for the function
|
||||
HSQOBJECT func;
|
||||
// Fetch the specified callback object
|
||||
res = sq_getstackobj(vm, 3, &func);
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
|
||||
// The number of iterations and interval to execute the routine
|
||||
SQInteger intrv = 0, itr = 0;
|
||||
// Was there an interval specified?
|
||||
if (top > 3)
|
||||
{
|
||||
// Grab the interval from the stack
|
||||
res = sq_getinteger(vm, 4, &intrv);
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
}
|
||||
// Was there a number of iterations specified?
|
||||
if (top > 4)
|
||||
{
|
||||
// Grab the iterations from the stack
|
||||
res = sq_getinteger(vm, 5, &itr);
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to create a routine instance
|
||||
try
|
||||
{
|
||||
ClassType< Routine >::PushInstance(vm, new Routine());
|
||||
}
|
||||
catch (const Sqrat::Exception & e)
|
||||
{
|
||||
return sq_throwerror(vm, "Unable to create the routine instance");
|
||||
}
|
||||
// Prepare an object for the routine
|
||||
HSQOBJECT obj;
|
||||
// Fetch the created routine object
|
||||
res = sq_getstackobj(vm, -1, &obj);
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
|
||||
// At this point we can grab a reference to our slot
|
||||
Instance & inst = s_Instances[slot];
|
||||
// Were there any arguments specified?
|
||||
if (top > 5)
|
||||
{
|
||||
// Grab a pointer to the arguments array
|
||||
Argument * args = inst.mArgv;
|
||||
// Reset the argument counter
|
||||
inst.mArgc = 0;
|
||||
// Grab the specified arguments from the stack
|
||||
for (SQInteger i = 6; i <= top; ++i)
|
||||
{
|
||||
res = sq_getstackobj(vm, i, &(args[inst.mArgc].mObj));
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
// Clear previous arguments
|
||||
inst.Clear();
|
||||
// Propagate the error
|
||||
return res;
|
||||
}
|
||||
// Keep a strong reference to the argument
|
||||
sq_addref(vm, &(args[inst.mArgc].mObj));
|
||||
// Increase the argument counter
|
||||
++inst.mArgc;
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to retrieve the routine from the stack and associate it with the slot
|
||||
try
|
||||
{
|
||||
Var< Routine * >(vm, -1).value->m_Slot = ConvTo< Uint32 >::From(slot);
|
||||
}
|
||||
catch (const Sqrat::Exception & e)
|
||||
{
|
||||
// Clear extracted arguments
|
||||
inst.Clear();
|
||||
// Now it's safe to throw the error
|
||||
return sq_throwerror(vm, "Unable to create the routine instance");
|
||||
}
|
||||
|
||||
// Alright, at this point we can initialize the slot
|
||||
inst.Init(env, func, obj, intrv, itr);
|
||||
// Now initialize the timer
|
||||
s_Intervals[slot] = intrv;
|
||||
// We have the created routine on the stack, so let's return it
|
||||
return 1;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
bool Routine::IsWithTag(const StackStrF & tag)
|
||||
{
|
||||
// Is the specified tag valid?
|
||||
if (tag.mPtr != nullptr)
|
||||
{
|
||||
// Iterate routine list
|
||||
for (const auto & r : s_Instances)
|
||||
{
|
||||
if (!r.mInst.IsNull() && r.mTag.compare(tag.mPtr) == 0)
|
||||
{
|
||||
return true; // Yup, we're doing this
|
||||
}
|
||||
}
|
||||
}
|
||||
// Unable to find such routine
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Forward the call to process routines.
|
||||
*/
|
||||
void ProcessRoutines()
|
||||
{
|
||||
Routine::Process();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Forward the call to initialize routines.
|
||||
*/
|
||||
void InitializeRoutines()
|
||||
{
|
||||
Routine::Initialize();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Forward the call to terminate routines.
|
||||
*/
|
||||
void TerminateRoutines()
|
||||
{
|
||||
Routine::Deinitialize();
|
||||
}
|
||||
|
||||
// ================================================================================================
|
||||
void Register_Routine(HSQUIRRELVM vm)
|
||||
{
|
||||
RootTable(vm).Bind(Typename::Str,
|
||||
Class< Routine, NoConstructor< Routine > >(vm, Typename::Str)
|
||||
// Meta-methods
|
||||
.SquirrelFunc(_SC("_typename"), &Typename::Fn)
|
||||
.Func(_SC("_tostring"), &Routine::ToString)
|
||||
// Properties
|
||||
.Prop(_SC("Tag"), &Routine::GetTag, &Routine::SetTag)
|
||||
.Prop(_SC("Env"), &Routine::GetEnv, &Routine::SetEnv)
|
||||
.Prop(_SC("Func"), &Routine::GetFunc, &Routine::SetFunc)
|
||||
.Prop(_SC("Data"), &Routine::GetData, &Routine::SetData)
|
||||
.Prop(_SC("Interval"), &Routine::GetInterval, &Routine::SetInterval)
|
||||
.Prop(_SC("Iterations"), &Routine::GetIterations, &Routine::SetIterations)
|
||||
.Prop(_SC("Suspended"), &Routine::GetSuspended, &Routine::SetSuspended)
|
||||
.Prop(_SC("Quiet"), &Routine::GetQuiet, &Routine::SetQuiet)
|
||||
.Prop(_SC("Endure"), &Routine::GetEndure, &Routine::SetEndure)
|
||||
.Prop(_SC("Arguments"), &Routine::GetArguments)
|
||||
// Member Methods
|
||||
.FmtFunc(_SC("SetTag"), &Routine::SetTag)
|
||||
.Func(_SC("Terminate"), &Routine::Terminate)
|
||||
.Func(_SC("GetArgument"), &Routine::GetArgument)
|
||||
);
|
||||
// Global functions
|
||||
RootTable(vm).SquirrelFunc(_SC("SqRoutine"), &Routine::Create);
|
||||
RootTable(vm).FmtFunc(_SC("SqFindRoutineByTag"), &Routine::FindByTag);
|
||||
RootTable(vm).FmtFunc(_SC("SqIsRoutineWithTag"), &Routine::IsWithTag);
|
||||
}
|
||||
|
||||
} // Namespace:: SqMod
|
||||
553
source/Misc/Routine.hpp
Normal file
553
source/Misc/Routine.hpp
Normal file
@@ -0,0 +1,553 @@
|
||||
#ifndef _ROUTINE_HPP_
|
||||
#define _ROUTINE_HPP_
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
#include "Base/Shared.hpp"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
namespace SqMod {
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Execute callbacks after specific intervals of time.
|
||||
*/
|
||||
class Routine
|
||||
{
|
||||
public:
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Simplify future changes to a single point of change.
|
||||
*/
|
||||
typedef Int64 Time;
|
||||
typedef SQInteger Interval;
|
||||
typedef Uint32 Iterator;
|
||||
typedef LightObj Argument;
|
||||
|
||||
private:
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Structure that represents an active routine and keeps track of the routine information.
|
||||
*/
|
||||
struct Instance
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------
|
||||
LightObj mEnv; // A reference to the managed environment object.
|
||||
LightObj mFunc; // A reference to the managed function object.
|
||||
LightObj mInst; // Reference to the routine associated with this instance.
|
||||
LightObj mData; // A reference to the arbitrary data associated with this instance.
|
||||
String mTag; // An arbitrary string which represents the tag.
|
||||
Iterator mIterations; // Number of iterations before self destruct.
|
||||
Interval mInterval; // Interval between routine invocations.
|
||||
bool mSuspended; // Whether this instance is allowed to receive calls.
|
||||
bool mQuiet; // Whether this instance is allowed to handle errors.
|
||||
bool mEndure; // Whether this instance is allowed to terminate itself on errors.
|
||||
Uint8 mArgc; // The number of arguments that the routine must forward.
|
||||
Argument mArgv[14]; // The arguments that the routine must forward.
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Default constructor.
|
||||
*/
|
||||
Instance()
|
||||
: mEnv()
|
||||
, mFunc()
|
||||
, mInst()
|
||||
, mData()
|
||||
, mTag()
|
||||
, mIterations(0)
|
||||
, mInterval(0)
|
||||
, mSuspended(false)
|
||||
, mQuiet(ErrorHandling::IsEnabled())
|
||||
, mEndure(false)
|
||||
, mArgc(0)
|
||||
, mArgv()
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Copy constructor. (disabled)
|
||||
*/
|
||||
Instance(const Instance & o) = delete;
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Move constructor. (disabled)
|
||||
*/
|
||||
Instance(Instance && o) = delete;
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Destructor.
|
||||
*/
|
||||
~Instance()
|
||||
{
|
||||
Terminate();
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Copy assignment operator. (disabled)
|
||||
*/
|
||||
Instance & operator = (const Instance & o) = delete;
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Move assignment operator. (disabled)
|
||||
*/
|
||||
Instance & operator = (Instance && o) = delete;
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Initializes the routine parameters. (assumes previous values are already released)
|
||||
*/
|
||||
void Init(HSQOBJECT & env, HSQOBJECT & func, HSQOBJECT & inst, Interval intrv, Iterator itr)
|
||||
{
|
||||
// Initialize the callback objects
|
||||
mEnv = LightObj(env);
|
||||
mFunc = LightObj(func);
|
||||
// Associate with the routine instance
|
||||
mInst = LightObj(inst);
|
||||
// Initialize the routine options
|
||||
mIterations = itr;
|
||||
mInterval = intrv;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Release managed script resources.
|
||||
*/
|
||||
void Release()
|
||||
{
|
||||
mEnv.Release();
|
||||
mFunc.Release();
|
||||
mInst.Release();
|
||||
mData.Release();
|
||||
mIterations = 0;
|
||||
mInterval = 0;
|
||||
mTag.clear();
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Execute the managed routine.
|
||||
*/
|
||||
Interval Execute()
|
||||
{
|
||||
// is this even a valid routine?
|
||||
if (mInst.IsNull())
|
||||
{
|
||||
return 0; // Dunno how we got here but it ends now
|
||||
}
|
||||
// Are we allowed to forward calls?
|
||||
else if (!mSuspended)
|
||||
{
|
||||
// Grab the virtual machine once
|
||||
HSQUIRRELVM vm = DefaultVM::Get();
|
||||
// Push the function on the stack
|
||||
sq_pushobject(vm, mFunc);
|
||||
// Push the environment on the stack
|
||||
sq_pushobject(vm, mEnv);
|
||||
// Push function parameters, if any
|
||||
for (Uint32 n = 0; n < mArgc; ++n)
|
||||
{
|
||||
sq_pushobject(vm, mArgv[n].mObj);
|
||||
}
|
||||
// Make the function call and store the result
|
||||
const SQRESULT res = sq_call(vm, mArgc + 1, false, !mQuiet);
|
||||
// Pop the callback object from the stack
|
||||
sq_pop(vm, 1);
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
// Should we endure the errors?
|
||||
if (!mEndure)
|
||||
{
|
||||
Terminate(); // Destroy ourself on error
|
||||
}
|
||||
}
|
||||
}
|
||||
// Decrease the number of iterations if necessary
|
||||
if (mIterations && (--mIterations) == 0)
|
||||
{
|
||||
Terminate(); // This routine reached the end of it's life
|
||||
}
|
||||
// Return the current interval
|
||||
return mInterval;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Clear the arguments.
|
||||
*/
|
||||
void Clear()
|
||||
{
|
||||
// Now release the arguments
|
||||
for (auto & a : mArgv)
|
||||
{
|
||||
a.Release();
|
||||
}
|
||||
// Reset the counter
|
||||
mArgc = 0;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Terminate the routine.
|
||||
*/
|
||||
void Terminate()
|
||||
{
|
||||
Release();
|
||||
Clear();
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
static Time s_Last; // Last time point.
|
||||
static Time s_Prev; // Previous time point.
|
||||
static Interval s_Intervals[SQMOD_MAX_ROUTINES]; // List of intervals to be processed.
|
||||
static Instance s_Instances[SQMOD_MAX_ROUTINES]; // List of routines to be executed.
|
||||
|
||||
private:
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* The index of the slot in the pool of active routines.
|
||||
*/
|
||||
Uint32 m_Slot;
|
||||
|
||||
protected:
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Default constructor.
|
||||
*/
|
||||
Routine()
|
||||
: m_Slot(SQMOD_MAX_ROUTINES)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Default constructor.
|
||||
*/
|
||||
Routine(Uint32 slot)
|
||||
: m_Slot(slot)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copy constructor. (disabled)
|
||||
*/
|
||||
Routine(const Routine & o) = delete;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Move constructor. (disabled)
|
||||
*/
|
||||
Routine(Routine && o) = delete;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copy assignment operator. (disabled)
|
||||
*/
|
||||
Routine & operator = (const Routine & o) = delete;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Move assignment operator. (disabled)
|
||||
*/
|
||||
Routine & operator = (Routine && o) = delete;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Find an unoccupied routine slot.
|
||||
*/
|
||||
static SQInteger FindUnused()
|
||||
{
|
||||
for (const auto & r : s_Instances)
|
||||
{
|
||||
if (r.mInst.IsNull())
|
||||
{
|
||||
return (&r - s_Instances); // Return the index of this element
|
||||
}
|
||||
}
|
||||
// No available slot
|
||||
return -1;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Retrieve the number of used routine slots.
|
||||
*/
|
||||
static SQInteger GetUsed()
|
||||
{
|
||||
SQInteger n = 0;
|
||||
// Iterate routine list
|
||||
for (const auto & r : s_Instances)
|
||||
{
|
||||
if (!r.mInst.IsNull())
|
||||
{
|
||||
++n;
|
||||
}
|
||||
}
|
||||
// Return the final count
|
||||
return n;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Retrieve the number of used routine slots.
|
||||
*/
|
||||
static const LightObj & FindByTag(const StackStrF & tag)
|
||||
{
|
||||
// Is the specified tag valid?
|
||||
if (!tag.mPtr)
|
||||
{
|
||||
STHROWF("Invalid routine tag");
|
||||
}
|
||||
// Iterate routine list
|
||||
for (const auto & r : s_Instances)
|
||||
{
|
||||
if (!r.mInst.IsNull() && r.mTag.compare(tag.mPtr) == 0)
|
||||
{
|
||||
return r.mInst; // Return this routine instance
|
||||
}
|
||||
}
|
||||
// Unable to find such routine
|
||||
STHROWF("Unable to find a routine with tag (%s)", tag.mPtr);
|
||||
// Should not reach this point but if it did, we have to return something
|
||||
return s_Instances[SQMOD_MAX_ROUTINES].mInst; // Intentional Buffer overflow!
|
||||
}
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Check if a routine with a certain tag exists.
|
||||
*/
|
||||
static bool IsWithTag(const StackStrF & tag);
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Process all active routines and update elapsed time.
|
||||
*/
|
||||
static void Process();
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Initialize all resources and prepare for startup.
|
||||
*/
|
||||
static void Initialize();
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Release all resources and prepare for shutdown.
|
||||
*/
|
||||
static void Deinitialize();
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Create a routine with the specified parameters.
|
||||
*/
|
||||
static SQInteger Create(HSQUIRRELVM vm);
|
||||
|
||||
protected:
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* See whether this routine is valid otherwise throw an exception.
|
||||
*/
|
||||
void Validate() const
|
||||
{
|
||||
if (m_Slot >= SQMOD_MAX_ROUTINES)
|
||||
{
|
||||
STHROWF("This instance does not reference a valid routine");
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* See whether this routine is valid otherwise throw an exception.
|
||||
*/
|
||||
Instance & GetValid() const
|
||||
{
|
||||
if (m_Slot >= SQMOD_MAX_ROUTINES)
|
||||
{
|
||||
STHROWF("This instance does not reference a valid routine");
|
||||
}
|
||||
// We know it's valid so let's return it
|
||||
return s_Instances[m_Slot];
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Used by the script engine to convert an instance of this type to a string.
|
||||
*/
|
||||
const String & ToString() const
|
||||
{
|
||||
return (m_Slot >= SQMOD_MAX_ROUTINES) ? NullString() : s_Instances[m_Slot].mTag;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Terminate the routine.
|
||||
*/
|
||||
void Terminate()
|
||||
{
|
||||
GetValid().Terminate();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Retrieve the associated user tag.
|
||||
*/
|
||||
const String & GetTag() const
|
||||
{
|
||||
return GetValid().mTag;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Modify the associated user tag.
|
||||
*/
|
||||
void SetTag(const StackStrF & tag)
|
||||
{
|
||||
GetValid().mTag.assign(tag.mPtr, ClampMin(tag.mLen, 0));
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Retrieve the environment object.
|
||||
*/
|
||||
const LightObj & GetEnv() const
|
||||
{
|
||||
return GetValid().mEnv;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Modify the environment object.
|
||||
*/
|
||||
void SetEnv(const LightObj & env)
|
||||
{
|
||||
GetValid().mEnv = env.IsNull() ? LightObj(RootTable().GetObject()) : env;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Retrieve the function object.
|
||||
*/
|
||||
const LightObj & GetFunc() const
|
||||
{
|
||||
return GetValid().mFunc;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Modify the function object.
|
||||
*/
|
||||
void SetFunc(const Function & func)
|
||||
{
|
||||
// Validate the specified
|
||||
if (!sq_isclosure(func.GetFunc()) && !sq_isnativeclosure(func.GetFunc()))
|
||||
{
|
||||
STHROWF("Invalid callback type %s", SqTypeName(GetValid().mFunc.GetType()));
|
||||
}
|
||||
// Store the function without the environment
|
||||
GetValid().mFunc = LightObj(func.GetFunc());
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Retrieve the arbitrary user data object.
|
||||
*/
|
||||
const LightObj & GetData() const
|
||||
{
|
||||
return GetValid().mData;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Modify the arbitrary user data object.
|
||||
*/
|
||||
void SetData(const LightObj & data)
|
||||
{
|
||||
GetValid().mData = data;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Retrieve the execution interval.
|
||||
*/
|
||||
SQInteger GetInterval() const
|
||||
{
|
||||
return ConvTo< SQInteger >::From(GetValid().mInterval);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Modify the execution interval.
|
||||
*/
|
||||
void SetInterval(SQInteger itr)
|
||||
{
|
||||
GetValid().mInterval = ClampMin(ConvTo< Interval >::From(itr), static_cast< Interval >(0));
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Retrieve the number of iterations.
|
||||
*/
|
||||
SQInteger GetIterations() const
|
||||
{
|
||||
return ConvTo< SQInteger >::From(GetValid().mIterations);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Modify the number of iterations.
|
||||
*/
|
||||
void SetIterations(SQInteger itr)
|
||||
{
|
||||
GetValid().mIterations = ConvTo< Iterator >::From(itr);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* See whether the routine is suspended.
|
||||
*/
|
||||
bool GetSuspended() const
|
||||
{
|
||||
return GetValid().mSuspended;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Set whether the routine should be suspended.
|
||||
*/
|
||||
void SetSuspended(bool toggle)
|
||||
{
|
||||
GetValid().mSuspended = toggle;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* See whether the routine is quite.
|
||||
*/
|
||||
bool GetQuiet() const
|
||||
{
|
||||
return GetValid().mQuiet;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Set whether the routine should be quiet.
|
||||
*/
|
||||
void SetQuiet(bool toggle)
|
||||
{
|
||||
GetValid().mQuiet = toggle;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* See whether the routine endures.
|
||||
*/
|
||||
bool GetEndure() const
|
||||
{
|
||||
return GetValid().mEndure;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Set whether the routine should endure.
|
||||
*/
|
||||
void SetEndure(bool toggle)
|
||||
{
|
||||
GetValid().mEndure = toggle;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Retrieve the number of arguments to be forwarded.
|
||||
*/
|
||||
SQInteger GetArguments() const
|
||||
{
|
||||
return ConvTo< SQInteger >::From(GetValid().mArgc);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Retrieve a certain argument.
|
||||
*/
|
||||
const Argument & GetArgument(SQInteger arg) const
|
||||
{
|
||||
// Cast the index to the proper value
|
||||
Uint8 idx = ConvTo< Uint8 >::From(arg);
|
||||
// Validate the specified index
|
||||
if (idx >= 14)
|
||||
{
|
||||
STHROWF("The specified index is out of range: %u >= %u", idx, 14);
|
||||
}
|
||||
// Return the requested argument
|
||||
return GetValid().mArgv[idx];
|
||||
}
|
||||
};
|
||||
|
||||
} // Namespace:: SqMod
|
||||
|
||||
#endif // _ROUTINE_HPP_
|
||||
1716
source/Misc/Signal.cpp
Normal file
1716
source/Misc/Signal.cpp
Normal file
File diff suppressed because it is too large
Load Diff
767
source/Misc/Signal.hpp
Normal file
767
source/Misc/Signal.hpp
Normal file
@@ -0,0 +1,767 @@
|
||||
#ifndef _SIGNAL_HPP_
|
||||
#define _SIGNAL_HPP_
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
#include "Base/Shared.hpp"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
namespace SqMod {
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
struct Signal;
|
||||
struct SignalWrapper;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Class used to deliver events to one or more listeners.
|
||||
*/
|
||||
struct Signal
|
||||
{
|
||||
friend class SignalWrapper;
|
||||
// --------------------------------------------------------------------------------------------
|
||||
typedef unsigned int SizeType; // Type of value used to represent sizes and/or indexes.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
enum { SMB_SIZE = 8 };
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Default constructor.
|
||||
*/
|
||||
Signal();
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Default constructor.
|
||||
*/
|
||||
explicit Signal(const char * name)
|
||||
: Signal(String(name))
|
||||
{
|
||||
//...
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Default constructor.
|
||||
*/
|
||||
explicit Signal(const String & name)
|
||||
: Signal(String(name))
|
||||
{
|
||||
//...
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Default constructor.
|
||||
*/
|
||||
Signal(String && name);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copy constructor (disabled).
|
||||
*/
|
||||
Signal(const Signal & o) = delete;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Move constructor (disabled).
|
||||
*/
|
||||
Signal(Signal && o) = delete;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Destructor.
|
||||
*/
|
||||
~Signal();
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Copy assignment operator (disabled).
|
||||
*/
|
||||
Signal & operator = (const Signal & o) = delete;
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Move assignment operator (disabled).
|
||||
*/
|
||||
Signal & operator = (Signal && o) = delete;
|
||||
|
||||
protected:
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Adjust the internal buffer size if necessary.
|
||||
*/
|
||||
bool AdjustSlots(SizeType capacity);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Structure responsible for storing information about a slot.
|
||||
*/
|
||||
struct Slot {
|
||||
SQHash mThisHash; // The hash of the specified environment.
|
||||
SQHash mFuncHash; // The hash of the specified callback.
|
||||
HSQOBJECT mThisRef; // The specified script environment.
|
||||
HSQOBJECT mFuncRef; // The specified script callback.
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Default constructor.
|
||||
*/
|
||||
Slot()
|
||||
: mThisHash(0)
|
||||
, mFuncHash(0)
|
||||
, mThisRef()
|
||||
, mFuncRef()
|
||||
{
|
||||
sq_resetobject(&mThisRef);
|
||||
sq_resetobject(&mFuncRef);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Forwarding constructor.
|
||||
*/
|
||||
Slot(Object & env, Function & func)
|
||||
: Slot(env.GetObject(), func.GetFunc())
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Base constructor.
|
||||
*/
|
||||
Slot(HSQOBJECT & env, HSQOBJECT & func)
|
||||
: mThisHash(0)
|
||||
, mFuncHash(0)
|
||||
, mThisRef(env)
|
||||
, mFuncRef(func)
|
||||
{
|
||||
HSQUIRRELVM vm = DefaultVM::Get();
|
||||
// Remember the current stack size
|
||||
const StackGuard sg(vm);
|
||||
// Is there an explicit environment?
|
||||
if (!sq_isnull(mThisRef))
|
||||
{
|
||||
// Keep a reference to this environment
|
||||
sq_addref(vm, &mThisRef);
|
||||
// Push the environment on the stack
|
||||
sq_pushobject(vm, mThisRef);
|
||||
// Grab the hash of the environment object
|
||||
mThisHash = sq_gethash(vm, -1);
|
||||
}
|
||||
// Is there an explicit function?
|
||||
if (!sq_isnull(mFuncRef))
|
||||
{
|
||||
// Keep a reference to this function
|
||||
sq_addref(vm, &mFuncRef);
|
||||
// Push the callback on the stack
|
||||
sq_pushobject(vm, mFuncRef);
|
||||
// Grab the hash of the callback object
|
||||
mFuncHash = sq_gethash(vm, -1);
|
||||
}
|
||||
}
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Base constructor.
|
||||
*/
|
||||
Slot(HSQOBJECT & env, HSQOBJECT & func, SQHash envh, SQHash funch)
|
||||
: mThisHash(envh)
|
||||
, mFuncHash(funch)
|
||||
, mThisRef(env)
|
||||
, mFuncRef(func)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Copy constructor.
|
||||
*/
|
||||
Slot(const Slot & o)
|
||||
: mThisHash(o.mThisHash)
|
||||
, mFuncHash(o.mFuncHash)
|
||||
, mThisRef(o.mThisRef)
|
||||
, mFuncRef(o.mFuncRef)
|
||||
{
|
||||
// Track reference
|
||||
if (mFuncHash != 0)
|
||||
{
|
||||
sq_addref(DefaultVM::Get(), &mThisRef);
|
||||
sq_addref(DefaultVM::Get(), &mFuncRef);
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Move constructor.
|
||||
*/
|
||||
Slot(Slot && o)
|
||||
: mThisHash(o.mThisHash)
|
||||
, mFuncHash(o.mFuncHash)
|
||||
, mThisRef(o.mThisRef)
|
||||
, mFuncRef(o.mFuncRef)
|
||||
{
|
||||
// Take ownership
|
||||
sq_resetobject(&o.mThisRef);
|
||||
sq_resetobject(&o.mFuncRef);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Destructor.
|
||||
*/
|
||||
~Slot()
|
||||
{
|
||||
Release();
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Copy assignment operator. (disabled)
|
||||
*/
|
||||
Slot & operator = (const Slot & o)
|
||||
{
|
||||
if (this != &o)
|
||||
{
|
||||
// Release current resources, if any
|
||||
Release();
|
||||
// Replicate data
|
||||
mThisHash = o.mThisHash;
|
||||
mFuncHash = o.mFuncHash;
|
||||
mThisRef = o.mThisRef;
|
||||
mFuncRef = o.mFuncRef;
|
||||
// Track reference
|
||||
sq_addref(DefaultVM::Get(), &const_cast< HSQOBJECT & >(o.mThisRef));
|
||||
sq_addref(DefaultVM::Get(), &const_cast< HSQOBJECT & >(o.mFuncRef));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Move assignment operator.
|
||||
*/
|
||||
Slot & operator = (Slot && o)
|
||||
{
|
||||
if (this != &o)
|
||||
{
|
||||
// Release current resources, if any
|
||||
Release();
|
||||
// Replicate data
|
||||
mThisHash = o.mThisHash;
|
||||
mFuncHash = o.mFuncHash;
|
||||
mThisRef = o.mThisRef;
|
||||
mFuncRef = o.mFuncRef;
|
||||
// Take ownership
|
||||
sq_resetobject(&o.mThisRef);
|
||||
sq_resetobject(&o.mFuncRef);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Equality comparison operator.
|
||||
*/
|
||||
bool operator == (const Slot & o) const
|
||||
{
|
||||
return (mThisHash == o.mThisHash) && (mFuncHash == o.mFuncHash);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Inequality comparison operator.
|
||||
*/
|
||||
bool operator != (const Slot & o) const
|
||||
{
|
||||
return (mThisHash != o.mThisHash) || (mFuncHash != o.mFuncHash);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Release managed script resources.
|
||||
*/
|
||||
bool Available() const
|
||||
{
|
||||
return (mFuncHash == 0);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Release managed script resources.
|
||||
*/
|
||||
void Release()
|
||||
{
|
||||
// Should we release any environment object?
|
||||
if (mThisHash != 0)
|
||||
{
|
||||
sq_release(DefaultVM::Get(), &mThisRef);
|
||||
sq_resetobject(&mThisRef);
|
||||
// Also reset the hash
|
||||
mThisHash = 0;
|
||||
}
|
||||
// Should we release any callback object?
|
||||
if (mFuncHash != 0)
|
||||
{
|
||||
sq_release(DefaultVM::Get(), &mFuncRef);
|
||||
sq_resetobject(&mFuncRef);
|
||||
// Also reset the hash
|
||||
mFuncHash = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------------------
|
||||
* Swap the values of two slots.
|
||||
*/
|
||||
void Swap(Slot & s)
|
||||
{
|
||||
// Swap the environment hash
|
||||
SQHash h = mThisHash;
|
||||
mThisHash = s.mThisHash;
|
||||
s.mThisHash = h;
|
||||
// Swap the callback hash
|
||||
h = mFuncHash;
|
||||
mFuncHash = s.mFuncHash;
|
||||
s.mFuncHash = h;
|
||||
// Swap the environment object
|
||||
HSQOBJECT o = mThisRef;
|
||||
mThisRef = s.mThisRef;
|
||||
s.mThisRef = o;
|
||||
// Swap the callback object
|
||||
o = mFuncRef;
|
||||
mFuncRef = s.mFuncRef;
|
||||
s.mFuncRef = o;
|
||||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
typedef Slot ValueType; // Value type used to represent a slot.
|
||||
typedef ValueType & Reference; // Reference to the stored value type
|
||||
typedef const ValueType & ConstReference; // Constant reference to the stored value type.
|
||||
typedef ValueType * Pointer; // Pointer to the stored value type
|
||||
typedef const ValueType * ConstPointer; // Constant pointer to the stored value type.
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
/// Execution scope used to adjust iterators when removing slots or adjusting the buffer.
|
||||
struct Scope {
|
||||
// ----------------------------------------------------------------------------------------
|
||||
Pointer mItr; ///< Currently executed slot.
|
||||
Pointer mEnd; ///< Where the execution ends.
|
||||
Scope * mParent; ///< Previous execution scope.
|
||||
Scope * mChild; ///< Next execution scope.
|
||||
// ----------------------------------------------------------------------------------------
|
||||
/// Default constructor.
|
||||
Scope(Scope * parent, Pointer begin, Pointer end)
|
||||
: mItr(begin), mEnd(end), mParent(parent), mChild(nullptr)
|
||||
{
|
||||
if (mParent != nullptr) mParent->mChild = this;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------
|
||||
/// Destructor.
|
||||
~Scope() {
|
||||
if (mParent != nullptr) mParent->mChild = nullptr;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------
|
||||
/// Adjust the iterators to account for the fact that the specified slot was removed.
|
||||
void Descend(Pointer ptr);
|
||||
/// Adjust the iterators to account for the fact that the specified slot is now leading.
|
||||
void Lead(Pointer ptr);
|
||||
/// Adjust the iterators to account for the fact that the specified slot is now tailing.
|
||||
void Tail(Pointer ptr);
|
||||
/// Adjust the iterators to finish the execution abruptly.
|
||||
void Finish();
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
SizeType m_Used; // The number of stored slots that are valid.
|
||||
SizeType m_Size; // The size of the memory allocated for slots.
|
||||
Pointer m_Slots; // Pointer to the memory containing the slots.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
Scope * m_Scope; // Current execution state.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
String m_Name; // The name that identifies this signal.
|
||||
LightObj m_Data; // User data associated with this instance.
|
||||
// --------------------------------------------------------------------------------------------
|
||||
ValueType m_SMB[SMB_SIZE]{}; // Small buffer optimization.
|
||||
|
||||
public:
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Used by the script engine to convert an instance of this type to a string.
|
||||
*/
|
||||
const String & ToString() const
|
||||
{
|
||||
return m_Name;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Retrieve the associated user data.
|
||||
*/
|
||||
LightObj & GetData()
|
||||
{
|
||||
return m_Data;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Modify the associated user data.
|
||||
*/
|
||||
void SetData(LightObj & data)
|
||||
{
|
||||
m_Data = data;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* The number of slots connected to the signal.
|
||||
*/
|
||||
SQInteger GetUsed() const
|
||||
{
|
||||
return static_cast< SQInteger >(m_Used);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Clear all slots connected to the signal.
|
||||
*/
|
||||
void ClearSlots();
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* See if there are any slots connected.
|
||||
*/
|
||||
bool IsEmpty() const
|
||||
{
|
||||
return (m_Used == 0);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Connect the specified slot to the signal.
|
||||
*/
|
||||
SQInteger Connect(SignalWrapper & w);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Connect the specified slot but not before disconnecting all other occurrences.
|
||||
*/
|
||||
SQInteger ConnectOnce(SignalWrapper & w);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Disconnect all occurrences of the specified slot from the signal.
|
||||
*/
|
||||
SQInteger Disconnect(SignalWrapper & w);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* See if the specified slot is connected to the signal.
|
||||
*/
|
||||
SQInteger Exists(SignalWrapper & w);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* See if the specified slot environment is connected to the signal.
|
||||
*/
|
||||
SQInteger ExistsThis(SignalWrapper & w);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* See if the specified slot callback is connected to the signal.
|
||||
*/
|
||||
SQInteger ExistsFunc(SignalWrapper & w);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Count all occurrences of the specified slot.
|
||||
*/
|
||||
SQInteger Count(SignalWrapper & w);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Count all occurrences of the specified slot environment.
|
||||
*/
|
||||
SQInteger CountThis(SignalWrapper & w);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Count all occurrences of the specified slot callback.
|
||||
*/
|
||||
SQInteger CountFunc(SignalWrapper & w);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Move all occurrences of the specified slot to the front.
|
||||
*/
|
||||
SQInteger Lead(SignalWrapper & w);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Move all occurrences of the specified slot environment to the front.
|
||||
*/
|
||||
SQInteger LeadThis(SignalWrapper & w);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Move all occurrences of the specified slot callback to the front.
|
||||
*/
|
||||
SQInteger LeadFunc(SignalWrapper & w);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Move all occurrences of the specified slot to the back.
|
||||
*/
|
||||
SQInteger Tail(SignalWrapper & w);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Move all occurrences of the specified slot environment to the back.
|
||||
*/
|
||||
SQInteger TailThis(SignalWrapper & w);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Move all occurrences of the specified slot callback to the back.
|
||||
*/
|
||||
SQInteger TailFunc(SignalWrapper & w);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Remove all occurrences of the specified slot.
|
||||
*/
|
||||
SQInteger Eliminate(SignalWrapper & w);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Remove all occurrences of the specified slot environment.
|
||||
*/
|
||||
SQInteger EliminateThis(SignalWrapper & w);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Remove all occurrences of the specified slot callback.
|
||||
*/
|
||||
SQInteger EliminateFunc(SignalWrapper & w);
|
||||
|
||||
public:
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Squirrel wrapper for the `Connect` method of this class.
|
||||
*/
|
||||
static SQInteger SqConnect(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Squirrel wrapper for the `ConnectOnce` method of this class.
|
||||
*/
|
||||
static SQInteger SqConnectOnce(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Squirrel wrapper for the `Exists` method of this class.
|
||||
*/
|
||||
static SQInteger SqExists(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Squirrel wrapper for the `Disconnect` method of this class.
|
||||
*/
|
||||
static SQInteger SqDisconnect(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Squirrel wrapper for the `ExistsThis` method of this class.
|
||||
*/
|
||||
static SQInteger SqExistsThis(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Squirrel wrapper for the `ExistsFunc` method of this class.
|
||||
*/
|
||||
static SQInteger SqExistsFunc(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Squirrel wrapper for the `Count` method of this class.
|
||||
*/
|
||||
static SQInteger SqCount(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Squirrel wrapper for the `CountThis` method of this class.
|
||||
*/
|
||||
static SQInteger SqCountThis(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Squirrel wrapper for the `CountFunc` method of this class.
|
||||
*/
|
||||
static SQInteger SqCountFunc(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Squirrel wrapper for the `Lead` method of this class.
|
||||
*/
|
||||
static SQInteger SqLead(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Squirrel wrapper for the `LeadThis` method of this class.
|
||||
*/
|
||||
static SQInteger SqLeadThis(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Squirrel wrapper for the `LeadFunc` method of this class.
|
||||
*/
|
||||
static SQInteger SqLeadFunc(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Squirrel wrapper for the `Tail` method of this class.
|
||||
*/
|
||||
static SQInteger SqTail(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Squirrel wrapper for the `TailThis` method of this class.
|
||||
*/
|
||||
static SQInteger SqTailThis(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Squirrel wrapper for the `TailFunc` method of this class.
|
||||
*/
|
||||
static SQInteger SqTailFunc(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Squirrel wrapper for the `Eliminate` method of this class.
|
||||
*/
|
||||
static SQInteger SqEliminate(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Squirrel wrapper for the `EliminateThis` method of this class.
|
||||
*/
|
||||
static SQInteger SqEliminateThis(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Squirrel wrapper for the `EliminateFunc` method of this class.
|
||||
*/
|
||||
static SQInteger SqEliminateFunc(HSQUIRRELVM vm);
|
||||
|
||||
protected:
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Emit the event to the connected slots.
|
||||
*/
|
||||
SQInteger Emit(HSQUIRRELVM vm, SQInteger top);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Emit the event to the connected slots and collect returned values.
|
||||
*/
|
||||
SQInteger Query(HSQUIRRELVM vm, SQInteger top);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Emit the event to the connected slots and see if they consume it.
|
||||
*/
|
||||
SQInteger Consume(HSQUIRRELVM vm, SQInteger top);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Emit the event to the connected slots and see if they approve it.
|
||||
*/
|
||||
SQInteger Approve(HSQUIRRELVM vm, SQInteger top);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Emit the event to the connected slots and see if they return something.
|
||||
*/
|
||||
SQInteger Request(HSQUIRRELVM vm, SQInteger top);
|
||||
|
||||
public:
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Squirrel wrapper for the `Emit` method of this class.
|
||||
*/
|
||||
static SQInteger SqEmit(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Squirrel wrapper for the `Query` method of this class.
|
||||
*/
|
||||
static SQInteger SqQuery(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Squirrel wrapper for the `Consume` method of this class.
|
||||
*/
|
||||
static SQInteger SqConsume(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Squirrel wrapper for the `Approve` method of this class.
|
||||
*/
|
||||
static SQInteger SqApprove(HSQUIRRELVM vm);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Squirrel wrapper for the `Request` method of this class.
|
||||
*/
|
||||
static SQInteger SqRequest(HSQUIRRELVM vm);
|
||||
|
||||
protected:
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
typedef std::pair< std::size_t, SignalPair > SignalElement;
|
||||
typedef std::vector< SignalElement > SignalPool;
|
||||
typedef std::vector< Signal * > FreeSignals;
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
static SignalPool s_Signals; // List of all created signals.
|
||||
static FreeSignals s_FreeSignals; // List of signals without a name.
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Specialization for when there are no arguments given.
|
||||
*/
|
||||
void PushParameters()
|
||||
{
|
||||
//...
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Specialization for when there's only one argument given/remaining.
|
||||
*/
|
||||
template < typename T > void PushParameters(T v)
|
||||
{
|
||||
Var< T >::push(DefaultVM::Get(), v);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Specialization for when there's more than one argument given.
|
||||
*/
|
||||
template < typename T, typename... Args > void PushParameters(T v, Args... args)
|
||||
{
|
||||
Var< T >::push(DefaultVM::Get(), v);
|
||||
PushParameters(args...);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Terminate all signal instances and release any script resources.
|
||||
*/
|
||||
static void Terminate();
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Create a free signal without a specific name.
|
||||
*/
|
||||
static LightObj CreateFree();
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Create a new signal with the specified name.
|
||||
*/
|
||||
static LightObj Create(const StackStrF & name);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Remove the signal with the specified name.
|
||||
*/
|
||||
static void Remove(const StackStrF & name);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Retrieve the signal with the specified name.
|
||||
*/
|
||||
static const LightObj & Fetch(const StackStrF & name);
|
||||
|
||||
/* --------------------------------------------------------------------------------------------
|
||||
* Emit a signal from the module.
|
||||
*/
|
||||
template < typename... Args > void operator () (Args&&... args)
|
||||
{
|
||||
// Are there any slots connected?
|
||||
if (!m_Used) return;
|
||||
// Enter a new execution scope
|
||||
Scope scope(m_Scope, m_Slots, m_Slots + m_Used);
|
||||
// Activate the current scope and create a guard to restore it
|
||||
const AutoAssign< Scope * > aa(m_Scope, scope.mParent, &scope);
|
||||
// Grab the default virtual machine
|
||||
HSQUIRRELVM vm = DefaultVM::Get();
|
||||
// Process the slots from this scope
|
||||
while (scope.mItr != scope.mEnd)
|
||||
{
|
||||
// Grab a reference to the current slot
|
||||
const Slot & slot = *(scope.mItr++);
|
||||
// Push the callback object
|
||||
sq_pushobject(vm, slot.mFuncRef);
|
||||
// Is there an explicit environment?
|
||||
if (slot.mThisHash == 0)
|
||||
{
|
||||
sq_pushroottable(vm);
|
||||
}
|
||||
else
|
||||
{
|
||||
sq_pushobject(vm, slot.mThisRef);
|
||||
}
|
||||
// Push the given parameters on the stack
|
||||
PushParameters(args...);
|
||||
// Make the function call and store the result
|
||||
const SQRESULT res = sq_call(vm, 1 + sizeof...(Args), false, ErrorHandling::IsEnabled());
|
||||
// Pop the callback object from the stack
|
||||
sq_pop(vm, 1);
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
SQTHROW(vm, LastErrorString(vm)); // Stop emitting signals
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // Namespace:: SqMod
|
||||
|
||||
#endif // _SIGNAL_HPP_
|
||||
524
source/Misc/Tasks.cpp
Normal file
524
source/Misc/Tasks.cpp
Normal file
@@ -0,0 +1,524 @@
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
#include "Tasks.hpp"
|
||||
#include "Core.hpp"
|
||||
#include "Library/Chrono.hpp"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
#include <utility>
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
namespace SqMod {
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQMODE_DECL_TYPENAME(Typename, _SC("SqTask"))
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Tasks::Time Tasks::s_Last = 0;
|
||||
Tasks::Time Tasks::s_Prev = 0;
|
||||
Tasks::Interval Tasks::s_Intervals[SQMOD_MAX_TASKS];
|
||||
Tasks::Task Tasks::s_Tasks[SQMOD_MAX_TASKS];
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Tasks::Task::Init(HSQOBJECT & func, HSQOBJECT & inst, Interval intrv, Iterator itr, Int32 id, Int32 type)
|
||||
{
|
||||
// Initialize the callback hash
|
||||
mHash = 0;
|
||||
// Initialize the callback objects
|
||||
mFunc = LightObj(func);
|
||||
mInst = LightObj(inst);
|
||||
// Initialize the task options
|
||||
mIterations = itr;
|
||||
mInterval = intrv;
|
||||
// Initialize the entity information
|
||||
mEntity = ConvTo< Int16 >::From(id);
|
||||
mType = ConvTo< Uint8 >::From(type);
|
||||
// Grab the virtual machine once
|
||||
HSQUIRRELVM vm = DefaultVM::Get();
|
||||
// Remember the current stack size
|
||||
const StackGuard sg(vm);
|
||||
// Is there a valid function?
|
||||
if (!mFunc.IsNull())
|
||||
{
|
||||
// Push the callback on the stack
|
||||
sq_pushobject(vm, mFunc);
|
||||
// Grab the hash of the callback object
|
||||
mHash = sq_gethash(vm, -1);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Tasks::Task::Release()
|
||||
{
|
||||
mHash = 0;
|
||||
mTag.clear();
|
||||
mFunc.Release();
|
||||
mInst.Release();
|
||||
mData.Release();
|
||||
mIterations = 0;
|
||||
mInterval = 0;
|
||||
mEntity = -1;
|
||||
mType = -1;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
Tasks::Interval Tasks::Task::Execute()
|
||||
{
|
||||
// Are we even a valid task?
|
||||
if (INVALID_ENTITY(mEntity))
|
||||
{
|
||||
return 0; // Dunno how we got here but it ends now
|
||||
}
|
||||
// Grab the virtual machine once
|
||||
HSQUIRRELVM vm = DefaultVM::Get();
|
||||
// Push the function on the stack
|
||||
sq_pushobject(vm, mFunc);
|
||||
// Push the environment on the stack
|
||||
sq_pushobject(vm, mSelf);
|
||||
// Push function parameters, if any
|
||||
for (Uint32 n = 0; n < mArgc; ++n)
|
||||
{
|
||||
sq_pushobject(vm, mArgv[n].mObj);
|
||||
}
|
||||
// Make the function call and store the result
|
||||
const SQRESULT res = sq_call(vm, mArgc + 1, false, ErrorHandling::IsEnabled());
|
||||
// Pop the callback object from the stack
|
||||
sq_pop(vm, 1);
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
Terminate(); // Destroy ourself on error
|
||||
}
|
||||
// Decrease the number of iterations if necessary
|
||||
if (mIterations && (--mIterations) == 0)
|
||||
{
|
||||
Terminate(); // This routine reached the end of it's life
|
||||
}
|
||||
// Return the current interval
|
||||
return mInterval;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Tasks::Process()
|
||||
{
|
||||
// Is this the first call?
|
||||
if (s_Last == 0)
|
||||
{
|
||||
s_Last = Chrono::GetCurrentSysTime();
|
||||
// We'll do it text time
|
||||
return;
|
||||
}
|
||||
// Backup the last known time-stamp
|
||||
s_Prev = s_Last;
|
||||
// Get the current time-stamp
|
||||
s_Last = Chrono::GetCurrentSysTime();
|
||||
// Calculate the elapsed time
|
||||
const Int32 delta = Int32((s_Last - s_Prev) / 1000L);
|
||||
// Process all active tasks
|
||||
for (Interval * itr = s_Intervals; itr != (s_Intervals + SQMOD_MAX_TASKS); ++itr)
|
||||
{
|
||||
// Is this task valid?
|
||||
if (*itr)
|
||||
{
|
||||
// Decrease the elapsed time
|
||||
(*itr) -= delta;
|
||||
// Have we completed the routine interval?
|
||||
if ((*itr) <= 0)
|
||||
{
|
||||
// Execute and reset the elapsed time
|
||||
(*itr) = s_Tasks[itr - s_Intervals].Execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Tasks::Initialize()
|
||||
{
|
||||
std::memset(s_Intervals, 0, sizeof(s_Intervals));
|
||||
// Transform all task instances to script objects
|
||||
for (auto & t : s_Tasks)
|
||||
{
|
||||
// This is fine because they'll always outlive the virtual machine
|
||||
t.mSelf = LightObj(&t);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Tasks::Register(HSQUIRRELVM vm)
|
||||
{
|
||||
RootTable(vm).Bind(Typename::Str,
|
||||
Class< Task, NoDestructor< Task > >(vm, Typename::Str)
|
||||
// Meta-methods
|
||||
.SquirrelFunc(_SC("_typename"), &Typename::Fn)
|
||||
.Func(_SC("_tostring"), &Task::ToString)
|
||||
// Properties
|
||||
.Prop(_SC("Tag"), &Task::GetTag, &Task::SetTag)
|
||||
.Prop(_SC("Entity"), &Task::GetInst)
|
||||
.Prop(_SC("Func"), &Task::GetFunc, &Task::SetFunc)
|
||||
.Prop(_SC("Data"), &Task::GetData, &Task::SetData)
|
||||
.Prop(_SC("Interval"), &Task::GetInterval, &Task::SetInterval)
|
||||
.Prop(_SC("Iterations"), &Task::GetIterations, &Task::SetIterations)
|
||||
.Prop(_SC("Arguments"), &Task::GetArguments)
|
||||
.Prop(_SC("Inst"), &Task::GetInst)
|
||||
// Member Methods
|
||||
.FmtFunc(_SC("SetTag"), &Task::SetTag)
|
||||
.Func(_SC("Terminate"), &Task::Terminate)
|
||||
.Func(_SC("GetArgument"), &Task::GetArgument)
|
||||
// Static functions
|
||||
.StaticFunc(_SC("Used"), &Tasks::GetUsed)
|
||||
);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Tasks::Deinitialize()
|
||||
{
|
||||
// Release any script resources that the tasks might store
|
||||
for (auto & t : s_Tasks)
|
||||
{
|
||||
t.Terminate();
|
||||
t.mSelf.Release();
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
LightObj & Tasks::FindEntity(Int32 id, Int32 type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ENT_BLIP: return Core::Get().GetBlip(id).mObj;
|
||||
case ENT_CHECKPOINT: return Core::Get().GetCheckpoint(id).mObj;
|
||||
case ENT_KEYBIND: return Core::Get().GetKeybind(id).mObj;
|
||||
case ENT_OBJECT: return Core::Get().GetObject(id).mObj;
|
||||
case ENT_PICKUP: return Core::Get().GetPickup(id).mObj;
|
||||
case ENT_PLAYER: return Core::Get().GetPlayer(id).mObj;
|
||||
case ENT_VEHICLE: return Core::Get().GetVehicle(id).mObj;
|
||||
default: return NullLightObj();
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQInteger Tasks::FindUnused()
|
||||
{
|
||||
for (const auto & t : s_Tasks)
|
||||
{
|
||||
if (INVALID_ENTITY(t.mEntity))
|
||||
{
|
||||
return (&t - s_Tasks); // Return the index of this element
|
||||
}
|
||||
}
|
||||
// No available slot
|
||||
return -1;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQInteger Tasks::Create(Int32 id, Int32 type, HSQUIRRELVM vm)
|
||||
{
|
||||
// Locate the identifier of a free slot
|
||||
const SQInteger slot = FindUnused();
|
||||
// See if we have where to store this task
|
||||
if (slot < 0)
|
||||
{
|
||||
return sq_throwerror(vm, "Reached the maximum number of tasks");
|
||||
}
|
||||
// Grab the top of the stack
|
||||
const SQInteger top = sq_gettop(vm);
|
||||
// See if too many arguments were specified
|
||||
if (top > 12) /* 4 base + 8 parameters = 12 */
|
||||
{
|
||||
return sq_throwerror(vm, "Too many parameters specified");
|
||||
}
|
||||
// Was there was a callback specified?
|
||||
else if (top <= 1)
|
||||
{
|
||||
return sq_throwerror(vm, "Missing task callback");
|
||||
}
|
||||
// Validate the callback type
|
||||
else if (sq_gettype(vm, 2) != OT_CLOSURE && sq_gettype(vm, 2) != OT_NATIVECLOSURE)
|
||||
{
|
||||
return sq_throwerror(vm, "Invalid callback type");
|
||||
}
|
||||
// Prepare an entity instance object
|
||||
HSQOBJECT inst;
|
||||
// Attempt to retrieve the entity instance
|
||||
try
|
||||
{
|
||||
inst = FindEntity(id, type).GetObject();
|
||||
}
|
||||
catch (const std::exception & e)
|
||||
{
|
||||
return sq_throwerror(vm, e.what());
|
||||
}
|
||||
// Prepare the function object
|
||||
HSQOBJECT func;
|
||||
// Fetch the specified callback
|
||||
SQRESULT res = sq_getstackobj(vm, 2, &func);
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
|
||||
// The number of iterations and interval to execute the task
|
||||
SQInteger intrv = 0, itr = 0;
|
||||
// Was there an interval specified?
|
||||
if (top > 2)
|
||||
{
|
||||
// Grab the interval from the stack
|
||||
res = sq_getinteger(vm, 3, &intrv);
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
}
|
||||
// Was there a number of iterations specified?
|
||||
if (top > 3)
|
||||
{
|
||||
// Grab the iterations from the stack
|
||||
res = sq_getinteger(vm, 4, &itr);
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
}
|
||||
|
||||
// At this point we can grab a reference to our slot
|
||||
Task & task = s_Tasks[slot];
|
||||
// Were there any arguments specified?
|
||||
if (top > 4)
|
||||
{
|
||||
// Grab a pointer to the arguments array
|
||||
Argument * args = task.mArgv;
|
||||
// Reset the argument counter
|
||||
task.mArgc = 0;
|
||||
// Grab the specified arguments from the stack
|
||||
for (SQInteger i = 5; i <= top; ++i)
|
||||
{
|
||||
res = sq_getstackobj(vm, i, &(args[task.mArgc].mObj));
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
// Clear previous arguments
|
||||
task.Clear();
|
||||
// Propagate the error
|
||||
return res;
|
||||
}
|
||||
// Keep a strong reference to the argument
|
||||
sq_addref(vm, &(args[task.mArgc].mObj));
|
||||
// Increase the argument counter
|
||||
++task.mArgc;
|
||||
}
|
||||
}
|
||||
|
||||
// Alright, at this point we can initialize the slot
|
||||
task.Init(func, inst, intrv, itr, id, type);
|
||||
// Now initialize the timer
|
||||
s_Intervals[slot] = intrv;
|
||||
// Push the tag instance on the stack
|
||||
sq_pushobject(vm, task.mSelf);
|
||||
// Specify that this function returns a value
|
||||
return 1;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQInteger Tasks::Find(Int32 id, Int32 type, SQInteger & pos, HSQUIRRELVM vm)
|
||||
{
|
||||
// Grab the top of the stack
|
||||
const SQInteger top = sq_gettop(vm);
|
||||
// Was there a callback specified?
|
||||
if (top <= 1)
|
||||
{
|
||||
return sq_throwerror(vm, "Missing task callback");
|
||||
}
|
||||
|
||||
SQRESULT res = SQ_OK;
|
||||
// Grab the hash of the callback object
|
||||
const SQHash chash = sq_gethash(vm, 2);
|
||||
// Should we include the iterations in the criteria?
|
||||
if (top > 3)
|
||||
{
|
||||
SQInteger intrv = 0;
|
||||
// Grab the interval from the stack
|
||||
res = sq_getinteger(vm, 3, &intrv);
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
// Attempt to find the requested task
|
||||
for (const auto & t : s_Tasks)
|
||||
{
|
||||
if (t.mHash == chash && t.mEntity == id && t.mType == type && t.mInterval == intrv)
|
||||
{
|
||||
pos = static_cast< SQInteger >(&t - s_Tasks); // Store the index of this element
|
||||
}
|
||||
}
|
||||
}
|
||||
// Should we include the interval in the criteria?
|
||||
else if (top > 2)
|
||||
{
|
||||
SQInteger intrv = 0, sqitr = 0;
|
||||
// Grab the interval from the stack
|
||||
res = sq_getinteger(vm, 3, &intrv);
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
// Grab the iterations from the stack
|
||||
res = sq_getinteger(vm, 4, &sqitr);
|
||||
// Validate the result
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
// Cast iterations to the right type
|
||||
const Iterator itr = ConvTo< Iterator >::From(sqitr);
|
||||
// Attempt to find the requested task
|
||||
for (const auto & t : s_Tasks)
|
||||
{
|
||||
if (t.mHash == chash && t.mEntity == id && t.mType == type && t.mInterval == intrv && t.mIterations == itr)
|
||||
{
|
||||
pos = static_cast< SQInteger >(&t - s_Tasks); // Store the index of this element
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Attempt to find the requested task
|
||||
for (const auto & t : s_Tasks)
|
||||
{
|
||||
if (t.mHash == chash && t.mEntity == id && t.mType == type)
|
||||
{
|
||||
pos = static_cast< SQInteger >(&t - s_Tasks); // Store the index of this element
|
||||
}
|
||||
}
|
||||
}
|
||||
// We could not find such task
|
||||
return res;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQInteger Tasks::Remove(Int32 id, Int32 type, HSQUIRRELVM vm)
|
||||
{
|
||||
// Default to not found
|
||||
SQInteger pos = -1;
|
||||
// Perform a search
|
||||
SQRESULT res = Find(id, type, pos, vm);
|
||||
// Did the search failed?
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
// Did we find anything?
|
||||
else if (pos < 0)
|
||||
{
|
||||
return sq_throwerror(vm, "Unable to locate such task");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Release task resources
|
||||
s_Tasks[pos].Terminate();
|
||||
// Reset the timer
|
||||
s_Intervals[pos] = 0;
|
||||
}
|
||||
// Specify that we don't return anything
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
SQInteger Tasks::Exists(Int32 id, Int32 type, HSQUIRRELVM vm)
|
||||
{
|
||||
// Default to not found
|
||||
SQInteger pos = -1;
|
||||
// Perform a search
|
||||
SQRESULT res = Find(id, type, pos, vm);
|
||||
// Did the search failed?
|
||||
if (SQ_FAILED(res))
|
||||
{
|
||||
return res; // Propagate the error
|
||||
}
|
||||
// Push a boolean on whether this task was found
|
||||
sq_pushbool(vm, pos >= 0);
|
||||
// Specify that we're returning a value
|
||||
return 1;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
const Tasks::Task & Tasks::FindByTag(Int32 id, Int32 type, const StackStrF & tag)
|
||||
{
|
||||
// Attempt to find the requested task
|
||||
for (const auto & t : s_Tasks)
|
||||
{
|
||||
if (t.mEntity == id && t.mType == type && t.mTag.compare(tag.mPtr) == 0)
|
||||
{
|
||||
return t; // Return this task instance
|
||||
}
|
||||
}
|
||||
// Unable to find such task
|
||||
STHROWF("Unable to find a task with tag (%s)", tag.mPtr);
|
||||
// Should not reach this point but if it did, we have to return something
|
||||
return s_Tasks[SQMOD_MAX_TASKS]; // Intentional Buffer overflow!
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Tasks::Cleanup(Int32 id, Int32 type)
|
||||
{
|
||||
for (auto & t : s_Tasks)
|
||||
{
|
||||
if (t.mEntity == id && t.mType == type)
|
||||
{
|
||||
t.Terminate();
|
||||
// Also disable the timer
|
||||
s_Intervals[&t - s_Tasks] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Forward the call to process tasks.
|
||||
*/
|
||||
void ProcessTasks()
|
||||
{
|
||||
Tasks::Process();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Forward the call to initialize tasks.
|
||||
*/
|
||||
void InitializeTasks()
|
||||
{
|
||||
Tasks::Initialize();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Forward the call to register tasks.
|
||||
*/
|
||||
void RegisterTask(HSQUIRRELVM vm)
|
||||
{
|
||||
Tasks::Register(vm);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Forward the call to terminate tasks.
|
||||
*/
|
||||
void TerminateTasks()
|
||||
{
|
||||
Tasks::Deinitialize();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Forward the call to cleanup certain tasks.
|
||||
*/
|
||||
void CleanupTasks(Int32 id, Int32 type)
|
||||
{
|
||||
Tasks::Cleanup(id, type);
|
||||
}
|
||||
|
||||
} // Namespace:: SqMod
|
||||
527
source/Misc/Tasks.hpp
Normal file
527
source/Misc/Tasks.hpp
Normal 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_
|
||||
Reference in New Issue
Block a user