Browse Source

event system

I know, I know, this segfaults on exit.

I need to make each mod subclass a Swan::Mod superclass
so that they can have EventListener structs which are
destroyed when the mod exits, and the mod has to exit
before the world goes away so that mods' EventListener
dtors can access their respective EventEmitters.
opengl-renderer-broken
Martin Dørum 4 years ago
parent
commit
1fb53a1df3

+ 12
- 5
core.mod/src/entities/ItemStackEntity.cc View File

@@ -2,19 +2,26 @@

#include <random>

ItemStackEntity::ItemStackEntity(const Swan::Context &ctx, const PackObject &obj):
PhysicsEntity(SIZE, MASS) {
PhysicsEntity::body_.bounciness_ = 0.6;

deserialize(ctx, obj);
ItemStackEntity::ItemStackEntity(
const Swan::Context &ctx, Swan::Vec2 pos, const std::string &item):
PhysicsEntity(SIZE, MASS) {

static std::uniform_real_distribution vx(-2.3f, 2.3f);
static std::uniform_real_distribution vy(-2.3f, -1.2f);

item_ = &ctx.world.getItem(item);
body_.pos_ = pos;
body_.pos_.y += 0.5 - body_.size_.y / 2;
body_.vel_ += Swan::Vec2{ vx(ctx.world.random_), vy(ctx.world.random_) };
}

ItemStackEntity::ItemStackEntity(const Swan::Context &ctx, const PackObject &obj):
PhysicsEntity(SIZE, MASS) {
PhysicsEntity::body_.bounciness_ = 0.6;

deserialize(ctx, obj);
}

void ItemStackEntity::draw(const Swan::Context &ctx, Swan::Win &win) {
SDL_Rect rect = item_->image_.frameRect();


+ 1
- 0
core.mod/src/entities/ItemStackEntity.h View File

@@ -4,6 +4,7 @@

class ItemStackEntity: public Swan::PhysicsEntity {
public:
ItemStackEntity(const Swan::Context &ctx, Swan::Vec2 pos, const std::string &item);
ItemStackEntity(const Swan::Context &ctx, const PackObject &obj);

void draw(const Swan::Context &ctx, Swan::Win &win) override;

+ 5
- 5
core.mod/src/entities/PlayerEntity.cc View File

@@ -4,24 +4,24 @@

#include "ItemStackEntity.h"

PlayerEntity::PlayerEntity(const Swan::Context &ctx, const PackObject &obj):
PlayerEntity::PlayerEntity(const Swan::Context &ctx, Swan::Vec2 pos):
PhysicsEntity(SIZE, MASS), inventory_(INVENTORY_SIZE),
anims_{
Swan::Animation(ctx.resources.getImage("core/entity/player-still"), 0.8),
Swan::Animation(ctx.resources.getImage("core/entity/player-running"), 1, SDL_FLIP_HORIZONTAL),
Swan::Animation(ctx.resources.getImage("core/entity/player-running"), 1) } {

deserialize(ctx, obj);
body_.pos_ = pos;
}

PlayerEntity::PlayerEntity(const Swan::Context &ctx, Swan::Vec2 pos):
PlayerEntity::PlayerEntity(const Swan::Context &ctx, const PackObject &obj):
PhysicsEntity(SIZE, MASS), inventory_(INVENTORY_SIZE),
anims_{
Swan::Animation(ctx.resources.getImage("core/entity/player-still"), 0.8),
Swan::Animation(ctx.resources.getImage("core/entity/player-running"), 1, SDL_FLIP_HORIZONTAL),
Swan::Animation(ctx.resources.getImage("core/entity/player-running"), 1) } {

body_.pos_ = pos;
deserialize(ctx, obj);
}

void PlayerEntity::draw(const Swan::Context &ctx, Swan::Win &win) {
@@ -39,7 +39,7 @@ void PlayerEntity::update(const Swan::Context &ctx, float dt) {

// Break block
if (ctx.game.isMousePressed(SDL_BUTTON_LEFT))
ctx.plane.breakBlock(mouse_tile_);
ctx.plane.breakTile(mouse_tile_);

// Move left
if (ctx.game.isKeyPressed(SDL_SCANCODE_A) || ctx.game.isKeyPressed(SDL_SCANCODE_LEFT)) {

+ 1
- 1
core.mod/src/entities/PlayerEntity.h View File

@@ -5,8 +5,8 @@

class PlayerEntity: public Swan::PhysicsEntity, public Swan::InventoryTrait::HasInventory {
public:
PlayerEntity(const Swan::Context &ctx, const PackObject &obj);
PlayerEntity(const Swan::Context &ctx, Swan::Vec2 pos);
PlayerEntity(const Swan::Context &ctx, const PackObject &obj);

Swan::InventoryTrait::Inventory &getInventory() override { return inventory_; }


+ 12
- 1
core.mod/src/main.cc View File

@@ -4,9 +4,20 @@
#include "entities/PlayerEntity.h"
#include "entities/ItemStackEntity.h"

extern "C" void mod_init(Swan::Mod &mod) {
static Swan::EventListener break_listener;

extern "C" void mod_init(Swan::Mod &mod, Swan::World &world) {
mod.init("core");

break_listener = world.evt_tile_break_.subscribe([](
const Swan::Context &ctx, Swan::TilePos pos, Swan::Tile &tile) {

if (tile.dropped_item_) {
ctx.plane.spawnEntity(std::make_unique<ItemStackEntity>(
ctx, pos, *tile.dropped_item_));
}
});

mod.registerImage("tile/air");
mod.registerImage("tile/stone");
mod.registerImage("tile/dirt");

+ 75
- 0
libswan/include/swan/EventEmitter.h View File

@@ -0,0 +1,75 @@
#pragma once

#include <functional>

#include "util.h"
#include "SlotVector.h"

namespace Swan {

// So that EventListener doesn't have to be a template
class EventEmitterInterface {
public:
virtual void unsubscribe(size_t id) = 0;
};

class EventListener: NonCopyable {
public:
EventListener() {}

EventListener(EventEmitterInterface *emitter, size_t id):
emitter_(emitter), id_(id) {}

EventListener(EventListener &&other):
emitter_(other.emitter_), id_(other.id_) {
other.emitter_ = nullptr;
}

EventListener &operator=(EventListener &&other) {
emitter_ = other.emitter_;
id_ = other.id_;
other.emitter_ = nullptr;
return *this;
}

~EventListener() {
if (emitter_)
emitter_->unsubscribe(id_);
}

void unsubscribe() {
if (emitter_) {
emitter_->unsubscribe(id_);
emitter_ = nullptr;
}
}

private:
EventEmitterInterface *emitter_ = nullptr;
size_t id_;
};

template<typename... Evt>
class EventEmitter: public EventEmitterInterface {
public:
using Callback = std::function<void(Evt...)>;

void emit(Evt... evt) {
for (auto &cb: callbacks_)
cb(evt...);
}

EventListener subscribe(Callback cb) {
size_t id = callbacks_.insert(std::move(cb));
return EventListener(this, id);
}

void unsubscribe(size_t id) {
callbacks_.erase(id);
}

private:
SlotVector<Callback, SlotVectorDefaultSentinel<nullptr_t>> callbacks_;
};

}

+ 2
- 2
libswan/include/swan/Game.h View File

@@ -18,8 +18,8 @@ public:
win_(win),
mouse_pos_(0, 0) {}

std::unique_ptr<Mod> loadMod(const std::string &path);
void createWorld(const std::string &worldgen, std::vector<std::unique_ptr<Mod>> &&mods);
std::unique_ptr<Mod> loadMod(const std::string &path, World &world);
void createWorld(const std::string &worldgen, std::vector<std::string> mods);

void onKeyDown(SDL_Keysym sym) {
pressed_keys_[sym.scancode] = true;

+ 112
- 0
libswan/include/swan/SlotVector.h View File

@@ -0,0 +1,112 @@
#pragma once

#include <vector>
#include <type_traits>

namespace Swan {

template<typename T>
struct SlotVectorDefaultSentinel {
static constexpr T sentinel() { return T{}; }
};

template<typename T, T val>
struct SlotVectorValueSentinel {
static constexpr T sentinel() { return val; }
};

template<typename T, typename Sentinel = SlotVectorDefaultSentinel<T>>
class SlotVector {
public:
class Iterator {
public:
Iterator(SlotVector<T, Sentinel> *vec, size_t idx): vec_(vec), idx_(idx) {}
Iterator(const Iterator &) = default;

void seek() {
size_t size = vec_->vec_.size();
while (idx_ < size && (*vec_)[idx_] == Sentinel::sentinel())
idx_ += 1;
}

void operator++() {
idx_ += 1;
seek();
}

T &operator*() {
return (*vec_)[idx_];
}

bool operator==(const Iterator &other) const {
return idx_ == other.idx_;
}
bool operator!=(const Iterator &other) const {
return !(idx_ == other.idx_);
}
size_t operator-(const Iterator &other) const {
return idx_ - other.idx_;
}
void operator=(const Iterator &other) {
idx_ = other.idx_;
vec_ = other.vec_;
}

private:
SlotVector<T, Sentinel> *vec_;
size_t idx_;
};

T &operator[](size_t idx) { return vec_[idx]; }
T &at(size_t idx) { return vec_.at(idx); }
bool empty() { return vec_.size() == free_.size(); }

Iterator begin() { Iterator it(this, 0); it.seek(); return it; }
Iterator end() { return Iterator(this, vec_.size()); }

void clear() noexcept { vec_.clear(); free_.clear(); }

void shrink_to_fit() {
vec_.shrink_to_fit();
free_.shrink_to_fit();
}

size_t insert(T val) {
if (free_.size() > 0) {
size_t idx = free_.back();
vec_[idx] = std::move(val);
free_.pop_back();
return idx;
} else {
size_t idx = vec_.size();
vec_.push_back(std::move(val));
return idx;
}
}

template<typename... Args>
size_t emplace(Args&&... args) {
if (free_.size() > 0) {
size_t idx = free_.back();
T *slot = vec_.data() + idx;
slot->~T();
new (slot) T(std::forward<Args>(args)...);
return idx;
} else {
size_t idx = vec_.size();
vec_.emplace_back(std::forward<Args>(args)...);
return idx;
}
}

void erase(size_t idx) {
vec_[idx] = Sentinel::sentinel();
free_.push_back(idx);
}

private:
std::vector<T> vec_;
std::vector<size_t> free_;
};

}

+ 6
- 1
libswan/include/swan/World.h View File

@@ -14,6 +14,7 @@
#include "Entity.h"
#include "Resource.h"
#include "Mod.h"
#include "EventEmitter.h"

namespace Swan {

@@ -24,7 +25,7 @@ public:
World(Game *game, unsigned long rand_seed);

void addMod(std::unique_ptr<Mod> mod);
void setWorldGen(const std::string &gen);
void setWorldGen(std::string gen);
void spawnPlayer();

void setCurrentPlane(WorldPlane &plane);
@@ -41,6 +42,10 @@ public:
void update(float dt);
void tick(float dt);

// Event emitters
EventEmitter<const Context &, TilePos, Tile &>
evt_tile_break_;

std::vector<std::unique_ptr<Mod>> mods_;

// World owns tiles and items, the mod just has Builder objects

+ 1
- 1
libswan/include/swan/WorldPlane.h View File

@@ -54,7 +54,7 @@ public:
}

BodyTrait::HasBody *spawnPlayer();
void breakBlock(TilePos pos);
void breakTile(TilePos pos);

SDL_Color backgroundColor();
void draw(Win &win);

+ 2
- 0
libswan/include/swan/swan.h View File

@@ -6,6 +6,7 @@
#include <swan/Chunk.h>
#include <swan/Clock.h>
#include <swan/Entity.h>
#include <swan/EventEmitter.h>
#include <swan/Game.h>
#include <swan/Item.h>
#include <swan/ItemStack.h>
@@ -13,6 +14,7 @@
#include <swan/PerfCounter.h>
#include <swan/OS.h>
#include <swan/Resource.h>
#include <swan/SlotVector.h>
#include <swan/Tile.h>
#include <swan/Vector2.h>
#include <swan/Win.h>

+ 6
- 6
libswan/src/Game.cc View File

@@ -11,24 +11,24 @@

namespace Swan {

std::unique_ptr<Mod> Game::loadMod(const std::string &path) {
std::unique_ptr<Mod> Game::loadMod(const std::string &path, World &world) {
OS::Dynlib dl(path + "/mod");
auto init = dl.get<void (*)(Swan::Mod &)>("mod_init");
auto init = dl.get<void (*)(Mod &, World &)>("mod_init");
if (init == NULL) {
warn << path << ": No 'mod_init' function!";
return nullptr;
}

std::unique_ptr<Mod> mod = std::make_unique<Mod>(path, std::move(dl));
init(*mod);
init(*mod, world);
return mod;
}

void Game::createWorld(const std::string &worldgen, std::vector<std::unique_ptr<Mod>> &&mods) {
void Game::createWorld(const std::string &worldgen, std::vector<std::string> modpaths) {
world_.reset(new World(this, time(NULL)));

for (auto &mod: mods) {
world_->addMod(std::move(mod));
for (auto &modpath: modpaths) {
world_->addMod(std::move(loadMod(modpath, *world_)));
}

world_->setWorldGen(worldgen);

+ 2
- 2
libswan/src/World.cc View File

@@ -76,8 +76,8 @@ void World::addMod(std::unique_ptr<Mod> mod) {
mods_.push_back(std::move(mod));
}

void World::setWorldGen(const std::string &gen) {
default_world_gen_ = gen;
void World::setWorldGen(std::string gen) {
default_world_gen_ = std::move(gen);
}

void World::spawnPlayer() {

+ 5
- 2
libswan/src/WorldPlane.cc View File

@@ -153,7 +153,7 @@ BodyTrait::HasBody *WorldPlane::spawnPlayer() {
return gen_->spawnPlayer(getContext());
}

void WorldPlane::breakBlock(TilePos pos) {
void WorldPlane::breakTile(TilePos pos) {

// If the block is already air, do nothing
Tile::ID id = getTileID(pos);
@@ -161,9 +161,11 @@ void WorldPlane::breakBlock(TilePos pos) {
if (id == air)
return;

// Change the block to air...
// Change tile to air and emit event
setTileID(pos, air);
world_->evt_tile_break_.emit(getContext(), pos, world_->getTileByID(id));

/*
// Then spawn an item stack entity.
Tile &t = world_->getTileByID(id);
if (t.dropped_item_) {
@@ -173,6 +175,7 @@ void WorldPlane::breakBlock(TilePos pos) {
{ "item", msgpack::object(*t.dropped_item_, zone) },
});
}
*/
}

SDL_Color WorldPlane::backgroundColor() {

+ 1
- 2
src/main.cc View File

@@ -110,8 +110,7 @@ int main(int argc, char **argv) {

// Create a world
Game game(win);
std::vector<std::unique_ptr<Mod>> mods;
mods.push_back(game.loadMod("core.mod"));
std::vector<std::string> mods{ "core.mod" };
game.createWorld("core::default", std::move(mods));

PerfCounter pcounter;

Loading…
Cancel
Save