Skip to content

Instantly share code, notes, and snippets.

@apples
Last active November 17, 2018 19:45
Show Gist options
  • Select an option

  • Save apples/15638dd39a25932be78831e17f75b723 to your computer and use it in GitHub Desktop.

Select an option

Save apples/15638dd39a25932be78831e17f75b723 to your computer and use it in GitHub Desktop.

Database usertype

#include <ginseng/ginseng.hpp>
#include <sol.hpp>

auto create_ginseng_usertype(sol::state_view& lua) -> sol::simple_usertype<ginseng::database> {
    using ginseng::database;
    using ent_id = database::ent_id;
    
    auto usertype = lua.create_simple_usertype<database>();
    
    usertype.set("create_entity", &database::create_entity);
    
    usertype.set("destroy_entity", &database::destroy_entity);
    
    usertype.set("add_component", [](database& db, ent_id eid, sol::userdata com) {
        return com["_add_component"](db, eid, com);
    });
    
    usertype.set("remove_component", [](database& db, ent_id eid, sol::table com_type){
        return com_type["_remove_component"](db, eid);
    });
    
    usertype.set("get_component", [](database& db, ent_id eid, sol::table com_type){
        return com_type["_get_component"](db, eid);
    });
    
    usertype.set("has_component", [](database& db, ent_id eid, sol::table com_type){
        return com_type["_has_component"](db, eid);
    });
    
    usertype.set("visit", [](database& db, sol::protected_function func){
        db.visit([&func](ent_id eid) {
            auto result = func(eid);
            if (!result.valid()) {
                throw result.get<sol::error>();
            }
        });
    });
    
    return usertype;
}

C++ usage

{
    auto lua = sol::state{};
    lua.set_usertype("ginseng_database", create_ginseng_usertype(lua));
}

Lua usage

function create_entity(db)
    local ent = db:create_entity()
    
    db:add_component(ent, components.position.new(0, 0))
    db:add_component(ent, components.sprite.new('goomba'))
    
    return ent
end

function draw_sprites(db)
    db:visit(function (ent)
        local position = db:get_component(ent, components.position)
        local sprite   = db:get_component(ent, components.sprite)
        
        draw_sprite_at(position.x, position.y, sprite.name)
    end)
end

Component usertypes

template <typename T>
struct is_tag : std::false_type {};

template <typename T>
struct is_tag<ginseng::tag<T>> : std::true_type {};

namespace _detail {

using database = ginseng::database;
using ent_id = database::ent_id;
using com_id = database::com_id;

template <typename T>
void add_component(database& db, ent_id eid, T com) {
    db.add_component(eid, std::move(com));
}

template <typename T>
void remove_component(database& db, ent_id eid) {
    if (!db.has_component<T>(eid)) {
        auto name = std::string(meta::getName<T>());
        auto id = std::to_string(eid.get_index());

        throw std::runtime_error("Bad component removal: " + name + " from entity " + id);
    }

    db.remove_component<T>(eid);
}

template <typename T>
auto get_component(database& db, ent_id eid) -> T& {
    if (!db.has_component<T>(eid)) {
        auto name = std::string(meta::getName<T>());
        auto id = std::to_string(eid.get_index());

        throw std::runtime_error("Bad component access: " + name + " from entity " + id);
    }

    return db.get_component<T>(eid);
}

template <typename T>
auto has_component(database& db, ent_id eid) -> bool {
    return db.has_component<T>(eid);
}

} //namespace _detail

template <typename T>
auto create_component_usertype(sol::state_view& lua) -> sol::simple_usertype<T> {
    if constexpr (!is_tag<T>::value) {
        usertype.set("_get_component", &_detail::get_component<T>);
    }
    usertype.set("_add_component", &_detail::add_component<T>);
    usertype.set("_remove_component", &_detail::remove_component<T>);
    usertype.set("_has_component", &_detail::has_component<T>);
}

C++ usage

struct position {
    position() = default;
    position(float x, float y) : x(x), y(y) {}

    float x, y;
};

void register_position_usertype(sol::state_view& lua) {
    auto usertype = create_component_usertype<position>(lua);
    
    usertype.set("new", sol::constructors<
        position(),
        position(const position&),
        position(float x, float y)>{});
    
    usertype.set("x", &position::x);
    usertype.set("y", &position::y);
    
    lua.set_usertype("position", usertype);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment