Added Entityx to project
This commit is contained in:
8345
external/entityx-1.1.2/entityx/3rdparty/catch.hpp
vendored
Normal file
8345
external/entityx-1.1.2/entityx/3rdparty/catch.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
580
external/entityx-1.1.2/entityx/3rdparty/simplesignal.h
vendored
Normal file
580
external/entityx-1.1.2/entityx/3rdparty/simplesignal.h
vendored
Normal file
@@ -0,0 +1,580 @@
|
||||
// CC0 Public Domain: http://creativecommons.org/publicdomain/zero/1.0/
|
||||
#ifndef SIMPLE_SIGNAL_H__
|
||||
#define SIMPLE_SIGNAL_H__
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
namespace Simple {
|
||||
|
||||
namespace Lib {
|
||||
|
||||
/// ProtoSignal is the template implementation for callback list.
|
||||
template <typename, typename>
|
||||
class ProtoSignal; // undefined
|
||||
|
||||
/// CollectorInvocation invokes signal handlers differently depending on return
|
||||
/// type.
|
||||
template <typename, typename>
|
||||
struct CollectorInvocation;
|
||||
|
||||
/// CollectorLast returns the result of the last signal handler from a signal
|
||||
/// emission.
|
||||
template <typename Result>
|
||||
struct CollectorLast {
|
||||
typedef Result CollectorResult;
|
||||
explicit CollectorLast() : last_() {}
|
||||
inline bool operator()(Result r) {
|
||||
last_ = r;
|
||||
return true;
|
||||
}
|
||||
CollectorResult result() { return last_; }
|
||||
|
||||
private:
|
||||
Result last_;
|
||||
};
|
||||
|
||||
/// CollectorDefault implements the default signal handler collection behaviour.
|
||||
template <typename Result>
|
||||
struct CollectorDefault : CollectorLast<Result> {};
|
||||
|
||||
/// CollectorDefault specialisation for signals with void return type.
|
||||
template <>
|
||||
struct CollectorDefault<void> {
|
||||
typedef void CollectorResult;
|
||||
void result() {}
|
||||
inline bool operator()(void) { return true; }
|
||||
};
|
||||
|
||||
/// CollectorInvocation specialisation for regular signals.
|
||||
template <class Collector, class R, class... Args>
|
||||
struct CollectorInvocation<Collector, R(Args...)> {
|
||||
inline bool invoke(Collector &collector, const std::function<R(Args...)> &cbf,
|
||||
Args... args) {
|
||||
return collector(cbf(args...));
|
||||
}
|
||||
};
|
||||
|
||||
/// CollectorInvocation specialisation for signals with void return type.
|
||||
template <class Collector, class... Args>
|
||||
struct CollectorInvocation<Collector, void(Args...)> {
|
||||
inline bool invoke(Collector &collector,
|
||||
const std::function<void(Args...)> &cbf, Args... args) {
|
||||
cbf(args...);
|
||||
return collector();
|
||||
}
|
||||
};
|
||||
|
||||
/// ProtoSignal template specialised for the callback signature and collector.
|
||||
template <class Collector, class R, class... Args>
|
||||
class ProtoSignal<R(Args...), Collector> : private CollectorInvocation<
|
||||
Collector, R(Args...)> {
|
||||
protected:
|
||||
typedef std::function<R(Args...)> CbFunction;
|
||||
typedef typename CbFunction::result_type Result;
|
||||
typedef typename Collector::CollectorResult CollectorResult;
|
||||
|
||||
private:
|
||||
/// SignalLink implements a doubly-linked ring with ref-counted nodes
|
||||
/// containing the signal handlers.
|
||||
struct SignalLink {
|
||||
SignalLink *next, *prev;
|
||||
CbFunction function;
|
||||
int ref_count;
|
||||
explicit SignalLink(const CbFunction &cbf)
|
||||
: next(0), prev(0), function(cbf), ref_count(1) {}
|
||||
/*dtor*/ ~SignalLink() { assert(ref_count == 0); }
|
||||
void incref() {
|
||||
ref_count += 1;
|
||||
assert(ref_count > 0);
|
||||
}
|
||||
void decref() {
|
||||
ref_count -= 1;
|
||||
if (!ref_count)
|
||||
delete this;
|
||||
else
|
||||
assert(ref_count > 0);
|
||||
}
|
||||
void unlink() {
|
||||
function = nullptr;
|
||||
if (next) next->prev = prev;
|
||||
if (prev) prev->next = next;
|
||||
decref();
|
||||
// leave intact ->next, ->prev for stale iterators
|
||||
}
|
||||
size_t add_before(const CbFunction &cb) {
|
||||
SignalLink *link = new SignalLink(cb);
|
||||
link->prev = prev; // link to last
|
||||
link->next = this;
|
||||
prev->next = link; // link from last
|
||||
prev = link;
|
||||
static_assert(sizeof(link) == sizeof(size_t), "sizeof size_t");
|
||||
return size_t(link);
|
||||
}
|
||||
bool deactivate(const CbFunction &cbf) {
|
||||
if (cbf == function) {
|
||||
function = 0; // deactivate static head
|
||||
return true;
|
||||
}
|
||||
for (SignalLink *link = this->next ? this->next : this; link != this;
|
||||
link = link->next)
|
||||
if (cbf == link->function) {
|
||||
link->unlink(); // deactivate and unlink sibling
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool remove_sibling(size_t id) {
|
||||
for (SignalLink *link = this->next ? this->next : this; link != this;
|
||||
link = link->next)
|
||||
if (id == size_t(link)) {
|
||||
link->unlink(); // deactivate and unlink sibling
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
SignalLink *callback_ring_; // linked ring of callback nodes
|
||||
/*copy-ctor*/ ProtoSignal(const ProtoSignal &) = delete;
|
||||
ProtoSignal &operator=(const ProtoSignal &) = delete;
|
||||
void ensure_ring() {
|
||||
if (!callback_ring_) {
|
||||
callback_ring_ = new SignalLink(CbFunction()); // ref_count = 1
|
||||
callback_ring_->incref(); // ref_count = 2, head of ring, can be
|
||||
// deactivated but not removed
|
||||
callback_ring_->next = callback_ring_; // ring head initialization
|
||||
callback_ring_->prev = callback_ring_; // ring tail initialization
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/// ProtoSignal constructor, connects default callback if non-0.
|
||||
ProtoSignal(const CbFunction &method) : callback_ring_(0) {
|
||||
if (method != 0) {
|
||||
ensure_ring();
|
||||
callback_ring_->function = method;
|
||||
}
|
||||
}
|
||||
/// ProtoSignal destructor releases all resources associated with this signal.
|
||||
~ProtoSignal() {
|
||||
if (callback_ring_) {
|
||||
while (callback_ring_->next != callback_ring_)
|
||||
callback_ring_->next->unlink();
|
||||
assert(callback_ring_->ref_count >= 2);
|
||||
callback_ring_->decref();
|
||||
callback_ring_->decref();
|
||||
}
|
||||
}
|
||||
/// Operator to add a new function or lambda as signal handler, returns a
|
||||
/// handler connection ID.
|
||||
size_t connect(const CbFunction &cb) {
|
||||
ensure_ring();
|
||||
return callback_ring_->add_before(cb);
|
||||
}
|
||||
/// Operator to remove a signal handler through it connection ID, returns if a
|
||||
/// handler was removed.
|
||||
bool disconnect(size_t connection) {
|
||||
return callback_ring_ ? callback_ring_->remove_sibling(connection) : false;
|
||||
}
|
||||
/// Emit a signal, i.e. invoke all its callbacks and collect return types with
|
||||
/// the Collector.
|
||||
CollectorResult emit(Args... args) {
|
||||
Collector collector;
|
||||
if (!callback_ring_) return collector.result();
|
||||
SignalLink *link = callback_ring_;
|
||||
link->incref();
|
||||
do {
|
||||
if (link->function != 0) {
|
||||
const bool continue_emission =
|
||||
this->invoke(collector, link->function, args...);
|
||||
if (!continue_emission) break;
|
||||
}
|
||||
SignalLink *old = link;
|
||||
link = old->next;
|
||||
link->incref();
|
||||
old->decref();
|
||||
} while (link != callback_ring_);
|
||||
link->decref();
|
||||
return collector.result();
|
||||
}
|
||||
// Number of connected slots.
|
||||
std::size_t size() {
|
||||
std::size_t size = 0;
|
||||
SignalLink *link = callback_ring_;
|
||||
link->incref();
|
||||
do {
|
||||
if (link->function != 0) {
|
||||
size++;
|
||||
}
|
||||
SignalLink *old = link;
|
||||
link = old->next;
|
||||
link->incref();
|
||||
old->decref();
|
||||
} while (link != callback_ring_);
|
||||
return size;
|
||||
}
|
||||
};
|
||||
|
||||
} // Lib
|
||||
// namespace Simple
|
||||
|
||||
/**
|
||||
* Signal is a template type providing an interface for arbitrary callback
|
||||
* lists.
|
||||
* A signal type needs to be declared with the function signature of its
|
||||
* callbacks,
|
||||
* and optionally a return result collector class type.
|
||||
* Signal callbacks can be added with operator+= to a signal and removed with
|
||||
* operator-=, using
|
||||
* a callback connection ID return by operator+= as argument.
|
||||
* The callbacks of a signal are invoked with the emit() method and arguments
|
||||
* according to the signature.
|
||||
* The result returned by emit() depends on the signal collector class. By
|
||||
* default, the result of
|
||||
* the last callback is returned from emit(). Collectors can be implemented to
|
||||
* accumulate callback
|
||||
* results or to halt a running emissions in correspondance to callback results.
|
||||
* The signal implementation is safe against recursion, so callbacks may be
|
||||
* removed and
|
||||
* added during a signal emission and recursive emit() calls are also safe.
|
||||
* The overhead of an unused signal is intentionally kept very low, around the
|
||||
* size of a single pointer.
|
||||
* Note that the Signal template types is non-copyable.
|
||||
*/
|
||||
template <typename SignalSignature,
|
||||
class Collector = Lib::CollectorDefault<
|
||||
typename std::function<SignalSignature>::result_type>>
|
||||
struct Signal /*final*/ : Lib::ProtoSignal<SignalSignature, Collector> {
|
||||
typedef Lib::ProtoSignal<SignalSignature, Collector> ProtoSignal;
|
||||
typedef typename ProtoSignal::CbFunction CbFunction;
|
||||
/// Signal constructor, supports a default callback as argument.
|
||||
Signal(const CbFunction &method = CbFunction()) : ProtoSignal(method) {}
|
||||
};
|
||||
|
||||
/// This function creates a std::function by binding @a object to the member
|
||||
/// function pointer @a method.
|
||||
template <class Instance, class Class, class R, class... Args>
|
||||
std::function<R(Args...)> slot(Instance &object, R (Class::*method)(Args...)) {
|
||||
return [&object, method](Args... args) { return (object.*method)(args...); };
|
||||
}
|
||||
|
||||
/// This function creates a std::function by binding @a object to the member
|
||||
/// function pointer @a method.
|
||||
template <class Class, class R, class... Args>
|
||||
std::function<R(Args...)> slot(Class *object, R (Class::*method)(Args...)) {
|
||||
return [object, method](Args... args) { return (object->*method)(args...); };
|
||||
}
|
||||
|
||||
/// Keep signal emissions going while all handlers return !0 (true).
|
||||
template <typename Result>
|
||||
struct CollectorUntil0 {
|
||||
typedef Result CollectorResult;
|
||||
explicit CollectorUntil0() : result_() {}
|
||||
const CollectorResult &result() { return result_; }
|
||||
inline bool operator()(Result r) {
|
||||
result_ = r;
|
||||
return result_ ? true : false;
|
||||
}
|
||||
|
||||
private:
|
||||
CollectorResult result_;
|
||||
};
|
||||
|
||||
/// Keep signal emissions going while all handlers return 0 (false).
|
||||
template <typename Result>
|
||||
struct CollectorWhile0 {
|
||||
typedef Result CollectorResult;
|
||||
explicit CollectorWhile0() : result_() {}
|
||||
const CollectorResult &result() { return result_; }
|
||||
inline bool operator()(Result r) {
|
||||
result_ = r;
|
||||
return result_ ? false : true;
|
||||
}
|
||||
|
||||
private:
|
||||
CollectorResult result_;
|
||||
};
|
||||
|
||||
/// CollectorVector returns the result of the all signal handlers from a signal
|
||||
/// emission in a std::vector.
|
||||
template <typename Result>
|
||||
struct CollectorVector {
|
||||
typedef std::vector<Result> CollectorResult;
|
||||
const CollectorResult &result() { return result_; }
|
||||
inline bool operator()(Result r) {
|
||||
result_.push_back(r);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
CollectorResult result_;
|
||||
};
|
||||
|
||||
} // Simple
|
||||
|
||||
#endif // SIMPLE_SIGNAL_H__
|
||||
|
||||
#ifdef ENABLE_SIMPLE_SIGNAL_TESTS
|
||||
|
||||
#include <string>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#ifdef __MACH__
|
||||
#include <mach/clock.h>
|
||||
#include <mach/mach.h>
|
||||
#endif
|
||||
|
||||
static std::string string_printf(const char *format, ...)
|
||||
__attribute__((__format__(__printf__, 1, 2)));
|
||||
static std::string string_printf(const char *format, ...) {
|
||||
std::string result;
|
||||
char *str = 0;
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
if (vasprintf(&str, format, args) >= 0) result = str;
|
||||
va_end(args);
|
||||
if (str) free(str);
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint64_t timestamp_benchmark() {
|
||||
struct timespec tp = {0, 0};
|
||||
|
||||
#ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time
|
||||
clock_serv_t cclock;
|
||||
mach_timespec_t mts;
|
||||
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
|
||||
clock_get_time(cclock, &mts);
|
||||
mach_port_deallocate(mach_task_self(), cclock);
|
||||
tp.tv_sec = mts.tv_sec;
|
||||
tp.tv_nsec = mts.tv_nsec;
|
||||
#else
|
||||
clock_gettime(CLOCK_REALTIME, &tp);
|
||||
#endif
|
||||
uint64_t stamp = tp.tv_sec * 1000000000ULL + tp.tv_nsec;
|
||||
return stamp;
|
||||
}
|
||||
|
||||
struct TestCounter {
|
||||
static uint64_t get();
|
||||
static void set(uint64_t);
|
||||
static void add2(void *, uint64_t);
|
||||
};
|
||||
|
||||
namespace { // Anon
|
||||
void (*test_counter_add2)(void *, uint64_t) =
|
||||
TestCounter::add2; // external symbol to prevent easy inlining
|
||||
static uint64_t test_counter_var = 0;
|
||||
} // Anon
|
||||
|
||||
class BasicSignalTests {
|
||||
static std::string accu;
|
||||
struct Foo {
|
||||
char foo_bool(float f, int i, std::string s) {
|
||||
accu += string_printf("Foo: %.2f\n", f + i + s.size());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
static char float_callback(float f, int, std::string) {
|
||||
accu += string_printf("float: %.2f\n", f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public:
|
||||
static void run() {
|
||||
accu = "";
|
||||
Simple::Signal<char(float, int, std::string)> sig1;
|
||||
size_t id1 = sig1.connect(float_callback);
|
||||
size_t id2 = sig1.connect([](float, int i, std::string) {
|
||||
accu += string_printf("int: %d\n", i);
|
||||
return 0;
|
||||
});
|
||||
size_t id3 = sig1.connect([](float, int, const std::string &s) {
|
||||
accu += string_printf("string: %s\n", s.c_str());
|
||||
return 0;
|
||||
});
|
||||
sig1.emit(.3, 4, "huhu");
|
||||
bool success;
|
||||
success = sig1.disconnect(id1);
|
||||
assert(success == true);
|
||||
success = sig1.disconnect(id1);
|
||||
assert(success == false);
|
||||
success = sig1.disconnect(id2);
|
||||
assert(success == true);
|
||||
success = sig1.disconnect(id3);
|
||||
assert(success == true);
|
||||
success = sig1.disconnect(id3);
|
||||
assert(success == false);
|
||||
success = sig1.disconnect(id2);
|
||||
assert(success == false);
|
||||
Foo foo;
|
||||
sig1.connect(Simple::slot(foo, &Foo::foo_bool));
|
||||
sig1.connect(Simple::slot(&foo, &Foo::foo_bool));
|
||||
sig1.emit(.5, 1, "12");
|
||||
|
||||
Simple::Signal<void(std::string, int)> sig2;
|
||||
sig2.connect([](std::string msg,
|
||||
int) { accu += string_printf("msg: %s", msg.c_str()); });
|
||||
sig2.connect([](std::string,
|
||||
int d) { accu += string_printf(" *%d*\n", d); });
|
||||
sig2.emit("in sig2", 17);
|
||||
|
||||
accu += "DONE";
|
||||
|
||||
const char *expected =
|
||||
"float: 0.30\n"
|
||||
"int: 4\n"
|
||||
"string: huhu\n"
|
||||
"Foo: 3.50\n"
|
||||
"Foo: 3.50\n"
|
||||
"msg: in sig2 *17*\n"
|
||||
"DONE";
|
||||
assert(accu == expected);
|
||||
}
|
||||
};
|
||||
std::string BasicSignalTests::accu;
|
||||
|
||||
class TestCollectorVector {
|
||||
static int handler1() { return 1; }
|
||||
static int handler42() { return 42; }
|
||||
static int handler777() { return 777; }
|
||||
|
||||
public:
|
||||
static void run() {
|
||||
Simple::Signal<int(), Simple::CollectorVector<int>> sig_vector;
|
||||
sig_vector.connect(handler777);
|
||||
sig_vector.connect(handler42);
|
||||
sig_vector.connect(handler1);
|
||||
sig_vector.connect(handler42);
|
||||
sig_vector.connect(handler777);
|
||||
std::vector<int> results = sig_vector.emit();
|
||||
const std::vector<int> reference = {777, 42, 1, 42, 777, };
|
||||
assert(results == reference);
|
||||
}
|
||||
};
|
||||
|
||||
class TestCollectorUntil0 {
|
||||
bool check1, check2;
|
||||
TestCollectorUntil0() : check1(0), check2(0) {}
|
||||
bool handler_true() {
|
||||
check1 = true;
|
||||
return true;
|
||||
}
|
||||
bool handler_false() {
|
||||
check2 = true;
|
||||
return false;
|
||||
}
|
||||
bool handler_abort() { abort(); }
|
||||
|
||||
public:
|
||||
static void run() {
|
||||
TestCollectorUntil0 self;
|
||||
Simple::Signal<bool(), Simple::CollectorUntil0<bool>> sig_until0;
|
||||
sig_until0.connect(Simple::slot(self, &TestCollectorUntil0::handler_true));
|
||||
sig_until0.connect(Simple::slot(self, &TestCollectorUntil0::handler_false));
|
||||
sig_until0.connect(Simple::slot(self, &TestCollectorUntil0::handler_abort));
|
||||
assert(!self.check1 && !self.check2);
|
||||
const bool result = sig_until0.emit();
|
||||
assert(!result && self.check1 && self.check2);
|
||||
}
|
||||
};
|
||||
|
||||
class TestCollectorWhile0 {
|
||||
bool check1, check2;
|
||||
TestCollectorWhile0() : check1(0), check2(0) {}
|
||||
bool handler_0() {
|
||||
check1 = true;
|
||||
return false;
|
||||
}
|
||||
bool handler_1() {
|
||||
check2 = true;
|
||||
return true;
|
||||
}
|
||||
bool handler_abort() { abort(); }
|
||||
|
||||
public:
|
||||
static void run() {
|
||||
TestCollectorWhile0 self;
|
||||
Simple::Signal<bool(), Simple::CollectorWhile0<bool>> sig_while0;
|
||||
sig_while0.connect(Simple::slot(self, &TestCollectorWhile0::handler_0));
|
||||
sig_while0.connect(Simple::slot(self, &TestCollectorWhile0::handler_1));
|
||||
sig_while0.connect(Simple::slot(self, &TestCollectorWhile0::handler_abort));
|
||||
assert(!self.check1 && !self.check2);
|
||||
const bool result = sig_while0.emit();
|
||||
assert(result == true && self.check1 && self.check2);
|
||||
}
|
||||
};
|
||||
|
||||
static void bench_simple_signal() {
|
||||
Simple::Signal<void(void *, uint64_t)> sig_increment;
|
||||
sig_increment.connect(test_counter_add2);
|
||||
const uint64_t start_counter = TestCounter::get();
|
||||
const uint64_t benchstart = timestamp_benchmark();
|
||||
uint64_t i;
|
||||
for (i = 0; i < 999999; i++) {
|
||||
sig_increment.emit(0, 1);
|
||||
}
|
||||
const uint64_t benchdone = timestamp_benchmark();
|
||||
const uint64_t end_counter = TestCounter::get();
|
||||
assert(end_counter - start_counter == i);
|
||||
printf("OK\n Benchmark: Simple::Signal: %fns per emission (size=%zu): ",
|
||||
size_t(benchdone - benchstart) * 1.0 / size_t(i),
|
||||
sizeof(sig_increment));
|
||||
}
|
||||
|
||||
static void bench_callback_loop() {
|
||||
void (*counter_increment)(void *, uint64_t) = test_counter_add2;
|
||||
const uint64_t start_counter = TestCounter::get();
|
||||
const uint64_t benchstart = timestamp_benchmark();
|
||||
uint64_t i;
|
||||
for (i = 0; i < 999999; i++) {
|
||||
counter_increment(0, 1);
|
||||
}
|
||||
const uint64_t benchdone = timestamp_benchmark();
|
||||
const uint64_t end_counter = TestCounter::get();
|
||||
assert(end_counter - start_counter == i);
|
||||
printf("OK\n Benchmark: callback loop: %fns per round: ",
|
||||
size_t(benchdone - benchstart) * 1.0 / size_t(i));
|
||||
}
|
||||
|
||||
uint64_t TestCounter::get() { return test_counter_var; }
|
||||
|
||||
void TestCounter::set(uint64_t v) { test_counter_var = v; }
|
||||
|
||||
void TestCounter::add2(void *, uint64_t v) { test_counter_var += v; }
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
printf("Signal/Basic Tests: ");
|
||||
BasicSignalTests::run();
|
||||
printf("OK\n");
|
||||
|
||||
printf("Signal/CollectorVector: ");
|
||||
TestCollectorVector::run();
|
||||
printf("OK\n");
|
||||
|
||||
printf("Signal/CollectorUntil0: ");
|
||||
TestCollectorUntil0::run();
|
||||
printf("OK\n");
|
||||
|
||||
printf("Signal/CollectorWhile0: ");
|
||||
TestCollectorWhile0::run();
|
||||
printf("OK\n");
|
||||
|
||||
printf("Signal/Benchmark: Simple::Signal: ");
|
||||
bench_simple_signal();
|
||||
printf("OK\n");
|
||||
|
||||
printf("Signal/Benchmark: callback loop: ");
|
||||
bench_callback_loop();
|
||||
printf("OK\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // DISABLE_TESTS
|
||||
|
||||
// g++ -Wall -O2 -std=gnu++0x -pthread simplesignal.cc -lrt && ./a.out
|
||||
146
external/entityx-1.1.2/entityx/Benchmarks_test.cc
vendored
Normal file
146
external/entityx-1.1.2/entityx/Benchmarks_test.cc
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include "entityx/3rdparty/catch.hpp"
|
||||
#include "entityx/help/Timer.h"
|
||||
#include "entityx/Entity.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace entityx;
|
||||
|
||||
using std::uint64_t;
|
||||
|
||||
|
||||
struct AutoTimer {
|
||||
~AutoTimer() {
|
||||
cout << timer_.elapsed() << " seconds elapsed" << endl;
|
||||
}
|
||||
|
||||
private:
|
||||
entityx::help::Timer timer_;
|
||||
};
|
||||
|
||||
struct Listener : public Receiver<Listener> {
|
||||
void receive(const EntityCreatedEvent &event) { ++created; }
|
||||
void receive(const EntityDestroyedEvent &event) { ++destroyed; }
|
||||
|
||||
int created = 0;
|
||||
int destroyed = 0;
|
||||
};
|
||||
|
||||
struct Position : public Component<Position> {
|
||||
};
|
||||
|
||||
|
||||
struct Direction : public Component<Direction> {
|
||||
};
|
||||
|
||||
|
||||
struct BenchmarkFixture {
|
||||
BenchmarkFixture() : em(ev) {}
|
||||
|
||||
EventManager ev;
|
||||
EntityManager em;
|
||||
};
|
||||
|
||||
|
||||
TEST_CASE_METHOD(BenchmarkFixture, "TestCreateEntities") {
|
||||
AutoTimer t;
|
||||
|
||||
uint64_t count = 10000000L;
|
||||
cout << "creating " << count << " entities" << endl;
|
||||
|
||||
for (uint64_t i = 0; i < count; i++) {
|
||||
em.create();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE_METHOD(BenchmarkFixture, "TestDestroyEntities") {
|
||||
uint64_t count = 10000000L;
|
||||
vector<Entity> entities;
|
||||
for (uint64_t i = 0; i < count; i++) {
|
||||
entities.push_back(em.create());
|
||||
}
|
||||
|
||||
AutoTimer t;
|
||||
cout << "destroying " << count << " entities" << endl;
|
||||
|
||||
for (auto e : entities) {
|
||||
e.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(BenchmarkFixture, "TestCreateEntitiesWithListener") {
|
||||
Listener listen;
|
||||
ev.subscribe<EntityCreatedEvent>(listen);
|
||||
|
||||
int count = 10000000L;
|
||||
|
||||
AutoTimer t;
|
||||
cout << "creating " << count << " entities while notifying a single EntityCreatedEvent listener" << endl;
|
||||
|
||||
vector<Entity> entities;
|
||||
for (int i = 0; i < count; i++) {
|
||||
entities.push_back(em.create());
|
||||
}
|
||||
|
||||
REQUIRE(entities.size() == count);
|
||||
REQUIRE(listen.created == count);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(BenchmarkFixture, "TestDestroyEntitiesWithListener") {
|
||||
int count = 10000000;
|
||||
vector<Entity> entities;
|
||||
for (int i = 0; i < count; i++) {
|
||||
entities.push_back(em.create());
|
||||
}
|
||||
|
||||
Listener listen;
|
||||
ev.subscribe<EntityDestroyedEvent>(listen);
|
||||
|
||||
AutoTimer t;
|
||||
cout << "destroying " << count << " entities while notifying a single EntityDestroyedEvent listener" << endl;
|
||||
|
||||
for (auto &e : entities) {
|
||||
e.destroy();
|
||||
}
|
||||
|
||||
REQUIRE(entities.size() == count);
|
||||
REQUIRE(listen.destroyed == count);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(BenchmarkFixture, "TestEntityIteration") {
|
||||
int count = 10000000;
|
||||
for (int i = 0; i < count; i++) {
|
||||
auto e = em.create();
|
||||
e.assign<Position>();
|
||||
}
|
||||
|
||||
AutoTimer t;
|
||||
cout << "iterating over " << count << " entities, unpacking one component" << endl;
|
||||
|
||||
ComponentHandle<Position> position;
|
||||
for (auto e : em.entities_with_components(position)) {
|
||||
(void)e;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(BenchmarkFixture, "TestEntityIterationUnpackTwo") {
|
||||
int count = 10000000;
|
||||
for (int i = 0; i < count; i++) {
|
||||
auto e = em.create();
|
||||
e.assign<Position>();
|
||||
e.assign<Direction>();
|
||||
}
|
||||
|
||||
AutoTimer t;
|
||||
cout << "iterating over " << count << " entities, unpacking two components" << endl;
|
||||
|
||||
ComponentHandle<Position> position;
|
||||
ComponentHandle<Direction> direction;
|
||||
for (auto e : em.entities_with_components(position, direction)) {
|
||||
(void)e;
|
||||
}
|
||||
}
|
||||
57
external/entityx-1.1.2/entityx/Entity.cc
vendored
Normal file
57
external/entityx-1.1.2/entityx/Entity.cc
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution.
|
||||
*
|
||||
* Author: Alec Thomas <alec@swapoff.org>
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include "entityx/Entity.h"
|
||||
|
||||
namespace entityx {
|
||||
|
||||
const Entity::Id Entity::INVALID;
|
||||
BaseComponent::Family BaseComponent::family_counter_ = 0;
|
||||
|
||||
void Entity::invalidate() {
|
||||
id_ = INVALID;
|
||||
manager_ = nullptr;
|
||||
}
|
||||
|
||||
void Entity::destroy() {
|
||||
assert(valid());
|
||||
manager_->destroy(id_);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
std::bitset<entityx::MAX_COMPONENTS> Entity::component_mask() const {
|
||||
return manager_->component_mask(id_);
|
||||
}
|
||||
|
||||
EntityManager::EntityManager(EventManager &event_manager) : event_manager_(event_manager) {
|
||||
}
|
||||
|
||||
EntityManager::~EntityManager() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void EntityManager::reset() {
|
||||
for (Entity entity : entities_for_debugging()) entity.destroy();
|
||||
for (BasePool *pool : component_pools_) {
|
||||
if (pool) delete pool;
|
||||
}
|
||||
component_pools_.clear();
|
||||
entity_component_mask_.clear();
|
||||
entity_version_.clear();
|
||||
free_list_.clear();
|
||||
index_counter_ = 0;
|
||||
}
|
||||
|
||||
EntityCreatedEvent::~EntityCreatedEvent() {}
|
||||
EntityDestroyedEvent::~EntityDestroyedEvent() {}
|
||||
|
||||
|
||||
} // namespace entityx
|
||||
987
external/entityx-1.1.2/entityx/Entity.h
vendored
Normal file
987
external/entityx-1.1.2/entityx/Entity.h
vendored
Normal file
@@ -0,0 +1,987 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution.
|
||||
*
|
||||
* Author: Alec Thomas <alec@swapoff.org>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <cstdint>
|
||||
#include <tuple>
|
||||
#include <new>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
|
||||
#include "entityx/help/Pool.h"
|
||||
#include "entityx/config.h"
|
||||
#include "entityx/Event.h"
|
||||
#include "entityx/help/NonCopyable.h"
|
||||
|
||||
namespace entityx {
|
||||
|
||||
typedef std::uint32_t uint32_t;
|
||||
typedef std::uint64_t uint64_t;
|
||||
|
||||
class EntityManager;
|
||||
|
||||
|
||||
template <typename C, typename EM = EntityManager>
|
||||
class ComponentHandle;
|
||||
|
||||
/** A convenience handle around an Entity::Id.
|
||||
*
|
||||
* If an entity is destroyed, any copies will be invalidated. Use valid() to
|
||||
* check for validity before using.
|
||||
*
|
||||
* Create entities with `EntityManager`:
|
||||
*
|
||||
* Entity entity = entity_manager->create();
|
||||
*/
|
||||
class Entity {
|
||||
public:
|
||||
struct Id {
|
||||
Id() : id_(0) {}
|
||||
explicit Id(uint64_t id) : id_(id) {}
|
||||
Id(uint32_t index, uint32_t version) : id_(uint64_t(index) | uint64_t(version) << 32UL) {}
|
||||
|
||||
uint64_t id() const { return id_; }
|
||||
|
||||
bool operator == (const Id &other) const { return id_ == other.id_; }
|
||||
bool operator != (const Id &other) const { return id_ != other.id_; }
|
||||
bool operator < (const Id &other) const { return id_ < other.id_; }
|
||||
|
||||
uint32_t index() const { return id_ & 0xffffffffUL; }
|
||||
uint32_t version() const { return id_ >> 32; }
|
||||
|
||||
private:
|
||||
friend class EntityManager;
|
||||
|
||||
uint64_t id_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Id of an invalid Entity.
|
||||
*/
|
||||
static const Id INVALID;
|
||||
|
||||
Entity() = default;
|
||||
Entity(EntityManager *manager, Entity::Id id) : manager_(manager), id_(id) {}
|
||||
Entity(const Entity &other) = default;
|
||||
Entity &operator = (const Entity &other) = default;
|
||||
|
||||
/**
|
||||
* Check if Entity handle is invalid.
|
||||
*/
|
||||
operator bool() const {
|
||||
return valid();
|
||||
}
|
||||
|
||||
bool operator == (const Entity &other) const {
|
||||
return other.manager_ == manager_ && other.id_ == id_;
|
||||
}
|
||||
|
||||
bool operator != (const Entity &other) const {
|
||||
return !(other == *this);
|
||||
}
|
||||
|
||||
bool operator < (const Entity &other) const {
|
||||
return other.id_ < id_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this Entity handle valid?
|
||||
*
|
||||
* In older versions of EntityX, there were no guarantees around entity
|
||||
* validity if a previously allocated entity slot was reassigned. That is no
|
||||
* longer the case: if a slot is reassigned, old Entity::Id's will be
|
||||
* invalid.
|
||||
*/
|
||||
bool valid() const;
|
||||
|
||||
/**
|
||||
* Invalidate Entity handle, disassociating it from an EntityManager and invalidating its ID.
|
||||
*
|
||||
* Note that this does *not* affect the underlying entity and its
|
||||
* components. Use destroy() to destroy the associated Entity and components.
|
||||
*/
|
||||
void invalidate();
|
||||
|
||||
Id id() const { return id_; }
|
||||
|
||||
template <typename C, typename ... Args>
|
||||
ComponentHandle<C> assign(Args && ... args);
|
||||
|
||||
template <typename C>
|
||||
ComponentHandle<C> assign_from_copy(const C &component);
|
||||
|
||||
template <typename C, typename ... Args>
|
||||
ComponentHandle<C> replace(Args && ... args);
|
||||
|
||||
template <typename C>
|
||||
void remove();
|
||||
|
||||
template <typename C, typename = typename std::enable_if<!std::is_const<C>::value>::type>
|
||||
ComponentHandle<C> component();
|
||||
|
||||
template <typename C, typename = typename std::enable_if<std::is_const<C>::value>::type>
|
||||
const ComponentHandle<C, const EntityManager> component() const;
|
||||
|
||||
template <typename ... Components>
|
||||
std::tuple<ComponentHandle<Components>...> components();
|
||||
|
||||
template <typename ... Components>
|
||||
std::tuple<ComponentHandle<const Components, const EntityManager>...> components() const;
|
||||
|
||||
template <typename C>
|
||||
bool has_component() const;
|
||||
|
||||
template <typename A, typename ... Args>
|
||||
void unpack(ComponentHandle<A> &a, ComponentHandle<Args> & ... args);
|
||||
|
||||
/**
|
||||
* Destroy and invalidate this Entity.
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
std::bitset<entityx::MAX_COMPONENTS> component_mask() const;
|
||||
|
||||
private:
|
||||
EntityManager *manager_ = nullptr;
|
||||
Entity::Id id_ = INVALID;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A ComponentHandle<C> is a wrapper around an instance of a component.
|
||||
*
|
||||
* It provides safe access to components. The handle will be invalidated under
|
||||
* the following conditions:
|
||||
*
|
||||
* - If a component is removed from its host entity.
|
||||
* - If its host entity is destroyed.
|
||||
*/
|
||||
template <typename C, typename EM>
|
||||
class ComponentHandle {
|
||||
public:
|
||||
typedef C ComponentType;
|
||||
|
||||
ComponentHandle() : manager_(nullptr) {}
|
||||
|
||||
bool valid() const;
|
||||
operator bool() const;
|
||||
|
||||
C *operator -> ();
|
||||
const C *operator -> () const;
|
||||
|
||||
C *get();
|
||||
const C *get() const;
|
||||
|
||||
/**
|
||||
* Remove the component from its entity and destroy it.
|
||||
*/
|
||||
void remove();
|
||||
|
||||
bool operator == (const ComponentHandle<C> &other) const {
|
||||
return manager_ == other.manager_ && id_ == other.id_;
|
||||
}
|
||||
|
||||
bool operator != (const ComponentHandle<C> &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class EntityManager;
|
||||
|
||||
ComponentHandle(EM *manager, Entity::Id id) :
|
||||
manager_(manager), id_(id) {}
|
||||
|
||||
EM *manager_;
|
||||
Entity::Id id_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Base component class, only used for insertion into collections.
|
||||
*
|
||||
* Family is used for registration.
|
||||
*/
|
||||
struct BaseComponent {
|
||||
public:
|
||||
typedef size_t Family;
|
||||
|
||||
// NOTE: Component memory is *always* managed by the EntityManager.
|
||||
// Use Entity::destroy() instead.
|
||||
void operator delete(void *p) { fail(); }
|
||||
void operator delete[](void *p) { fail(); }
|
||||
|
||||
|
||||
protected:
|
||||
static void fail() {
|
||||
#if defined(_HAS_EXCEPTIONS) || defined(__EXCEPTIONS)
|
||||
throw std::bad_alloc();
|
||||
#else
|
||||
std::abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
static Family family_counter_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Component implementations should inherit from this.
|
||||
*
|
||||
* Components MUST provide a no-argument constructor.
|
||||
* Components SHOULD provide convenience constructors for initializing on assignment to an Entity::Id.
|
||||
*
|
||||
* This is a struct to imply that components should be data-only.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* struct Position : public Component<Position> {
|
||||
* Position(float x = 0.0f, float y = 0.0f) : x(x), y(y) {}
|
||||
*
|
||||
* float x, y;
|
||||
* };
|
||||
*
|
||||
* family() is used for registration.
|
||||
*/
|
||||
template <typename Derived>
|
||||
struct Component : public BaseComponent {
|
||||
public:
|
||||
typedef ComponentHandle<Derived> Handle;
|
||||
typedef ComponentHandle<const Derived, const EntityManager> ConstHandle;
|
||||
|
||||
private:
|
||||
friend class EntityManager;
|
||||
/// Used internally for registration.
|
||||
static Family family();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Emitted when an entity is added to the system.
|
||||
*/
|
||||
struct EntityCreatedEvent : public Event<EntityCreatedEvent> {
|
||||
explicit EntityCreatedEvent(Entity entity) : entity(entity) {}
|
||||
virtual ~EntityCreatedEvent();
|
||||
|
||||
Entity entity;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Called just prior to an entity being destroyed.
|
||||
*/
|
||||
struct EntityDestroyedEvent : public Event<EntityDestroyedEvent> {
|
||||
explicit EntityDestroyedEvent(Entity entity) : entity(entity) {}
|
||||
virtual ~EntityDestroyedEvent();
|
||||
|
||||
Entity entity;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Emitted when any component is added to an entity.
|
||||
*/
|
||||
template <typename C>
|
||||
struct ComponentAddedEvent : public Event<ComponentAddedEvent<C>> {
|
||||
ComponentAddedEvent(Entity entity, ComponentHandle<C> component) :
|
||||
entity(entity), component(component) {}
|
||||
|
||||
Entity entity;
|
||||
ComponentHandle<C> component;
|
||||
};
|
||||
|
||||
/**
|
||||
* Emitted when any component is removed from an entity.
|
||||
*/
|
||||
template <typename C>
|
||||
struct ComponentRemovedEvent : public Event<ComponentRemovedEvent<C>> {
|
||||
ComponentRemovedEvent(Entity entity, ComponentHandle<C> component) :
|
||||
entity(entity), component(component) {}
|
||||
|
||||
Entity entity;
|
||||
ComponentHandle<C> component;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Manages Entity::Id creation and component assignment.
|
||||
*/
|
||||
class EntityManager : entityx::help::NonCopyable {
|
||||
public:
|
||||
typedef std::bitset<entityx::MAX_COMPONENTS> ComponentMask;
|
||||
|
||||
explicit EntityManager(EventManager &event_manager);
|
||||
virtual ~EntityManager();
|
||||
|
||||
/// An iterator over a view of the entities in an EntityManager.
|
||||
/// If All is true it will iterate over all valid entities and will ignore the entity mask.
|
||||
template <class Delegate, bool All = false>
|
||||
class ViewIterator : public std::iterator<std::input_iterator_tag, Entity::Id> {
|
||||
public:
|
||||
Delegate &operator ++() {
|
||||
++i_;
|
||||
next();
|
||||
return *static_cast<Delegate*>(this);
|
||||
}
|
||||
bool operator == (const Delegate& rhs) const { return i_ == rhs.i_; }
|
||||
bool operator != (const Delegate& rhs) const { return i_ != rhs.i_; }
|
||||
Entity operator * () { return Entity(manager_, manager_->create_id(i_)); }
|
||||
const Entity operator * () const { return Entity(manager_, manager_->create_id(i_)); }
|
||||
|
||||
protected:
|
||||
ViewIterator(EntityManager *manager, uint32_t index)
|
||||
: manager_(manager), i_(index), capacity_(manager_->capacity()), free_cursor_(~0UL) {
|
||||
if (All) {
|
||||
std::sort(manager_->free_list_.begin(), manager_->free_list_.end());
|
||||
free_cursor_ = 0;
|
||||
}
|
||||
}
|
||||
ViewIterator(EntityManager *manager, const ComponentMask mask, uint32_t index)
|
||||
: manager_(manager), mask_(mask), i_(index), capacity_(manager_->capacity()), free_cursor_(~0UL) {
|
||||
if (All) {
|
||||
std::sort(manager_->free_list_.begin(), manager_->free_list_.end());
|
||||
free_cursor_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void next() {
|
||||
while (i_ < capacity_ && !predicate()) {
|
||||
++i_;
|
||||
}
|
||||
|
||||
if (i_ < capacity_) {
|
||||
Entity entity = manager_->get(manager_->create_id(i_));
|
||||
static_cast<Delegate*>(this)->next_entity(entity);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool predicate() {
|
||||
return (All && valid_entity()) || (manager_->entity_component_mask_[i_] & mask_) == mask_;
|
||||
}
|
||||
|
||||
inline bool valid_entity() {
|
||||
const std::vector<uint32_t> &free_list = manager_->free_list_;
|
||||
if (free_cursor_ < free_list.size() && free_list[free_cursor_] == i_) {
|
||||
++free_cursor_;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
EntityManager *manager_;
|
||||
ComponentMask mask_;
|
||||
uint32_t i_;
|
||||
size_t capacity_;
|
||||
size_t free_cursor_;
|
||||
};
|
||||
|
||||
template <bool All>
|
||||
class BaseView {
|
||||
public:
|
||||
class Iterator : public ViewIterator<Iterator, All> {
|
||||
public:
|
||||
Iterator(EntityManager *manager,
|
||||
const ComponentMask mask,
|
||||
uint32_t index) : ViewIterator<Iterator, All>(manager, mask, index) {
|
||||
ViewIterator<Iterator, All>::next();
|
||||
}
|
||||
|
||||
void next_entity(Entity &entity) {}
|
||||
};
|
||||
|
||||
|
||||
Iterator begin() { return Iterator(manager_, mask_, 0); }
|
||||
Iterator end() { return Iterator(manager_, mask_, uint32_t(manager_->capacity())); }
|
||||
const Iterator begin() const { return Iterator(manager_, mask_, 0); }
|
||||
const Iterator end() const { return Iterator(manager_, mask_, manager_->capacity()); }
|
||||
|
||||
private:
|
||||
friend class EntityManager;
|
||||
|
||||
explicit BaseView(EntityManager *manager) : manager_(manager) { mask_.set(); }
|
||||
BaseView(EntityManager *manager, ComponentMask mask) :
|
||||
manager_(manager), mask_(mask) {}
|
||||
|
||||
EntityManager *manager_;
|
||||
ComponentMask mask_;
|
||||
};
|
||||
|
||||
typedef BaseView<false> View;
|
||||
typedef BaseView<true> DebugView;
|
||||
|
||||
template <typename ... Components>
|
||||
class UnpackingView {
|
||||
public:
|
||||
struct Unpacker {
|
||||
explicit Unpacker(ComponentHandle<Components> & ... handles) :
|
||||
handles(std::tuple<ComponentHandle<Components> & ...>(handles...)) {}
|
||||
|
||||
void unpack(entityx::Entity &entity) const {
|
||||
unpack_<0, Components...>(entity);
|
||||
}
|
||||
|
||||
private:
|
||||
template <int N, typename C>
|
||||
void unpack_(entityx::Entity &entity) const {
|
||||
std::get<N>(handles) = entity.component<C>();
|
||||
}
|
||||
|
||||
template <int N, typename C0, typename C1, typename ... Cn>
|
||||
void unpack_(entityx::Entity &entity) const {
|
||||
std::get<N>(handles) = entity.component<C0>();
|
||||
unpack_<N + 1, C1, Cn...>(entity);
|
||||
}
|
||||
|
||||
std::tuple<ComponentHandle<Components> & ...> handles;
|
||||
};
|
||||
|
||||
|
||||
class Iterator : public ViewIterator<Iterator> {
|
||||
public:
|
||||
Iterator(EntityManager *manager,
|
||||
const ComponentMask mask,
|
||||
uint32_t index,
|
||||
const Unpacker &unpacker) : ViewIterator<Iterator>(manager, mask, index), unpacker_(unpacker) {
|
||||
ViewIterator<Iterator>::next();
|
||||
}
|
||||
|
||||
void next_entity(Entity &entity) {
|
||||
unpacker_.unpack(entity);
|
||||
}
|
||||
|
||||
private:
|
||||
const Unpacker &unpacker_;
|
||||
};
|
||||
|
||||
|
||||
Iterator begin() { return Iterator(manager_, mask_, 0, unpacker_); }
|
||||
Iterator end() { return Iterator(manager_, mask_, manager_->capacity(), unpacker_); }
|
||||
const Iterator begin() const { return Iterator(manager_, mask_, 0, unpacker_); }
|
||||
const Iterator end() const { return Iterator(manager_, mask_, manager_->capacity(), unpacker_); }
|
||||
|
||||
|
||||
private:
|
||||
friend class EntityManager;
|
||||
|
||||
UnpackingView(EntityManager *manager, ComponentMask mask, ComponentHandle<Components> & ... handles) :
|
||||
manager_(manager), mask_(mask), unpacker_(handles...) {}
|
||||
|
||||
EntityManager *manager_;
|
||||
ComponentMask mask_;
|
||||
Unpacker unpacker_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Number of managed entities.
|
||||
*/
|
||||
size_t size() const { return entity_component_mask_.size() - free_list_.size(); }
|
||||
|
||||
/**
|
||||
* Current entity capacity.
|
||||
*/
|
||||
size_t capacity() const { return entity_component_mask_.size(); }
|
||||
|
||||
/**
|
||||
* Return true if the given entity ID is still valid.
|
||||
*/
|
||||
bool valid(Entity::Id id) const {
|
||||
return id.index() < entity_version_.size() && entity_version_[id.index()] == id.version();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Entity::Id.
|
||||
*
|
||||
* Emits EntityCreatedEvent.
|
||||
*/
|
||||
Entity create() {
|
||||
uint32_t index, version;
|
||||
if (free_list_.empty()) {
|
||||
index = index_counter_++;
|
||||
accomodate_entity(index);
|
||||
version = entity_version_[index] = 1;
|
||||
} else {
|
||||
index = free_list_.back();
|
||||
free_list_.pop_back();
|
||||
version = entity_version_[index];
|
||||
}
|
||||
Entity entity(this, Entity::Id(index, version));
|
||||
event_manager_.emit<EntityCreatedEvent>(entity);
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy an existing Entity::Id and its associated Components.
|
||||
*
|
||||
* Emits EntityDestroyedEvent.
|
||||
*/
|
||||
void destroy(Entity::Id entity) {
|
||||
assert_valid(entity);
|
||||
uint32_t index = entity.index();
|
||||
auto mask = entity_component_mask_[entity.index()];
|
||||
event_manager_.emit<EntityDestroyedEvent>(Entity(this, entity));
|
||||
for (size_t i = 0; i < component_pools_.size(); i++) {
|
||||
BasePool *pool = component_pools_[i];
|
||||
if (pool && mask.test(i))
|
||||
pool->destroy(index);
|
||||
}
|
||||
entity_component_mask_[index].reset();
|
||||
entity_version_[index]++;
|
||||
free_list_.push_back(index);
|
||||
}
|
||||
|
||||
Entity get(Entity::Id id) {
|
||||
assert_valid(id);
|
||||
return Entity(this, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an Entity::Id for a slot.
|
||||
*
|
||||
* NOTE: Does *not* check for validity, but the Entity::Id constructor will
|
||||
* fail if the ID is invalid.
|
||||
*/
|
||||
Entity::Id create_id(uint32_t index) const {
|
||||
return Entity::Id(index, entity_version_[index]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a Component to an Entity::Id, passing through Component constructor arguments.
|
||||
*
|
||||
* Position &position = em.assign<Position>(e, x, y);
|
||||
*
|
||||
* @returns Smart pointer to newly created component.
|
||||
*/
|
||||
template <typename C, typename ... Args>
|
||||
ComponentHandle<C> assign(Entity::Id id, Args && ... args) {
|
||||
assert_valid(id);
|
||||
const BaseComponent::Family family = component_family<C>();
|
||||
assert(!entity_component_mask_[id.index()].test(family));
|
||||
|
||||
// Placement new into the component pool.
|
||||
Pool<C> *pool = accomodate_component<C>();
|
||||
new(pool->get(id.index())) C(std::forward<Args>(args) ...);
|
||||
|
||||
// Set the bit for this component.
|
||||
entity_component_mask_[id.index()].set(family);
|
||||
|
||||
// Create and return handle.
|
||||
ComponentHandle<C> component(this, id);
|
||||
event_manager_.emit<ComponentAddedEvent<C>>(Entity(this, id), component);
|
||||
return component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a Component from an Entity::Id
|
||||
*
|
||||
* Emits a ComponentRemovedEvent<C> event.
|
||||
*/
|
||||
template <typename C>
|
||||
void remove(Entity::Id id) {
|
||||
assert_valid(id);
|
||||
const BaseComponent::Family family = component_family<C>();
|
||||
const uint32_t index = id.index();
|
||||
|
||||
// Find the pool for this component family.
|
||||
BasePool *pool = component_pools_[family];
|
||||
ComponentHandle<C> component(this, id);
|
||||
event_manager_.emit<ComponentRemovedEvent<C>>(Entity(this, id), component);
|
||||
|
||||
// Remove component bit.
|
||||
entity_component_mask_[id.index()].reset(family);
|
||||
|
||||
// Call destructor.
|
||||
pool->destroy(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an Entity has a component.
|
||||
*/
|
||||
template <typename C>
|
||||
bool has_component(Entity::Id id) const {
|
||||
assert_valid(id);
|
||||
size_t family = component_family<C>();
|
||||
// We don't bother checking the component mask, as we return a nullptr anyway.
|
||||
if (family >= component_pools_.size())
|
||||
return false;
|
||||
BasePool *pool = component_pools_[family];
|
||||
if (!pool || !entity_component_mask_[id.index()][family])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a Component assigned to an Entity::Id.
|
||||
*
|
||||
* @returns Pointer to an instance of C, or nullptr if the Entity::Id does not have that Component.
|
||||
*/
|
||||
template <typename C, typename = typename std::enable_if<!std::is_const<C>::value>::type>
|
||||
ComponentHandle<C> component(Entity::Id id) {
|
||||
assert_valid(id);
|
||||
size_t family = component_family<C>();
|
||||
// We don't bother checking the component mask, as we return a nullptr anyway.
|
||||
if (family >= component_pools_.size())
|
||||
return ComponentHandle<C>();
|
||||
BasePool *pool = component_pools_[family];
|
||||
if (!pool || !entity_component_mask_[id.index()][family])
|
||||
return ComponentHandle<C>();
|
||||
return ComponentHandle<C>(this, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a Component assigned to an Entity::Id.
|
||||
*
|
||||
* @returns Component instance, or nullptr if the Entity::Id does not have that Component.
|
||||
*/
|
||||
template <typename C, typename = typename std::enable_if<std::is_const<C>::value>::type>
|
||||
const ComponentHandle<C, const EntityManager> component(Entity::Id id) const {
|
||||
assert_valid(id);
|
||||
size_t family = component_family<C>();
|
||||
// We don't bother checking the component mask, as we return a nullptr anyway.
|
||||
if (family >= component_pools_.size())
|
||||
return ComponentHandle<C, const EntityManager>();
|
||||
BasePool *pool = component_pools_[family];
|
||||
if (!pool || !entity_component_mask_[id.index()][family])
|
||||
return ComponentHandle<C, const EntityManager>();
|
||||
return ComponentHandle<C, const EntityManager>(this, id);
|
||||
}
|
||||
|
||||
template <typename ... Components>
|
||||
std::tuple<ComponentHandle<Components>...> components(Entity::Id id) {
|
||||
return std::make_tuple(component<Components>(id)...);
|
||||
}
|
||||
|
||||
template <typename ... Components>
|
||||
std::tuple<ComponentHandle<const Components, const EntityManager>...> components(Entity::Id id) const {
|
||||
return std::make_tuple(component<const Components>(id)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find Entities that have all of the specified Components.
|
||||
*
|
||||
* @code
|
||||
* for (Entity entity : entity_manager.entities_with_components<Position, Direction>()) {
|
||||
* ComponentHandle<Position> position = entity.component<Position>();
|
||||
* ComponentHandle<Direction> direction = entity.component<Direction>();
|
||||
*
|
||||
* ...
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
template <typename ... Components>
|
||||
View entities_with_components() {
|
||||
auto mask = component_mask<Components ...>();
|
||||
return View(this, mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find Entities that have all of the specified Components and assign them
|
||||
* to the given parameters.
|
||||
*
|
||||
* @code
|
||||
* ComponentHandle<Position> position;
|
||||
* ComponentHandle<Direction> direction;
|
||||
* for (Entity entity : entity_manager.entities_with_components(position, direction)) {
|
||||
* // Use position and component here.
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
template <typename ... Components>
|
||||
UnpackingView<Components...> entities_with_components(ComponentHandle<Components> & ... components) {
|
||||
auto mask = component_mask<Components...>();
|
||||
return UnpackingView<Components...>(this, mask, components...);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over all *valid* entities (ie. not in the free list). Not fast,
|
||||
* so should only be used for debugging.
|
||||
*
|
||||
* @code
|
||||
* for (Entity entity : entity_manager.entities_for_debugging()) {}
|
||||
*
|
||||
* @return An iterator view over all valid entities.
|
||||
*/
|
||||
DebugView entities_for_debugging() {
|
||||
return DebugView(this);
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
void unpack(Entity::Id id, ComponentHandle<C> &a) {
|
||||
assert_valid(id);
|
||||
a = component<C>(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack components directly into pointers.
|
||||
*
|
||||
* Components missing from the entity will be set to nullptr.
|
||||
*
|
||||
* Useful for fast bulk iterations.
|
||||
*
|
||||
* ComponentHandle<Position> p;
|
||||
* ComponentHandle<Direction> d;
|
||||
* unpack<Position, Direction>(e, p, d);
|
||||
*/
|
||||
template <typename A, typename ... Args>
|
||||
void unpack(Entity::Id id, ComponentHandle<A> &a, ComponentHandle<Args> & ... args) {
|
||||
assert_valid(id);
|
||||
a = component<A>(id);
|
||||
unpack<Args ...>(id, args ...);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy all entities and reset the EntityManager.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
// Retrieve the component family for a type.
|
||||
template <typename C>
|
||||
static BaseComponent::Family component_family() {
|
||||
return Component<typename std::remove_const<C>::type>::family();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Entity;
|
||||
template <typename C, typename EM>
|
||||
friend class ComponentHandle;
|
||||
|
||||
|
||||
inline void assert_valid(Entity::Id id) const {
|
||||
assert(id.index() < entity_component_mask_.size() && "Entity::Id ID outside entity vector range");
|
||||
assert(entity_version_[id.index()] == id.version() && "Attempt to access Entity via a stale Entity::Id");
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
C *get_component_ptr(Entity::Id id) {
|
||||
assert(valid(id));
|
||||
BasePool *pool = component_pools_[component_family<C>()];
|
||||
assert(pool);
|
||||
return static_cast<C*>(pool->get(id.index()));
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
const C *get_component_ptr(Entity::Id id) const {
|
||||
assert_valid(id);
|
||||
BasePool *pool = component_pools_[component_family<C>()];
|
||||
assert(pool);
|
||||
return static_cast<const C*>(pool->get(id.index()));
|
||||
}
|
||||
|
||||
ComponentMask component_mask(Entity::Id id) {
|
||||
assert_valid(id);
|
||||
return entity_component_mask_.at(id.index());
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
ComponentMask component_mask() {
|
||||
ComponentMask mask;
|
||||
mask.set(component_family<C>());
|
||||
return mask;
|
||||
}
|
||||
|
||||
template <typename C1, typename C2, typename ... Components>
|
||||
ComponentMask component_mask() {
|
||||
return component_mask<C1>() | component_mask<C2, Components ...>();
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
ComponentMask component_mask(const ComponentHandle<C> &c) {
|
||||
return component_mask<C>();
|
||||
}
|
||||
|
||||
template <typename C1, typename ... Components>
|
||||
ComponentMask component_mask(const ComponentHandle<C1> &c1, const ComponentHandle<Components> &... args) {
|
||||
return component_mask<C1, Components ...>();
|
||||
}
|
||||
|
||||
inline void accomodate_entity(uint32_t index) {
|
||||
if (entity_component_mask_.size() <= index) {
|
||||
entity_component_mask_.resize(index + 1);
|
||||
entity_version_.resize(index + 1);
|
||||
for (BasePool *pool : component_pools_)
|
||||
if (pool) pool->expand(index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
Pool<C> *accomodate_component() {
|
||||
BaseComponent::Family family = component_family<C>();
|
||||
if (component_pools_.size() <= family) {
|
||||
component_pools_.resize(family + 1, nullptr);
|
||||
}
|
||||
if (!component_pools_[family]) {
|
||||
Pool<C> *pool = new Pool<C>();
|
||||
pool->expand(index_counter_);
|
||||
component_pools_[family] = pool;
|
||||
}
|
||||
return static_cast<Pool<C>*>(component_pools_[family]);
|
||||
}
|
||||
|
||||
|
||||
uint32_t index_counter_ = 0;
|
||||
|
||||
EventManager &event_manager_;
|
||||
// Each element in component_pools_ corresponds to a Pool for a Component.
|
||||
// The index into the vector is the Component::family().
|
||||
std::vector<BasePool*> component_pools_;
|
||||
// Bitmask of components associated with each entity. Index into the vector is the Entity::Id.
|
||||
std::vector<ComponentMask> entity_component_mask_;
|
||||
// Vector of entity version numbers. Incremented each time an entity is destroyed
|
||||
std::vector<uint32_t> entity_version_;
|
||||
// List of available entity slots.
|
||||
std::vector<uint32_t> free_list_;
|
||||
};
|
||||
|
||||
|
||||
template <typename C>
|
||||
BaseComponent::Family Component<C>::family() {
|
||||
static Family family = family_counter_++;
|
||||
assert(family < entityx::MAX_COMPONENTS);
|
||||
return family;
|
||||
}
|
||||
|
||||
|
||||
template <typename C, typename ... Args>
|
||||
ComponentHandle<C> Entity::assign(Args && ... args) {
|
||||
assert(valid());
|
||||
return manager_->assign<C>(id_, std::forward<Args>(args) ...);
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
ComponentHandle<C> Entity::assign_from_copy(const C &component) {
|
||||
assert(valid());
|
||||
return manager_->assign<C>(id_, std::forward<const C &>(component));
|
||||
}
|
||||
|
||||
template <typename C, typename ... Args>
|
||||
ComponentHandle<C> Entity::replace(Args && ... args) {
|
||||
assert(valid());
|
||||
auto handle = component<C>();
|
||||
if (handle) {
|
||||
*(handle.get()) = C(std::forward<Args>(args) ...);
|
||||
} else {
|
||||
handle = manager_->assign<C>(id_, std::forward<Args>(args) ...);
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
void Entity::remove() {
|
||||
assert(valid() && has_component<C>());
|
||||
manager_->remove<C>(id_);
|
||||
}
|
||||
|
||||
template <typename C, typename>
|
||||
ComponentHandle<C> Entity::component() {
|
||||
assert(valid());
|
||||
return manager_->component<C>(id_);
|
||||
}
|
||||
|
||||
template <typename C, typename>
|
||||
const ComponentHandle<C, const EntityManager> Entity::component() const {
|
||||
assert(valid());
|
||||
return const_cast<const EntityManager*>(manager_)->component<const C>(id_);
|
||||
}
|
||||
|
||||
template <typename ... Components>
|
||||
std::tuple<ComponentHandle<Components>...> Entity::components() {
|
||||
assert(valid());
|
||||
return manager_->components<Components...>(id_);
|
||||
}
|
||||
|
||||
template <typename ... Components>
|
||||
std::tuple<ComponentHandle<const Components, const EntityManager>...> Entity::components() const {
|
||||
assert(valid());
|
||||
return const_cast<const EntityManager*>(manager_)->components<const Components...>(id_);
|
||||
}
|
||||
|
||||
|
||||
template <typename C>
|
||||
bool Entity::has_component() const {
|
||||
assert(valid());
|
||||
return manager_->has_component<C>(id_);
|
||||
}
|
||||
|
||||
template <typename A, typename ... Args>
|
||||
void Entity::unpack(ComponentHandle<A> &a, ComponentHandle<Args> & ... args) {
|
||||
assert(valid());
|
||||
manager_->unpack(id_, a, args ...);
|
||||
}
|
||||
|
||||
inline bool Entity::valid() const {
|
||||
return manager_ && manager_->valid(id_);
|
||||
}
|
||||
|
||||
inline std::ostream &operator << (std::ostream &out, const Entity::Id &id) {
|
||||
out << "Entity::Id(" << id.index() << "." << id.version() << ")";
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
inline std::ostream &operator << (std::ostream &out, const Entity &entity) {
|
||||
out << "Entity(" << entity.id() << ")";
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
template <typename C, typename EM>
|
||||
inline ComponentHandle<C, EM>::operator bool() const {
|
||||
return valid();
|
||||
}
|
||||
|
||||
template <typename C, typename EM>
|
||||
inline bool ComponentHandle<C, EM>::valid() const {
|
||||
return manager_ && manager_->valid(id_) && manager_->template has_component<C>(id_);
|
||||
}
|
||||
|
||||
template <typename C, typename EM>
|
||||
inline C *ComponentHandle<C, EM>::operator -> () {
|
||||
assert(valid());
|
||||
return manager_->template get_component_ptr<C>(id_);
|
||||
}
|
||||
|
||||
template <typename C, typename EM>
|
||||
inline const C *ComponentHandle<C, EM>::operator -> () const {
|
||||
assert(valid());
|
||||
return manager_->template get_component_ptr<C>(id_);
|
||||
}
|
||||
|
||||
template <typename C, typename EM>
|
||||
inline C *ComponentHandle<C, EM>::get() {
|
||||
assert(valid());
|
||||
return manager_->template get_component_ptr<C>(id_);
|
||||
}
|
||||
|
||||
template <typename C, typename EM>
|
||||
inline const C *ComponentHandle<C, EM>::get() const {
|
||||
assert(valid());
|
||||
return manager_->template get_component_ptr<C>(id_);
|
||||
}
|
||||
|
||||
template <typename C, typename EM>
|
||||
inline void ComponentHandle<C, EM>::remove() {
|
||||
assert(valid());
|
||||
manager_->template remove<C>(id_);
|
||||
}
|
||||
|
||||
|
||||
} // namespace entityx
|
||||
606
external/entityx-1.1.2/entityx/Entity_test.cc
vendored
Normal file
606
external/entityx-1.1.2/entityx/Entity_test.cc
vendored
Normal file
@@ -0,0 +1,606 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution.
|
||||
*
|
||||
* Author: Alec Thomas <alec@swapoff.org>
|
||||
*/
|
||||
|
||||
#define CATCH_CONFIG_MAIN
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include "entityx/3rdparty/catch.hpp"
|
||||
#include "entityx/entityx.h"
|
||||
|
||||
// using namespace std;
|
||||
using namespace entityx;
|
||||
|
||||
using std::ostream;
|
||||
using std::vector;
|
||||
using std::set;
|
||||
using std::map;
|
||||
using std::pair;
|
||||
using std::string;
|
||||
|
||||
template <typename T>
|
||||
int size(const T &t) {
|
||||
int n = 0;
|
||||
for (auto i : t) {
|
||||
++n;
|
||||
(void)i; // Unused on purpose, suppress warning
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
struct Position {
|
||||
Position(float x = 0.0f, float y = 0.0f) : x(x), y(y) {}
|
||||
|
||||
bool operator==(const Position &other) const {
|
||||
return x == other.x && y == other.y;
|
||||
}
|
||||
|
||||
float x, y;
|
||||
};
|
||||
|
||||
ostream &operator<<(ostream &out, const Position &position) {
|
||||
out << "Position(" << position.x << ", " << position.y << ")";
|
||||
return out;
|
||||
}
|
||||
|
||||
struct Direction {
|
||||
Direction(float x = 0.0f, float y = 0.0f) : x(x), y(y) {}
|
||||
|
||||
bool operator==(const Direction &other) const {
|
||||
return x == other.x && y == other.y;
|
||||
}
|
||||
|
||||
float x, y;
|
||||
};
|
||||
|
||||
ostream &operator<<(ostream &out, const Direction &direction) {
|
||||
out << "Direction(" << direction.x << ", " << direction.y << ")";
|
||||
return out;
|
||||
}
|
||||
|
||||
struct Tag : Component<Tag> {
|
||||
explicit Tag(string tag) : tag(tag) {}
|
||||
|
||||
bool operator==(const Tag &other) const { return tag == other.tag; }
|
||||
|
||||
string tag;
|
||||
};
|
||||
|
||||
ostream &operator<<(ostream &out, const Tag &tag) {
|
||||
out << "Tag(" << tag.tag << ")";
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
struct EntityManagerFixture {
|
||||
EntityManagerFixture() : em(ev) {}
|
||||
|
||||
EventManager ev;
|
||||
EntityManager em;
|
||||
};
|
||||
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestCreateEntity") {
|
||||
REQUIRE(em.size() == 0UL);
|
||||
|
||||
Entity e2;
|
||||
REQUIRE(!(e2.valid()));
|
||||
|
||||
Entity e = em.create();
|
||||
REQUIRE(e.valid());
|
||||
REQUIRE(em.size() == 1UL);
|
||||
|
||||
e2 = e;
|
||||
REQUIRE(e2.valid());
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityAsBoolean") {
|
||||
REQUIRE(em.size() == 0UL);
|
||||
Entity e = em.create();
|
||||
REQUIRE(e.valid());
|
||||
REQUIRE(em.size() == 1UL);
|
||||
REQUIRE(!(!e));
|
||||
|
||||
e.destroy();
|
||||
|
||||
REQUIRE(em.size() == 0UL);
|
||||
|
||||
REQUIRE(!e);
|
||||
|
||||
Entity e2; // Not initialized
|
||||
REQUIRE(!e2);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityReuse") {
|
||||
Entity e1 = em.create();
|
||||
Entity e2 = e1;
|
||||
auto id = e1.id();
|
||||
REQUIRE(e1.valid());
|
||||
REQUIRE(e2.valid());
|
||||
e1.destroy();
|
||||
REQUIRE(!e1.valid());
|
||||
REQUIRE(!e2.valid());
|
||||
Entity e3 = em.create();
|
||||
// It is assumed that the allocation will reuse the same entity id, though
|
||||
// the version will change.
|
||||
auto new_id = e3.id();
|
||||
REQUIRE(new_id != id);
|
||||
REQUIRE((new_id.id() & 0xffffffffUL) == (id.id() & 0xffffffffUL));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentConstruction") {
|
||||
auto e = em.create();
|
||||
auto p = e.assign<Position>(1, 2);
|
||||
auto cp = e.component<Position>();
|
||||
REQUIRE(p == cp);
|
||||
REQUIRE(1.0 == Approx(cp->x));
|
||||
REQUIRE(2.0 == Approx(cp->y));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestDestroyEntity") {
|
||||
Entity e = em.create();
|
||||
Entity f = em.create();
|
||||
e.assign<Position>();
|
||||
f.assign<Position>();
|
||||
e.assign<Direction>();
|
||||
f.assign<Direction>();
|
||||
|
||||
REQUIRE(e.valid());
|
||||
REQUIRE(f.valid());
|
||||
REQUIRE(static_cast<bool>(e.component<Position>()));
|
||||
REQUIRE(static_cast<bool>(e.component<Direction>()));
|
||||
REQUIRE(static_cast<bool>(f.component<Position>()));
|
||||
REQUIRE(static_cast<bool>(f.component<Direction>()));
|
||||
|
||||
e.destroy();
|
||||
|
||||
REQUIRE(!(e.valid()));
|
||||
REQUIRE(f.valid());
|
||||
REQUIRE(static_cast<bool>(f.component<Position>()));
|
||||
REQUIRE(static_cast<bool>(f.component<Direction>()));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestGetEntitiesWithComponent") {
|
||||
Entity e = em.create();
|
||||
Entity f = em.create();
|
||||
Entity g = em.create();
|
||||
e.assign<Position>();
|
||||
e.assign<Direction>();
|
||||
f.assign<Position>();
|
||||
g.assign<Position>();
|
||||
REQUIRE(3 == size(em.entities_with_components<Position>()));
|
||||
REQUIRE(1 == size(em.entities_with_components<Direction>()));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestGetEntitiesWithIntersectionOfComponents") {
|
||||
vector<Entity> entities;
|
||||
for (int i = 0; i < 150; ++i) {
|
||||
Entity e = em.create();
|
||||
entities.push_back(e);
|
||||
if (i % 2 == 0) e.assign<Position>();
|
||||
if (i % 3 == 0) e.assign<Direction>();
|
||||
}
|
||||
REQUIRE(50 == size(em.entities_with_components<Direction>()));
|
||||
REQUIRE(75 == size(em.entities_with_components<Position>()));
|
||||
REQUIRE(25 == size(em.entities_with_components<Direction, Position>()));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestGetEntitiesWithComponentAndUnpacking") {
|
||||
vector<Entity::Id> entities;
|
||||
Entity e = em.create();
|
||||
Entity f = em.create();
|
||||
Entity g = em.create();
|
||||
std::vector<std::pair<ComponentHandle<Position>, ComponentHandle<Direction>>> position_directions;
|
||||
position_directions.push_back(std::make_pair(
|
||||
e.assign<Position>(1.0f, 2.0f), e.assign<Direction>(3.0f, 4.0f)));
|
||||
position_directions.push_back(std::make_pair(
|
||||
f.assign<Position>(7.0f, 8.0f), f.assign<Direction>(9.0f, 10.0f)));
|
||||
auto thetag = f.assign<Tag>("tag");
|
||||
g.assign<Position>(5.0f, 6.0f);
|
||||
int i = 0;
|
||||
|
||||
ComponentHandle<Position> position;
|
||||
REQUIRE(3 == size(em.entities_with_components(position)));
|
||||
|
||||
ComponentHandle<Direction> direction;
|
||||
for (auto unused_entity : em.entities_with_components(position, direction)) {
|
||||
(void)unused_entity;
|
||||
REQUIRE(position);
|
||||
REQUIRE(direction);
|
||||
auto pd = position_directions.at(i);
|
||||
REQUIRE(position == pd.first);
|
||||
REQUIRE(direction == pd.second);
|
||||
++i;
|
||||
}
|
||||
REQUIRE(2 == i);
|
||||
ComponentHandle<Tag> tag;
|
||||
i = 0;
|
||||
for (auto unused_entity :
|
||||
em.entities_with_components(position, direction, tag)) {
|
||||
(void)unused_entity;
|
||||
REQUIRE(static_cast<bool>(position));
|
||||
REQUIRE(static_cast<bool>(direction));
|
||||
REQUIRE(static_cast<bool>(tag));
|
||||
auto pd = position_directions.at(1);
|
||||
REQUIRE(position == pd.first);
|
||||
REQUIRE(direction == pd.second);
|
||||
REQUIRE(tag == thetag);
|
||||
i++;
|
||||
}
|
||||
REQUIRE(1 == i);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestIterateAllEntitiesSkipsDestroyed") {
|
||||
Entity a = em.create();
|
||||
Entity b = em.create();
|
||||
Entity c = em.create();
|
||||
|
||||
b.destroy();
|
||||
|
||||
auto it = em.entities_for_debugging().begin();
|
||||
REQUIRE(a.id() == (*it).id());
|
||||
++it;
|
||||
REQUIRE(c.id() == (*it).id());
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestUnpack") {
|
||||
Entity e = em.create();
|
||||
auto p = e.assign<Position>(1.0, 2.0);
|
||||
auto d = e.assign<Direction>(3.0, 4.0);
|
||||
auto t = e.assign<Tag>("tag");
|
||||
|
||||
ComponentHandle<Position> up;
|
||||
ComponentHandle<Direction> ud;
|
||||
ComponentHandle<Tag> ut;
|
||||
e.unpack(up);
|
||||
REQUIRE(p == up);
|
||||
e.unpack(up, ud);
|
||||
REQUIRE(p == up);
|
||||
REQUIRE(d == ud);
|
||||
e.unpack(up, ud, ut);
|
||||
REQUIRE(p == up);
|
||||
REQUIRE(d == ud);
|
||||
REQUIRE(t == ut);
|
||||
}
|
||||
|
||||
// gcc 4.7.2 does not allow this struct to be declared locally inside the
|
||||
// TEST_CASE_METHOD.EntityManagerFixture, " //" TEST_CASE_METHOD(EntityManagerFixture, "TestUnpackNullMissing") {
|
||||
// Entity e = em.create();
|
||||
// auto p = e.assign<Position>();
|
||||
|
||||
// std::shared_ptr<Position> up(reinterpret_cast<Position*>(0Xdeadbeef),
|
||||
// NullDeleter());
|
||||
// std::shared_ptr<Direction> ud(reinterpret_cast<Direction*>(0Xdeadbeef),
|
||||
// NullDeleter());
|
||||
// e.unpack<Position, Direction>(up, ud);
|
||||
// REQUIRE(p == up);
|
||||
// REQUIRE(std::shared_ptr<Direction>() == ud);
|
||||
// }
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentIdsDiffer") {
|
||||
REQUIRE(EntityManager::component_family<Position>() != EntityManager::component_family<Direction>());
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityCreatedEvent") {
|
||||
struct EntityCreatedEventReceiver
|
||||
: public Receiver<EntityCreatedEventReceiver> {
|
||||
void receive(const EntityCreatedEvent &event) {
|
||||
created.push_back(event.entity);
|
||||
}
|
||||
|
||||
vector<Entity> created;
|
||||
};
|
||||
|
||||
EntityCreatedEventReceiver receiver;
|
||||
ev.subscribe<EntityCreatedEvent>(receiver);
|
||||
|
||||
REQUIRE(0UL == receiver.created.size());
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
em.create();
|
||||
}
|
||||
REQUIRE(10UL == receiver.created.size());
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityDestroyedEvent") {
|
||||
struct EntityDestroyedEventReceiver
|
||||
: public Receiver<EntityDestroyedEventReceiver> {
|
||||
void receive(const EntityDestroyedEvent &event) {
|
||||
destroyed.push_back(event.entity);
|
||||
}
|
||||
|
||||
vector<Entity> destroyed;
|
||||
};
|
||||
|
||||
EntityDestroyedEventReceiver receiver;
|
||||
ev.subscribe<EntityDestroyedEvent>(receiver);
|
||||
|
||||
REQUIRE(0UL == receiver.destroyed.size());
|
||||
vector<Entity> entities;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
entities.push_back(em.create());
|
||||
}
|
||||
REQUIRE(0UL == receiver.destroyed.size());
|
||||
for (auto e : entities) {
|
||||
e.destroy();
|
||||
}
|
||||
REQUIRE(10UL == receiver.destroyed.size());
|
||||
REQUIRE(entities == receiver.destroyed);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentAddedEvent") {
|
||||
struct ComponentAddedEventReceiver
|
||||
: public Receiver<ComponentAddedEventReceiver> {
|
||||
|
||||
ComponentAddedEventReceiver()
|
||||
: position_events(0), direction_events(0) {}
|
||||
|
||||
void receive(const ComponentAddedEvent<Position> &event) {
|
||||
auto p = event.component;
|
||||
float n = static_cast<float>(position_events);
|
||||
REQUIRE(p->x == n);
|
||||
REQUIRE(p->y == n);
|
||||
position_events++;
|
||||
}
|
||||
|
||||
void receive(const ComponentAddedEvent<Direction> &event) {
|
||||
auto p = event.component;
|
||||
float n = static_cast<float>(direction_events);
|
||||
REQUIRE(p->x == -n);
|
||||
REQUIRE(p->y == -n);
|
||||
direction_events++;
|
||||
}
|
||||
|
||||
int position_events;
|
||||
int direction_events;
|
||||
};
|
||||
|
||||
ComponentAddedEventReceiver receiver;
|
||||
ev.subscribe<ComponentAddedEvent<Position>>(receiver);
|
||||
ev.subscribe<ComponentAddedEvent<Direction>>(receiver);
|
||||
|
||||
REQUIRE(ComponentAddedEvent<Position>::family() !=
|
||||
ComponentAddedEvent<Direction>::family());
|
||||
|
||||
REQUIRE(0 == receiver.position_events);
|
||||
REQUIRE(0 == receiver.direction_events);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
Entity e = em.create();
|
||||
e.assign<Position>(static_cast<float>(i), static_cast<float>(i));
|
||||
e.assign<Direction>(static_cast<float>(-i), static_cast<float>(-i));
|
||||
}
|
||||
REQUIRE(10 == receiver.position_events);
|
||||
REQUIRE(10 == receiver.direction_events);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentRemovedEvent") {
|
||||
struct ComponentRemovedReceiver : public Receiver<ComponentRemovedReceiver> {
|
||||
void receive(const ComponentRemovedEvent<Direction> &event) {
|
||||
removed = event.component;
|
||||
}
|
||||
|
||||
ComponentHandle<Direction> removed;
|
||||
};
|
||||
|
||||
ComponentRemovedReceiver receiver;
|
||||
ev.subscribe<ComponentRemovedEvent<Direction>>(receiver);
|
||||
|
||||
REQUIRE(!(receiver.removed));
|
||||
Entity e = em.create();
|
||||
ComponentHandle<Direction> p = e.assign<Direction>(1.0, 2.0);
|
||||
e.remove<Direction>();
|
||||
REQUIRE(receiver.removed == p);
|
||||
REQUIRE(!(e.component<Direction>()));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityAssignment") {
|
||||
Entity a, b;
|
||||
a = em.create();
|
||||
REQUIRE(a != b);
|
||||
b = a;
|
||||
REQUIRE(a == b);
|
||||
a.invalidate();
|
||||
REQUIRE(a != b);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityDestroyAll") {
|
||||
Entity a = em.create(), b = em.create();
|
||||
em.reset();
|
||||
REQUIRE(!(a.valid()));
|
||||
REQUIRE(!(b.valid()));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityDestroyHole") {
|
||||
std::vector<Entity> entities;
|
||||
|
||||
auto count = [this]()->int {
|
||||
auto e = em.entities_with_components<Position>();
|
||||
return std::count_if(e.begin(), e.end(),
|
||||
[](const Entity &) { return true; });
|
||||
};
|
||||
|
||||
for (int i = 0; i < 5000; i++) {
|
||||
auto e = em.create();
|
||||
e.assign<Position>();
|
||||
entities.push_back(e);
|
||||
}
|
||||
|
||||
REQUIRE(count() == 5000);
|
||||
|
||||
entities[2500].destroy();
|
||||
|
||||
REQUIRE(count() == 4999);
|
||||
}
|
||||
|
||||
// TODO(alec): Disable this on OSX - it doesn't seem to be possible to catch it?!?
|
||||
// TEST_CASE_METHOD(EntityManagerFixture, "DeleteComponentThrowsBadAlloc") {
|
||||
// Position *position = new Position();
|
||||
// REQUIRE_THROWS_AS(delete position, std::bad_alloc);
|
||||
// }
|
||||
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentHandleInvalidatedWhenEntityDestroyed") {
|
||||
Entity a = em.create();
|
||||
ComponentHandle<Position> position = a.assign<Position>(1, 2);
|
||||
REQUIRE(position);
|
||||
REQUIRE(position->x == 1);
|
||||
REQUIRE(position->y == 2);
|
||||
a.destroy();
|
||||
REQUIRE(!position);
|
||||
}
|
||||
|
||||
struct CopyVerifier : Component<CopyVerifier> {
|
||||
CopyVerifier() : copied(false) {}
|
||||
CopyVerifier(const CopyVerifier &other) {
|
||||
copied = other.copied + 1;
|
||||
}
|
||||
|
||||
int copied;
|
||||
};
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentAssignmentFromCopy") {
|
||||
Entity a = em.create();
|
||||
CopyVerifier original;
|
||||
ComponentHandle<CopyVerifier> copy = a.assign_from_copy(original);
|
||||
REQUIRE(copy);
|
||||
REQUIRE(copy->copied == 1);
|
||||
a.destroy();
|
||||
REQUIRE(!copy);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentHandleInvalidatedWhenComponentDestroyed") {
|
||||
Entity a = em.create();
|
||||
ComponentHandle<Position> position = a.assign<Position>(1, 2);
|
||||
REQUIRE(position);
|
||||
REQUIRE(position->x == 1);
|
||||
REQUIRE(position->y == 2);
|
||||
a.remove<Position>();
|
||||
REQUIRE(!position);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestDeleteEntityWithNoComponents") {
|
||||
Entity a = em.create();
|
||||
a.assign<Position>(1, 2);
|
||||
Entity b = em.create();
|
||||
b.destroy();
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityInStdSet") {
|
||||
Entity a = em.create();
|
||||
Entity b = em.create();
|
||||
Entity c = em.create();
|
||||
set<Entity> entitySet;
|
||||
REQUIRE(entitySet.insert(a).second);
|
||||
REQUIRE(entitySet.insert(b).second);
|
||||
REQUIRE(entitySet.insert(c).second);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityInStdMap") {
|
||||
Entity a = em.create();
|
||||
Entity b = em.create();
|
||||
Entity c = em.create();
|
||||
map<Entity, int> entityMap;
|
||||
REQUIRE(entityMap.insert(pair<Entity, int>(a, 1)).second);
|
||||
REQUIRE(entityMap.insert(pair<Entity, int>(b, 2)).second);
|
||||
REQUIRE(entityMap.insert(pair<Entity, int>(c, 3)).second);
|
||||
REQUIRE(entityMap[a] == 1);
|
||||
REQUIRE(entityMap[b] == 2);
|
||||
REQUIRE(entityMap[c] == 3);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityComponentsFromTuple") {
|
||||
Entity e = em.create();
|
||||
e.assign<Position>(1, 2);
|
||||
e.assign<Direction>(3, 4);
|
||||
|
||||
std::tuple<ComponentHandle<Position>, ComponentHandle<Direction>> components = e.components<Position, Direction>();
|
||||
|
||||
REQUIRE(std::get<0>(components)->x == 1);
|
||||
REQUIRE(std::get<0>(components)->y == 2);
|
||||
REQUIRE(std::get<1>(components)->x == 3);
|
||||
REQUIRE(std::get<1>(components)->y == 4);
|
||||
}
|
||||
|
||||
TEST_CASE("TestComponentDestructorCalledWhenManagerDestroyed") {
|
||||
struct Freed {
|
||||
explicit Freed(bool &yes) : yes(yes) {}
|
||||
~Freed() { yes = true; }
|
||||
|
||||
bool &yes;
|
||||
};
|
||||
|
||||
struct Test : Component<Test> {
|
||||
explicit Test(bool &yes) : freed(yes) {}
|
||||
|
||||
Freed freed;
|
||||
};
|
||||
|
||||
bool freed = false;
|
||||
{
|
||||
EntityX e;
|
||||
auto test = e.entities.create();
|
||||
test.assign<Test>(freed);
|
||||
}
|
||||
REQUIRE(freed == true);
|
||||
}
|
||||
|
||||
TEST_CASE("TestComponentDestructorCalledWhenEntityDestroyed") {
|
||||
struct Freed {
|
||||
explicit Freed(bool &yes) : yes(yes) {}
|
||||
~Freed() { yes = true; }
|
||||
|
||||
bool &yes;
|
||||
};
|
||||
|
||||
struct Test : Component<Test> {
|
||||
explicit Test(bool &yes) : freed(yes) {}
|
||||
|
||||
Freed freed;
|
||||
};
|
||||
|
||||
bool freed = false;
|
||||
EntityX e;
|
||||
auto test = e.entities.create();
|
||||
test.assign<Test>(freed);
|
||||
REQUIRE(freed == false);
|
||||
test.destroy();
|
||||
REQUIRE(freed == true);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentsRemovedFromReusedEntities") {
|
||||
Entity a = em.create();
|
||||
Entity::Id aid = a.id();
|
||||
a.assign<Position>(1, 2);
|
||||
a.destroy();
|
||||
|
||||
Entity b = em.create();
|
||||
Entity::Id bid = b.id();
|
||||
|
||||
REQUIRE(aid.index() == bid.index());
|
||||
REQUIRE(!b.has_component<Position>());
|
||||
b.assign<Position>(3, 4);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntityManagerFixture, "TestConstComponentsNotInstantiatedTwice") {
|
||||
Entity a = em.create();
|
||||
a.assign<Position>(1, 2);
|
||||
|
||||
const Entity b = a;
|
||||
|
||||
REQUIRE(a.component<Position>().valid());
|
||||
REQUIRE(b.component<const Position>().valid());
|
||||
REQUIRE(b.component<const Position>()->x == 1);
|
||||
REQUIRE(b.component<const Position>()->y == 2);
|
||||
}
|
||||
26
external/entityx-1.1.2/entityx/Event.cc
vendored
Normal file
26
external/entityx-1.1.2/entityx/Event.cc
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution.
|
||||
*
|
||||
* Author: Alec Thomas <alec@swapoff.org>
|
||||
*/
|
||||
|
||||
#include "entityx/Event.h"
|
||||
|
||||
namespace entityx {
|
||||
|
||||
BaseEvent::Family BaseEvent::family_counter_ = 0;
|
||||
|
||||
BaseEvent::~BaseEvent() {
|
||||
}
|
||||
|
||||
EventManager::EventManager() {
|
||||
}
|
||||
|
||||
EventManager::~EventManager() {
|
||||
}
|
||||
|
||||
} // namespace entityx
|
||||
216
external/entityx-1.1.2/entityx/Event.h
vendored
Normal file
216
external/entityx-1.1.2/entityx/Event.h
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution.
|
||||
*
|
||||
* Author: Alec Thomas <alec@swapoff.org>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include "entityx/config.h"
|
||||
#include "entityx/3rdparty/simplesignal.h"
|
||||
#include "entityx/help/NonCopyable.h"
|
||||
|
||||
|
||||
namespace entityx {
|
||||
|
||||
|
||||
/// Used internally by the EventManager.
|
||||
class BaseEvent {
|
||||
public:
|
||||
typedef std::size_t Family;
|
||||
|
||||
virtual ~BaseEvent();
|
||||
|
||||
protected:
|
||||
static Family family_counter_;
|
||||
};
|
||||
|
||||
|
||||
typedef Simple::Signal<void (const void*)> EventSignal;
|
||||
typedef std::shared_ptr<EventSignal> EventSignalPtr;
|
||||
typedef std::weak_ptr<EventSignal> EventSignalWeakPtr;
|
||||
|
||||
|
||||
/**
|
||||
* Event types should subclass from this.
|
||||
*
|
||||
* struct Explosion : public Event<Explosion> {
|
||||
* Explosion(int damage) : damage(damage) {}
|
||||
* int damage;
|
||||
* };
|
||||
*/
|
||||
template <typename Derived>
|
||||
class Event : public BaseEvent {
|
||||
public:
|
||||
/// Used internally for registration.
|
||||
static Family family() {
|
||||
static Family family = family_counter_++;
|
||||
return family;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class BaseReceiver {
|
||||
public:
|
||||
virtual ~BaseReceiver() {
|
||||
for (auto connection : connections_) {
|
||||
auto &ptr = connection.second.first;
|
||||
if (!ptr.expired()) {
|
||||
ptr.lock()->disconnect(connection.second.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return number of signals connected to this receiver.
|
||||
std::size_t connected_signals() const {
|
||||
std::size_t size = 0;
|
||||
for (auto connection : connections_) {
|
||||
if (!connection.second.first.expired()) {
|
||||
size++;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class EventManager;
|
||||
std::unordered_map<BaseEvent::Family, std::pair<EventSignalWeakPtr, std::size_t>> connections_;
|
||||
};
|
||||
|
||||
|
||||
template <typename Derived>
|
||||
class Receiver : public BaseReceiver {
|
||||
public:
|
||||
virtual ~Receiver() {}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles event subscription and delivery.
|
||||
*
|
||||
* Subscriptions are automatically removed when receivers are destroyed..
|
||||
*/
|
||||
class EventManager : entityx::help::NonCopyable {
|
||||
public:
|
||||
EventManager();
|
||||
virtual ~EventManager();
|
||||
|
||||
/**
|
||||
* Subscribe an object to receive events of type E.
|
||||
*
|
||||
* Receivers must be subclasses of Receiver and must implement a receive() method accepting the given event type.
|
||||
*
|
||||
* eg.
|
||||
*
|
||||
* struct ExplosionReceiver : public Receiver<ExplosionReceiver> {
|
||||
* void receive(const Explosion &explosion) {
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* ExplosionReceiver receiver;
|
||||
* em.subscribe<Explosion>(receiver);
|
||||
*/
|
||||
template <typename E, typename Receiver>
|
||||
void subscribe(Receiver &receiver) {
|
||||
void (Receiver::*receive)(const E &) = &Receiver::receive;
|
||||
auto sig = signal_for(Event<E>::family());
|
||||
auto wrapper = EventCallbackWrapper<E>(std::bind(receive, &receiver, std::placeholders::_1));
|
||||
auto connection = sig->connect(wrapper);
|
||||
BaseReceiver &base = receiver;
|
||||
base.connections_.insert(std::make_pair(Event<E>::family(), std::make_pair(EventSignalWeakPtr(sig), connection)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe an object in order to not receive events of type E anymore.
|
||||
*
|
||||
* Receivers must have subscribed for event E before unsubscribing from event E.
|
||||
*
|
||||
*/
|
||||
template <typename E, typename Receiver>
|
||||
void unsubscribe(Receiver &receiver) {
|
||||
BaseReceiver &base = receiver;
|
||||
//Assert that it has been subscribed before
|
||||
assert(base.connections_.find(Event<E>::family()) != base.connections_.end());
|
||||
auto pair = base.connections_[Event<E>::family()];
|
||||
auto connection = pair.second;
|
||||
auto &ptr = pair.first;
|
||||
if (!ptr.expired()) {
|
||||
ptr.lock()->disconnect(connection);
|
||||
}
|
||||
base.connections_.erase(Event<E>::family());
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void emit(const E &event) {
|
||||
auto sig = signal_for(Event<E>::family());
|
||||
sig->emit(&event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit an already constructed event.
|
||||
*/
|
||||
template <typename E>
|
||||
void emit(std::unique_ptr<E> event) {
|
||||
auto sig = signal_for(Event<E>::family());
|
||||
sig->emit(event.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit an event to receivers.
|
||||
*
|
||||
* This method constructs a new event object of type E with the provided arguments, then delivers it to all receivers.
|
||||
*
|
||||
* eg.
|
||||
*
|
||||
* std::shared_ptr<EventManager> em = new EventManager();
|
||||
* em->emit<Explosion>(10);
|
||||
*
|
||||
*/
|
||||
template <typename E, typename ... Args>
|
||||
void emit(Args && ... args) {
|
||||
// Using 'E event(std::forward...)' causes VS to fail with an internal error. Hack around it.
|
||||
E event = E(std::forward<Args>(args) ...);
|
||||
auto sig = signal_for(std::size_t(Event<E>::family()));
|
||||
sig->emit(&event);
|
||||
}
|
||||
|
||||
std::size_t connected_receivers() const {
|
||||
std::size_t size = 0;
|
||||
for (EventSignalPtr handler : handlers_) {
|
||||
if (handler) size += handler->size();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
private:
|
||||
EventSignalPtr &signal_for(std::size_t id) {
|
||||
if (id >= handlers_.size())
|
||||
handlers_.resize(id + 1);
|
||||
if (!handlers_[id])
|
||||
handlers_[id] = std::make_shared<EventSignal>();
|
||||
return handlers_[id];
|
||||
}
|
||||
|
||||
// Functor used as an event signal callback that casts to E.
|
||||
template <typename E>
|
||||
struct EventCallbackWrapper {
|
||||
EventCallbackWrapper(std::function<void(const E &)> callback) : callback(callback) {}
|
||||
void operator()(const void *event) { callback(*(static_cast<const E*>(event))); }
|
||||
std::function<void(const E &)> callback;
|
||||
};
|
||||
|
||||
std::vector<EventSignalPtr> handlers_;
|
||||
};
|
||||
|
||||
} // namespace entityx
|
||||
109
external/entityx-1.1.2/entityx/Event_test.cc
vendored
Normal file
109
external/entityx-1.1.2/entityx/Event_test.cc
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution.
|
||||
*
|
||||
* Author: Alec Thomas <alec@swapoff.org>
|
||||
*/
|
||||
|
||||
#define CATCH_CONFIG_MAIN
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "entityx/3rdparty/catch.hpp"
|
||||
#include "entityx/Event.h"
|
||||
|
||||
|
||||
using entityx::EventManager;
|
||||
using entityx::Event;
|
||||
using entityx::Receiver;
|
||||
|
||||
|
||||
struct Explosion {
|
||||
explicit Explosion(int damage) : damage(damage) {}
|
||||
int damage;
|
||||
};
|
||||
|
||||
|
||||
struct Collision {
|
||||
explicit Collision(int damage) : damage(damage) {}
|
||||
int damage;
|
||||
};
|
||||
|
||||
struct ExplosionSystem : public Receiver<ExplosionSystem> {
|
||||
void receive(const Explosion &explosion) {
|
||||
damage_received += explosion.damage;
|
||||
}
|
||||
|
||||
void receive(const Collision &collision) {
|
||||
damage_received += collision.damage;
|
||||
}
|
||||
|
||||
int damage_received = 0;
|
||||
};
|
||||
|
||||
TEST_CASE("TestEmitReceive") {
|
||||
EventManager em;
|
||||
ExplosionSystem explosion_system;
|
||||
em.subscribe<Explosion>(explosion_system);
|
||||
em.subscribe<Collision>(explosion_system);
|
||||
REQUIRE(0 == explosion_system.damage_received);
|
||||
em.emit<Explosion>(10);
|
||||
REQUIRE(10 == explosion_system.damage_received);
|
||||
em.emit<Collision>(10);
|
||||
REQUIRE(20 == explosion_system.damage_received);
|
||||
}
|
||||
|
||||
TEST_CASE("TestUntypedEmitReceive") {
|
||||
EventManager em;
|
||||
ExplosionSystem explosion_system;
|
||||
em.subscribe<Explosion>(explosion_system);
|
||||
REQUIRE(0 == explosion_system.damage_received);
|
||||
Explosion explosion(10);
|
||||
em.emit(explosion);
|
||||
REQUIRE(10 == explosion_system.damage_received);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("TestReceiverExpired") {
|
||||
EventManager em;
|
||||
{
|
||||
ExplosionSystem explosion_system;
|
||||
em.subscribe<Explosion>(explosion_system);
|
||||
em.emit<Explosion>(10);
|
||||
REQUIRE(10 == explosion_system.damage_received);
|
||||
REQUIRE(1 == explosion_system.connected_signals());
|
||||
REQUIRE(1 == em.connected_receivers());
|
||||
}
|
||||
REQUIRE(0 == em.connected_receivers());
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("TestSenderExpired") {
|
||||
ExplosionSystem explosion_system;
|
||||
{
|
||||
EventManager em;
|
||||
em.subscribe<Explosion>(explosion_system);
|
||||
em.emit<Explosion>(10);
|
||||
REQUIRE(10 == explosion_system.damage_received);
|
||||
REQUIRE(1 == explosion_system.connected_signals());
|
||||
REQUIRE(1 == em.connected_receivers());
|
||||
}
|
||||
REQUIRE(0 == explosion_system.connected_signals());
|
||||
}
|
||||
|
||||
TEST_CASE("TestUnsubscription") {
|
||||
ExplosionSystem explosion_system;
|
||||
{
|
||||
EventManager em;
|
||||
em.subscribe<Explosion>(explosion_system);
|
||||
REQUIRE(explosion_system.damage_received == 0);
|
||||
em.emit<Explosion>(1);
|
||||
REQUIRE(explosion_system.damage_received == 1);
|
||||
em.unsubscribe<Explosion>(explosion_system);
|
||||
em.emit<Explosion>(1);
|
||||
REQUIRE(explosion_system.damage_received == 1);
|
||||
}
|
||||
}
|
||||
34
external/entityx-1.1.2/entityx/System.cc
vendored
Normal file
34
external/entityx-1.1.2/entityx/System.cc
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution.
|
||||
*
|
||||
* Author: Alec Thomas <alec@swapoff.org>
|
||||
*/
|
||||
|
||||
#include "entityx/System.h"
|
||||
|
||||
namespace entityx {
|
||||
|
||||
BaseSystem::Family BaseSystem::family_counter_;
|
||||
|
||||
BaseSystem::~BaseSystem() {
|
||||
}
|
||||
|
||||
void SystemManager::update_all(TimeDelta dt) {
|
||||
assert(initialized_ && "SystemManager::configure() not called");
|
||||
for (auto &pair : systems_) {
|
||||
pair.second->update(entity_manager_, event_manager_, dt);
|
||||
}
|
||||
}
|
||||
|
||||
void SystemManager::configure() {
|
||||
for (auto &pair : systems_) {
|
||||
pair.second->configure(event_manager_);
|
||||
}
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
} // namespace entityx
|
||||
172
external/entityx-1.1.2/entityx/System.h
vendored
Normal file
172
external/entityx-1.1.2/entityx/System.h
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution.
|
||||
*
|
||||
* Author: Alec Thomas <alec@swapoff.org>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <cassert>
|
||||
#include "entityx/config.h"
|
||||
#include "entityx/Entity.h"
|
||||
#include "entityx/Event.h"
|
||||
#include "entityx/help/NonCopyable.h"
|
||||
|
||||
|
||||
namespace entityx {
|
||||
|
||||
|
||||
class SystemManager;
|
||||
|
||||
|
||||
/**
|
||||
* Base System class. Generally should not be directly used, instead see System<Derived>.
|
||||
*/
|
||||
class BaseSystem : entityx::help::NonCopyable {
|
||||
public:
|
||||
typedef size_t Family;
|
||||
|
||||
virtual ~BaseSystem();
|
||||
|
||||
/**
|
||||
* Called once all Systems have been added to the SystemManager.
|
||||
*
|
||||
* Typically used to set up event handlers.
|
||||
*/
|
||||
virtual void configure(EventManager &events) {}
|
||||
|
||||
/**
|
||||
* Apply System behavior.
|
||||
*
|
||||
* Called every game step.
|
||||
*/
|
||||
virtual void update(EntityManager &entities, EventManager &events, TimeDelta dt) = 0;
|
||||
|
||||
static Family family_counter_;
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Use this class when implementing Systems.
|
||||
*
|
||||
* struct MovementSystem : public System<MovementSystem> {
|
||||
* void update(EntityManager &entities, EventManager &events, TimeDelta dt) {
|
||||
* // Do stuff to/with entities...
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
template <typename Derived>
|
||||
class System : public BaseSystem {
|
||||
public:
|
||||
virtual ~System() {}
|
||||
|
||||
private:
|
||||
friend class SystemManager;
|
||||
|
||||
static Family family() {
|
||||
static Family family = family_counter_++;
|
||||
return family;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class SystemManager : entityx::help::NonCopyable {
|
||||
public:
|
||||
SystemManager(EntityManager &entity_manager,
|
||||
EventManager &event_manager) :
|
||||
entity_manager_(entity_manager),
|
||||
event_manager_(event_manager) {}
|
||||
|
||||
/**
|
||||
* Add a System to the SystemManager.
|
||||
*
|
||||
* Must be called before Systems can be used.
|
||||
*
|
||||
* eg.
|
||||
* std::shared_ptr<MovementSystem> movement = entityx::make_shared<MovementSystem>();
|
||||
* system.add(movement);
|
||||
*/
|
||||
template <typename S>
|
||||
void add(std::shared_ptr<S> system) {
|
||||
systems_.insert(std::make_pair(S::family(), system));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a System to the SystemManager.
|
||||
*
|
||||
* Must be called before Systems can be used.
|
||||
*
|
||||
* eg.
|
||||
* auto movement = system.add<MovementSystem>();
|
||||
*/
|
||||
template <typename S, typename ... Args>
|
||||
std::shared_ptr<S> add(Args && ... args) {
|
||||
std::shared_ptr<S> s(new S(std::forward<Args>(args) ...));
|
||||
add(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the registered System instance, if any.
|
||||
*
|
||||
* std::shared_ptr<CollisionSystem> collisions = systems.system<CollisionSystem>();
|
||||
*
|
||||
* @return System instance or empty shared_std::shared_ptr<S>.
|
||||
*/
|
||||
template <typename S>
|
||||
std::shared_ptr<S> system() {
|
||||
auto it = systems_.find(S::family());
|
||||
assert(it != systems_.end());
|
||||
return it == systems_.end()
|
||||
? std::shared_ptr<S>()
|
||||
: std::shared_ptr<S>(std::static_pointer_cast<S>(it->second));
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the System::update() method for a registered system.
|
||||
*/
|
||||
template <typename S>
|
||||
void update(TimeDelta dt) {
|
||||
assert(initialized_ && "SystemManager::configure() not called");
|
||||
std::shared_ptr<S> s = system<S>();
|
||||
s->update(entity_manager_, event_manager_, dt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call System::update() on all registered systems.
|
||||
*
|
||||
* The order which the registered systems are updated is arbitrary but consistent,
|
||||
* meaning the order which they will be updated cannot be specified, but that order
|
||||
* will stay the same as long no systems are added or removed.
|
||||
*
|
||||
* If the order in which systems update is important, use SystemManager::update()
|
||||
* to manually specify the update order. EntityX does not yet support a way of
|
||||
* specifying priority for update_all().
|
||||
*/
|
||||
void update_all(TimeDelta dt);
|
||||
|
||||
/**
|
||||
* Configure the system. Call after adding all Systems.
|
||||
*
|
||||
* This is typically used to set up event handlers.
|
||||
*/
|
||||
void configure();
|
||||
|
||||
private:
|
||||
bool initialized_ = false;
|
||||
EntityManager &entity_manager_;
|
||||
EventManager &event_manager_;
|
||||
std::unordered_map<BaseSystem::Family, std::shared_ptr<BaseSystem>> systems_;
|
||||
};
|
||||
|
||||
} // namespace entityx
|
||||
135
external/entityx-1.1.2/entityx/System_test.cc
vendored
Normal file
135
external/entityx-1.1.2/entityx/System_test.cc
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution.
|
||||
*
|
||||
* Author: Alec Thomas <alec@swapoff.org>
|
||||
*/
|
||||
|
||||
#define CATCH_CONFIG_MAIN
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "entityx/3rdparty/catch.hpp"
|
||||
#include "entityx/System.h"
|
||||
#include "entityx/quick.h"
|
||||
|
||||
// using namespace std;
|
||||
using namespace entityx;
|
||||
using std::string;
|
||||
|
||||
struct Position : Component<Position> {
|
||||
explicit Position(float x = 0.0f, float y = 0.0f) : x(x), y(y) {}
|
||||
|
||||
float x, y;
|
||||
};
|
||||
|
||||
struct Direction : Component<Direction> {
|
||||
explicit Direction(float x = 0.0f, float y = 0.0f) : x(x), y(y) {}
|
||||
|
||||
float x, y;
|
||||
};
|
||||
|
||||
struct Counter : Component<Counter> {
|
||||
explicit Counter(int counter = 0) : counter(counter) {}
|
||||
|
||||
int counter;
|
||||
};
|
||||
|
||||
class MovementSystem : public System<MovementSystem> {
|
||||
public:
|
||||
explicit MovementSystem(string label = "") : label(label) {}
|
||||
|
||||
void update(EntityManager &es, EventManager &events, TimeDelta) override {
|
||||
EntityManager::View entities =
|
||||
es.entities_with_components<Position, Direction>();
|
||||
ComponentHandle<Position> position;
|
||||
ComponentHandle<Direction> direction;
|
||||
for (auto entity : entities) {
|
||||
entity.unpack<Position, Direction>(position, direction);
|
||||
position->x += direction->x;
|
||||
position->y += direction->y;
|
||||
}
|
||||
}
|
||||
|
||||
string label;
|
||||
};
|
||||
|
||||
class CounterSystem : public System<CounterSystem> {
|
||||
public:
|
||||
void update(EntityManager &es, EventManager &events, TimeDelta) override {
|
||||
EntityManager::View entities =
|
||||
es.entities_with_components<Counter>();
|
||||
Counter::Handle counter;
|
||||
for (auto entity : entities) {
|
||||
entity.unpack<Counter>(counter);
|
||||
counter->counter++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class EntitiesFixture : public EntityX {
|
||||
public:
|
||||
std::vector<Entity> created_entities;
|
||||
|
||||
EntitiesFixture() {
|
||||
for (int i = 0; i < 150; ++i) {
|
||||
Entity e = entities.create();
|
||||
created_entities.push_back(e);
|
||||
if (i % 2 == 0) e.assign<Position>(1, 2);
|
||||
if (i % 3 == 0) e.assign<Direction>(1, 1);
|
||||
|
||||
e.assign<Counter>(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE_METHOD(EntitiesFixture, "TestConstructSystemWithArgs") {
|
||||
systems.add<MovementSystem>("movement");
|
||||
systems.configure();
|
||||
|
||||
REQUIRE("movement" == systems.system<MovementSystem>()->label);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntitiesFixture, "TestApplySystem") {
|
||||
systems.add<MovementSystem>();
|
||||
systems.configure();
|
||||
|
||||
systems.update<MovementSystem>(0.0);
|
||||
ComponentHandle<Position> position;
|
||||
ComponentHandle<Direction> direction;
|
||||
for (auto entity : created_entities) {
|
||||
entity.unpack<Position, Direction>(position, direction);
|
||||
if (position && direction) {
|
||||
REQUIRE(2.0 == Approx(position->x));
|
||||
REQUIRE(3.0 == Approx(position->y));
|
||||
} else if (position) {
|
||||
REQUIRE(1.0 == Approx(position->x));
|
||||
REQUIRE(2.0 == Approx(position->y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(EntitiesFixture, "TestApplyAllSystems") {
|
||||
systems.add<MovementSystem>();
|
||||
systems.add<CounterSystem>();
|
||||
systems.configure();
|
||||
|
||||
systems.update_all(0.0);
|
||||
Position::Handle position;
|
||||
Direction::Handle direction;
|
||||
Counter::Handle counter;
|
||||
for (auto entity : created_entities) {
|
||||
entity.unpack<Position, Direction, Counter>(position, direction, counter);
|
||||
if (position && direction) {
|
||||
REQUIRE(2.0 == Approx(position->x));
|
||||
REQUIRE(3.0 == Approx(position->y));
|
||||
} else if (position) {
|
||||
REQUIRE(1.0 == Approx(position->x));
|
||||
REQUIRE(2.0 == Approx(position->y));
|
||||
}
|
||||
REQUIRE(1 == counter->counter);
|
||||
}
|
||||
}
|
||||
11
external/entityx-1.1.2/entityx/config.h.in
vendored
Normal file
11
external/entityx-1.1.2/entityx/config.h.in
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
namespace entityx {
|
||||
|
||||
static const size_t MAX_COMPONENTS = @ENTITYX_MAX_COMPONENTS@;
|
||||
typedef @ENTITYX_DT_TYPE@ TimeDelta;
|
||||
|
||||
} // namespace entityx
|
||||
54
external/entityx-1.1.2/entityx/deps/Dependencies.h
vendored
Normal file
54
external/entityx-1.1.2/entityx/deps/Dependencies.h
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Alec Thomas <alec@swapoff.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution.
|
||||
*
|
||||
* Author: Alec Thomas <alec@swapoff.org>
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "entityx/System.h"
|
||||
#include "entityx/Event.h"
|
||||
#include "entityx/Entity.h"
|
||||
|
||||
namespace entityx {
|
||||
namespace deps {
|
||||
|
||||
/**
|
||||
* An entityx::System for declaring component dependencies.
|
||||
*
|
||||
* eg. To declare that a `Physics` component must always be paired with `Position`
|
||||
* and `Direction` components:
|
||||
*
|
||||
* system_manager->add<Dependency<Physics, Position, Direction>>();
|
||||
*/
|
||||
template <typename C, typename ... Deps>
|
||||
class Dependency : public System<Dependency<C, Deps...>>, public Receiver<Dependency<C, Deps...>> {
|
||||
public:
|
||||
void receive(const ComponentAddedEvent<C> &event) {
|
||||
assign<Deps...>(event.entity);
|
||||
}
|
||||
|
||||
virtual void configure(EventManager &events) override {
|
||||
events.subscribe<ComponentAddedEvent<C>>(*this);
|
||||
}
|
||||
|
||||
virtual void update(EntityManager &entities, EventManager &events, TimeDelta dt) override {}
|
||||
|
||||
private:
|
||||
template <typename D>
|
||||
void assign(Entity entity) {
|
||||
if (!entity.component<D>()) entity.assign<D>();
|
||||
}
|
||||
|
||||
template <typename D, typename D1, typename ... Ds>
|
||||
void assign(Entity entity) {
|
||||
assign<D>(entity);
|
||||
assign<D1, Ds...>(entity);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace deps
|
||||
} // namespace entityx
|
||||
64
external/entityx-1.1.2/entityx/deps/Dependencies_test.cc
vendored
Normal file
64
external/entityx-1.1.2/entityx/deps/Dependencies_test.cc
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Alec Thomas <alec@swapoff.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution.
|
||||
*
|
||||
* Author: Alec Thomas <alec@swapoff.org>
|
||||
*/
|
||||
|
||||
#define CATCH_CONFIG_MAIN
|
||||
|
||||
#include "entityx/3rdparty/catch.hpp"
|
||||
#include "entityx/deps/Dependencies.h"
|
||||
#include "entityx/quick.h"
|
||||
|
||||
|
||||
namespace deps = entityx::deps;
|
||||
|
||||
|
||||
struct A : public entityx::Component<A> {};
|
||||
struct B : public entityx::Component<B> {
|
||||
explicit B(bool b = false) : b(b) {}
|
||||
|
||||
bool b;
|
||||
};
|
||||
struct C : public entityx::Component<C> {};
|
||||
|
||||
TEST_CASE_METHOD(entityx::EntityX, "TestSingleDependency") {
|
||||
systems.add<deps::Dependency<A, B>>();
|
||||
systems.configure();
|
||||
|
||||
entityx::Entity e = entities.create();
|
||||
REQUIRE(!static_cast<bool>(e.component<A>()));
|
||||
REQUIRE(!static_cast<bool>(e.component<B>()));
|
||||
e.assign<A>();
|
||||
REQUIRE(static_cast<bool>(e.component<A>()));
|
||||
REQUIRE(static_cast<bool>(e.component<B>()));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(entityx::EntityX, "TestMultipleDependencies") {
|
||||
systems.add<deps::Dependency<A, B, C>>();
|
||||
systems.configure();
|
||||
|
||||
entityx::Entity e = entities.create();
|
||||
REQUIRE(!static_cast<bool>(e.component<A>()));
|
||||
REQUIRE(!static_cast<bool>(e.component<B>()));
|
||||
REQUIRE(!static_cast<bool>(e.component<C>()));
|
||||
e.assign<A>();
|
||||
REQUIRE(static_cast<bool>(e.component<A>()));
|
||||
REQUIRE(static_cast<bool>(e.component<B>()));
|
||||
REQUIRE(static_cast<bool>(e.component<C>()));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(entityx::EntityX, "TestDependencyDoesNotRecreateComponent") {
|
||||
systems.add<deps::Dependency<A, B>>();
|
||||
systems.configure();
|
||||
|
||||
entityx::Entity e = entities.create();
|
||||
e.assign<B>(true);
|
||||
REQUIRE(e.component<B>()->b);
|
||||
e.assign<A>();
|
||||
REQUIRE(e.component<B>()->b);
|
||||
}
|
||||
7
external/entityx-1.1.2/entityx/entityx.h
vendored
Normal file
7
external/entityx-1.1.2/entityx/entityx.h
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "entityx/config.h"
|
||||
#include "entityx/Event.h"
|
||||
#include "entityx/Entity.h"
|
||||
#include "entityx/System.h"
|
||||
#include "entityx/quick.h"
|
||||
20
external/entityx-1.1.2/entityx/help/NonCopyable.h
vendored
Normal file
20
external/entityx-1.1.2/entityx/help/NonCopyable.h
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// Inspired heavily by boost::noncopyable
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace entityx {
|
||||
namespace help {
|
||||
|
||||
class NonCopyable {
|
||||
protected:
|
||||
NonCopyable() = default;
|
||||
~NonCopyable() = default;
|
||||
|
||||
|
||||
NonCopyable(const NonCopyable&) = delete;
|
||||
NonCopyable& operator = (const NonCopyable &) = delete;
|
||||
};
|
||||
|
||||
|
||||
} // namespace help
|
||||
} // namespace entityx
|
||||
21
external/entityx-1.1.2/entityx/help/Pool.cc
vendored
Normal file
21
external/entityx-1.1.2/entityx/help/Pool.cc
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2014 Alec Thomas <alec@swapoff.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution.
|
||||
*
|
||||
* Author: Alec Thomas <alec@swapoff.org>
|
||||
*/
|
||||
|
||||
#include "entityx/help/Pool.h"
|
||||
|
||||
namespace entityx {
|
||||
|
||||
BasePool::~BasePool() {
|
||||
for (char *ptr : blocks_) {
|
||||
delete[] ptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace entityx
|
||||
95
external/entityx-1.1.2/entityx/help/Pool.h
vendored
Normal file
95
external/entityx-1.1.2/entityx/help/Pool.h
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2014 Alec Thomas <alec@swapoff.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution.
|
||||
*
|
||||
* Author: Alec Thomas <alec@swapoff.org>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
||||
namespace entityx {
|
||||
|
||||
/**
|
||||
* Provides a resizable, semi-contiguous pool of memory for constructing
|
||||
* objects in. Pointers into the pool will be invalided only when the pool is
|
||||
* destroyed.
|
||||
*
|
||||
* The semi-contiguous nature aims to provide cache-friendly iteration.
|
||||
*
|
||||
* Lookups are O(1).
|
||||
* Appends are amortized O(1).
|
||||
*/
|
||||
class BasePool {
|
||||
public:
|
||||
explicit BasePool(std::size_t element_size, std::size_t chunk_size = 8192)
|
||||
: element_size_(element_size), chunk_size_(chunk_size), capacity_(0) {}
|
||||
virtual ~BasePool();
|
||||
|
||||
std::size_t size() const { return size_; }
|
||||
std::size_t capacity() const { return capacity_; }
|
||||
std::size_t chunks() const { return blocks_.size(); }
|
||||
|
||||
/// Ensure at least n elements will fit in the pool.
|
||||
inline void expand(std::size_t n) {
|
||||
if (n >= size_) {
|
||||
if (n >= capacity_) reserve(n);
|
||||
size_ = n;
|
||||
}
|
||||
}
|
||||
|
||||
inline void reserve(std::size_t n) {
|
||||
while (capacity_ < n) {
|
||||
char *chunk = new char[element_size_ * chunk_size_];
|
||||
blocks_.push_back(chunk);
|
||||
capacity_ += chunk_size_;
|
||||
}
|
||||
}
|
||||
|
||||
inline void *get(std::size_t n) {
|
||||
assert(n < size_);
|
||||
return blocks_[n / chunk_size_] + (n % chunk_size_) * element_size_;
|
||||
}
|
||||
|
||||
inline const void *get(std::size_t n) const {
|
||||
assert(n < size_);
|
||||
return blocks_[n / chunk_size_] + (n % chunk_size_) * element_size_;
|
||||
}
|
||||
|
||||
virtual void destroy(std::size_t n) = 0;
|
||||
|
||||
protected:
|
||||
std::vector<char *> blocks_;
|
||||
std::size_t element_size_;
|
||||
std::size_t chunk_size_;
|
||||
std::size_t size_ = 0;
|
||||
std::size_t capacity_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of BasePool that provides type-"safe" deconstruction of
|
||||
* elements in the pool.
|
||||
*/
|
||||
template <typename T, std::size_t ChunkSize = 8192>
|
||||
class Pool : public BasePool {
|
||||
public:
|
||||
Pool() : BasePool(sizeof(T), ChunkSize) {}
|
||||
virtual ~Pool() {
|
||||
// Component destructors *must* be called by owner.
|
||||
}
|
||||
|
||||
virtual void destroy(std::size_t n) override {
|
||||
assert(n < size_);
|
||||
T *ptr = static_cast<T*>(get(n));
|
||||
ptr->~T();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace entityx
|
||||
80
external/entityx-1.1.2/entityx/help/Pool_test.cc
vendored
Normal file
80
external/entityx-1.1.2/entityx/help/Pool_test.cc
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2014 Alec Thomas <alec@swapoff.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution.
|
||||
*
|
||||
* Author: Alec Thomas <alec@swapoff.org>
|
||||
*/
|
||||
|
||||
#define CATCH_CONFIG_MAIN
|
||||
|
||||
#include <vector>
|
||||
#include "entityx/3rdparty/catch.hpp"
|
||||
#include "entityx/help/Pool.h"
|
||||
|
||||
struct Position {
|
||||
explicit Position(int *ptr = nullptr) : ptr(ptr) {
|
||||
if (ptr) (*ptr)++;
|
||||
}
|
||||
~Position() {
|
||||
if (ptr) (*ptr)++;
|
||||
}
|
||||
|
||||
float x, y;
|
||||
int *ptr;
|
||||
};
|
||||
|
||||
|
||||
TEST_CASE("TestPoolReserve") {
|
||||
entityx::Pool<Position, 8> pool;
|
||||
REQUIRE(0 == pool.capacity());
|
||||
REQUIRE(0 == pool.chunks());
|
||||
pool.reserve(8);
|
||||
REQUIRE(0 == pool.size());
|
||||
REQUIRE(8 == pool.capacity());
|
||||
REQUIRE(1 == pool.chunks());
|
||||
pool.reserve(16);
|
||||
REQUIRE(0 == pool.size());
|
||||
REQUIRE(16 == pool.capacity());
|
||||
REQUIRE(2 == pool.chunks());
|
||||
}
|
||||
|
||||
TEST_CASE("TestPoolPointers") {
|
||||
entityx::Pool<Position, 8> pool;
|
||||
std::vector<char*> ptrs;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
pool.expand(i * 8 + 8);
|
||||
// NOTE: This is an attempt to ensure non-contiguous allocations from
|
||||
// arena allocators.
|
||||
ptrs.push_back(new char[8 * sizeof(Position)]);
|
||||
}
|
||||
char *p0 = static_cast<char*>(pool.get(0));
|
||||
char *p7 = static_cast<char*>(pool.get(7));
|
||||
char *p8 = static_cast<char*>(pool.get(8));
|
||||
char *p16 = static_cast<char*>(pool.get(16));
|
||||
char *p24 = static_cast<char*>(pool.get(24));
|
||||
|
||||
void *expected_p7 = p0 + 7 * sizeof(Position);
|
||||
REQUIRE(expected_p7 == static_cast<void*>(p7));
|
||||
void *extrapolated_p8 = p0 + 8 * sizeof(Position);
|
||||
REQUIRE(extrapolated_p8 != static_cast<void*>(p8));
|
||||
void *extrapolated_p16 = p8 + 8 * sizeof(Position);
|
||||
REQUIRE(extrapolated_p16 != static_cast<void*>(p16));
|
||||
void *extrapolated_p24 = p16 + 8 * sizeof(Position);
|
||||
REQUIRE(extrapolated_p24 != static_cast<void*>(p24));
|
||||
}
|
||||
|
||||
TEST_CASE("TestDeconstruct") {
|
||||
entityx::Pool<Position, 8> pool;
|
||||
pool.expand(8);
|
||||
|
||||
void *p0 = pool.get(0);
|
||||
|
||||
int counter = 0;
|
||||
new(p0) Position(&counter);
|
||||
REQUIRE(1 == counter);
|
||||
pool.destroy(0);
|
||||
REQUIRE(2 == counter);
|
||||
}
|
||||
32
external/entityx-1.1.2/entityx/help/Timer.cc
vendored
Normal file
32
external/entityx-1.1.2/entityx/help/Timer.cc
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Antony Woods <antony@teamwoods.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution.
|
||||
*
|
||||
* Author: Antony Woods <antony@teamwoods.org>
|
||||
*/
|
||||
|
||||
#include "entityx/help/Timer.h"
|
||||
|
||||
namespace entityx {
|
||||
namespace help {
|
||||
|
||||
Timer::Timer() {
|
||||
_start = std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
Timer::~Timer() {
|
||||
}
|
||||
|
||||
void Timer::restart() {
|
||||
_start = std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
double Timer::elapsed() {
|
||||
return std::chrono::duration<double>(std::chrono::system_clock::now() - _start).count();
|
||||
}
|
||||
|
||||
} // namespace help
|
||||
} // namespace entityx
|
||||
29
external/entityx-1.1.2/entityx/help/Timer.h
vendored
Normal file
29
external/entityx-1.1.2/entityx/help/Timer.h
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Antony Woods <antony@teamwoods.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution.
|
||||
*
|
||||
* Author: Antony Woods <antony@teamwoods.org>
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace entityx {
|
||||
namespace help {
|
||||
|
||||
class Timer {
|
||||
public:
|
||||
Timer();
|
||||
~Timer();
|
||||
|
||||
void restart();
|
||||
double elapsed();
|
||||
private:
|
||||
std::chrono::time_point<std::chrono::system_clock> _start;
|
||||
};
|
||||
|
||||
} // namespace help
|
||||
} // namespace entityx
|
||||
33
external/entityx-1.1.2/entityx/quick.h
vendored
Normal file
33
external/entityx-1.1.2/entityx/quick.h
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Alec Thomas <alec@swapoff.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution.
|
||||
*
|
||||
* Author: Alec Thomas <alec@swapoff.org>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "entityx/Entity.h"
|
||||
#include "entityx/Event.h"
|
||||
#include "entityx/System.h"
|
||||
#include "entityx/config.h"
|
||||
|
||||
namespace entityx {
|
||||
|
||||
/**
|
||||
* A convenience class for instantiating an EventManager, EntityManager and
|
||||
* SystemManager.
|
||||
*/
|
||||
class EntityX {
|
||||
public:
|
||||
EntityX() : entities(events), systems(entities, events) {}
|
||||
|
||||
EventManager events;
|
||||
EntityManager entities;
|
||||
SystemManager systems;
|
||||
};
|
||||
|
||||
} // namespace entityx
|
||||
55
external/entityx-1.1.2/entityx/tags/TagsComponent.h
vendored
Normal file
55
external/entityx-1.1.2/entityx/tags/TagsComponent.h
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution.
|
||||
*
|
||||
* Author: Alec Thomas <alec@swapoff.org>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_set>
|
||||
#include <string>
|
||||
#include "entityx/Entity.h"
|
||||
|
||||
namespace entityx {
|
||||
namespace tags {
|
||||
|
||||
/**
|
||||
* Allow entities to be tagged with strings.
|
||||
*
|
||||
* entity.assign<TagsComponent>("tag1", "tag2");
|
||||
*
|
||||
* ComponentPtr<TagsComponent> tags;
|
||||
* for (Entity entity : entity_manager.entities_with_components(tags))
|
||||
*/
|
||||
class TagsComponent : public Component<TagsComponent> {
|
||||
public:
|
||||
/**
|
||||
* Construct a new TagsComponent with the given tags.
|
||||
*
|
||||
* eg. TagsComponent tags("a", "b", "c");
|
||||
*/
|
||||
template <typename ... Args>
|
||||
TagsComponent(const std::string &tag, const Args & ... tags) {
|
||||
set_tags(tag, tags ...);
|
||||
}
|
||||
|
||||
std::unordered_set<std::string> tags;
|
||||
|
||||
private:
|
||||
template <typename ... Args>
|
||||
void set_tags(const std::string &tag1, const std::string &tag2, const Args & ... tags) {
|
||||
this->tags.insert(tag1);
|
||||
set_tags(tag2, tags ...);
|
||||
}
|
||||
|
||||
void set_tags(const std::string &tag) {
|
||||
tags.insert(tag);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace tags
|
||||
} // namespace entityx
|
||||
42
external/entityx-1.1.2/entityx/tags/TagsComponent_test.cc
vendored
Normal file
42
external/entityx-1.1.2/entityx/tags/TagsComponent_test.cc
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution.
|
||||
*
|
||||
* Author: Alec Thomas <alec@swapoff.org>
|
||||
*/
|
||||
|
||||
#define CATCH_CONFIG_MAIN
|
||||
|
||||
#include <string>
|
||||
#include "entityx/3rdparty/catch.hpp"
|
||||
#include "entityx/tags/TagsComponent.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace entityx;
|
||||
using namespace entityx::tags;
|
||||
|
||||
|
||||
struct Position : public Component<Position> {};
|
||||
|
||||
|
||||
template <typename T>
|
||||
int size(const T &t) {
|
||||
int n = 0;
|
||||
for (auto i : t) {
|
||||
++n;
|
||||
(void)i; // Unused on purpose, suppress warning
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("TestVariadicConstruction", "TagsComponentTest") {
|
||||
auto tags = TagsComponent("player", "indestructible");
|
||||
unordered_set<string> expected;
|
||||
expected.insert("player");
|
||||
expected.insert("indestructible");
|
||||
REQUIRE(expected == tags.tags);
|
||||
}
|
||||
Reference in New Issue
Block a user