@@ -6,77 +6,85 @@ | |||
static Swan::EventListener break_listener; | |||
extern "C" void mod_init(Swan::Mod &mod, Swan::World &world) { | |||
mod.init("core"); | |||
class CoreMod: public Swan::Mod { | |||
public: | |||
CoreMod(Swan::World &world): Swan::Mod("core") { | |||
break_listener_ = world.evt_tile_break_.subscribe( | |||
std::bind_front(&CoreMod::onTileBreak, this)); | |||
break_listener = world.evt_tile_break_.subscribe([]( | |||
const Swan::Context &ctx, Swan::TilePos pos, Swan::Tile &tile) { | |||
registerImage("tile/air"); | |||
registerImage("tile/stone"); | |||
registerImage("tile/dirt"); | |||
registerImage("tile/grass"); | |||
registerImage("tile/tree-trunk"); | |||
registerImage("tile/leaves"); | |||
registerImage("entity/player-running"); | |||
registerImage("entity/player-still"); | |||
registerImage("misc/background-cave"); | |||
registerTile({ | |||
.name = "air", | |||
.image = "core/tile/air", | |||
.is_solid = false, | |||
}); | |||
registerTile({ | |||
.name = "stone", | |||
.image = "core/tile/stone", | |||
.dropped_item = "core::stone", | |||
}); | |||
registerTile({ | |||
.name = "dirt", | |||
.image = "core/tile/dirt", | |||
.dropped_item = "core::dirt", | |||
}); | |||
registerTile({ | |||
.name = "grass", | |||
.image = "core/tile/grass", | |||
.dropped_item = "core::dirt", | |||
}); | |||
registerTile({ | |||
.name = "tree-trunk", | |||
.image = "core/tile/tree-trunk", | |||
.dropped_item = "core::tree-trunk", | |||
}); | |||
registerTile({ | |||
.name = "leaves", | |||
.image = "core/tile/leaves", | |||
}); | |||
registerItem({ | |||
.name = "stone", | |||
.image = "core/tile/stone", | |||
}); | |||
registerItem({ | |||
.name = "dirt", | |||
.image = "core/tile/dirt", | |||
}); | |||
registerItem({ | |||
.name = "grass", | |||
.image = "core/tile/grass", | |||
}); | |||
registerItem({ | |||
.name = "tree-trunk", | |||
.image = "core/tile/tree-trunk", | |||
}); | |||
registerWorldGen<DefaultWorldGen>("default"); | |||
registerEntity<PlayerEntity>("player"); | |||
registerEntity<ItemStackEntity>("item-stack"); | |||
} | |||
void onTileBreak(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"); | |||
mod.registerImage("tile/grass"); | |||
mod.registerImage("tile/tree-trunk"); | |||
mod.registerImage("tile/leaves"); | |||
mod.registerImage("entity/player-running"); | |||
mod.registerImage("entity/player-still"); | |||
mod.registerImage("misc/background-cave"); | |||
mod.registerTile({ | |||
.name = "air", | |||
.image = "core/tile/air", | |||
.is_solid = false, | |||
}); | |||
mod.registerTile({ | |||
.name = "stone", | |||
.image = "core/tile/stone", | |||
.dropped_item = "core::stone", | |||
}); | |||
mod.registerTile({ | |||
.name = "dirt", | |||
.image = "core/tile/dirt", | |||
.dropped_item = "core::dirt", | |||
}); | |||
mod.registerTile({ | |||
.name = "grass", | |||
.image = "core/tile/grass", | |||
.dropped_item = "core::dirt", | |||
}); | |||
mod.registerTile({ | |||
.name = "tree-trunk", | |||
.image = "core/tile/tree-trunk", | |||
.dropped_item = "core::tree-trunk", | |||
}); | |||
mod.registerTile({ | |||
.name = "leaves", | |||
.image = "core/tile/leaves", | |||
}); | |||
mod.registerItem({ | |||
.name = "stone", | |||
.image = "core/tile/stone", | |||
}); | |||
mod.registerItem({ | |||
.name = "dirt", | |||
.image = "core/tile/dirt", | |||
}); | |||
mod.registerItem({ | |||
.name = "grass", | |||
.image = "core/tile/grass", | |||
}); | |||
mod.registerItem({ | |||
.name = "tree-trunk", | |||
.image = "core/tile/tree-trunk", | |||
}); | |||
} | |||
mod.registerWorldGen<DefaultWorldGen>("default"); | |||
Swan::EventListener break_listener_; | |||
}; | |||
mod.registerEntity<PlayerEntity>("player"); | |||
mod.registerEntity<ItemStackEntity>("item-stack"); | |||
extern "C" std::unique_ptr<Swan::Mod> mod_create(Swan::World &world) { | |||
return std::make_unique<CoreMod>(world); | |||
} |
@@ -3,6 +3,7 @@ | |||
#include <bitset> | |||
#include <map> | |||
#include <string> | |||
#include <optional> | |||
#include <SDL.h> | |||
#include "common.h" | |||
@@ -18,7 +19,7 @@ public: | |||
win_(win), | |||
mouse_pos_(0, 0) {} | |||
std::unique_ptr<Mod> loadMod(const std::string &path, World &world); | |||
std::optional<ModWrapper> loadMod(std::string path, World &world); | |||
void createWorld(const std::string &worldgen, std::vector<std::string> mods); | |||
void onKeyDown(SDL_Keysym sym) { |
@@ -18,14 +18,8 @@ namespace Swan { | |||
class Mod { | |||
public: | |||
Mod(const std::string &path, OS::Dynlib &&dynlib): | |||
path_(path), dynlib_(std::move(dynlib)) {} | |||
// We have to manually ensure anything created from the dynlib | |||
// is destructed before the dynlib itself is destructed | |||
~Mod(); | |||
void init(const std::string &name); | |||
Mod(std::string name): name_(std::move(name)) {} | |||
virtual ~Mod() = default; | |||
void registerImage(const std::string &id); | |||
void registerTile(Tile::Builder tile); | |||
@@ -54,24 +48,36 @@ public: | |||
}); | |||
} | |||
Iter<std::unique_ptr<ImageResource>> buildImages(SDL_Renderer *renderer); | |||
Iter<std::unique_ptr<Tile>> buildTiles(const ResourceManager &resources); | |||
Iter<std::unique_ptr<Item>> buildItems(const ResourceManager &resources); | |||
Iter<WorldGen::Factory> getWorldGens(); | |||
Iter<Entity::Factory> getEntities(); | |||
std::string name_ = "@uninitialized"; | |||
private: | |||
const std::string name_; | |||
std::vector<std::string> images_; | |||
std::vector<Tile::Builder> tiles_; | |||
std::vector<Item::Builder> items_; | |||
std::vector<WorldGen::Factory> worldgens_; | |||
std::vector<Entity::Factory> entities_; | |||
}; | |||
class ModWrapper { | |||
public: | |||
ModWrapper(std::unique_ptr<Mod> mod, std::string path, OS::Dynlib lib): | |||
mod_(std::move(mod)), path_(std::move(path)), dynlib_(std::move(lib)) {} | |||
ModWrapper(ModWrapper &&other) noexcept = default; | |||
~ModWrapper() { | |||
// Mod::~Mod will destroy stuff allocated by the dynlib, | |||
// so we must run its destructor before deleting the dynlib | |||
mod_.reset(); | |||
} | |||
Iter<std::unique_ptr<ImageResource>> buildImages(SDL_Renderer *renderer); | |||
Iter<std::unique_ptr<Tile>> buildTiles(const ResourceManager &resources); | |||
Iter<std::unique_ptr<Item>> buildItems(const ResourceManager &resources); | |||
Iter<WorldGen::Factory> getWorldGens(); | |||
Iter<Entity::Factory> getEntities(); | |||
std::unique_ptr<Mod> mod_; | |||
std::string path_; | |||
OS::Dynlib dynlib_; | |||
bool inited_ = false; | |||
}; | |||
} |
@@ -24,7 +24,7 @@ class World { | |||
public: | |||
World(Game *game, unsigned long rand_seed); | |||
void addMod(std::unique_ptr<Mod> mod); | |||
void addMod(ModWrapper &&mod); | |||
void setWorldGen(std::string gen); | |||
void spawnPlayer(); | |||
@@ -46,7 +46,8 @@ public: | |||
EventEmitter<const Context &, TilePos, Tile &> | |||
evt_tile_break_; | |||
std::vector<std::unique_ptr<Mod>> mods_; | |||
// World owns all mods | |||
std::vector<ModWrapper> mods_; | |||
// World owns tiles and items, the mod just has Builder objects | |||
std::vector<std::unique_ptr<Tile>> tiles_; |
@@ -12,12 +12,14 @@ namespace Swan { | |||
// Inherit from this class to make a class non-copyable | |||
class NonCopyable { | |||
protected: | |||
NonCopyable() = default; | |||
NonCopyable(NonCopyable &&) = default; | |||
public: | |||
NonCopyable(const NonCopyable &) = delete; | |||
NonCopyable &operator=(const NonCopyable &) = delete; | |||
NonCopyable(NonCopyable &&) noexcept = default; | |||
protected: | |||
NonCopyable() = default; | |||
~NonCopyable() = default; | |||
}; | |||
// Take a deleter function, turn it into a class with an operator() for unique_ptr |
@@ -11,24 +11,26 @@ | |||
namespace Swan { | |||
std::unique_ptr<Mod> Game::loadMod(const std::string &path, World &world) { | |||
std::optional<ModWrapper> Game::loadMod(std::string path, World &world) { | |||
OS::Dynlib dl(path + "/mod"); | |||
auto init = dl.get<void (*)(Mod &, World &)>("mod_init"); | |||
if (init == NULL) { | |||
warn << path << ": No 'mod_init' function!"; | |||
return nullptr; | |||
auto create = dl.get<std::unique_ptr<Mod> (*)(World &)>("mod_create"); | |||
if (create == NULL) { | |||
warn << path << ": No 'mod_create' function!"; | |||
return std::nullopt; | |||
} | |||
std::unique_ptr<Mod> mod = std::make_unique<Mod>(path, std::move(dl)); | |||
init(*mod, world); | |||
return mod; | |||
std::unique_ptr<Mod> mod = create(world); | |||
return std::make_optional<ModWrapper>( | |||
std::move(mod), std::move(path), std::move(dl)); | |||
} | |||
void Game::createWorld(const std::string &worldgen, std::vector<std::string> modpaths) { | |||
world_.reset(new World(this, time(NULL))); | |||
for (auto &modpath: modpaths) { | |||
world_->addMod(std::move(loadMod(modpath, *world_))); | |||
auto mod = loadMod(modpath, *world_); | |||
if (mod) | |||
world_->addMod(std::move(*mod)); | |||
} | |||
world_->setWorldGen(worldgen); |
@@ -8,21 +8,6 @@ | |||
namespace Swan { | |||
Mod::~Mod() { | |||
images_.clear(); | |||
tiles_.clear(); | |||
items_.clear(); | |||
worldgens_.clear(); | |||
entities_.clear(); | |||
} | |||
void Mod::init(const std::string &name) { | |||
name_ = name; | |||
inited_ = true; | |||
info << "Mod initing: " << name_; | |||
} | |||
void Mod::registerImage(const std::string &id) { | |||
images_.push_back(name_ + "/" + id); | |||
info << " Adding image: " << images_.back(); | |||
@@ -40,32 +25,37 @@ void Mod::registerItem(Item::Builder item) { | |||
info << " Adding item: " << item.name; | |||
} | |||
Iter<std::unique_ptr<ImageResource>> Mod::buildImages(SDL_Renderer *renderer) { | |||
return map(begin(images_), end(images_), [renderer, this](const std::string &id) { | |||
Iter<std::unique_ptr<ImageResource>> ModWrapper::buildImages(SDL_Renderer *renderer) { | |||
return map(begin(mod_->images_), end(mod_->images_), | |||
[renderer, this](const std::string &id) { | |||
return std::make_unique<ImageResource>(renderer, path_, id); | |||
}); | |||
} | |||
Iter<std::unique_ptr<Tile>> Mod::buildTiles(const ResourceManager &resources) { | |||
return map(begin(tiles_), end(tiles_), [&](const Tile::Builder &builder) { | |||
Iter<std::unique_ptr<Tile>> ModWrapper::buildTiles(const ResourceManager &resources) { | |||
return map(begin(mod_->tiles_), end(mod_->tiles_), | |||
[&](const Tile::Builder &builder) { | |||
return std::make_unique<Tile>(resources, builder); | |||
}); | |||
} | |||
Iter<std::unique_ptr<Item>> Mod::buildItems(const ResourceManager &resources) { | |||
return map(begin(items_), end(items_), [&](const Item::Builder &builder) { | |||
Iter<std::unique_ptr<Item>> ModWrapper::buildItems(const ResourceManager &resources) { | |||
return map(begin(mod_->items_), end(mod_->items_), | |||
[&](const Item::Builder &builder) { | |||
return std::make_unique<Item>(resources, builder); | |||
}); | |||
} | |||
Iter<WorldGen::Factory> Mod::getWorldGens() { | |||
return map(begin(worldgens_), end(worldgens_), [](WorldGen::Factory &fact) { | |||
Iter<WorldGen::Factory> ModWrapper::getWorldGens() { | |||
return map(begin(mod_->worldgens_), end(mod_->worldgens_), | |||
[](WorldGen::Factory &fact) { | |||
return fact; | |||
}); | |||
} | |||
Iter<Entity::Factory> Mod::getEntities() { | |||
return map(begin(entities_), end(entities_), [](Entity::Factory &fact){ | |||
Iter<Entity::Factory> ModWrapper::getEntities() { | |||
return map(begin(mod_->entities_), end(mod_->entities_), | |||
[](Entity::Factory &fact){ | |||
return fact; | |||
}); | |||
} |
@@ -42,31 +42,31 @@ void World::ChunkRenderer::tick(WorldPlane &plane, ChunkPos abspos) { | |||
} | |||
} | |||
void World::addMod(std::unique_ptr<Mod> mod) { | |||
info << "World: adding mod " << mod->name_; | |||
void World::addMod(ModWrapper &&mod) { | |||
info << "World: adding mod " << mod.mod_->name_; | |||
for (auto i: mod->buildImages(game_->win_.renderer_)) { | |||
for (auto i: mod.buildImages(game_->win_.renderer_)) { | |||
resources_.addImage(std::move(i)); | |||
} | |||
for (auto t: mod->buildTiles(resources_)) { | |||
for (auto t: mod.buildTiles(resources_)) { | |||
Tile::ID id = tiles_.size(); | |||
tiles_map_[t->name_] = id; | |||
tiles_.push_back(std::move(t)); | |||
} | |||
for (auto i: mod->buildItems(resources_)) { | |||
for (auto i: mod.buildItems(resources_)) { | |||
items_[i->name_] = std::move(i); | |||
} | |||
for (auto gen: mod->getWorldGens()) { | |||
for (auto gen: mod.getWorldGens()) { | |||
worldgens_.emplace( | |||
std::piecewise_construct, | |||
std::forward_as_tuple(gen.name), | |||
std::forward_as_tuple(gen)); | |||
} | |||
for (auto ent: mod->getEntities()) { | |||
for (auto ent: mod.getEntities()) { | |||
ents_.emplace( | |||
std::piecewise_construct, | |||
std::forward_as_tuple(ent.name), |