1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
#pragma once
#include "compat/defs.hpp"
#include "chunk.hpp"
#include "global-coords.hpp"
#include "entity-type.hpp"
#include "compat/int-hash.hpp"
#include <memory>
#include <unordered_map>
#include <tsl/robin_map.h>
template<>
struct std::hash<floormat::chunk_coords_> final {
floormat::size_t operator()(const floormat::chunk_coords_& coord) const noexcept;
};
namespace floormat {
struct entity;
template<typename T> struct entity_type_;
struct object_id_hasher {
size_t operator()(object_id id) const noexcept { return int_hash(id); }
};
struct world final
{
static constexpr object_id entity_counter_init = 1024;
static constexpr size_t initial_capacity = 512;
static constexpr float max_load_factor = .5;
static constexpr size_t initial_collect_every = 64;
private:
struct chunk_tuple final {
static constexpr chunk_coords_ invalid_coords = { -1 << 15, -1 << 15, chunk_z_min };
chunk* c = nullptr;
chunk_coords_ pos = invalid_coords;
} _last_chunk;
std::unordered_map<chunk_coords_, chunk> _chunks;
tsl::robin_map<object_id, std::weak_ptr<entity>, object_id_hasher> _entities;
size_t _last_collection = 0;
size_t _collect_every = initial_collect_every;
std::shared_ptr<char> _unique_id = std::make_shared<char>('A');
object_id _entity_counter = entity_counter_init;
uint64_t _current_frame = 1; // zero is special for struct entity
bool _teardown : 1 = false;
explicit world(size_t capacity);
void do_make_entity(const std::shared_ptr<entity>& e, global_coords pos, bool sorted);
void do_kill_entity(object_id id);
std::shared_ptr<entity> find_entity_(object_id id);
[[noreturn]] static void throw_on_wrong_entity_type(object_id id, entity_type actual, entity_type expected);
friend struct entity;
public:
explicit world();
~world() noexcept;
explicit world(std::unordered_map<chunk_coords_, chunk>&& chunks);
struct pair final { chunk& c; tile_ref t; }; // NOLINT
chunk& operator[](chunk_coords_ c) noexcept;
pair operator[](global_coords pt) noexcept;
chunk* at(chunk_coords_ c) noexcept;
bool contains(chunk_coords_ c) const noexcept;
void clear();
void collect(bool force = false);
void maybe_collect();
size_t size() const noexcept { return _chunks.size(); }
const auto& chunks() const noexcept { return _chunks; }
void serialize(StringView filename);
static world deserialize(StringView filename);
void set_collect_threshold(size_t value) { _collect_every = value; }
size_t collect_threshold() const noexcept { return _collect_every; }
auto frame_no() const { return _current_frame; }
auto increment_frame_no() { return _current_frame++; }
template<typename T, bool sorted = true, typename... Xs>
requires requires(chunk& c) {
T{object_id(), c, std::declval<Xs>()...};
std::is_base_of_v<entity, T>;
}
std::shared_ptr<T> make_entity(object_id id, global_coords pos, Xs&&... xs)
{
auto ret = std::shared_ptr<T>(new T{id, operator[](chunk_coords_{pos.chunk(), pos.z()}), std::forward<Xs>(xs)...});
do_make_entity(std::static_pointer_cast<entity>(ret), pos, sorted);
return ret;
}
template<typename T = entity> std::shared_ptr<T> find_entity(object_id id);
bool is_teardown() const { return _teardown; }
object_id entity_counter() const { return _entity_counter; }
[[nodiscard]] object_id make_id() { return ++_entity_counter; }
void set_entity_counter(object_id value);
world& operator=(world&& w) noexcept;
world(world&& w) noexcept;
fm_DECLARE_DEPRECATED_COPY_ASSIGNMENT(world);
};
template<typename T>
std::shared_ptr<T> world::find_entity(object_id id)
{
static_assert(std::is_same_v<entity, T> || std::is_base_of_v<entity, T>);
// make it a dependent name so that including "src/entity.hpp" isn't needed
using U = std::conditional_t<std::is_same_v<T, entity>, T, entity>;
if (std::shared_ptr<U> ptr = find_entity_(id); !ptr)
return {};
else if constexpr(std::is_same_v<T, entity>)
return ptr;
else
{
if (!(ptr->type() == entity_type_<T>::value)) [[unlikely]]
throw_on_wrong_entity_type(id, ptr->type(), entity_type_<T>::value);
return std::static_pointer_cast<T>(std::move(ptr));
}
}
} // namespace floormat
|