Initial revision of animqueue
This commit is contained in:
@@ -1,2 +1,2 @@
|
|||||||
clear
|
clear
|
||||||
g++ -std=c++17 -Wall -g -o main syscpp/util.cpp syscpp/main.cpp syscpp/planemap.cpp syscpp/world.cpp syscpp/traceback.cpp syscpp/luastack.cpp syscpp/source.cpp syscpp/table.cpp syscpp/idalloc.cpp syscpp/globaldb.cpp -Iinc -Llib lib/libluajit-dbg.a -Isyscpp
|
g++ -std=c++17 -Wall -g -o main syscpp/util.cpp syscpp/main.cpp syscpp/animqueue.cpp syscpp/planemap.cpp syscpp/world.cpp syscpp/traceback.cpp syscpp/luastack.cpp syscpp/source.cpp syscpp/table.cpp syscpp/idalloc.cpp syscpp/globaldb.cpp -Iinc -Llib lib/libluajit-dbg.a -Isyscpp
|
||||||
|
|||||||
61
luprex/syscpp/animqueue.cpp
Normal file
61
luprex/syscpp/animqueue.cpp
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
|
||||||
|
#include "animqueue.hpp"
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
|
||||||
|
AnimStep::AnimStep() {}
|
||||||
|
AnimStep::~AnimStep() {}
|
||||||
|
|
||||||
|
AnimQueue::AnimQueue(int size_limit) {
|
||||||
|
assert(size_limit >= 2);
|
||||||
|
size_limit_ = size_limit;
|
||||||
|
steps_.emplace_back();
|
||||||
|
AnimStep &init = steps_.back();
|
||||||
|
init.id_ = 0;
|
||||||
|
init.facing_ = 0;
|
||||||
|
init.xyz_ = util::XYZ(0,0,0);
|
||||||
|
init.graphic_ = "nothing";
|
||||||
|
init.plane_ = "nowhere";
|
||||||
|
init.bits_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimQueue::add(int64_t id, const std::string &action) {
|
||||||
|
steps_.emplace_back();
|
||||||
|
AnimStep &last = steps_.back();
|
||||||
|
last = steps_[steps_.size() - 2];
|
||||||
|
last.id_ = id;
|
||||||
|
last.action_ = action;
|
||||||
|
last.bits_ = 0;
|
||||||
|
|
||||||
|
while (int(steps_.size()) > size_limit_) {
|
||||||
|
steps_.pop_front();
|
||||||
|
}
|
||||||
|
AnimStep &init = steps_.front();
|
||||||
|
init.id_ = 0;
|
||||||
|
init.action_ = "";
|
||||||
|
init.bits_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimQueue::set_facing(float f) {
|
||||||
|
AnimStep &last = steps_.back();
|
||||||
|
last.bits_ |= AnimStep::HAS_FACING;
|
||||||
|
last.facing_ = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimQueue::set_xyz(util::XYZ xyz) {
|
||||||
|
AnimStep &last = steps_.back();
|
||||||
|
last.bits_ |= AnimStep::HAS_XYZ;
|
||||||
|
last.xyz_ = xyz;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimQueue::set_graphic(const std::string &g) {
|
||||||
|
AnimStep &last = steps_.back();
|
||||||
|
last.bits_ |= AnimStep::HAS_GRAPHIC;
|
||||||
|
last.graphic_ = g;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimQueue::set_plane(const std::string &p) {
|
||||||
|
AnimStep &last = steps_.back();
|
||||||
|
last.bits_ |= AnimStep::HAS_PLANE;
|
||||||
|
last.plane_ = p;
|
||||||
|
}
|
||||||
91
luprex/syscpp/animqueue.hpp
Normal file
91
luprex/syscpp/animqueue.hpp
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
///////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// ANIMATION QUEUES
|
||||||
|
//
|
||||||
|
// An animation queue is a fifo queue of animation steps. New animations are
|
||||||
|
// pushed on the back, and old ones are popped from the front.
|
||||||
|
//
|
||||||
|
// An animation step has an "action" which is usually the name of an animation,
|
||||||
|
// or it's a special token like "walk" or "warp." Each animation step shows the
|
||||||
|
// resulting AnimState that the player finds himself in after executing the
|
||||||
|
// action.
|
||||||
|
//
|
||||||
|
// The first step in an animation queue always has id=0 and action="". This step
|
||||||
|
// represents the initial state of the sprite before any animations or
|
||||||
|
// movements.
|
||||||
|
//
|
||||||
|
// To add new items to the AnimQueue, use this process: first, call add(id,
|
||||||
|
// action). This adds a new step to the queue. Then, call set_xyz, set_facing,
|
||||||
|
// set_plane, or an other setter. These setters are meant to only be used
|
||||||
|
// immediately after calling 'add' to populate the new step.
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef ANIMQUEUE_HPP
|
||||||
|
#define ANIMQUEUE_HPP
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#include <deque>
|
||||||
|
#include <cassert>
|
||||||
|
#include "util.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
class AnimStep {
|
||||||
|
friend class AnimQueue;
|
||||||
|
public:
|
||||||
|
enum {
|
||||||
|
HAS_FACING = 1,
|
||||||
|
HAS_XYZ = 2,
|
||||||
|
HAS_GRAPHIC = 4,
|
||||||
|
HAS_PLANE = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
int64_t id_;
|
||||||
|
std::string action_;
|
||||||
|
int bits_;
|
||||||
|
|
||||||
|
float facing_;
|
||||||
|
util::XYZ xyz_;
|
||||||
|
std::string graphic_;
|
||||||
|
std::string plane_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AnimStep();
|
||||||
|
~AnimStep();
|
||||||
|
|
||||||
|
int64_t id() const { return id_; }
|
||||||
|
const std::string &action() const { return action_; }
|
||||||
|
int bits() const { return bits_; }
|
||||||
|
|
||||||
|
double facing() const { return facing_; }
|
||||||
|
util::XYZ xyz() const { return xyz_; }
|
||||||
|
const std::string &graphic() const { return graphic_; }
|
||||||
|
const std::string &plane() const { return plane_; }
|
||||||
|
|
||||||
|
bool has_facing() const { return bits_ & AnimStep::HAS_FACING; }
|
||||||
|
bool has_xyz() const { return bits_ & AnimStep::HAS_XYZ; }
|
||||||
|
bool has_graphic() const { return bits_ & AnimStep::HAS_GRAPHIC; }
|
||||||
|
bool has_plane() const { return bits_ & AnimStep::HAS_PLANE; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class AnimQueue {
|
||||||
|
private:
|
||||||
|
int size_limit_;
|
||||||
|
std::deque<AnimStep> steps_;
|
||||||
|
public:
|
||||||
|
AnimQueue(int size_limit);
|
||||||
|
const AnimStep &nth(int n) const { return steps_[n]; }
|
||||||
|
int size() const { return steps_.size(); }
|
||||||
|
|
||||||
|
// Mutators to create new steps.
|
||||||
|
void add(int64_t id, const std::string &action);
|
||||||
|
void set_facing(float f);
|
||||||
|
void set_xyz(util::XYZ xyz);
|
||||||
|
void set_graphic(const std::string &g);
|
||||||
|
void set_plane(const std::string &p);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ANIMQUEUE_HPP
|
||||||
|
|
||||||
@@ -2,6 +2,17 @@
|
|||||||
#include "globaldb.hpp"
|
#include "globaldb.hpp"
|
||||||
#include "table.hpp"
|
#include "table.hpp"
|
||||||
|
|
||||||
|
LuaDefine(globaldb_enable, "c") {
|
||||||
|
LuaVar globaldb;
|
||||||
|
LuaStack LS(L, globaldb);
|
||||||
|
LS.getfield(globaldb, LuaRegistry, "globaldb");
|
||||||
|
if (!LS.istable(globaldb)) {
|
||||||
|
LS.newtable(globaldb);
|
||||||
|
LS.setfield(LuaRegistry, "globaldb", globaldb);
|
||||||
|
}
|
||||||
|
return LS.result();
|
||||||
|
}
|
||||||
|
|
||||||
// Get a table from the global database.
|
// Get a table from the global database.
|
||||||
//
|
//
|
||||||
// GLOBALNAME
|
// GLOBALNAME
|
||||||
@@ -15,15 +26,14 @@ LuaDefine(globaldb_global, "f") {
|
|||||||
LuaVar globaldb;
|
LuaVar globaldb;
|
||||||
LuaStack LS(L, globalname, globaltab, globaldb);
|
LuaStack LS(L, globalname, globaltab, globaldb);
|
||||||
|
|
||||||
LS.checkstring(globalname);
|
// Get a pointer to the globaldb.
|
||||||
|
|
||||||
// Get a pointer to the globaldb.
|
|
||||||
LS.getfield(globaldb, LuaRegistry, "globaldb");
|
LS.getfield(globaldb, LuaRegistry, "globaldb");
|
||||||
if (!LS.istable(globaldb)) {
|
if (!LS.istable(globaldb)) {
|
||||||
LS.newtable(globaldb);
|
luaL_error(L, "globaldb is not enabled");
|
||||||
LS.setfield(LuaRegistry, "globaldb", globaldb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LS.checkstring(globalname);
|
||||||
|
|
||||||
// Get the globaltab from the globaldb, sanity check it.
|
// Get the globaltab from the globaldb, sanity check it.
|
||||||
LS.rawget(globaltab, globaldb, globalname);
|
LS.rawget(globaltab, globaldb, globalname);
|
||||||
if (LS.istable(globaltab)) {
|
if (LS.istable(globaltab)) {
|
||||||
|
|||||||
@@ -1,10 +1,48 @@
|
|||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// GLOBALDB
|
||||||
|
//
|
||||||
|
// The master world model is allowed to maintain global data structures.
|
||||||
|
//
|
||||||
|
// Unfortunately, any attempt to put a global data structure into the global
|
||||||
|
// environment will fail, because the global environment periodically gets wiped
|
||||||
|
// clean by the "source rebuild" procedure (see module 'source').
|
||||||
|
//
|
||||||
|
// This module creates a safe space where you can put global data structures
|
||||||
|
// that won't get wiped out.
|
||||||
|
//
|
||||||
|
// THE GLOBAL OPERATOR
|
||||||
|
//
|
||||||
|
// This module adds a new function to lua: "global". Global takes a global data
|
||||||
|
// structure name (a string) and returns a table for that global data structure.
|
||||||
|
// You can put anything you want into the table.
|
||||||
|
//
|
||||||
|
// Since you're only allowed to use global data structures in the master world
|
||||||
|
// model, any attempt to call "global" in a synchronous world model will
|
||||||
|
// result in a 'donotpredict' error.
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#ifndef GLOBALDB_HPP
|
#ifndef GLOBALDB_HPP
|
||||||
#define GLOBALDB_HPP
|
#define GLOBALDB_HPP
|
||||||
|
|
||||||
#include "luastack.hpp"
|
#include "luastack.hpp"
|
||||||
|
|
||||||
|
// globaldb_enable
|
||||||
|
//
|
||||||
|
// Enable the use of the global DB. This is meant to be invoked in
|
||||||
|
// the master world model only. In other world models, you should simply
|
||||||
|
// not call this function.
|
||||||
|
//
|
||||||
|
int globaldb_enable(lua_State *L);
|
||||||
|
|
||||||
|
// The lua 'global' operator.
|
||||||
|
//
|
||||||
|
// Given a global name, returns a table for that global.
|
||||||
|
//
|
||||||
|
// If you haven't enabled the global DB using globaldb_enable, this
|
||||||
|
// raises a donotpredict error.
|
||||||
|
//
|
||||||
int globaldb_global(lua_State *L);
|
int globaldb_global(lua_State *L);
|
||||||
|
|
||||||
#endif // GLOBALDB_HPP
|
#endif // GLOBALDB_HPP
|
||||||
|
|||||||
@@ -99,7 +99,9 @@ void IdPlayerPool::purge() {
|
|||||||
|
|
||||||
void IdPlayerPool::refill() {
|
void IdPlayerPool::refill() {
|
||||||
while (int(ranges_.size()) < global_->queue_fill()) {
|
while (int(ranges_.size()) < global_->queue_fill()) {
|
||||||
ranges_.push_back(global_->get_batch());
|
int64_t batch = global_->get_batch();
|
||||||
|
if (batch == 0) break;
|
||||||
|
ranges_.push_back(batch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,11 +114,17 @@ void IdPlayerPool::unqueue() {
|
|||||||
|
|
||||||
int64_t IdPlayerPool::get_batch() {
|
int64_t IdPlayerPool::get_batch() {
|
||||||
while (int(ranges_.size()) < global_->queue_fill() + 1) {
|
while (int(ranges_.size()) < global_->queue_fill() + 1) {
|
||||||
ranges_.push_back(global_->get_batch());
|
int64_t batch = global_->get_batch();
|
||||||
|
if (batch == 0) break;
|
||||||
|
ranges_.push_back(batch);
|
||||||
|
}
|
||||||
|
if (ranges_.empty()) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
int64_t batch = ranges_.front();
|
||||||
|
ranges_.pop_front();
|
||||||
|
return batch;
|
||||||
}
|
}
|
||||||
int64_t batch = ranges_.front();
|
|
||||||
ranges_.pop_front();
|
|
||||||
return batch;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IdPlayerPool::salvage_thread(lua_State *L) {
|
void IdPlayerPool::salvage_thread(lua_State *L) {
|
||||||
@@ -184,6 +192,15 @@ LuaDefine(cunittests_idalloc, "c") {
|
|||||||
gp.salvage(nthbatch(142) + 145);
|
gp.salvage(nthbatch(142) + 145);
|
||||||
assert(gp.get_batch() == nthbatch(2));
|
assert(gp.get_batch() == nthbatch(2));
|
||||||
|
|
||||||
|
// In the synchronous model, refill should do nothing.
|
||||||
|
pp.purge();
|
||||||
|
gp.init_synch(3);
|
||||||
|
pp.refill();
|
||||||
|
assert(pp.size() == 0);
|
||||||
|
assert(pp.get_batch() == 0);
|
||||||
|
assert(pp.size() == 0);
|
||||||
|
assert(pp.get_batch() == 0);
|
||||||
|
|
||||||
// Test refill from master.
|
// Test refill from master.
|
||||||
pp.purge();
|
pp.purge();
|
||||||
gp.init_master(3);
|
gp.init_master(3);
|
||||||
|
|||||||
@@ -1,51 +1,66 @@
|
|||||||
// The ID allocator.
|
///////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// This ID allocator attempts to allocate IDs in such a way that the
|
// THE ID ALLOCATOR
|
||||||
|
//
|
||||||
|
// This ID allocator's goal is to allocate IDs in such a way that the
|
||||||
// synchronous model gets the same IDs as the master model.
|
// synchronous model gets the same IDs as the master model.
|
||||||
//
|
//
|
||||||
// Every logged-in player maintains a "batch queue". That's a fifo queue
|
// DESIGN PRINCIPLES
|
||||||
// of ID batches. An ID batch is a contiguous range of IDs, containing
|
//
|
||||||
// between 128 and 256 contiguous IDs. The batch queue is difference
|
// There are two classes defined here: IdGlobalPool, and IdPlayerPool.
|
||||||
// transmitted, to ensure that the synchronous model has the same
|
//
|
||||||
// batches as the master model.
|
// Every logged-in player maintains an IdPlayerPool. That's basically a fifo
|
||||||
|
// queue of ID batches. An ID batch is a contiguous range of IDs, containing
|
||||||
|
// between 128 and 256 contiguous IDs. The IdPlayerPool is difference
|
||||||
|
// transmitted, to ensure that the synchronous model has the same batches as the
|
||||||
|
// master model.
|
||||||
//
|
//
|
||||||
// When a player creates a thread, that thread gets an ID batch from the
|
// When a player creates a thread, that thread gets an ID batch from the
|
||||||
// player's batch queue. When that thread allocates IDs,
|
// IdPlayerPool. To make this possible, the Lua runtime has been modified so
|
||||||
// it uses the batch it was allocated. If the batch is used up, then the
|
// that a thread can contain an ID batch. When that thread allocates IDs, it
|
||||||
// thread falls back to using a global fallback allocator. Such fallback IDs
|
// uses the batch it was allocated. In the unlikely event that a thread's batch
|
||||||
// are not likely to be predicted correctly.
|
// is used up, the thread falls back to using the IdGlobalPool. Such fallback
|
||||||
|
// IDs are not likely to be predicted correctly.
|
||||||
//
|
//
|
||||||
// When a player creates a thread, he uses up one of his batches. In the
|
// When a player creates a thread, he uses up one batch from his IdPlayerPool.
|
||||||
// master model, the batch queue is 'refilled' by creating a new batch.
|
// In the master model, the player pool is 'refilled' by asking the IdGlobalPool
|
||||||
// In the synchronous model, the batch queue is not directly refilled,
|
// to create a new batch. In the synchronous model, the IdPlayerPool is not
|
||||||
// but the difference transmitter effectively refills it. It is imperative
|
// directly refilled, but the difference transmitter effectively refills it. It
|
||||||
// that this difference transmission happen before the player's asynchronous
|
// is imperative that this difference transmission happen before the player's
|
||||||
// model runs out of batches, otherwise we'll get prediction failures.
|
// asynchronous model runs out of batches, otherwise we'll get prediction
|
||||||
|
// failures.
|
||||||
//
|
//
|
||||||
// It is common that a thread will only use 0, 1, or 2 IDs. If a thread
|
// It is common that a thread will only use 1 or 2 IDs. If a thread exits
|
||||||
// exits without using up most of its IDs, then the batch it contains is
|
// without using up most of its IDs, then the batch it contains is still a
|
||||||
// still a pretty usable batch. The master model will reclaim that batch,
|
// pretty usable batch. The batch gets returned to the IdGlobalPool, which will
|
||||||
// putting it into a global salvage pool. The salvage batches are later
|
// eventually use that salvaged batch to satisfy an IdPlayerPool refill request.
|
||||||
// used when refilling batch queues.
|
|
||||||
//
|
//
|
||||||
// ID ranges are assigned as follows:
|
// THE NUMERIC RANGES
|
||||||
//
|
//
|
||||||
// 0x0000+ : reserved for future expansion.
|
// * 0x0000+ : reserved for future expansion.
|
||||||
// 0x0001+ : used by master model to allocate batches.
|
// * 0x0001+ : used by master model's IdGlobalPool to create batches.
|
||||||
// 0x0010+ : used by master model to allocate unpredictable IDs.
|
// * 0x0010+ : used by master model's IdGlobalPool to create individual IDs.
|
||||||
// 0x001E+ : used by synchronous model to allocate stopgap IDs.
|
// * 0x001E+ : used by sync model's IdGlobalPool to create individual IDs.
|
||||||
//
|
//
|
||||||
// The operations in this class are:
|
// BATCH REPRESENTATION
|
||||||
//
|
//
|
||||||
// idalloc.initmaster() - reinitialize the ID allocator in master mode.
|
// A batch is represented as a 64-bit integer. The batch contains all IDs
|
||||||
// idalloc.initsynch() - reinitialize the ID allocator in synchronous mode.
|
// starting with that integer, and incrementing, until the the two lowest digits
|
||||||
// idalloc.setqueue_fill() - override the default queue fill level.
|
// are 0x00.
|
||||||
// idalloc.refill(bq) - get batches from the global pool until the batch queue is full.
|
|
||||||
// idalloc.unqueue(bq) - push batches from the batch queue back into the global pool.
|
|
||||||
// idalloc.preparethread(bq, co) - transfer a batch from the batch queue to the coroutine
|
|
||||||
// idalloc.salvagethread(co) - push any batch in the dead coroutine back into the global pool
|
|
||||||
//
|
//
|
||||||
// idalloc.allocid() - get an ID using either the current thread's pool or the global pool
|
// For example, consider the batch ID 0x11111111111111FC. That batch includes
|
||||||
|
// these IDs:
|
||||||
|
//
|
||||||
|
// * 0x11111111111111FC
|
||||||
|
// * 0x11111111111111FD
|
||||||
|
// * 0x11111111111111FE
|
||||||
|
// * 0x11111111111111FF
|
||||||
|
//
|
||||||
|
// But it does not include 0x1111111111111200.
|
||||||
|
//
|
||||||
|
// As a special case, the number 0 is used to indicate "invalid batch".
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#ifndef IDALLOC_HPP
|
#ifndef IDALLOC_HPP
|
||||||
#define IDALLOC_HPP
|
#define IDALLOC_HPP
|
||||||
@@ -64,16 +79,44 @@ private:
|
|||||||
friend int cunittests_idalloc(lua_State *L);
|
friend int cunittests_idalloc(lua_State *L);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// Construct and destroy global pools. Note that after constructing
|
||||||
|
// a global pool, it is generally also necessary to initialize it
|
||||||
|
// for Master or Synchronous operation using init_master or init_synch.
|
||||||
|
// Any attempt to use a pool that hasn't been init_master or init_synch
|
||||||
|
// will fail.
|
||||||
IdGlobalPool();
|
IdGlobalPool();
|
||||||
~IdGlobalPool();
|
~IdGlobalPool();
|
||||||
|
|
||||||
|
// Initialize the pool for use in a master model.
|
||||||
void init_master(int qf);
|
void init_master(int qf);
|
||||||
|
|
||||||
|
// Initialize the pool for use in a synchronous model.
|
||||||
void init_synch(int qf);
|
void init_synch(int qf);
|
||||||
int64_t get_batch();
|
|
||||||
|
// Create a single unique ID. Ids allocated this way are
|
||||||
|
// unlikely to be predicted correctly.
|
||||||
int64_t get_one();
|
int64_t get_one();
|
||||||
void salvage(int64_t batch);
|
|
||||||
|
// Obtain a batch of IDs from the global pool. In a master
|
||||||
|
// model, the batch is guaranteed to contain at least 128 IDs.
|
||||||
|
// In a synchronous model, the batch is always zero (invalid).
|
||||||
|
int64_t get_batch();
|
||||||
|
|
||||||
|
// When a player pool refills its fifo queue, it refills it
|
||||||
|
// with this many batches.
|
||||||
int queue_fill() const { return queue_fill_; }
|
int queue_fill() const { return queue_fill_; }
|
||||||
|
|
||||||
|
// Try to return the specified batch to the global pool.
|
||||||
|
// The salvage operation quietly does nothing if the batch is
|
||||||
|
// zero, or the batch contains fewer than 128 IDs.
|
||||||
|
void salvage(int64_t batch);
|
||||||
|
|
||||||
|
// Return the thread's batch to the global pool. If no batch
|
||||||
|
// is present, that's okay. Set the thread's batch to zero (invalid).
|
||||||
void salvage_thread(lua_State *L);
|
void salvage_thread(lua_State *L);
|
||||||
|
|
||||||
|
// Allocate an ID for the specified thread. Uses the thread's
|
||||||
|
// batch if possible. If not, fetches one ID from the global pool.
|
||||||
int64_t alloc_id_for_thread(lua_State *L);
|
int64_t alloc_id_for_thread(lua_State *L);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -84,15 +127,34 @@ private:
|
|||||||
friend int cunittests_idalloc(lua_State *L);
|
friend int cunittests_idalloc(lua_State *L);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// Construct a player pool.
|
||||||
|
// The Player pool stores a pointer to the global pool.
|
||||||
IdPlayerPool(IdGlobalPool *gp);
|
IdPlayerPool(IdGlobalPool *gp);
|
||||||
~IdPlayerPool();
|
~IdPlayerPool();
|
||||||
|
|
||||||
|
// Refill the fifo queue of batches from the global pool.
|
||||||
void refill();
|
void refill();
|
||||||
|
|
||||||
|
// Return all batches to the global pool. Leave the fifo empty.
|
||||||
void unqueue();
|
void unqueue();
|
||||||
|
|
||||||
|
// Discard all batches. This is only for unit testing.
|
||||||
void purge();
|
void purge();
|
||||||
|
|
||||||
|
// Get a batch from the fifo. Also refills the fifo.
|
||||||
int64_t get_batch();
|
int64_t get_batch();
|
||||||
|
|
||||||
|
// Return the thread's batch to the global pool. If no batch
|
||||||
|
// is present, that's okay. Set the thread's batch to zero (invalid).
|
||||||
void salvage_thread(lua_State *L);
|
void salvage_thread(lua_State *L);
|
||||||
|
|
||||||
|
// Fetch a batch from the fifo and install it in the thread.
|
||||||
|
// If the thread already had a batch, the thread's previous batch
|
||||||
|
// is returned to the global pool.
|
||||||
void prepare_thread(lua_State *L);
|
void prepare_thread(lua_State *L);
|
||||||
|
|
||||||
|
// Return the size of the queue.
|
||||||
|
int size() { return ranges_.size(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // IDALLOC_HPP
|
#endif // IDALLOC_HPP
|
||||||
|
|||||||
@@ -185,8 +185,7 @@ static int pmain(lua_State *L)
|
|||||||
// Initialize the builtins, then copy a snapshot of the
|
// Initialize the builtins, then copy a snapshot of the
|
||||||
// builtins to the registry. This will allow us to restore
|
// builtins to the registry. This will allow us to restore
|
||||||
// the builtins during source_rebuild operations.
|
// the builtins during source_rebuild operations.
|
||||||
source_load_builtins(L);
|
source_install_and_snapshot_builtins(L);
|
||||||
source_snapshot_builtins(L);
|
|
||||||
|
|
||||||
// Load the lua source.
|
// Load the lua source.
|
||||||
source_update(L);
|
source_update(L);
|
||||||
@@ -194,9 +193,6 @@ static int pmain(lua_State *L)
|
|||||||
// Rebuild the global environment.
|
// Rebuild the global environment.
|
||||||
source_rebuild(L);
|
source_rebuild(L);
|
||||||
|
|
||||||
// Run all the autoinit functions.
|
|
||||||
source_autoinit(L);
|
|
||||||
|
|
||||||
dotty(L);
|
dotty(L);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ void PlaneMap::remove(const std::string &plane, int64_t cellid, PlaneItem *clien
|
|||||||
if (cellid == CELL_INVALID) {
|
if (cellid == CELL_INVALID) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (plane == "") {
|
if ((plane == "") || (plane == "nowhere")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto piter = planes_.find(plane);
|
auto piter = planes_.find(plane);
|
||||||
@@ -94,7 +94,7 @@ void PlaneMap::insert(const std::string &plane, int64_t cellid, PlaneItem *clien
|
|||||||
if (cellid == CELL_INVALID) {
|
if (cellid == CELL_INVALID) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (plane == "") {
|
if ((plane == "") || (plane == "nowhere")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Plane &p = planes_[plane];
|
Plane &p = planes_[plane];
|
||||||
@@ -107,10 +107,10 @@ void PlaneItem::set_pos(const std::string &plane, double x, double y, double z)
|
|||||||
int64_t new_cell = point_cell_id(x, y);
|
int64_t new_cell = point_cell_id(x, y);
|
||||||
|
|
||||||
// Update the grid.
|
// Update the grid.
|
||||||
if (grid_ != 0) {
|
if (pmap_ != 0) {
|
||||||
if ((plane_ != plane) || (old_cell != new_cell)) {
|
if ((plane_ != plane) || (old_cell != new_cell)) {
|
||||||
grid_->remove(plane_, old_cell, this);
|
pmap_->remove(plane_, old_cell, this);
|
||||||
grid_->insert(plane, new_cell, this);
|
pmap_->insert(plane, new_cell, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,32 +121,36 @@ void PlaneItem::set_pos(const std::string &plane, double x, double y, double z)
|
|||||||
z_ = z;
|
z_ = z;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlaneItem::use_map(PlaneMap *grid) {
|
void PlaneItem::untrack() {
|
||||||
int64_t cellid = point_cell_id(x_, y_);
|
if (pmap_ != 0) {
|
||||||
if (grid_ != 0) {
|
pmap_->remove(plane_, point_cell_id(x_, y_), this);
|
||||||
grid_->remove(plane_, cellid, this);
|
pmap_ = 0;
|
||||||
}
|
|
||||||
grid_ = grid;
|
|
||||||
if (grid_ != 0) {
|
|
||||||
grid_->insert(plane_, cellid, this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PlaneItem::PlaneItem() : grid_(NULL), x_(0.0), y_(0.0), z_(0.0) {
|
void PlaneMap::track(PlaneItem *item) {
|
||||||
|
if (item->pmap_ != this) {
|
||||||
|
item->untrack();
|
||||||
|
insert(item->plane(), point_cell_id(item->x(), item->y()), item);
|
||||||
|
item->pmap_ = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PlaneItem::PlaneItem() : pmap_(NULL), x_(0.0), y_(0.0), z_(0.0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
PlaneMap::PlaneMap() {
|
PlaneMap::PlaneMap() {
|
||||||
}
|
}
|
||||||
|
|
||||||
PlaneItem::~PlaneItem() {
|
PlaneItem::~PlaneItem() {
|
||||||
use_map(NULL);
|
untrack();
|
||||||
}
|
}
|
||||||
|
|
||||||
PlaneMap::~PlaneMap() {
|
PlaneMap::~PlaneMap() {
|
||||||
for (const auto &p : planes_) {
|
for (const auto &p : planes_) {
|
||||||
for (const auto &l : p.second) {
|
for (const auto &l : p.second) {
|
||||||
for (PlaneItem *i : l.second) {
|
for (PlaneItem *i : l.second) {
|
||||||
i->grid_ = NULL;
|
i->pmap_ = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -262,10 +266,10 @@ LuaDefine(cunittests_planemap, "c") {
|
|||||||
pm.remove("foo", 12345, &pib);
|
pm.remove("foo", 12345, &pib);
|
||||||
assert(pm.total_cells() == 0);
|
assert(pm.total_cells() == 0);
|
||||||
|
|
||||||
// Test the insert function on an invalid plane.
|
// Test the insert function on the nowhere plane.
|
||||||
pm.clear();
|
pm.clear();
|
||||||
pm.insert("", 12345, &pia);
|
pm.insert("nowhere", 12345, &pia);
|
||||||
pm.insert("", 12345, &pib);
|
pm.insert("nowhere", 12345, &pib);
|
||||||
assert(pm.total_cells() == 0);
|
assert(pm.total_cells() == 0);
|
||||||
|
|
||||||
// Test the insert function on an invalid cell.
|
// Test the insert function on an invalid cell.
|
||||||
@@ -283,17 +287,17 @@ LuaDefine(cunittests_planemap, "c") {
|
|||||||
|
|
||||||
// Attach pia to the grid. This should record it.
|
// Attach pia to the grid. This should record it.
|
||||||
pm.clear();
|
pm.clear();
|
||||||
pia.use_map(&pm);
|
pm.track(&pia);
|
||||||
elts = pm.get_cell("foo", point_cell_id(3.0, 4.0));
|
elts = pm.get_cell("foo", point_cell_id(3.0, 4.0));
|
||||||
assert(elts.size() == 1);
|
assert(elts.size() == 1);
|
||||||
assert(elts[0] == &pia);
|
assert(elts[0] == &pia);
|
||||||
|
|
||||||
// Unattach pia from the grid. This should unrecord it.
|
// Unattach pia from the grid. This should unrecord it.
|
||||||
pia.use_map(NULL);
|
pia.untrack();
|
||||||
assert(pm.total_cells() == 0);
|
assert(pm.total_cells() == 0);
|
||||||
|
|
||||||
// Reattach pia to the grid, then move it.
|
// Reattach pia to the grid, then move it.
|
||||||
pia.use_map(&pm);
|
pm.track(&pia);
|
||||||
assert(pm.total_cells() == 1);
|
assert(pm.total_cells() == 1);
|
||||||
pia.set_pos("bar", 1000.0, 1000.0, 0.0);
|
pia.set_pos("bar", 1000.0, 1000.0, 0.0);
|
||||||
assert(pm.total_cells() == 1);
|
assert(pm.total_cells() == 1);
|
||||||
@@ -302,7 +306,7 @@ LuaDefine(cunittests_planemap, "c") {
|
|||||||
assert(elts[0] == &pia);
|
assert(elts[0] == &pia);
|
||||||
|
|
||||||
// Insert the four elements, then test the scan function.
|
// Insert the four elements, then test the scan function.
|
||||||
pib.use_map(&pm);
|
pm.track(&pib);
|
||||||
pib.set_pos("bar", 1100.0, 1000.0, 0.0);
|
pib.set_pos("bar", 1100.0, 1000.0, 0.0);
|
||||||
elts = pm.scan_radius("bar", 1000.0, 1000.0, 1.0);
|
elts = pm.scan_radius("bar", 1000.0, 1000.0, 1.0);
|
||||||
assert(elts.size() == 1);
|
assert(elts.size() == 1);
|
||||||
|
|||||||
@@ -1,40 +1,74 @@
|
|||||||
|
//////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// PLANEMAP
|
// PLANEMAP
|
||||||
//
|
//
|
||||||
// Stores a map of the items that are on each plane. Doesn't
|
// This module defines two classes: PlaneMap, and PlaneItem. A
|
||||||
// store terrain, only items.
|
// PlaneItem is an object that has a plane (a string) and an
|
||||||
|
// XYZ position. A PlaneMap keeps track of potentially thousands
|
||||||
|
// of PlaneItem objects, allowing you to search for PlaneItems
|
||||||
|
// by scanning a geographic region.
|
||||||
|
//
|
||||||
|
// CLASS SPRITE DERIVES FROM PLANEITEM
|
||||||
|
//
|
||||||
|
// A PlaneMap records the positions of PlaneItems. The intent is
|
||||||
|
// that class Sprite should derive from PlaneItem. That way, the
|
||||||
|
// PlaneMap can record the positions of sprites.
|
||||||
|
//
|
||||||
|
// When you put derived types like Sprite into a PlaneMap, the
|
||||||
|
// PlaneMap will work fine. However, when you scan the PlaneMap,
|
||||||
|
// it will give return PlaneItem pointers, not Sprite pointers.
|
||||||
|
//
|
||||||
|
// If you're sure that the PlaneMap contains only sprites, then
|
||||||
|
// you can use static_cast to convert those PlaneItem pointers to
|
||||||
|
// Sprite pointers. Sadly, there's no simple way to avoid the casting.
|
||||||
|
//
|
||||||
|
// THE SCANNABLE REGION IS FINITE
|
||||||
//
|
//
|
||||||
// A plane is a rectangle, with a finite size: if you stray more
|
// A plane is a rectangle, with a finite size: if you stray more
|
||||||
// than 80,000,000 meters from the origin, then you are beyond the
|
// than 80,000,000 meters from the origin, then that PlaneItem
|
||||||
// edge of the plane.
|
// is beyond the scannable region.
|
||||||
//
|
//
|
||||||
// There's nothing stopping you from moving a PlaneItem farther
|
// There's nothing stopping you from moving a PlaneItem outside
|
||||||
// than that. However, if you do move a PlaneItem farther than
|
// the scannable region. It is not an error to do so.
|
||||||
// that, then it won't show up in radius-scans.
|
// However, if you do move a PlaneItem outside the scannable
|
||||||
|
// region, then that PlaneItem will not show up for scan_radius.
|
||||||
//
|
//
|
||||||
// The PlaneMap doesn't need you to "create" planes. You just
|
// PLANES CANNOT BE DESTROYED BUT THEY CAN BE UNINHABITED
|
||||||
// set the plane of a PlaneItem to any string, and that plane will
|
|
||||||
// pop into existence if it wasn't already there.
|
|
||||||
//
|
//
|
||||||
// If you use the empty string as a plane name, a special case is
|
// You can't "create" or "destroy" planes. Instead, we say
|
||||||
|
// that a plane is "uninhabited" until you put a PlaneItem
|
||||||
|
// there. Initially, all possible planes exist, but they're
|
||||||
|
// all uninhabited until you put a PlaneItem there. Planes
|
||||||
|
// that are uninhabited take no memory.
|
||||||
|
//
|
||||||
|
// THE NOWHERE PLANE
|
||||||
|
//
|
||||||
|
// If you use the literal "nowhere" as a plane name, a special case is
|
||||||
// triggered: you're "nowhere." Radius scans can never find items
|
// triggered: you're "nowhere." Radius scans can never find items
|
||||||
// whose plane is the empty string.
|
// whose plane is "nowhere." The same also applies when you use the
|
||||||
|
// empty string as a plane name.
|
||||||
//
|
//
|
||||||
// INHERITANCE
|
// THE Z COORDINATE IS IGNORED
|
||||||
//
|
//
|
||||||
// The intent is that class Sprite should derive from PlaneItem.
|
// Class PlaneItem stores X, Y, and Z. The Z coordinate is
|
||||||
// When you put sprites into a PlaneMap, it will work fine. However,
|
// ignored for all plane-scanning operations. In other words,
|
||||||
// when you scan the PlaneMap, it will give you PlaneItem pointers,
|
// planes are 2D. The only reason we store a Z coordinate
|
||||||
// not Sprite pointers. If you're sure that the PlaneMap contains
|
// is for the consistency of storing the model's X, Y, and Z
|
||||||
// only sprites, then you can use static_cast to convert those
|
// all in the same place.
|
||||||
// PlaneItem pointers to Sprite pointers. Sadly, there's no simple
|
|
||||||
// way to avoid the casting.
|
|
||||||
//
|
//
|
||||||
// MEMORY MANAGEMENT
|
// MEMORY MANAGEMENT
|
||||||
//
|
//
|
||||||
// The PlaneMap does not own the PlaneItems. You need to create,
|
// PlaneMaps do not own PlaneItems. This is a deliberate choice.
|
||||||
// destroy, and manage the PlaneItems yourself.
|
// We assume that sprites will be owned by the sprite ID table.
|
||||||
|
// So therefore, the PlaneMaps shouldn't own the sprites.
|
||||||
//
|
//
|
||||||
|
// So instead, we use this rule: a PlaneMap has a function 'track'
|
||||||
|
// that causes it to start tracking the location of a PlaneItem.
|
||||||
|
// However, the PlaneMap still doesn't own the PlaneItem.
|
||||||
|
// If you destroy a PlaneMap, all the PlaneItems
|
||||||
|
// will automatically be untracked, but they won't be deleted.
|
||||||
|
//
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#ifndef PLANEMAP_HPP
|
#ifndef PLANEMAP_HPP
|
||||||
#define PLANEMAP_HPP
|
#define PLANEMAP_HPP
|
||||||
@@ -47,10 +81,12 @@ class PlaneMap;
|
|||||||
|
|
||||||
class PlaneItem {
|
class PlaneItem {
|
||||||
friend class PlaneMap;
|
friend class PlaneMap;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PlaneMap *grid_;
|
PlaneMap *pmap_;
|
||||||
std::string plane_;
|
std::string plane_;
|
||||||
double x_, y_, z_;
|
double x_, y_, z_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PlaneItem();
|
PlaneItem();
|
||||||
~PlaneItem();
|
~PlaneItem();
|
||||||
@@ -60,32 +96,35 @@ public:
|
|||||||
const double y() const { return y_; }
|
const double y() const { return y_; }
|
||||||
const double z() const { return z_; }
|
const double z() const { return z_; }
|
||||||
|
|
||||||
void use_map(PlaneMap *map);
|
void untrack();
|
||||||
void set_pos(const std::string &plane, double x, double y, double z);
|
void set_pos(const std::string &plane, double x, double y, double z);
|
||||||
|
void set_xyz(double x, double y, double z) { set_pos(plane_, x, y, z); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class PlaneMap {
|
class PlaneMap {
|
||||||
friend class PlaneItem;
|
friend class PlaneItem;
|
||||||
friend int cunittests_planemap(lua_State *L);
|
|
||||||
public:
|
|
||||||
using Elt = PlaneItem *;
|
|
||||||
using EltVec = std::vector<Elt>;
|
|
||||||
private:
|
private:
|
||||||
|
using EltVec = std::vector<PlaneItem *>;
|
||||||
using Plane = std::map<int64_t, EltVec>;
|
using Plane = std::map<int64_t, EltVec>;
|
||||||
std::map<std::string, Plane> planes_;
|
std::map<std::string, Plane> planes_;
|
||||||
void remove(const std::string &plane, int64_t cell, PlaneItem *client);
|
void remove(const std::string &plane, int64_t cell, PlaneItem *client);
|
||||||
void insert(const std::string &plane, int64_t cell, PlaneItem *client);
|
void insert(const std::string &plane, int64_t cell, PlaneItem *client);
|
||||||
private:
|
|
||||||
// These functions are only used in unit tests.
|
|
||||||
EltVec get_cell(const std::string &plane, int64_t cell) const;
|
|
||||||
int total_cells() const;
|
|
||||||
void clear() { planes_.clear(); }
|
|
||||||
public:
|
public:
|
||||||
PlaneMap();
|
PlaneMap();
|
||||||
~PlaneMap();
|
~PlaneMap();
|
||||||
|
void track(PlaneItem *item);
|
||||||
EltVec scan_radius(const std::string &plane, double x, double y, double radius) const;
|
EltVec scan_radius(const std::string &plane, double x, double y, double radius) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// unit testing stuff.
|
||||||
|
friend int cunittests_planemap(lua_State *L);
|
||||||
|
EltVec get_cell(const std::string &plane, int64_t cell) const;
|
||||||
|
int total_cells() const;
|
||||||
|
void clear() { planes_.clear(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif // PLANEMAP_HPP
|
#endif // PLANEMAP_HPP
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,93 @@
|
|||||||
#include "traceback.hpp"
|
#include "traceback.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
LuaDefine(source_makeclass, "f") {
|
||||||
|
LuaArg classname;
|
||||||
|
LuaVar action, gname;
|
||||||
|
LuaRet classtab;
|
||||||
|
LuaStack LS(L, classname, classtab, action, gname);
|
||||||
|
|
||||||
|
LS.checkstring(classname);
|
||||||
|
|
||||||
|
// Special case: if the classname is _G, return global env.
|
||||||
|
LS.set(gname, "_G");
|
||||||
|
if (LS.equal(classname, gname)) {
|
||||||
|
LS.set(classtab, LuaGlobals);
|
||||||
|
return LS.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the classtab from the global environment.
|
||||||
|
// Create it if it doesn't exist.
|
||||||
|
LS.rawget(classtab, LuaGlobals, classname);
|
||||||
|
if (LS.isnil(classtab)) {
|
||||||
|
LS.newtable(classtab);
|
||||||
|
LS.rawset(LuaGlobals, classname, classtab);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the name isn't bound to a table, abort.
|
||||||
|
if (!LS.istable(classtab)) {
|
||||||
|
luaL_error(L, "%s is not a class", LS.ckstring(classname).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repair the special fields.
|
||||||
|
LS.setfield(classtab, "__index", classtab);
|
||||||
|
LS.setfield(classtab, "__class", classname);
|
||||||
|
|
||||||
|
// Repair the action table.
|
||||||
|
LS.getfield(action, classtab, "action");
|
||||||
|
if (!LS.istable(action)) {
|
||||||
|
LS.setfield(classtab, "action", LuaNewTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
return LS.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the builtins.
|
||||||
|
|
||||||
|
static void load_builtin(lua_State *L, const char *name, lua_CFunction func) {
|
||||||
|
lua_pushcfunction(L, func);
|
||||||
|
lua_pushstring(L, name);
|
||||||
|
lua_call(L, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
LuaDefine(source_install_builtins, "") {
|
||||||
|
LuaStack LS(L);
|
||||||
|
load_builtin(L, "base", luaopen_base);
|
||||||
|
load_builtin(L, "table", luaopen_table);
|
||||||
|
load_builtin(L, "string", luaopen_string);
|
||||||
|
load_builtin(L, "math", luaopen_math);
|
||||||
|
load_builtin(L, "bit", luaopen_math);
|
||||||
|
load_builtin(L, "debug", luaopen_debug);
|
||||||
|
// Do not load: package, io, os, debug, jit
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LuaDefine(source_install_and_snapshot_builtins, "") {
|
||||||
|
LuaVar key, value, skey, svalue, snapshot, ssnapshot;
|
||||||
|
LuaStack LS(L, snapshot, key, value, skey, svalue, ssnapshot);
|
||||||
|
|
||||||
|
// Note: the global environment contains _G. This routine
|
||||||
|
// perceives this as a subtable, so it backs up the top level
|
||||||
|
// as well.
|
||||||
|
source_install_builtins(L);
|
||||||
|
LS.newtable(snapshot);
|
||||||
|
LS.set(key, LuaNil);
|
||||||
|
while (LS.next(LuaGlobals, key, value) != 0) {
|
||||||
|
if (LS.istable(value)) {
|
||||||
|
LS.newtable(ssnapshot);
|
||||||
|
LS.rawset(snapshot, key, ssnapshot);
|
||||||
|
LS.set(skey, LuaNil);
|
||||||
|
while (LS.next(value, skey, svalue) != 0) {
|
||||||
|
if (LS.isfunction(svalue) || LS.isstring(svalue) || LS.isnumber(svalue)) {
|
||||||
|
LS.rawset(ssnapshot, skey, svalue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LS.setfield(LuaRegistry, "source_snapshot_builtins", snapshot);
|
||||||
|
return LS.result();
|
||||||
|
}
|
||||||
|
|
||||||
LuaDefine(source_updatefile, "") {
|
LuaDefine(source_updatefile, "") {
|
||||||
LuaArg source, fn;
|
LuaArg source, fn;
|
||||||
LuaRet info;
|
LuaRet info;
|
||||||
@@ -95,58 +182,8 @@ LuaDefine(source_update, "c") {
|
|||||||
return LS.result();
|
return LS.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a class. A class is a table in the global environment.
|
// Delete everything from the global environment except
|
||||||
//
|
// the class tables and the class action tables.
|
||||||
// The global environment is protected, but this function can
|
|
||||||
// override that protection.
|
|
||||||
//
|
|
||||||
LuaDefine(source_makeclass, "f") {
|
|
||||||
LuaArg classname;
|
|
||||||
LuaVar action, gname;
|
|
||||||
LuaRet classtab;
|
|
||||||
LuaStack LS(L, classname, classtab, action, gname);
|
|
||||||
|
|
||||||
LS.checkstring(classname);
|
|
||||||
|
|
||||||
// Special case: if the classname is _G, return global env.
|
|
||||||
LS.set(gname, "_G");
|
|
||||||
if (LS.equal(classname, gname)) {
|
|
||||||
LS.set(classtab, LuaGlobals);
|
|
||||||
return LS.result();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the classtab from the global environment.
|
|
||||||
// Create it if it doesn't exist.
|
|
||||||
LS.rawget(classtab, LuaGlobals, classname);
|
|
||||||
if (LS.isnil(classtab)) {
|
|
||||||
LS.newtable(classtab);
|
|
||||||
LS.rawset(LuaGlobals, classname, classtab);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the name isn't bound to a table, abort.
|
|
||||||
if (!LS.istable(classtab)) {
|
|
||||||
luaL_error(L, "%s is not a class", LS.ckstring(classname).c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Repair the special fields.
|
|
||||||
LS.setfield(classtab, "__index", classtab);
|
|
||||||
LS.setfield(classtab, "__class", classname);
|
|
||||||
|
|
||||||
// Repair the action table.
|
|
||||||
LS.getfield(action, classtab, "action");
|
|
||||||
if (!LS.istable(action)) {
|
|
||||||
LS.setfield(classtab, "action", LuaNewTable);
|
|
||||||
}
|
|
||||||
|
|
||||||
return LS.result();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear the global environment.
|
|
||||||
//
|
|
||||||
// Clears out almost everything from the global
|
|
||||||
// environment. However, does not delete class tables.
|
|
||||||
//
|
|
||||||
// This is used during source_rebuild operations.
|
|
||||||
//
|
//
|
||||||
LuaDefine(source_clear_globals, "") {
|
LuaDefine(source_clear_globals, "") {
|
||||||
LuaVar classname, classtab, action, key;
|
LuaVar classname, classtab, action, key;
|
||||||
@@ -156,13 +193,16 @@ LuaDefine(source_clear_globals, "") {
|
|||||||
LS.set(classname, LuaNil);
|
LS.set(classname, LuaNil);
|
||||||
while (LS.next(LuaGlobals, classname, classtab) != 0) {
|
while (LS.next(LuaGlobals, classname, classtab) != 0) {
|
||||||
if (LS.istable(classtab)) {
|
if (LS.istable(classtab)) {
|
||||||
|
bool keep_action = false;
|
||||||
LS.getfield(action, classtab, "action");
|
LS.getfield(action, classtab, "action");
|
||||||
if (LS.istable(action)) {
|
if (LS.istable(action)) {
|
||||||
LS.call(table_clear, action);
|
LS.call(table_clear, action);
|
||||||
} else {
|
keep_action = true;
|
||||||
LS.newtable(action);
|
|
||||||
}
|
}
|
||||||
LS.call(table_clear, classtab);
|
LS.call(table_clear, classtab);
|
||||||
|
if (keep_action) {
|
||||||
|
LS.setfield(classtab, "action", action);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
LS.rawset(LuaGlobals, classname, LuaNil);
|
LS.rawset(LuaGlobals, classname, LuaNil);
|
||||||
}
|
}
|
||||||
@@ -173,11 +213,7 @@ LuaDefine(source_clear_globals, "") {
|
|||||||
|
|
||||||
// Restore the lua builtins from the backup snapshot.
|
// Restore the lua builtins from the backup snapshot.
|
||||||
//
|
//
|
||||||
// The global environment is expected to be clean, but
|
LuaDefine(source_restore_builtins, "") {
|
||||||
// the subtables are expected to still be present, in
|
|
||||||
// accordance with how 'source_clear_globals' works.
|
|
||||||
//
|
|
||||||
LuaDefine(source_restore_builtins, "g") {
|
|
||||||
LuaVar snapshot, key, value, skey, svalue, subglobal;
|
LuaVar snapshot, key, value, skey, svalue, subglobal;
|
||||||
LuaStack LS(L, snapshot, key, value, skey, svalue, subglobal);
|
LuaStack LS(L, snapshot, key, value, skey, svalue, subglobal);
|
||||||
|
|
||||||
@@ -195,57 +231,8 @@ LuaDefine(source_restore_builtins, "g") {
|
|||||||
return LS.result();
|
return LS.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Snapshot all the lua builtin functions.
|
// Load all the 'LuaDefine' C functions into the lua state.
|
||||||
//
|
//
|
||||||
// Precondition: this is meant to be used on a pristine lua
|
|
||||||
// intepreter, before the user dumps a bunch of crap into the
|
|
||||||
// global environment. It won't work if the global environment
|
|
||||||
// contains anything other than the lua builtins.
|
|
||||||
//
|
|
||||||
// Note: the global environment contains _G. This routine
|
|
||||||
// perceives this as a subtable, so it backs up the top level
|
|
||||||
// as well.
|
|
||||||
//
|
|
||||||
LuaDefine(source_snapshot_builtins, "c") {
|
|
||||||
LuaVar key, value, skey, svalue, snapshot, ssnapshot;
|
|
||||||
LuaStack LS(L, snapshot, key, value, skey, svalue, ssnapshot);
|
|
||||||
|
|
||||||
LS.newtable(snapshot);
|
|
||||||
LS.set(key, LuaNil);
|
|
||||||
while (LS.next(LuaGlobals, key, value) != 0) {
|
|
||||||
if (LS.istable(value)) {
|
|
||||||
LS.newtable(ssnapshot);
|
|
||||||
LS.rawset(snapshot, key, ssnapshot);
|
|
||||||
LS.set(skey, LuaNil);
|
|
||||||
while (LS.next(value, skey, svalue) != 0) {
|
|
||||||
if (LS.isfunction(svalue) || LS.isstring(svalue) || LS.isnumber(svalue)) {
|
|
||||||
LS.rawset(ssnapshot, skey, svalue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LS.setfield(LuaRegistry, "source_snapshot_builtins", snapshot);
|
|
||||||
return LS.result();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void load_builtin(lua_State *L, const char *name, lua_CFunction func) {
|
|
||||||
lua_pushcfunction(L, func);
|
|
||||||
lua_pushstring(L, name);
|
|
||||||
lua_call(L, 1, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
LuaDefine(source_load_builtins, "") {
|
|
||||||
LuaStack LS(L);
|
|
||||||
load_builtin(L, "base", luaopen_base);
|
|
||||||
load_builtin(L, "table", luaopen_table);
|
|
||||||
load_builtin(L, "string", luaopen_string);
|
|
||||||
load_builtin(L, "math", luaopen_math);
|
|
||||||
load_builtin(L, "bit", luaopen_math);
|
|
||||||
load_builtin(L, "debug", luaopen_debug);
|
|
||||||
// Do not load: package, io, os, debug, jit
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
LuaDefine(source_load_cfunctions, "") {
|
LuaDefine(source_load_cfunctions, "") {
|
||||||
auto regs = LuaFunctionReg::all();
|
auto regs = LuaFunctionReg::all();
|
||||||
for (const LuaFunctionReg *r : regs) {
|
for (const LuaFunctionReg *r : regs) {
|
||||||
@@ -275,10 +262,7 @@ LuaDefine(source_load_cfunctions, "") {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetches the source database and runs all the loadresults.
|
// Run all the closures from the source database.
|
||||||
//
|
|
||||||
// Returns a single string, which is a bunch of concatenated error
|
|
||||||
// messages.
|
|
||||||
//
|
//
|
||||||
LuaDefine(source_load_lfunctions, "") {
|
LuaDefine(source_load_lfunctions, "") {
|
||||||
LuaRet errors;
|
LuaRet errors;
|
||||||
@@ -336,13 +320,3 @@ LuaDefine(source_rebuild, "c") {
|
|||||||
return LS.result();
|
return LS.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
LuaDefine(source_autoinit, "") {
|
|
||||||
auto regs = LuaFunctionReg::all();
|
|
||||||
for (const LuaFunctionReg *r : regs) {
|
|
||||||
std::string mode = r->get_mode();
|
|
||||||
if (mode.find('a') != std::string::npos) {
|
|
||||||
r->get_func()(L);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,18 +1,102 @@
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
// SOURCE
|
||||||
//
|
//
|
||||||
// The source database is a lua table that maps filenames
|
// This module manages the loading of lua source files into the Lua environment.
|
||||||
// to file info. The source database is stored in the registry.
|
// Since the source files can be reloaded over and over, this module doesn't
|
||||||
|
// just load the source files. Instead, it does "source rebuilds" in which it
|
||||||
|
// purges everything from the global environment, then it reinstalls everything
|
||||||
|
// that should be there. That way, if you delete something from a lua source
|
||||||
|
// file, it gets removed from the lua global environment.
|
||||||
//
|
//
|
||||||
// In the source database, the keys are filenames, and the values
|
|
||||||
// are tables containing the following fields:
|
|
||||||
//
|
//
|
||||||
// name: filename as a string
|
// THE MAKECLASS OPERATOR
|
||||||
// fingerprint: file modification, file length, as a string
|
|
||||||
// code: the entire contents of the source file as a string
|
|
||||||
// loadresult: a lua closure, or, an error message
|
|
||||||
// sequence: the position of the file in control.lst
|
|
||||||
//
|
//
|
||||||
|
// This module provides a new lua 'builtin' operator: "makeclass". This creates
|
||||||
|
// a table and stores it in the global environment. The new table is meant to be
|
||||||
|
// used as a class. Three fields are initially created:
|
||||||
|
//
|
||||||
|
// __class --> the name of the class as a string
|
||||||
|
//
|
||||||
|
// __index --> points back to the class. Makes it convenient to use the
|
||||||
|
// class as a metatable.
|
||||||
|
//
|
||||||
|
// action --> a subtable of additional methods.
|
||||||
|
//
|
||||||
|
// If you invoke 'makeclass' on a class that already exists, the existing table
|
||||||
|
// is "repaired" - the __class and __index fields are restored and the action
|
||||||
|
// subtable, if not present, is recreated. If there are already functions or
|
||||||
|
// constants inside the class, they are not affected.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// THE LUA SOURCE DATABASE
|
||||||
|
//
|
||||||
|
// The function 'source_update' loads the lua source code from disk, and stores
|
||||||
|
// it in the "source database." The source database is a table inside the lua
|
||||||
|
// registry.
|
||||||
|
//
|
||||||
|
// The source database is a lua table where the keys are filenames, and the
|
||||||
|
// values are information about that file:
|
||||||
|
//
|
||||||
|
// "foo.lua" : {
|
||||||
|
// "name": "foo.lua",
|
||||||
|
// "fingerprint": "12893129385854",
|
||||||
|
// "code": "function xyz ...",
|
||||||
|
// "loadresult": <function-23>,
|
||||||
|
// "sequence": 13
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Let's break that down a little. The "name" field is just the filename. The
|
||||||
|
// "fingerprint" is an encoding of the file modification date and the file
|
||||||
|
// length, it is used to detect whether the file has been altered on disk
|
||||||
|
// without having to reread the file. The "code" is the entire content of the
|
||||||
|
// source file. The "loadresult" is the closure that results from calling the
|
||||||
|
// lua 'load' function on the code. If load fails, then the "loadresult" is an
|
||||||
|
// error message. Finally, "sequence" indicates the order in which the source
|
||||||
|
// files are meant to be loaded.
|
||||||
|
//
|
||||||
|
// The operation "source_update" refreshes the source database from disk. It
|
||||||
|
// doesn't reread files whose fingerprints have not changed. In a synchronous
|
||||||
|
// model, we don't call source_update - instead, we update the source database
|
||||||
|
// by difference transmission.
|
||||||
|
//
|
||||||
|
// Note that updating the source database has *no effect* on the lua global
|
||||||
|
// variables (or lua function definitions). The source_update operation calls
|
||||||
|
// lua's "load" on the code, but all that does is return a loaded closure.
|
||||||
|
// Nothing happens to the lua invironment until you *invoke* the loaded closure.
|
||||||
|
// That doesn't happen until you do a source_rebuild operation, described below.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// SOURCE REBUILDS, IN DEPTH
|
||||||
|
//
|
||||||
|
// The function source_rebuild clears and then rebuilds the entire contents of
|
||||||
|
// the global environment. The reason to clear the environment is that if we
|
||||||
|
// didn't clear it, then removing a function from the lua source would not
|
||||||
|
// remove it from the lua environment. Rebuilding the lua environment is a
|
||||||
|
// multi-step process:
|
||||||
|
//
|
||||||
|
// * Delete everything from the global environment except class tables.
|
||||||
|
//
|
||||||
|
// - Class tables are kept, but the contents are cleared.
|
||||||
|
// - If a class has an "actions" subtable, that is kept and cleared.
|
||||||
|
// - Anything else is deleted.
|
||||||
|
//
|
||||||
|
// * Lua Builtin functions are reinstalled in the global environment.
|
||||||
|
//
|
||||||
|
// - To make this possible, a snapshot of these builtins is kept.
|
||||||
|
//
|
||||||
|
// * C++ functions registered with "LuaDefine" are reinstalled.
|
||||||
|
//
|
||||||
|
// - LuaDefine creates a registry, that registry is iterated over.
|
||||||
|
//
|
||||||
|
// * Lua code is reinstalled by running the closures in the source DB.
|
||||||
|
//
|
||||||
|
// - Simply call the "loadresult" closure to reinstall functions into the
|
||||||
|
// global environment.
|
||||||
|
//
|
||||||
|
// Note that if you've stored any global data in the lua environment, it's gone.
|
||||||
|
// So therefore, we have to provide a separate "safe" space for global data
|
||||||
|
// structures. That is provided elsewhere, in the module "globaldb".
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@@ -22,24 +106,41 @@
|
|||||||
|
|
||||||
#include "luastack.hpp"
|
#include "luastack.hpp"
|
||||||
|
|
||||||
// Get a class from the class database.
|
// The Lua 'makeclass' operator.
|
||||||
int source_class(lua_State *L);
|
//
|
||||||
|
// Creates a table in the global environment with the specified name.
|
||||||
|
// Adds a __class field and an __index field, and an action subtable.
|
||||||
|
// If there's already a table with this name in the global environment,
|
||||||
|
// leaves it there, and repairs the __class and __index fields.
|
||||||
|
//
|
||||||
|
int source_makeclass(lua_State *L);
|
||||||
|
|
||||||
// Update the source database from disk. No parameters, no return values.
|
// source_install_and_snapshot_builtins.
|
||||||
|
//
|
||||||
|
// Install the lua builtins into a brand new lua state, and then create the
|
||||||
|
// snapshot of the builtins (in the registry). This uses lua_openlibs, which
|
||||||
|
// only works if the lua environment is brand new. Do not try to use
|
||||||
|
// source_snapshot_builtins unless the lua environment has just been created
|
||||||
|
// using lua_newstate!
|
||||||
|
//
|
||||||
|
int source_install_and_snapshot_builtins(lua_State *L);
|
||||||
|
|
||||||
|
// source_update
|
||||||
|
//
|
||||||
|
// Read all the lua source files from disk and store them in the
|
||||||
|
// source database. Also compiles these files using lua's "load"
|
||||||
|
// function. Efficient: if a source file is already in the database
|
||||||
|
// and hasn't been modified, it is not reloaded.
|
||||||
|
//
|
||||||
int source_update(lua_State *L);
|
int source_update(lua_State *L);
|
||||||
|
|
||||||
// Load the builtins into the global environment using lua_openlibs
|
// source_rebuild
|
||||||
int source_load_builtins(lua_State *L);
|
//
|
||||||
|
// Rebuild the lua environment: clear it out, then reinstall all the
|
||||||
// Back up the pristine global environment to the registry.
|
// functions that should be there. See above for more information.
|
||||||
int source_snapshot_builtins(lua_State *L);
|
//
|
||||||
|
|
||||||
// Rebuild the class database from the source database. No parameters, no return values.
|
|
||||||
int source_rebuild(lua_State *L);
|
int source_rebuild(lua_State *L);
|
||||||
|
|
||||||
// Run all 'autoinit' functions.
|
|
||||||
int source_autoinit(lua_State *L);
|
|
||||||
|
|
||||||
#endif // SOURCE_HPP
|
#endif // SOURCE_HPP
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -29,16 +29,6 @@ LuaDefine(table_equal, "c") {
|
|||||||
return LS.result();
|
return LS.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
LuaDefine(table_append, "c") {
|
|
||||||
luaL_checktype(L, -2, LUA_TTABLE);
|
|
||||||
int len = lua_objlen(L, -2);
|
|
||||||
lua_pushinteger(L, len+1);
|
|
||||||
lua_pushvalue(L, -2);
|
|
||||||
lua_rawset(L, -4);
|
|
||||||
lua_pop(L, 2);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
LuaDefine(table_findremove, "c") {
|
LuaDefine(table_findremove, "c") {
|
||||||
luaL_checktype(L, -2, LUA_TTABLE);
|
luaL_checktype(L, -2, LUA_TTABLE);
|
||||||
int src = 1;
|
int src = 1;
|
||||||
@@ -75,6 +65,17 @@ LuaDefine(table_findremove, "c") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LuaDefine(table_push, "c") {
|
||||||
|
luaL_checktype(L, -2, LUA_TTABLE);
|
||||||
|
int len = lua_objlen(L, -2);
|
||||||
|
lua_pushinteger(L, len+1);
|
||||||
|
lua_pushvalue(L, -2);
|
||||||
|
lua_rawset(L, -4);
|
||||||
|
lua_pop(L, 2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
LuaDefine(table_find, "c") {
|
LuaDefine(table_find, "c") {
|
||||||
luaL_checktype(L, -2, LUA_TTABLE);
|
luaL_checktype(L, -2, LUA_TTABLE);
|
||||||
for (int i = 1; ; i++) {
|
for (int i = 1; ; i++) {
|
||||||
@@ -126,8 +127,6 @@ LuaDefine(table_clear, "c") {
|
|||||||
LuaStack LS(L, tab);
|
LuaStack LS(L, tab);
|
||||||
|
|
||||||
LS.checktable(tab);
|
LS.checktable(tab);
|
||||||
LS.clearmetatable(tab);
|
|
||||||
|
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
while (lua_next(L, tab.index()) != 0) {
|
while (lua_next(L, tab.index()) != 0) {
|
||||||
lua_pop(L, 1); // Pop the old value.
|
lua_pop(L, 1); // Pop the old value.
|
||||||
@@ -139,14 +138,6 @@ LuaDefine(table_clear, "c") {
|
|||||||
return LS.result();
|
return LS.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
LuaDefine(table_coerce, "c") {
|
|
||||||
if (!lua_istable(L, -1)) {
|
|
||||||
lua_pop(L, 1);
|
|
||||||
lua_newtable(L);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
LuaDefine(queue_create, "c") {
|
LuaDefine(queue_create, "c") {
|
||||||
LuaRet queue;
|
LuaRet queue;
|
||||||
LuaStack LS(L, queue);
|
LuaStack LS(L, queue);
|
||||||
|
|||||||
@@ -1,20 +1,113 @@
|
|||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// TABLE
|
||||||
|
//
|
||||||
|
// This module contains a library of lua functions
|
||||||
|
// for manipulating tables. Some of the functions only
|
||||||
|
// work on vectors (ie, tables with integer keys starting
|
||||||
|
// at one). When a function only works on vectors, it
|
||||||
|
// is noted.
|
||||||
|
//
|
||||||
|
// QUEUE
|
||||||
|
//
|
||||||
|
// This module contains a library of lua functions for
|
||||||
|
// manipulating queues. Queues are represented as a table
|
||||||
|
// with a "head" and a "tail", and with integer-keyed values.
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// "tail": 100,
|
||||||
|
// "head": 103,
|
||||||
|
// 100: "a",
|
||||||
|
// 101: "b",
|
||||||
|
// 102: "c"
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Values are pushed onto the head, and values are popped
|
||||||
|
// from the tail.
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
#ifndef TABLE_HPP
|
#ifndef TABLE_HPP
|
||||||
#define TABLE_HPP
|
#define TABLE_HPP
|
||||||
|
|
||||||
#include "luastack.hpp"
|
#include "luastack.hpp"
|
||||||
|
|
||||||
|
// table_equal
|
||||||
|
//
|
||||||
|
// True if two tables contain the same key/value pairs.
|
||||||
|
//
|
||||||
int table_equal(lua_State *L);
|
int table_equal(lua_State *L);
|
||||||
|
|
||||||
|
// table_findremove
|
||||||
|
//
|
||||||
|
// Given a vector and a value, remove the specified value from
|
||||||
|
// the vector, and shift elements downward to fill the gaps.
|
||||||
|
//
|
||||||
int table_findremove(lua_State *L);
|
int table_findremove(lua_State *L);
|
||||||
int table_append(lua_State *L);
|
|
||||||
|
// table_push
|
||||||
|
//
|
||||||
|
// Given a vector and a value, push the value onto the end.
|
||||||
|
//
|
||||||
|
int table_push(lua_State *L);
|
||||||
|
|
||||||
|
// table_find
|
||||||
|
//
|
||||||
|
// Given a vector and a value, search for the first occurrence
|
||||||
|
// of that value. Return the index.
|
||||||
|
//
|
||||||
int table_find(lua_State *L);
|
int table_find(lua_State *L);
|
||||||
|
|
||||||
|
// table_empty
|
||||||
|
//
|
||||||
|
// Return true if the table has no key/value pairs.
|
||||||
|
//
|
||||||
int table_empty(lua_State *L);
|
int table_empty(lua_State *L);
|
||||||
|
|
||||||
|
// table_count
|
||||||
|
//
|
||||||
|
// Return the number of key/value pairs in the table.
|
||||||
|
//
|
||||||
int table_count(lua_State *L);
|
int table_count(lua_State *L);
|
||||||
|
|
||||||
|
// table_clear
|
||||||
|
//
|
||||||
|
// Remove all key/value pairs from the table. Does not
|
||||||
|
// remove the metatable.
|
||||||
|
//
|
||||||
int table_clear(lua_State *L);
|
int table_clear(lua_State *L);
|
||||||
int table_coerce(lua_State *L);
|
|
||||||
|
// queue_create
|
||||||
|
//
|
||||||
|
// Create and return an empty queue.
|
||||||
|
//
|
||||||
int queue_create(lua_State *L);
|
int queue_create(lua_State *L);
|
||||||
|
|
||||||
|
// queue_push
|
||||||
|
//
|
||||||
|
// Given a queue and a value, pushes the value onto the queue.
|
||||||
|
//
|
||||||
int queue_push(lua_State *L);
|
int queue_push(lua_State *L);
|
||||||
|
|
||||||
|
// queue_pop
|
||||||
|
//
|
||||||
|
// Given a queue, pop and return a value. If the queue is empty,
|
||||||
|
// returns nil.
|
||||||
|
//
|
||||||
int queue_pop(lua_State *L);
|
int queue_pop(lua_State *L);
|
||||||
|
|
||||||
|
// queue_size
|
||||||
|
//
|
||||||
|
// Return the number of values in the queue.
|
||||||
|
//
|
||||||
int queue_size(lua_State *L);
|
int queue_size(lua_State *L);
|
||||||
|
|
||||||
|
// queue_nth
|
||||||
|
//
|
||||||
|
// Return the nth element in the queue.
|
||||||
|
//
|
||||||
int queue_nth(lua_State *L);
|
int queue_nth(lua_State *L);
|
||||||
|
|
||||||
#endif // TABLE_HPP
|
#endif // TABLE_HPP
|
||||||
|
|||||||
@@ -1,27 +1,37 @@
|
|||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
// TRACEBACK ROUTINES
|
||||||
//
|
//
|
||||||
// Traceback routines.
|
// The following routines are meant to help produce good-quality
|
||||||
//
|
// tracebacks from errors in lua code.
|
||||||
// traceback_handler: a traceback routine meant to be used
|
|
||||||
// as a message handler routine for 'pcall'.
|
|
||||||
//
|
|
||||||
// traceback_coroutine: takes a coroutine and an error message.
|
|
||||||
// Returns a traceback of the coroutine.
|
|
||||||
//
|
|
||||||
// traceback_pcall: same as lua_pcall, except that it supplies
|
|
||||||
// the default traceback_handler as a message handler.
|
|
||||||
//
|
|
||||||
//
|
//
|
||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
#ifndef TRACEBACK_HPP
|
#ifndef TRACEBACK_HPP
|
||||||
#define TRACEBACK_HPP
|
#define TRACEBACK_HPP
|
||||||
|
|
||||||
#include "luastack.hpp"
|
#include "luastack.hpp"
|
||||||
|
|
||||||
|
// traceback_coroutine
|
||||||
|
//
|
||||||
|
// Given a coroutine and an error message, returns a traceback
|
||||||
|
// of the coroutine.
|
||||||
|
//
|
||||||
int traceback_coroutine(lua_State *L);
|
int traceback_coroutine(lua_State *L);
|
||||||
|
|
||||||
|
// traceback_handler
|
||||||
|
//
|
||||||
|
// The function 'pcall' expects you to pass in a message handler routine.
|
||||||
|
// 'traceback_handler' is designed to be used as this argument to pcall.
|
||||||
|
//
|
||||||
int traceback_handler(lua_State *L);
|
int traceback_handler(lua_State *L);
|
||||||
|
|
||||||
|
// traceback_pcall
|
||||||
|
//
|
||||||
|
// same as lua_pcall, except that it automatically supplies traceback_handler as
|
||||||
|
// a message handler.
|
||||||
|
//
|
||||||
int traceback_pcall(lua_State *L, int narg, int nret);
|
int traceback_pcall(lua_State *L, int narg, int nret);
|
||||||
|
|
||||||
#endif // TRACEBACK_HPP
|
#endif // TRACEBACK_HPP
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
namespace util {
|
namespace util {
|
||||||
|
|
||||||
@@ -43,6 +44,7 @@ uint32_t hash3(uint32_t a, uint32_t b, uint32_t c);
|
|||||||
// Returns a floating point value between lo and hi inclusive.
|
// Returns a floating point value between lo and hi inclusive.
|
||||||
double hash_to_float(double lo, double hi, uint32_t a, uint32_t b, uint32_t c);
|
double hash_to_float(double lo, double hi, uint32_t a, uint32_t b, uint32_t c);
|
||||||
|
|
||||||
|
using XYZ = std::tuple<float, float, float>;
|
||||||
|
|
||||||
} // namespace util
|
} // namespace util
|
||||||
#endif // UTIL_HPP
|
#endif // UTIL_HPP
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
local ut = {}
|
local ut = {}
|
||||||
|
|
||||||
function ut.globaldb()
|
function ut.globaldb()
|
||||||
|
globaldb.enable()
|
||||||
local g1a = global("unittest-g1")
|
local g1a = global("unittest-g1")
|
||||||
local g2a = global("unittest-g2")
|
local g2a = global("unittest-g2")
|
||||||
local g1b = global("unittest-g1")
|
local g1b = global("unittest-g1")
|
||||||
|
|||||||
@@ -7,25 +7,12 @@ function ut.table_count()
|
|||||||
assert(table.count({[2]=5,[5]=3}) == 2)
|
assert(table.count({[2]=5,[5]=3}) == 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
function ut.table_coerce()
|
|
||||||
local t = {}
|
|
||||||
local t1 = table.coerce(t)
|
|
||||||
assert(t1==t)
|
|
||||||
t1 = table.coerce(0)
|
|
||||||
assert(type(t1) == "table")
|
|
||||||
t1 = table.coerce(nil)
|
|
||||||
assert(type(t1) == "table")
|
|
||||||
end
|
|
||||||
|
|
||||||
function ut.table_clear()
|
function ut.table_clear()
|
||||||
local t = { a = 1, b = 2 }
|
local t = { a = 1, b = 2 }
|
||||||
table.clear(t)
|
table.clear(t)
|
||||||
assert(t.a == nil)
|
assert(t.a == nil)
|
||||||
assert(t.b == nil)
|
assert(t.b == nil)
|
||||||
assert(table.count(t) == 0)
|
assert(table.count(t) == 0)
|
||||||
setmetatable(t, t)
|
|
||||||
table.clear(t)
|
|
||||||
assert(getmetatable(t) == nil)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function ut.table_empty()
|
function ut.table_empty()
|
||||||
@@ -46,13 +33,13 @@ function ut.table_equal()
|
|||||||
assert(not table.equal({a=1,b=3},{a=1,b=2}))
|
assert(not table.equal({a=1,b=3},{a=1,b=2}))
|
||||||
end
|
end
|
||||||
|
|
||||||
function ut.table_append()
|
function ut.table_push()
|
||||||
t = {}
|
t = {}
|
||||||
table.append(t, 1)
|
table.push(t, 1)
|
||||||
assert(table.equal(t, {1}))
|
assert(table.equal(t, {1}))
|
||||||
table.append(t, 2)
|
table.push(t, 2)
|
||||||
assert(table.equal(t, {1,2}))
|
assert(table.equal(t, {1,2}))
|
||||||
table.append(t, 3)
|
table.push(t, 3)
|
||||||
assert(table.equal(t, {1,2,3}))
|
assert(table.equal(t, {1,2,3}))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user