summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--editor/app.cpp2
-rw-r--r--editor/scenery-editor.cpp2
-rw-r--r--serialize/world-reader.cpp17
-rw-r--r--serialize/world-writer.cpp15
-rw-r--r--src/chunk.cpp5
-rw-r--r--src/chunk.hpp3
-rw-r--r--src/world.cpp10
-rw-r--r--src/world.hpp14
-rw-r--r--test/serializer.cpp27
9 files changed, 50 insertions, 45 deletions
diff --git a/editor/app.cpp b/editor/app.cpp
index 49d8e96c..5820c165 100644
--- a/editor/app.cpp
+++ b/editor/app.cpp
@@ -55,7 +55,7 @@ void app::reset_world(struct world&& w)
character_proto cproto;
cproto.name = "Player"_s;
cproto.playable = true;
- _character_id = w2.make_entity<character>(global_coords{}, cproto)->id;
+ _character_id = w2.make_entity<character>(w2.make_id(), global_coords{}, cproto)->id;
}
}
diff --git a/editor/scenery-editor.cpp b/editor/scenery-editor.cpp
index 1165219d..22a7fd16 100644
--- a/editor/scenery-editor.cpp
+++ b/editor/scenery-editor.cpp
@@ -98,7 +98,7 @@ void scenery_editor::place_tile(world& w, global_coords pos, const scenery_& s)
else
{
// todo check collision at pos
- w.make_entity<scenery>(pos, s.proto);
+ w.make_entity<scenery>(w.make_id(), pos, s.proto);
}
c.mark_scenery_modified();
}
diff --git a/serialize/world-reader.cpp b/serialize/world-reader.cpp
index 78e7e539..e9d4bc96 100644
--- a/serialize/world-reader.cpp
+++ b/serialize/world-reader.cpp
@@ -214,14 +214,13 @@ void reader_state::read_chunks(reader_t& s)
}
}
global_coords coord{ch, local_coords{i}};
- auto e = _world->make_entity<scenery>(coord, sc);
- c.add_entity_unsorted(e);
+ auto e = _world->make_entity<scenery, false>(_world->make_id(), coord, sc);
}
}
- if (PROTO < 8) [[unlikely]]
- c.sort_entities();
+ std::uint32_t entity_count = 0;
+ if (PROTO >= 8) [[likely]]
+ entity_count << s;
- std::uint32_t entity_count; entity_count << s;
for (auto i = 0_uz; i < entity_count; i++)
{
std::uint64_t _id; _id << s;
@@ -252,8 +251,8 @@ void reader_state::read_chunks(reader_t& s)
proto.name = StringView{name.buf, name.len, StringViewFlag::Global|StringViewFlag::NullTerminated};
if (id & meta_long_scenery_bit)
read_offsets(s, proto);
- auto C = _world->make_entity<character>(oid, {ch, local}, proto);
- c.add_entity_unsorted(C);
+ auto e = _world->make_entity<character>(oid, {ch, local}, proto);
+ (void)e;
break;
}
case entity_type::scenery: {
@@ -274,8 +273,8 @@ void reader_state::read_chunks(reader_t& s)
if (sc.active)
sc.delta << s;
}
- auto e = _world->make_entity<scenery>(oid, {ch, local}, sc);
- c.add_entity_unsorted(e);
+ auto e = _world->make_entity<scenery, false>(oid, {ch, local}, sc);
+ (void)e;
break;
}
default:
diff --git a/serialize/world-writer.cpp b/serialize/world-writer.cpp
index 9f724ef1..461f6dd6 100644
--- a/serialize/world-writer.cpp
+++ b/serialize/world-writer.cpp
@@ -335,11 +335,20 @@ void writer_state::serialize_chunk(const chunk& c, chunk_coords coord)
check_atlas(wall_west);
if (img_g != null_atlas)
- s << img_g, s << ground.variant;
+ {
+ s << img_g;
+ s << ground.variant;
+ }
if (img_n != null_atlas)
- s << img_n, s << wall_north.variant;
+ {
+ s << img_n;
+ s << wall_north.variant;
+ }
if (img_w != null_atlas)
- s << img_w, s << wall_west.variant;
+ {
+ s << img_w;
+ s << wall_west.variant;
+ }
}
s << (std::uint32_t)c.entities().size();
diff --git a/src/chunk.cpp b/src/chunk.cpp
index a99facf6..e0a09012 100644
--- a/src/chunk.cpp
+++ b/src/chunk.cpp
@@ -108,6 +108,7 @@ bool chunk::bbox::operator==(const bbox& other) const noexcept = default;
void chunk::add_entity_unsorted(const std::shared_ptr<entity>& e)
{
+ _entities_sorted = false;
if (!e->is_dynamic())
mark_scenery_modified(false);
if (bbox bb; _bbox_for_scenery(*e, bb))
@@ -117,6 +118,7 @@ void chunk::add_entity_unsorted(const std::shared_ptr<entity>& e)
void chunk::sort_entities()
{
+ _entities_sorted = true;
mark_scenery_modified(false);
std::sort(_entities.begin(), _entities.end(), [](const auto& a, const auto& b) {
@@ -126,6 +128,7 @@ void chunk::sort_entities()
void chunk::add_entity(const std::shared_ptr<entity>& e)
{
+ fm_assert(_entities_sorted);
if (e->atlas->info().fps == 0)
mark_scenery_modified(false);
if (bbox bb; _bbox_for_scenery(*e, bb))
@@ -140,6 +143,7 @@ void chunk::add_entity(const std::shared_ptr<entity>& e)
void chunk::remove_entity(std::size_t i)
{
+ fm_assert(_entities_sorted);
fm_debug_assert(i < _entities.size());
const auto& e = _entities[i];
if (!e->is_dynamic())
@@ -152,6 +156,7 @@ void chunk::remove_entity(std::size_t i)
const std::vector<std::shared_ptr<entity>>& chunk::entities() const
{
+ fm_assert(_entities_sorted);
return _entities;
}
diff --git a/src/chunk.hpp b/src/chunk.hpp
index e825ce33..e51c55ac 100644
--- a/src/chunk.hpp
+++ b/src/chunk.hpp
@@ -129,7 +129,8 @@ private:
_walls_modified : 1 = true,
_scenery_modified : 1 = true,
_pass_modified : 1 = true,
- _teardown : 1 = false;
+ _teardown : 1 = false,
+ _entities_sorted : 1 = true;
struct bbox final // NOLINT(cppcoreguidelines-pro-type-member-init)
{
diff --git a/src/world.cpp b/src/world.cpp
index ee1ef459..8743981f 100644
--- a/src/world.cpp
+++ b/src/world.cpp
@@ -106,15 +106,19 @@ void world::collect(bool force)
fm_debug("world: collected %zu/%zu chunks", len, len0);
}
-void world::do_make_entity(const std::shared_ptr<entity>& e, global_coords pos)
+void world::do_make_entity(const std::shared_ptr<entity>& e, global_coords pos, bool sorted)
{
- fm_debug_assert(e->id > 0);
+ fm_assert(e->id > 0);
fm_debug_assert(e->c->world()._unique_id == _unique_id);
+ fm_assert(!_entities.contains(e->id));
fm_assert(Vector2ui(e->bbox_size).product() > 0);
fm_assert(e->type != entity_type::none);
e->coord = pos;
_entities[e->id] = e;
- e->c->add_entity(e);
+ if (sorted)
+ e->c->add_entity(e);
+ else
+ e->c->add_entity_unsorted(e);
}
void world::do_kill_entity(std::uint64_t id)
diff --git a/src/world.hpp b/src/world.hpp
index 08e4d384..0b7b87a5 100644
--- a/src/world.hpp
+++ b/src/world.hpp
@@ -37,7 +37,7 @@ private:
explicit world(std::size_t capacity);
- void do_make_entity(const std::shared_ptr<entity>& e, global_coords pos);
+ void do_make_entity(const std::shared_ptr<entity>& e, global_coords pos, bool sorted);
void do_kill_entity(std::uint64_t id);
std::shared_ptr<entity> find_entity_(std::uint64_t id);
@@ -67,27 +67,21 @@ public:
void set_collect_threshold(std::size_t value) { _collect_every = value; }
std::size_t collect_threshold() const noexcept { return _collect_every; }
- template<typename T, typename... Xs>
+ template<typename T, bool sorted = true, typename... Xs>
requires requires(chunk& c) { T{std::uint64_t(), c, entity_type(), std::declval<Xs>()...}; }
std::shared_ptr<T> make_entity(std::uint64_t id, global_coords pos, Xs&&... xs)
{
static_assert(std::is_base_of_v<entity, T>);
auto ret = std::shared_ptr<T>(new T{id, operator[](pos.chunk()), entity_type_<T>::value, std::forward<Xs>(xs)...});
- do_make_entity(std::static_pointer_cast<entity>(ret), pos);
+ do_make_entity(std::static_pointer_cast<entity>(ret), pos, sorted);
return ret;
}
- template<typename T, typename... Xs>
- requires requires(chunk& c) { T{std::uint64_t(), c, entity_type(), std::declval<Xs>()...}; }
- inline std::shared_ptr<T> make_entity(global_coords pos, Xs&&... xs)
- {
- return make_entity<T, Xs...>(++_entity_counter, pos, std::forward<Xs>(xs)...);
- }
-
template<typename T = entity, typename U = entity> std::shared_ptr<T> find_entity(std::uint64_t id);
bool is_teardown() const { return _teardown; }
std::uint64_t entity_counter() const { return _entity_counter; }
+ [[nodiscard]] std::uint64_t make_id() { return ++_entity_counter; }
void set_entity_counter(std::uint64_t value);
world& operator=(world&& w) noexcept;
diff --git a/test/serializer.cpp b/test/serializer.cpp
index f71ad078..928fb25b 100644
--- a/test/serializer.cpp
+++ b/test/serializer.cpp
@@ -30,10 +30,10 @@ chunk& test_app::make_test_chunk(chunk_coords ch)
c[{K, K }].wall_west() = { metal2, 0 };
c[{K, K+1}].wall_north() = { metal1, 0 };
c[{K+1, K }].wall_west() = { metal2, 0 };
- w.make_entity<scenery>({ch, {3, 4}}, table);
- w.make_entity<scenery>({ch, {K, K+1}}, control_panel);
+ w.make_entity<scenery>(w.make_id(), {ch, {3, 4}}, table);
+ w.make_entity<scenery>(w.make_id(), {ch, {K, K+1}}, control_panel);
{
- auto& e = *w.make_entity<scenery>({ch, {K+3, K+1}}, door);
+ auto& e = *w.make_entity<scenery>(w.make_id(), {ch, {K+3, K+1}}, door);
auto i = e.index();
e.activate(i);
e.update(i, 1.f/60);
@@ -43,48 +43,41 @@ chunk& test_app::make_test_chunk(chunk_coords ch)
return c;
}
-static bool chunks_equal(const chunk& a, const chunk& b)
+static void assert_chunks_equal(const chunk& a, const chunk& b)
{
- if (a.entities().size() != b.entities().size())
- return false;
+ fm_assert(a.entities().size() == b.entities().size());
for (auto i = 0_uz; i < TILE_COUNT; i++)
{
const auto &a1 = a[i], &b1 = b[i];
- if (a1 != b1)
- return false;
+ fm_assert(a1 == b1);
}
for (auto i = 0_uz; i < a.entities().size(); i++)
{
const auto& ae = *a.entities()[i];
const auto& be = *b.entities()[i];
- if (ae.type != be.type)
- return false;
+ fm_assert(ae.type == be.type);
switch (ae.type)
{
case entity_type::character: {
const auto& e1 = static_cast<const character&>(ae);
const auto& e2 = static_cast<const character&>(be);
const auto p1 = character_proto(e1), p2 = character_proto(e2);
- if (p1 != p2)
- return false;
+ fm_assert(p1 == p2);
break;
}
case entity_type::scenery: {
const auto& e1 = static_cast<const scenery&>(ae);
const auto& e2 = static_cast<const scenery&>(be);
const auto p1 = scenery_proto(e1), p2 = scenery_proto(e2);
- if (p1 != p2)
- return false;
+ fm_assert(p1 == p2);
break;
}
default:
fm_abort("invalid entity type '%d'", (int)ae.type);
}
}
-
- return true;
}
void test_app::test_serializer()
@@ -97,7 +90,7 @@ void test_app::test_serializer()
w.serialize(filename);
auto w2 = world::deserialize(filename);
auto& c2 = w2[coord];
- fm_assert(chunks_equal(c, c2));
+ assert_chunks_equal(c, c2);
}
} // namespace floormat