Browse Source

almost ported to SDL

opengl-renderer-broken
Martin Dørum 4 years ago
parent
commit
c527655e79

+ 3
- 3
core.mod/src/entities/EntPlayer.cc View File

@@ -3,9 +3,9 @@
EntPlayer::EntPlayer(const Swan::Context &ctx, const Swan::SRF &params):
PhysicsEntity(SIZE, MASS),
anims_{
Swan::Animation(ctx.world.getImage("core::player-still"), 0.8),
Swan::Animation(ctx.world.getImage("core::player-running"), 1),
Swan::Animation(ctx.world.getImage("core::player-running"), 1, Swan::Animation::Flags::HFLIP) } {
Swan::Animation(ctx.resources.getImage("core::player-still"), 0.8),
Swan::Animation(ctx.resources.getImage("core::player-running"), 1),
Swan::Animation(ctx.resources.getImage("core::player-running"), 1, Swan::Animation::Flags::HFLIP) } {

readSRF(ctx, params);
}

+ 2
- 1
libswan/CMakeLists.txt View File

@@ -4,8 +4,9 @@ add_library(libswan SHARED
src/Chunk.cc
src/Game.cc
src/Item.cc
src/Resource.cc
src/Mod.cc
src/OS.cc
src/Resource.cc
src/SRF.cc
src/Tile.cc
src/Timer.cc

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

@@ -16,8 +16,6 @@ class Chunk {
public:
using RelPos = TilePos;

ChunkPos pos_;

Chunk(ChunkPos pos): pos_(pos) {
data_.reset(new uint8_t[CHUNK_WIDTH * CHUNK_HEIGHT * sizeof(Tile::ID)]);
visuals_.reset(new Visuals());
@@ -38,6 +36,8 @@ public:
bool keepActive(); // Returns true if chunk was inactive
bool isActive() { return deactivate_timer_ > 0; }

ChunkPos pos_;

private:
static constexpr float DEACTIVATE_INTERVAL = 20;
static uint8_t *renderbuf;

+ 3
- 1
libswan/include/swan/Game.h View File

@@ -5,6 +5,7 @@
#include <string>

#include "common.h"
#include "Mod.h"
#include "World.h"

namespace Swan {
@@ -15,7 +16,8 @@ public:
win_(win),
mouse_pos_(0, 0) {}

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

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

+ 3
- 2
libswan/include/swan/Item.h View File

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

namespace Swan {

// TODO: Switch to struct
class Item {
public:
struct Builder {
@@ -13,8 +14,8 @@ public:
std::string image;
};

Item(const ImageResource &image, const std::string &mod, const Builder &builder):
name_(mod+"::"+builder.name), image_(image) {}
Item(const ResourceManager &resources, const Builder &builder):
name_(builder.name), image_(resources.getImage(builder.image)) {}

const std::string name_;
const ImageResource &image_;

+ 19
- 9
libswan/include/swan/Mod.h View File

@@ -11,28 +11,38 @@
#include "WorldGen.h"
#include "Entity.h"
#include "Resource.h"
#include "util.h"

namespace Swan {

class Mod {
public:
Mod(const std::string &path, SDL_Renderer *renderer): path_(path), renderer_(renderer) {}

void init(const std::string &name);

void registerImage(const std::string &name, const std::string &path, int frame_height = -1);

void registerTile(const Tile::Builder &tile);
void registerItem(const Item::Builder &item);
void registerTile(Tile::Builder tile);
void registerItem(Item::Builder item);
void registerWorldGen(const std::string &name, std::unique_ptr<WorldGen::Factory> gen);
void registerEntity(const std::string &name, std::unique_ptr<Entity::Factory> ent);

std::string name_;
std::string path_;
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";

std::unordered_map<std::string, std::unique_ptr<ImageResource>> images_;
std::unordered_map<std::string, Tile::Builder> tiles_;
std::unordered_map<std::string, Item::Builder> items_;
std::unordered_map<std::string, std::unique_ptr<WorldGen::Factory>> worldgens_;
std::unordered_map<std::string, std::unique_ptr<Entity::Factory>> entities_;
private:
std::vector<std::unique_ptr<ImageResource>> images_;
std::vector<Tile::Builder> tiles_;
std::vector<Item::Builder> items_;
std::vector<std::unique_ptr<WorldGen::Factory>> worldgens_;
std::vector<std::unique_ptr<Entity::Factory>> entities_;

std::string path_;
SDL_Renderer *renderer_;
bool inited_ = false;
};

+ 21
- 0
libswan/include/swan/OS.h View File

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

#include <string>

namespace Swan {
namespace OS {

class Dynlib {
public:
Dynlib(const std::string &path);
~Dynlib();

template<typename T> T get(const std::string &name) { return (T)getVoid(name); }
void *getVoid(const std::string &name);

private:
void *handle_;
};

}
}

+ 16
- 3
libswan/include/swan/Resource.h View File

@@ -4,6 +4,7 @@
#include <stdint.h>
#include <string>
#include <memory>
#include <unordered_map>

#include "common.h"

@@ -18,9 +19,10 @@ public:
SDL_Renderer *renderer, const std::string &name,
int w, int h, uint8_t r, uint8_t g, uint8_t b);


void tick(float dt);

static std::unique_ptr<ImageResource> createInvalid(Context &ctx);

std::unique_ptr<SDL_Surface, void (*)(SDL_Surface *)> surface_{nullptr, &SDL_FreeSurface};
std::unique_ptr<SDL_Texture, void (*)(SDL_Texture *)> texture_{nullptr, &SDL_DestroyTexture};
int frame_height_;
@@ -28,11 +30,22 @@ public:
std::string name_;
int frame_ = 0;

static std::unique_ptr<ImageResource> createInvalid(Context &ctx);

private:
float switch_interval_ = 1;
float switch_timer_ = switch_interval_;
};

class ResourceManager {
public:
void tick(float dt);

ImageResource &getImage(const std::string &name) const;
void addImage(std::string name, std::unique_ptr<ImageResource> img);

std::unique_ptr<ImageResource> invalid_image_;

private:
std::unordered_map<std::string, std::unique_ptr<ImageResource>> images_;
};

}

+ 3
- 2
libswan/include/swan/Tile.h View File

@@ -10,6 +10,7 @@

namespace Swan {

// TODO: Switch to struct
class Tile {
public:
using ID = uint16_t;
@@ -21,8 +22,8 @@ public:
std::optional<std::string> dropped_item = std::nullopt;
};

Tile(const ImageResource &image, const std::string &mod, const Builder &builder):
name_(mod+"::"+builder.name), image_(image),
Tile(const ResourceManager &resources, const Builder &builder):
name_(builder.name), image_(resources.getImage(builder.image)),
is_solid_(builder.is_solid), dropped_item_(builder.dropped_item) {}

const std::string name_;

+ 19
- 15
libswan/include/swan/World.h View File

@@ -12,6 +12,7 @@
#include "WorldGen.h"
#include "Entity.h"
#include "Resource.h"
#include "Mod.h"

namespace Swan {

@@ -21,33 +22,36 @@ class World {
public:
World(Game *game, unsigned long rand_seed): game_(game), random_(rand_seed) {}

WorldPlane &addPlane(const std::string &gen);
WorldPlane &addPlane() { return addPlane(default_world_gen_); }
void setCurrentPlane(WorldPlane &plane);
void addMod(std::unique_ptr<Mod> mod);
void setWorldGen(const std::string &gen);
void spawnPlayer();
void registerTile(std::shared_ptr<Tile> t);
void registerItem(std::shared_ptr<Item> i);
void registerWorldGen(std::shared_ptr<WorldGen::Factory> gen);
void registerEntity(std::shared_ptr<Entity::Factory> ent);
void registerImage(std::shared_ptr<ImageResource> i);

void setCurrentPlane(WorldPlane &plane);
WorldPlane &addPlane(const std::string &gen);
WorldPlane &addPlane() { return addPlane(default_world_gen_); }

Tile &getTileByID(Tile::ID id);
Tile::ID getTileID(const std::string &name);
Tile &getTile(const std::string &name);
Item &getItem(const std::string &name);
ImageResource &getImage(const std::string &name);

void draw(Win &win);
void update(float dt);
void tick(float dt);

std::vector<std::shared_ptr<Tile>> tiles_;
std::map<std::string, Tile::ID> tiles_map_;
std::map<std::string, std::shared_ptr<Item>> items_;
std::map<std::string, std::shared_ptr<WorldGen::Factory>> worldgens_;
std::map<std::string, std::shared_ptr<Entity::Factory>> ents_;
std::map<std::string, std::shared_ptr<ImageResource>> images_;
std::vector<std::unique_ptr<Mod>> mods_;

// World owns tiles and items, the mod just has Builder objects
std::vector<std::unique_ptr<Tile>> tiles_;
std::unordered_map<std::string, Tile::ID> tiles_map_;
std::unordered_map<std::string, std::unique_ptr<Item>> items_;

// The mods themselves retain ownership of world gens and entities,
// the world just has non-owning pointers to them
std::unordered_map<std::string, WorldGen::Factory *> worldgens_;
std::unordered_map<std::string, Entity::Factory *> ents_;

ResourceManager resources_;
Entity *player_;
Game *game_;


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

@@ -19,11 +19,13 @@ class Game;
class World;
class WorldPlane;
class Win;
class ResourceManager;

struct Context {
Game &game;
World &world;
WorldPlane &plane;
ResourceManager &resources;
};

}

+ 71
- 0
libswan/include/swan/util.h View File

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

#include <optional>
#include <functional>

namespace Swan {

// Ret can't be a reference, because C++ doesn't support optional<T&>.
template<typename Ret, typename Func = std::function<std::optional<Ret>()>>
class Iter {
public:
class It {
public:
It(std::optional<Ret> next, Func &func): next_(std::move(next)), func_(func) {}

bool operator==(It &other) {
return next_ == std::nullopt && other.next_ == std::nullopt;
}

bool operator!=(It &other) {
return !(*this == other);
}

void operator++() {
next_ = std::move(func_());
}

Ret operator*() {
return std::move(*next_);
}

private:
std::optional<Ret> next_;
Func &func_;
};

Iter(Func func): func_(func) {}

operator Iter<Ret, std::function<std::optional<Ret>()>>() {
return Iter<Ret, std::function<std::optional<Ret>()>>(func_);
}

It begin() {
return It(func_(), func_);
}

It end() {
return It(std::nullopt, func_);
}

private:
Func func_;
};

template<typename InputIterator, typename Func>
auto map(InputIterator first, InputIterator last, Func func) {
using RetT = decltype(func(*first));

auto l = [=]() mutable -> std::optional<RetT> {
if (first == last)
return std::nullopt;

RetT r = func(*first);
first++;
return r;
};

return Iter<RetT, decltype(l)>(l);
}

}

+ 20
- 2
libswan/src/Game.cc View File

@@ -1,18 +1,36 @@
#include "Game.h"

#include <dlfcn.h>
#include <math.h>
#include <time.h>
#include <memory>

#include "Tile.h"
#include "OS.h"
#include "Win.h"

namespace Swan {

void Game::createWorld(const std::string &worldgen) {
std::unique_ptr<Mod> Game::loadMod(const std::string &path) {
OS::Dynlib dl(path + "/mod");

auto init = dl.get<void (*)(Swan::Mod &)>("mod_init");
if (init == NULL) {
fprintf(stderr, "%s: No 'mod_init' function!\n", path.c_str());
return NULL;
}

std::unique_ptr<Mod> mod = std::make_unique<Mod>(path, win_.renderer_);
init(*mod);
return mod;
}

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

for (auto &mod: mods) {
world_->addMod(std::move(mod));
}

world_->setWorldGen(worldgen);
world_->setCurrentPlane(world_->addPlane());
world_->spawnPlayer();

+ 3
- 3
libswan/src/Item.cc View File

@@ -7,9 +7,9 @@
namespace Swan {

std::unique_ptr<Item> Item::createInvalid(Context &ctx) {
return std::make_unique<Item>(*ctx.game.invalid_image_, "@internal", Builder{
.name = "invalid",
.image = "invalid",
return std::make_unique<Item>(ctx.resources, Builder{
.name = "@internal::invalid",
.image = "@internal::invalid",
});
}


+ 40
- 11
libswan/src/Mod.cc View File

@@ -1,6 +1,9 @@
#include "Mod.h"

#include <stdio.h>
#include <algorithm>

#include "util.h"

namespace Swan {

@@ -12,31 +15,57 @@ void Mod::init(const std::string &name) {
}

void Mod::registerImage(const std::string &name, const std::string &path, int frame_height) {
images_[name] = std::make_unique<ImageResource>(
renderer_, name_ = "::" + name, path, frame_height);
fprintf(stderr, "Adding image: %s\n", images_[name]->name_.c_str());
images_.push_back(std::make_unique<ImageResource>(
renderer_, name_ + "::" + name, path_ + "/assets/" + path, frame_height));
fprintf(stderr, "Adding image: %s\n", (name_ + "::" + name).c_str());
}

void Mod::registerTile(const Tile::Builder &tile) {
tiles_[tile.name] = tile;
fprintf(stderr, "Adding tile: %s::%s\n", name_.c_str(), tile.name.c_str());
void Mod::registerTile(Tile::Builder tile) {
tile.name = name_ + "::" + tile.name;
tiles_.push_back(tile);
fprintf(stderr, "Adding tile: %s\n", tile.name.c_str());
}

void Mod::registerItem(const Item::Builder &item) {
items_[item.name] = item;
fprintf(stderr, "Adding item: %s::%s\n", name_.c_str(), item.name.c_str());
void Mod::registerItem(Item::Builder item) {
item.name = name_ + "::" + item.name;
items_.push_back(item);
fprintf(stderr, "Adding item: %s\n", item.name.c_str());
}

void Mod::registerWorldGen(const std::string &name, std::unique_ptr<WorldGen::Factory> gen) {
gen->name_ = name_ + "::" + name;
fprintf(stderr, "Adding world gen: %s\n", gen->name_.c_str());
worldgens_[name] = std::move(gen);
worldgens_.push_back(std::move(gen));
}

void Mod::registerEntity(const std::string &name, std::unique_ptr<Entity::Factory> ent) {
ent->name_ = name_ + "::" + name;
fprintf(stderr, "Adding entity: %s\n",ent->name_.c_str());
entities_[name] = std::move(ent);
entities_.push_back(std::move(ent));
}

Iter<std::unique_ptr<Tile>> Mod::buildTiles(const ResourceManager &resources) {
return map(begin(tiles_), end(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) {
return std::make_unique<Item>(resources, builder);
});
}

Iter<WorldGen::Factory *> Mod::getWorldGens() {
return map(begin(worldgens_), end(worldgens_), [](const std::unique_ptr<WorldGen::Factory> &gen) {
return gen.get();
});
}

Iter<Entity::Factory *> Mod::getEntities() {
return map(begin(entities_), end(entities_), [](const std::unique_ptr<Entity::Factory> &ent){
return ent.get();
});
}

}

+ 26
- 0
libswan/src/OS.cc View File

@@ -0,0 +1,26 @@
#include "OS.h"

#include <stdexcept>
#include <unistd.h>
#include <dlfcn.h>

namespace Swan {
namespace OS {

Dynlib::Dynlib(const std::string &path) {
handle_ = dlopen((path + ".so").c_str(), RTLD_LAZY);
if (handle_ == NULL) {
throw std::runtime_error(dlerror());
}
}

Dynlib::~Dynlib() {
dlclose(handle_);
}

void *Dynlib::getVoid(const std::string &name) {
return dlsym(handle_, name.c_str());
}

}
}

+ 13
- 0
libswan/src/Resource.cc View File

@@ -67,4 +67,17 @@ std::unique_ptr<ImageResource> ImageResource::createInvalid(Context &ctx) {
PLACEHOLDER_RED, PLACEHOLDER_GREEN, PLACEHOLDER_BLUE);
}

void ResourceManager::tick(float dt) {
for (auto &[k, v]: images_) {
v->tick(dt);
}
}

ImageResource &ResourceManager::getImage(const std::string &name) const {
auto it = images_.find(name);
if (it == end(images_))
return *invalid_image_;
return *it->second;
}

}

+ 3
- 3
libswan/src/Tile.cc View File

@@ -8,9 +8,9 @@ namespace Swan {
Tile::ID Tile::INVALID_ID = 0;

std::unique_ptr<Tile> Tile::createInvalid(Context &ctx) {
return std::make_unique<Tile>(*ctx.game.invalid_image_, "@internal", Builder{
.name = "invalid",
.image = "invalid",
return std::make_unique<Tile>(ctx.resources, Builder{
.name = "@internal::invalid",
.image = "@internal::invalid",
});
}


+ 33
- 40
libswan/src/World.cc View File

@@ -9,6 +9,7 @@ static bool chunkLine(int l, WorldPlane &plane, ChunkPos &abspos, const Vec2i &d
for (int i = 0; i < l; ++i) {
plane.getChunk(abspos);

// TODO: Fix this with a non-SFML clock implementation
// Don't blow our frame budget on generating chunks,
// but generate as many as possible within the budget
//if (clock.getElapsedTime().asSeconds() > 1.0 / 100)
@@ -32,48 +33,53 @@ void World::ChunkRenderer::tick(WorldPlane &plane, ChunkPos abspos) {
}
}

void World::setCurrentPlane(WorldPlane &plane) {
current_plane_ = plane.id_;
}
void World::addMod(std::unique_ptr<Mod> mod) {
printf("World: Adding mod %s\n", mod->name_.c_str());

void World::setWorldGen(const std::string &gen) {
default_world_gen_ = gen;
}
for (auto t: mod->buildTiles(resources_)) {
Tile::ID id = tiles_.size();
tiles_map_[t->name_] = id;
tiles_.push_back(std::move(t));
}

void World::spawnPlayer() {
player_ = &planes_[current_plane_].spawnPlayer();
}
for (auto i: mod->buildItems(resources_)) {
items_[i->name_] = std::move(i);
}

void World::registerTile(std::shared_ptr<Tile> t) {
Tile::ID id = tiles_.size();
tiles_map_[t->name_] = id;
tiles_.push_back(std::move(t));
}
for (auto *gen: mod->getWorldGens()) {
worldgens_[gen->name_] = gen;
}

for (auto *ent: mod->getEntities()) {
ents_[ent->name_] = ent;
}

void World::registerItem(std::shared_ptr<Item> i) {
items_[i->name_] = std::move(i);
mods_.push_back(std::move(mod));
}

void World::registerWorldGen(std::shared_ptr<WorldGen::Factory> gen) {
worldgens_[gen->name_] = std::move(gen);
void World::setWorldGen(const std::string &gen) {
default_world_gen_ = gen;
}

void World::registerEntity(std::shared_ptr<Entity::Factory> ent) {
ents_[ent->name_] = std::move(ent);
void World::spawnPlayer() {
player_ = &planes_[current_plane_].spawnPlayer();
}

void World::registerImage(std::shared_ptr<ImageResource> i) {
images_[i->name_] = std::move(i);
void World::setCurrentPlane(WorldPlane &plane) {
current_plane_ = plane.id_;
}

ImageResource &World::getImage(const std::string &name) {
auto iter = images_.find(name);
if (iter == images_.end()) {
fprintf(stderr, "Tried to get non-existant asset ''%s'!\n", name.c_str());
WorldPlane &World::addPlane(const std::string &gen) {
WorldPlane::ID id = planes_.size();
if (worldgens_.find(gen) == worldgens_.end()) {
fprintf(stderr, "Tried to add plane with non-existant world gen '%s'!\n",
gen.c_str());
abort();
}

return *iter->second;
WorldGen *g = worldgens_[gen]->create(*this);
planes_.push_back(WorldPlane(id, this, std::shared_ptr<WorldGen>(g)));
return planes_[id];
}

Item &World::getItem(const std::string &name) {
@@ -105,19 +111,6 @@ Tile &World::getTile(const std::string &name) {
return getTileByID(id);
}

WorldPlane &World::addPlane(const std::string &gen) {
WorldPlane::ID id = planes_.size();
if (worldgens_.find(gen) == worldgens_.end()) {
fprintf(stderr, "Tried to add plane with non-existant world gen '%s'!\n",
gen.c_str());
abort();
}

WorldGen *g = worldgens_[gen]->create(*this);
planes_.push_back(WorldPlane(id, this, std::shared_ptr<WorldGen>(g)));
return planes_[id];
}

void World::draw(Win &win) {
auto bounds = *player_->getBounds();
win.cam_ = bounds.pos - (win.getSize() / 2) + (bounds.size / 2);

+ 1
- 1
libswan/src/WorldPlane.cc View File

@@ -27,7 +27,7 @@ static Chunk::RelPos relPos(TilePos pos) {
}

Context WorldPlane::getContext() {
return { .game = *world_->game_, .world = *world_, .plane = *this };
return { .game = *world_->game_, .world = *world_, .plane = *this, .resources = world_->resources_ };
}

Entity &WorldPlane::spawnEntity(const std::string &name, const SRF &params) {

+ 3
- 0
libswan/src/util.cc View File

@@ -0,0 +1,3 @@
#include "util.h"

// This file is intentionally left blank.

+ 3
- 0
src/main.cc View File

@@ -55,6 +55,9 @@ int main() {
Win win(renderer.get());

Game game(win);
std::vector<std::unique_ptr<Mod>> mods;
mods.push_back(game.loadMod("core.mod"));
game.createWorld("core::default", std::move(mods));

auto prevTime = std::chrono::steady_clock::now();


Loading…
Cancel
Save