Skip to content

Instantly share code, notes, and snippets.

@shakesoda
Last active April 18, 2017 07:26
Show Gist options
  • Save shakesoda/cb09229d69a1144901a23a7d8f82d10f to your computer and use it in GitHub Desktop.
Save shakesoda/cb09229d69a1144901a23a7d8f82d10f to your computer and use it in GitHub Desktop.
basic binding of PECS to lua, using kaguya.
#include <lua.hpp>
#include <cstdint>
#include <vector>
#include "kaguya.hpp"
#include "pecs.hpp"
using namespace std;
using namespace pecs;
#define GENERATE_COMPONENT(ENUM, TYPE, NAME) \
vector<TYPE> NAME##s; \
void assign(entity_t *entity, TYPE component) { \
entity->mask |= ENUM ; \
size_t c = this->NAME##s.capacity(); \
while (c <= entity->id) { \
c = max<size_t>(c, 1); \
c = c << 1; \
} \
if (this->NAME##s.size() <= entity->id) { \
this->NAME##s.reserve(c); \
this->NAME##s.resize(entity->id+1); \
} \
this->NAME##s[entity->id] = component; \
} \
TYPE & get_##NAME (int id) { \
return NAME##s[id]; \
} \
void add_##NAME (entity_t *entity, TYPE comp) { \
this->assign(entity, comp); \
}
enum {
// NB: component 0 can't be used.
COMPONENT_NONE = ~0,
COMPONENT_THING = 1 << 1
};
static lua_State *s_lua = nullptr;
struct LuaSystem : system_t {
kaguya::LuaFunction luaProcess;
kaguya::LuaFunction luaUpdate;
LuaSystem(int priority, uint64_t mask, kaguya::LuaFunction process_fn, kaguya::LuaFunction update_fn):
luaProcess(process_fn),
luaUpdate(update_fn)
{
this->priority = priority;
this->mask = mask;
}
// depends on za_warudo, so define later.
void update(double dt);
};
struct component_thing {
int thing() {
return 1;
}
int thang = 5;
};
struct za_warudo : world_t {
GENERATE_COMPONENT(COMPONENT_THING, component_thing, thing);
// expects table such as:
// {
// priority = int,
// components = { COMPONENT_FOO, ... } (optional),
// update = function(dt) end (optional),
// process = function(dt, e, c) end (optional)
// }
void add_lua_system(kaguya::LuaTable params) {
int priority = params["priority"].get<int>();
bool valid;
auto values = params["components"].get<vector<uint64_t>>(valid, true);
uint64_t mask = 0;
for (uint64_t v : values) {
mask |= v;
}
kaguya::LuaFunction fn1 = params["process"];
kaguya::LuaFunction fn2 = params["update"];
if (mask == 0) mask = COMPONENT_NONE;
if (fn1.isNilref()) fn1 = kaguya::LuaFunction();
if (fn2.isNilref()) fn2 = kaguya::LuaFunction();
auto sys = new LuaSystem(priority, mask, fn1, fn2);
this->add(sys);
}
// these fix lua binding
void add_entity(entity_t ent) { this->add(ent); }
entity_t get_entity() { return world_t::get_entity(); }
void update(double dt) { world_t::update(dt); }
};
// calls system.update(dt) and system.process(dt, entity, components = {})
void LuaSystem::update(double dt) {
za_warudo *world = (za_warudo*)this->world;
kaguya::State lua(s_lua);
this->luaUpdate(dt);
for (auto &entity : world->entities) {
PECS_SKIP_INVALID_ENTITY;
auto components = lua.newTable();
#define PUSH_COMPONENT(MASK, NAME) if ((this->mask & MASK) == MASK) { components[#NAME] = &world->get_##NAME(entity.id); }
PUSH_COMPONENT(COMPONENT_THING, thing);
#undef PUSH_COMPONENT
this->luaProcess(dt, entity, components);
}
}
extern "C" int luaopen_ldex(lua_State *L) {
s_lua = L;
kaguya::State lua(L);
kaguya::LuaTable module = lua.newTable();
module["Thing"].setClass(kaguya::UserdataMetatable<component_thing>()
.setConstructors<component_thing()>()
.addFunction("thing", &component_thing::thing)
.addProperty("thang", &component_thing::thang)
);
module["World"].setClass(kaguya::UserdataMetatable<za_warudo, world_t>()
.setConstructors<za_warudo()>()
.addFunction("add_system", &za_warudo::add_lua_system)
.addFunction("new_entity", &za_warudo::get_entity)
.addFunction("add_entity", &za_warudo::add_entity)
.addFunction("add_thing", &za_warudo::add_thing)
.addFunction("update", &za_warudo::update)
);
#define LSET(NAME) module[#NAME] = NAME
LSET(COMPONENT_NONE);
LSET(COMPONENT_THING);
#undef LSET
return module.push();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment