<file_sep>GEDI - Game Engine DIstribuído
==============================
__Gedi__ é um motor de jogos que usa técnicas de computação distribuída para
conversar o núcleo com os módulos. Módulos podem usar computação distribuída
dentro de si também, mas não é necessário.


Mais infos
----------
Leia os documentos de ideias e afins da pasta [ideias](ideias/index.md)
<file_sep>#include "Module.hpp"
#include "GameObject.hpp"
#include "Exception.hpp"
#include <unordered_map>
#include <utility>
#include <cstdint>
#include <SFML/Graphics.hpp>

using namespace std;
using namespace zmq;

struct Ispraite {
	sf::Drawable *drawable;
	sf::Shader *shader {nullptr};

	Ispraite (sf::Drawable *d) : drawable (d) {}
	~Ispraite () {
		delete drawable;
	}

	void draw (sf::RenderTarget & target) {
		target.draw (*drawable, shader ? shader : sf::RenderStates::Default);
	}
};

using ObjMap = unordered_map<ID, Ispraite *>;
using TextureMap = unordered_map<string, sf::Texture>;
using ShaderMap = unordered_map<string, sf::Shader *>;

extern "C" void abre (context_t * ctx, const char * endereco);

int main () {
	context_t ctx;
	abre (&ctx, "ipc://teste");
	return 0;
}

extern "C" void abre (context_t * ctx, const char * endereco) {
	// módulo do gráfico
	Module M (*ctx, endereco);
	ObjMap objetos;
	TextureMap texturas;
	ShaderMap shaders;

	sf::RenderWindow window;
	//--  Sai da janela  --//
	M.on ("quit", [&] (Arguments & args) {
		window.close ();
		M.close ();
	});
	M.on ("open", [&] (Arguments & args) {
		M.sync ();
	});
	//--  Criação/instanciação  --//
	M.on ("window", [&] (Arguments & args) {
		int width = args.as<int> (0);
		int height = args.as<int> (1);
		string title = args.asDefault<string> (2, "Teste");
		window.create (sf::VideoMode (width, height), title);
		window.display ();
	});
	M.on ("texture", [&] (Arguments & args) {
		auto nome = args.as<string> (0);
		auto arquivo = args.as<string> (1);
		sf::Texture tex;
		if (!tex.loadFromFile (arquivo)){
			throw runtime_error ("[grafico::texture] Erro ao carregar textura \""
					+ arquivo + "\"");
		}
		texturas.insert (make_pair (move (nome), move (tex)));
	});
	//--  Criação de objetos, com ID  --//
	M.on ("sprite", [&] (Arguments & args) {
		auto id = args.as<ID> (0);
		auto nomeTextura = args.as<string> (1);
		const auto & tex = texturas.at (nomeTextura);
		auto sprite = new sf::Sprite (tex);
		objetos.emplace (id, new Ispraite (sprite));
	});
	//--  Transformables  --//
	M.on ("setOrigin", [&] (Arguments & args) {
		auto id = args.as<ID> (0);
		if (auto * obj = dynamic_cast<sf::Transformable *> (objetos.at (id)->drawable)) {
			int x = args.as<int> (1);
			int y = args.as<int> (2);
			obj->setOrigin (x, y);
		}
	});
	M.on ("setPosition", [&] (Arguments & args) {
		auto id = args.as<ID> (0);
		if (auto * obj = dynamic_cast<sf::Transformable *> (objetos.at (id)->drawable)) {
			double x = args.as<double> (1);
			double y = args.as<double> (2);
			obj->setPosition (x, y);
		}
	});
	M.on ("setRotation", [&] (Arguments & args) {
		auto id = args.as<ID> (0);
		if (auto * obj = dynamic_cast<sf::Transformable *> (objetos.at (id)->drawable)) {
			double angulo = args.as<double> (1);
			obj->setRotation (angulo);
		}
	});
	M.on ("setScale", [&] (Arguments & args) {
		auto id = args.as<ID> (0);
		if (auto * obj = dynamic_cast<sf::Transformable *> (objetos.at (id)->drawable)) {
			double sx = args.as<double> (1);
			double sy = args.as<double> (2);
			obj->setScale (sx, sy);
		}
	});
	M.on ("move", [&] (Arguments & args) {
		auto id = args.as<ID> (0);
		if (auto * obj = dynamic_cast<sf::Transformable *> (objetos.at (id)->drawable)) {
			double x = args.as<double> (1);
			double y = args.as<double> (2);
			obj->move (x, y);
		}
	});
	M.on ("rotate", [&] (Arguments & args) {
		auto id = args.as<ID> (0);
		if (auto * obj = dynamic_cast<sf::Transformable *> (objetos.at (id)->drawable)) {
			double angulo = args.as<double> (1);
			obj->rotate (angulo);
		}
	});
	M.on ("scale", [&] (Arguments & args) {
		auto id = args.as<ID> (0);
		if (auto * obj = dynamic_cast<sf::Transformable *> (objetos.at (id)->drawable)) {
			double sx = args.as<double> (1);
			double sy = args.as<double> (2);
			obj->scale (sx, sy);
		}
	});
	//--  Shaders  --//
	M.on ("shader", [&] (Arguments & args) {
		auto nome = args.as<string> (0);
		auto arquivo1 = args.as<string> (1);
		auto arquivo2 = args.as<string> (2);
		auto *shader = new sf::Shader;
		if (arquivo2 == "vertex") {
			shader->loadFromFile (arquivo1, sf::Shader::Type::Vertex);
		}
		else if (arquivo2 == "fragment") {
			shader->loadFromFile (arquivo1, sf::Shader::Type::Fragment);
		}
		else if (arquivo2 == "geometry") {
			shader->loadFromFile (arquivo1, sf::Shader::Type::Geometry);
		}
		else if (args.size () > 3) {
			shader->loadFromFile (arquivo1, arquivo2, args.as<string> (3));
		}
		else {
			shader->loadFromFile (arquivo1, arquivo2);
		}
		shaders.emplace (move (nome), shader);
	});
	M.on ("attach", [&] (Arguments & args) {
		auto id = args.as<ID> (0);
		auto shader = args.as<string> (1);
		objetos.at (id)->shader = shaders.at (shader);
	});
	M.on ("unattach", [&] (Arguments & args) {
		auto id = args.as<ID> (0);
		objetos.at (id)->shader = nullptr;
	});
	//--  Controle  --//
	M.on ("draw", [&] (Arguments & args) {
		window.clear ();
		for (auto & it : objetos) {
			it.second->draw (window);
		}
		window.display ();
	});
	bool didClose = false; // bool que conta se fechou janela, pega no 'input' e checa no 'didClose'
	M.on ("input", [&] (Arguments & args) {
		static sf::Event ev;
		while (window.pollEvent (ev)) {
			switch (ev.type) {
				case sf::Event::Closed:
					didClose = true;
					break;

				default:
					break;
			}
		}
	});
	M.on ("didClose", [&] (Arguments & args) {
		M.respond (didClose);
	});

	while (M.isOpen ()) {
		M.handle ();
	}

	// limpa os rolê
	for (auto & it : objetos) {
		delete it.second;
	}
	for (auto & it : shaders) {
		delete it.second;
	}
}
<file_sep>#!/usr/bin/env lua

soma = 0
n = 0

for l in io.lines () do
	local inc = tonumber (l)
	if inc then
		soma = soma + inc
		n = n + 1
	else
		print (l)
	end
end
print ('FPS médio:', n / soma)
<file_sep>/// Teste de um ModuleManager, que conecta com os módulos e talz
#pragma once

#include "ModuleConnection.hpp"
#include "ModuleHandle.hpp"
#include <iostream>
#include <utility>
#include <cstring>
#include <unordered_map>
#include <zmq.hpp>
#include <yaml-cpp/yaml.h>

using namespace zmq;
using namespace std;

class ModuleManager {
public:
	/// Ctor
	ModuleManager () {}

	/// Dtor
	~ModuleManager () {
		ModuleHandle h;
		for (auto & it : modulos) {
			h.set (it.second);
			cout << "Mandando quit pro \"" << it.first << '"' << endl;
			h ("quit").resp ();
			
			delete it.second;
		}
	}

	void readConfig (const string & fname) {
		auto config = YAML::LoadFile (fname);
		for (auto mod : config) {
			auto nome = mod.first.as<string> ();
			cout << "Achei módulo: " << nome << endl;
			auto opts = mod.second;
			auto endereco = opts["endereco"].as<string> ();
			// Tenta ler arquivo de config específico do módulo, que é melhor né
			try {
				ostringstream os;
				if (const char *prefix = getenv ("GEDI_MODULE_PATH")) {
					os << prefix << '/';
				}
				os << nome << '/' << nome << ".yml";
				opts = YAML::LoadFile (os.str ());
				cout << "\tUsando config específica do módulo" << endl;
			} catch (...) {}
			// e conecta
			ModuleHandle h;
			if (strncmp (endereco.data (), "inproc", 6) == 0 && opts["thread"]) {
				cout << "\tCriando novo thread pro módulo" << endl;
				auto no = opts["thread"];
				h = threadedConnection (nome, endereco, no["lib"].as<string> ()
						, no["open"].as<string> ());
			}
			else if (strncmp (endereco.data (), "ipc", 3) == 0 || opts["spawn"]) {
				cout << "\tRodando comando do módulo" << endl;
				h = spawnConnection (nome, endereco, opts["spawn"].as<string> ());
			}
			else {
				cout << "\tAguardando conexão" << endl;
				h = addConnection (nome, endereco);
			}
			// sincroniza com o novo módulo
			h ("open").sync ();
		}
	}

	/// Adiciona uma conexão com módulo
	ModuleHandle addConnection (const string & nome, const string & conexao) {
		auto it = modulos.emplace (nome, new ModuleConnection (ctx, conexao)).first;
		return move (ModuleHandle (it->second));
	}

	/// Spawna uma conexão com módulo
	ModuleHandle spawnConnection (const string & nome, const string & conexao
			, const string & comando) {
		auto it = modulos.emplace (nome, new ModuleConnectionProcess (ctx, conexao, comando)).first;
		return move (ModuleHandle (it->second));
	}

	ModuleHandle threadedConnection (const string & nome, const string & conexao
			, const string & nomeLib, const string & simbolo) {
		auto it = modulos.emplace (nome, new ModuleConnectionThread (ctx, conexao, nomeLib, simbolo)).first;
		return move (ModuleHandle (it->second));
	}

	/// Pega módulo referente ao nome dado, nullptr se não existir
	ModuleHandle get (const string & nome) {
		auto it = modulos.find (nome);
		if (it == modulos.end ()) {
			throw GEDI_API_EXCEPTION ("ModuleManager", "Não achei módulo de nome \""
					+ nome + "\" =/");
		}
		return move (ModuleHandle (it->second));
	}

private:
	/// Context
	context_t ctx;

	/// Mapa dos módulos
	unordered_map<string, ModuleConnection *> modulos;
};
<file_sep>#pragma once

#include <iostream>
using namespace std;

/// Vetor 2d =]
struct Vec2 {
	float x, y;

	/// Ctor, com parâmetros
	Vec2 (float x = 0, float y = 0) : x (x), y (y) {}
	/// Operações básicas
	Vec2 operator+ (const Vec2 & outro) const {
		return Vec2 (x + outro.x, y + outro.y);
	}
	Vec2 operator- (const Vec2 & outro) const {
		return Vec2 (x - outro.x, y - outro.y);
	}
	Vec2 & operator+= (const Vec2 & outro) {
		x += outro.x;
		y += outro.y;
		return *this;
	}
	Vec2 & operator-= (const Vec2 & outro) {
		x -= outro.x;
		y -= outro.y;
		return *this;
	}
};

ostream& operator<< (ostream& os, const Vec2 & v) {
	os << '(' << v.x << ',' << v.y << ')';
	return os;
}
<file_sep>#include "Module.hpp"
#include "Exception.hpp"
#include <unordered_map>
#include <SFML/Audio.hpp>

using namespace std;
using namespace zmq;

using MusicMap = unordered_map<string, sf::Music *>;
using SoundMap = unordered_map<string, sf::Sound>;
using BufferMap = unordered_map<string, sf::SoundBuffer>;

extern "C" void abre (context_t * ctx, const char * endereco);

int main () {
	context_t ctx;
	abre (&ctx, "ipc://gedi_audio");
	return 0;
}

extern "C" void abre (context_t * ctx, const char * endereco) {
	// módulo do audio
	Module M (*ctx, endereco);
	MusicMap musicas;
	SoundMap sons;
	BufferMap buffers;

	M.on ("open", [&] (Arguments & args) {
		M.sync ();
	});
	M.on ("quit", [&] (Arguments & args) {
		M.close ();
	});
	//--  Música  --//
	M.on ("music", [&] (Arguments & args) {
		auto nome = args.as<string> (0);
		auto arquivo = args.as<string> (1);

		auto novaMusica = new sf::Music;
		novaMusica->openFromFile (arquivo);
		musicas.emplace (move (nome), novaMusica);
	});
	M.on ("mplay", [&] (Arguments & args) {
		auto nome = args.as<string> (0);
		musicas.at (nome)->play ();
	});
	M.on ("mstop", [&] (Arguments & args) {
		auto nome = args.as<string> (0);
		musicas.at (nome)->stop ();
	});
	M.on ("mpause", [&] (Arguments & args) {
		auto nome = args.as<string> (0);
		musicas.at (nome)->pause ();
	});
	M.on ("setLoop", [&] (Arguments & args) {
		auto nome = args.as<string> (0);
		auto loop = args.as<bool> (1);
		musicas.at (nome)->setLoop (loop);
	});
	//--  Buffer pros sonzinhos  --//
	M.on ("buffer", [&] (Arguments & args) {
		auto nome = args.as<string> (0);
		auto arquivo = args.as<string> (1);

		sf::SoundBuffer novoBuffer;
		novoBuffer.loadFromFile (arquivo);
		buffers.emplace (move (nome), novoBuffer);
	});
	//--  Sonzinhos  --//
	M.on ("sound", [&] (Arguments & args) {
		auto nome = args.as<string> (0);
		auto buffer = args.as<string> (1);

		sf::Sound novoSom;
		novoSom.setBuffer (buffers.at (buffer));
		sons.emplace (move (nome), novoSom);
	});
	M.on ("splay", [&] (Arguments & args) {
		auto nome = args.as<string> (0);
		sons.at (nome).play ();
	});
	M.on ("sstop", [&] (Arguments & args) {
		auto nome = args.as<string> (0);
		sons.at (nome).stop ();
	});
	M.on ("spause", [&] (Arguments & args) {
		auto nome = args.as<string> (0);
		sons.at (nome).pause ();
	});

	while (M.isOpen ()) {
		M.handle ();
	}
	// apaga os rolê
	for (auto & it : musicas) {
		delete it.second;
	}
}
<file_sep>Game Engine DIstribuído - GEDI
==============================

O quê?
------
Um motor de jogos que usa técnicas de computação distribuída para conversar o
núcleo com os módulos. Módulos podem usar computação distribuída dentro de si
também, mas não é necessário.


Por quê?
--------
Flexibilidade.

A comunicação entre o núcleo e os módulos feita via _sockets_ abstrai o modo
como as coisas são implementadas, forçando somente especificação de protocolo
de chamada. Desse modo, módulos (ou mesmo o núcleo) podem ser implementados em
qualquer linguagem de programação, podem diferir entre plataformas (SO,
processador), podem usar quaisquer tecnologias que convier, podem residir em
diferentes máquinas, etc...

Além disso, módulos podem ser facilmente ligados ou desligados ao núcleo,
diminuindo as dependências de cada jogo e facilitando a criação,
compartilhamento e testes de diferentes módulos, ou mesmo diferentes versões de
um mesmo módulo. Um sistema gerenciador de pacotes para módulos pode/será
feito, para facilitar a instalação de módulos e não precisar de várias cópias
do mesmo módulo no mesmo ambiente.


Pesquisa
--------
A flexibilidade proporcionada pela arquitetura distribuída possibilita uma
facilidade na pesquisa de algoritmos novos ou implementações diferentes de tais
algoritmos.


Performance
-----------
Chamadas de procedimento remoto (RPC) são mais demoradas e imprevisíveis. Pra
que o __Gedi__ funcione, algum paranauê vai ter que rolar pra não ficar muito
lento, talvez assincronia e talz.


Segurança
---------
Comunicação por sockets podem ser interceptadas e talz. Criar segurança nisso
talvez deixe as coisas muito lentas, o que é ruim pra jogos. Como a maioria das
comunicações serão locais, ou provavelmente LANs, acho que é de boa não fazer.


API
---
Leia sobre a até então pensada [API](api.md)
<file_sep>#pragma once

#include <sstream>
#include <exception>

using namespace std;

class Exception : public exception {
public:
	Exception (const string & what_arg) : what_arg (what_arg) {}

	Exception (const char * what_arg) : what_arg (what_arg) {}

	const char * what () const noexcept override {
		return what_arg.c_str ();
	}
private:
	string what_arg;
};

#define GEDI_API_EXCEPTION(funcName, what) \
	[&] () -> Exception { \
		ostringstream os; \
		os << "[gedi::" << funcName << " @ " << __FILE__ << ':' << __LINE__ \
				<< "] " << what; \
		return move (Exception (move (os.str ()))); \
	} ()
<file_sep>#include "ModuleHandle.hpp"
#include "ModuleManager.hpp"
#include "GameObject.hpp"

#include <iostream>
#include <chrono>
using namespace std;

constexpr double largura = 800;
constexpr double altura = 600;

int main () {
	// conta os tempo dos frames
	chrono::time_point<chrono::system_clock> inicio, fim;

	ModuleManager mgr;
	mgr.readConfig ("gedimods.yml");

	auto audio = mgr.get ("audio");
	audio ("music", "fundo", "musica.ogg");
	audio ("setLoop", "fundo", true);
	audio ("mplay", "fundo");
	audio ("buffer", "shot", "shotgun.ogg");
	audio ("sound", "shot", "shot");
	audio ("splay", "shot");

	auto gfx = mgr.get ("grafico");
	gfx ("window", (int) largura, (int) altura, "Minha janela pocoto");

	// primeiro círculo
	GameObject primeiro;
	gfx ("texture", "flango", "flango.png");
	gfx ("sprite", primeiro, "flango");
	// gfx ("shader", "meuShader", "f.glsl", "fragment");
	// gfx ("attach", primeiro, "meuShader");
	gfx ("move", primeiro, largura / 2, altura / 2);
	gfx ("setOrigin", primeiro, 71, 105);

	bool fechou = false;
	chrono::duration<double> timeDiff;
	double ang = 0, delta = 0;
	while (!fechou) {
		inicio = chrono::system_clock::now ();
		gfx ("input");
		double sc = 1.5 + sin (ang) / 2; ang += 0.001;
		gfx ("setScale", primeiro, sc, sc);
		gfx ("rotate", primeiro, 10 * delta);
		gfx ("draw");
		fechou = gfx ("didClose").resp ().as<bool> (0);
		// verifica o tempo
		fim = chrono::system_clock::now ();
		timeDiff = fim - inicio;
		delta = timeDiff.count ();
		cout << delta << endl;
	}

	return 0;
}
<file_sep>#include <SFML/Graphics.hpp>
#include <cmath>
#include <iostream>
#include <chrono>
using namespace std;

constexpr int largura = 800;
constexpr int altura = 600;

int main () {
	// conta os tempo de tudo!
	chrono::time_point<chrono::system_clock> inicio, fim;

	sf::RenderWindow window (sf::VideoMode (largura, altura), "Minha janela pocoto");
	sf::Texture tex;
	tex.loadFromFile ("flango.png");

	sf::Sprite objeto (tex);
	objeto.move (largura / 2, altura / 2);
	objeto.setOrigin (71, 105);

	chrono::duration<double> timeDiff;
	double ang = 0;
	while (window.isOpen ()) {
		inicio = chrono::system_clock::now ();

		double sc = 1.5 + sin (ang) / 2; ang += 0.001;
		objeto.rotate (0.01);
		objeto.setScale (sc, sc);

		sf::Event ev;
		while (window.pollEvent (ev)) {
			switch (ev.type) {
				case sf::Event::Closed:
					window.close ();
					break;

				default:
					break;
			}
		}

		window.clear ();
		window.draw (objeto);
		window.display ();

		fim = chrono::system_clock::now ();
		timeDiff = fim - inicio;
		cout << timeDiff.count () << endl;
	}
}
<file_sep>#pragma once

#include "Vec2.hpp"
#include "Connection.hpp"

using ID = uintptr_t;

/// GameObject: o objeto na sua main que tem as infos gerais necessárias, diga-se
// um ID e seu transform
struct GameObject {
	Vec2 position;
	Vec2 rotation;
	const ID id { reinterpret_cast<ID> (this) };
};

BuffNSize getBuffer (const GameObject & x) {
	return getBuffer (x.id);
}
<file_sep>#!/usr/bin/env python

import sdl2 as sdl
import sdl2.ext as sdlx
import zmq
import time

class Conexao:
    def __init__ (self, título, tamanho):
        self.título = título
        self.win = sdlx.Window (título, tamanho)
        self.win.show ()
        self.time = time.perf_counter ()
        self.tempoTotal = 0
        self.i = 0

    def update (self):
        último = self.time
        self.time = time.perf_counter ()
        # desenha cinza
        sdlx.fill (self.win.get_surface (), sdlx.string_to_color ('#555'))
        sdl.SDL_UpdateWindowSurface (self.win.window)
        # e soma o tempo total
        self.tempoTotal += self.time - último
        self.i += 1

    def getFps (self):
        return self.i / self.tempoTotal


class ConnectionManager:
    """Um gerenciador de conexões pra esse módulo =P"""
    def __init__ (self):
        self.conexões = {}
        self.ctx = zmq.Context ()
        self.skt = self.ctx.socket (zmq.ROUTER)
        self.skt.bind ('tcp://*:5555')


    def create (self, id, nome = b'MeuModuloTeste', largura = 640, altura = 480):
        # se alguém pedir pra abrir uma janela nova, mata
        if self.conexões.get (id):
            del self.conexões[id]
        self.conexões[id] = Conexao (nome.decode ('utf-8'), (int (largura), int (altura)))
        print (id, 'conectou')

    def loop (self, id):
        # processa eventos
        events = sdlx.get_events ()
        for ev in events:
            if ev.type == sdl.SDL_QUIT:
                self.close (id)
                return
        # atualiza os fps e tela
        self.conexões[id].update ()

    def close (self, id):
        self.skt.send_multipart ([id, b'flws'])
        print (id, 'quitou. Média de FPS:', self.conexões[id].getFps ())
        del self.conexões[id]

def main ():
    cabou = False
    clientes = ConnectionManager ()

    while not cabou:
        msgs = clientes.skt.recv_multipart ()

        if msgs[1] == b'quit':
            cabou = True
        elif msgs[1] == b'create':
            clientes.create (msgs[0], *msgs[2:])
        elif clientes.conexões.get (msgs[0]):
            getattr (clientes, msgs[1].decode ('utf-8')) (msgs[0], *msgs[2:])
            
    sdlx.quit ()

if __name__ == '__main__':
    main ()
<file_sep>/// Conexão com módulos, tenha um só por módulo pliz
#pragma once

#include "Exception.hpp"
#include "Connection.hpp"
#include "Arguments.hpp"
#include <iostream>
#include <tuple>
#include <thread>
#include <zmq.hpp>
#include <cstdlib>
#include <cstring>
#include <glib.h>
#include <gmodule.h>

using namespace std;
using namespace zmq;

/// Conexão com o módulo de fato, sendo extendida para inicialização automática e talz
class ModuleConnection {
public:
	/// Ctor, com contexto zmq, endereço de conexão
	ModuleConnection (context_t & ctx, const string & endereco) : conn (ctx, ZMQ_DEALER) {
		conn.skt.connect (endereco);
	}

	virtual ~ModuleConnection () {}

	/// Chamar operação sem argumentos
	void call (const string & func) {
		conn.send (func);
	}
	/// Chamar operação com argumentos, transformando-os usando `getBuffer`
	template<typename ...Args>
	void call (const string & func, const Args & ... args) {
		conn.send (func, args...);
	}

	void fillResponse (Arguments & args) {
		args.recebeMensagens (conn.skt);
	}

private:
	/// O socket pra comunicar, sempre DEALER
	Connection conn;
};


/// Conexão com módulo que spawna o processo
class ModuleConnectionProcess : public ModuleConnection {
public:
	ModuleConnectionProcess (context_t & ctx, const string & endereco
			, const string & comando) : ModuleConnection (ctx, endereco) {
		GError * err = NULL;
		gchar * cmd = strdup (comando.data ());
		gchar * argv[] = { cmd, NULL };
		if (!g_spawn_async (NULL, argv, NULL, G_SPAWN_DEFAULT
				, NULL, NULL, &pid, &err)) {
			throw GEDI_API_EXCEPTION ("ModuleConnectionProcess", err->message);
		}
		free (cmd);
	}

	virtual ~ModuleConnectionProcess () {
		g_spawn_close_pid (pid);
	}
private:
	/// PID do processo, usado pra fechá-lo depois
	GPid pid;
};

/// Conexão com módulo que spawna um thread
class ModuleConnectionThread : public ModuleConnection {
	using gediopenFunc = void (*) (context_t *, const char *);
public:
	ModuleConnectionThread (context_t & ctx, const string & endereco
			, const string & nomeLib, const string & simbolo) : ModuleConnection (ctx, endereco) {
		lib = g_module_open (nomeLib.c_str (), G_MODULE_BIND_LAZY);
		// tenta pegar a função lá
		gediopenFunc openFunc;
		if (!g_module_symbol (lib, simbolo.c_str (), (gpointer *) &openFunc)) {
			auto ex = GEDI_API_EXCEPTION ("ModuleConnectionThread", g_module_error ());
			g_module_close (lib);
			throw ex;
		}

		T = thread (openFunc, &ctx, endereco.c_str ());
	}

	virtual ~ModuleConnectionThread () {
		const char * nomeLib = g_module_name (lib);
		cout << "Esperando módulo \"" << nomeLib << "\" terminar" << endl;
		T.join ();
		if (!g_module_close (lib)) {
			g_warning ("%s: %s\n", nomeLib, g_module_error ());
		}
	}
private:
	/// Módulo do glib da lib dinâmica
	GModule * lib;

	/// Thread onde tá rodando
	thread T;
};
<file_sep>#pragma once

#include <iostream>
#include <tuple>
#include <zmq.hpp>

using namespace zmq;

struct Vazio {};
const Vazio VAZIO;

using BuffNSize = pair<void *, size_t>;

BuffNSize getBuffer (const Vazio & v) {
	return make_pair (nullptr, 0);
}
BuffNSize getBuffer (const bool & b) {
	return make_pair ((void *) &b, 1);
}

BuffNSize getBuffer (const char * const & str) {
	return make_pair ((void *) str, strlen (str));
}

BuffNSize getBuffer (const string & str) {
	return make_pair ((void *) str.data (), str.size ());
}

BuffNSize getBuffer (const double & x) {
	return make_pair ((void *) &x, sizeof (double));
}

BuffNSize getBuffer (const int & x) {
	return make_pair ((void *) &x, sizeof (int));
}

BuffNSize getBuffer (const uintptr_t & x) {
	return make_pair ((void *) &x, sizeof (uintptr_t));
}

template<typename T>
BuffNSize getBuffer (const pair<size_t, T *> & P) {
	return make_pair (P.second, P.first * sizeof (T));
}


/// Wrapper pros sockets zmq, pra mandar/receber coisas mais fácil
class Connection {
public:
	/// Ctor, com contexto e tipo do socket ZMQ
	Connection (context_t & ctx, int type) : skt (ctx, type) {}

	~Connection () {
		skt.close ();
	}

	/// Caso base, mandando último argumento, sem mandar ZMQ_SNDMORE, fechando
	// mensagem
	template<class T>
	void send (const T & arg) {
		size_t size;
		void * buf;

		tie (buf, size) = getBuffer (arg);
		skt.send (buf, size);
	}

	/// Manda argumentos para o módulo, usando `getBuffer` pra mandar argumentos
	// direitim
	template<typename T, typename...Args>
	void send (const T & head, const Args & ... tail) {
		size_t size;
		void * buf;

		tie (buf, size) = getBuffer (head);
		skt.send (buf, size, ZMQ_SNDMORE);

		send (tail...);
	}

	socket_t skt;
};
<file_sep>/// Argumentos, pra pegar no Module
#pragma once

#include <string>
#include <vector>
#include <cstdint>
#include <zmq.hpp>

using namespace std;
using namespace zmq;

template<typename T>
T fromMessage (message_t & msg);

template<>
string fromMessage (message_t & msg) {
	return move (string (msg.data<char> (), msg.size ()));
}
template<>
double fromMessage (message_t & msg) {
	return (double) *msg.data<double> ();
}
template<>
int fromMessage (message_t & msg) {
	return (int) *msg.data<int> ();
}
template<>
uintptr_t fromMessage (message_t & msg) {
	return (uintptr_t) *msg.data<uintptr_t> ();
}
template<>
bool fromMessage (message_t & msg) {
	return (bool) *msg.data<bool> ();
}

class Arguments {
public:
	/// Ctor
	Arguments () {}
	/// Lê mensagens
	void recebeComando (socket_t & skt) {
		args.clear ();
		skt.recv (&id);
		skt.recv (&comando);
		while (skt.getsockopt<int> (ZMQ_RCVMORE)) {
			message_t msg;
			skt.recv (&msg);
			args.push_back (move (msg));
		}
	}
	void recebeMensagens (socket_t & skt) {
		args.clear ();
		do {
			message_t msg;
			skt.recv (&msg);
			args.push_back (move (msg));
		} while (skt.getsockopt<int> (ZMQ_RCVMORE));
	}
	/// Pega o comando
	string getComando () {
		return fromMessage<string> (comando);
	}
	/// Pega o ID de quem mandou, como string mesmo
	string getId () {
		return fromMessage<string> (id);
	}
	/// Pega mensagem idx como um T
	template<typename T>
	T as (int idx) {
		auto & msg = args.at (idx);
		return fromMessage<T> (msg);
	}
	/// Pega a mensagem idx como um T. Se não tiver, torna o padrão
	template<typename T>
	T asDefault (int idx, const T & dflt) {
		try {
			return as<T> (idx);
		}
		catch (...) {
			return dflt;
		}
	}

	size_t size () {
		return args.size ();
	}

private:
	message_t comando;
	message_t id;
	vector<message_t> args;
};

<file_sep>#!/usr/bin/env python

import sdl2 as sdl
import sdl2.ext as sdlx
import time

win = sdlx.Window ('Direto', (800, 600))
win.show ()
tempo = time.perf_counter ()

done = False
tempoTotal = 0
i = 0

while not done:
    events = sdlx.get_events ()
    for ev in events:
        if ev.type == sdl.SDL_QUIT:
            done = True
            break;
    # desenha =P
    sdlx.fill (win.get_surface (), sdlx.string_to_color ('#555'))
    sdl.SDL_UpdateWindowSurface (win.window)

    último = tempo
    tempo = time.perf_counter ()
    tempoTotal += tempo - último
    i += 1

print ('Média de FPS:', 1 / (tempoTotal / i))
<file_sep>#!/usr/bin/env python

import zmq

def main ():
    ctx = zmq.Context ()
    skt = ctx.socket (zmq.DEALER)
    skt.connect ('tcp://127.0.0.1:5555')

    skt.send_string ('create', zmq.SNDMORE)
    skt.send_string ('ZMQ', zmq.SNDMORE)
    skt.send_string ('800', zmq.SNDMORE)
    skt.send_string ('600')

    while skt.getsockopt (zmq.EVENTS) & zmq.POLLIN == 0:
        skt.send_string ('loop')
    print (skt.recv_string ())
    skt.send_string ('quit')

if __name__ == '__main__':
    main ()
<file_sep>/// Teste do módulo, um dos jeitos de implementar a bagaça
#pragma once

#include "Arguments.hpp"
#include "Connection.hpp"
#include <unordered_map>
#include <functional>
#include <iostream>
#include <zmq.hpp>

using namespace std;
using namespace zmq;


using comando = function<void (Arguments &)>;
using mapaComando = unordered_map<string, comando>;

class Module {
public:
	/// Ctor
	Module (context_t & ctx, const string & endereco) : conn (ctx, ZMQ_ROUTER)
			, endereco (endereco), taberto (true) {
		conn.skt.bind (endereco);
	}

	/// Dtor, manda mensagem confirmando que acabou sussa
	~Module () {
		sync ();
	}
	
	/// Fecha módulo
	void close () {
		taberto = false;
	}

	/// Módulo tá aberto?
	bool isOpen () {
		return taberto;
	}

	void on (const string & nome, comando cmd) {
		comandosConhecidos[nome] = cmd;
	}

	void handle () {
		args.recebeComando (conn.skt);
		auto cmd = args.getComando ();
		auto it = comandosConhecidos.find (cmd);
		if (it == comandosConhecidos.end ()) {
			throw runtime_error ("[" + endereco + "] Comando \"" + cmd + "\" desconhecido!");
		}
		it->second (args);
	}

	/// Manda mensagem vazia, pra sincronização
	void sync () {
		conn.send (args.getId (), VAZIO);
	}

	template<typename...Args>
	void respond (Args... arguments) {
		conn.send (args.getId (), arguments...);
	}


private:
	/// O socket pra comunicar, sempre ROUTER
	Connection conn;

	string endereco;

	/// Tá aberto o módulo?
	bool taberto;

	/// Nossos argumentos =]
	Arguments args;
	/// E o mapa de comandos, pra chamar hora que receber um comando
	mapaComando comandosConhecidos;
};
<file_sep>Projeto de API
==============
Quais funcionalidades podem ser proporcionadas para facilitar criação e uso de
módulos?


ModuleManager
-------------
Uma classe que gerencia a conexão com os módulos, contendo info sobre os
módulos conectados. Uma instância é suficiente por jogo, talvez compense fazer
singleton.

Métodos:
- __construtor__: inicializa podendo ler o arquivo de configuração, e
  conectando com os módulos. Pode usar `argc` e `argv` pra sacar umas coisa,
  incluindo caminho da configuração.
- __Module connect (string nome, string endereço)__: conecta com o módulo
  baseado no endereço `endereço`, salvando-o com o nome especificado. Note que
  esse método só realiza a conexão, então trate de já ter o módulo ligado e
  pronto (ao ler do arquivo de configuração, criação do processo do módulo vai
  ser feita automaticamente pelo __Gedi__, talvez métodos específicos como
  "connectIpc", ou "connectInproc")
- __Module get (string)__: pega uma instância de módulo baseado em seu nome, o
  mesmo usado no arquivo de configuração.
<file_sep>/// Teste de um Module, que conversa com o módulo e recebe resposta (se precisar)
#pragma once

#include "ModuleConnection.hpp"
#include "Arguments.hpp"
#include <iostream>

using namespace std;

/// Handler de módulos, pra podermos usar polimorfismo =]
class ModuleHandle {
public:
	ModuleHandle () : ModuleHandle (nullptr) {}
	ModuleHandle (ModuleConnection * con) : connection (con) {}

	/// Setter da conexão, pra poder iterar sobre conexões reutilizando o handle
	void set (ModuleConnection * con) {
		connection = con;
	}

	template<typename ...Args>
	ModuleHandle & operator() (const Args & ... args) {
		connection->call (args...);
		return *this;
	}

	void sync () {
		connection->fillResponse (respostas);
	}

	Arguments & resp () {
		connection->fillResponse (respostas);
		return respostas;
	}

private:
	ModuleConnection * connection;
	Arguments respostas;
};
