diff --git a/luprex/core/cpp/spookyv2.cpp b/luprex/core/cpp/spookyv2.cpp index e4628fd2..f8f368b7 100644 --- a/luprex/core/cpp/spookyv2.cpp +++ b/luprex/core/cpp/spookyv2.cpp @@ -298,6 +298,11 @@ void SpookyHash::Hash128( uint64_t *hash1, uint64_t *hash2) { + if ((*hash1 == 0) && (*hash2 == 0)) { + *hash1 = 0x9438478934792837; + *hash2 = 0x8347848738748378; + } + if (length < sc_bufSize) { Short(message, length, hash1, hash2); diff --git a/luprex/core/cpp/spookyv2.hpp b/luprex/core/cpp/spookyv2.hpp index d1ce55b5..813fba1b 100644 --- a/luprex/core/cpp/spookyv2.hpp +++ b/luprex/core/cpp/spookyv2.hpp @@ -26,6 +26,9 @@ // slower than MD5. // +#ifndef SPOOKYV2_HPP +#define SPOOKYV2_HPP + #include #include #include @@ -43,5 +46,4 @@ public: uint64_t *hash2); // input seed1, output hash1 }; - - +#endif // SPOOKYV2_HPP diff --git a/luprex/core/cpp/util.cpp b/luprex/core/cpp/util.cpp index c8514448..0ddec568 100644 --- a/luprex/core/cpp/util.cpp +++ b/luprex/core/cpp/util.cpp @@ -28,7 +28,6 @@ std::string id_vector_debug_string(const IdVector &idv) { return oss.str(); } - IdVector sort_union_id_vectors(const IdVector &v1, const IdVector &v2) { IdVector result(v1.size() + v2.size()); int next = 0; @@ -47,6 +46,13 @@ IdVector sort_union_id_vectors(const IdVector &v1, const IdVector &v2) { return result; } +HashValue hash_id_vector(const IdVector &idv) { + uint64_t hash1 = 0; + uint64_t hash2 = 0; + SpookyHash::Hash128(&idv[0], idv.size() * sizeof(int64_t), &hash1, &hash2); + return std::make_pair(hash1, hash2); +} + StringVec split(const std::string &s, char sep) { StringVec result; int start = 0; diff --git a/luprex/core/cpp/util.hpp b/luprex/core/cpp/util.hpp index e18dbca3..6ecd04dc 100644 --- a/luprex/core/cpp/util.hpp +++ b/luprex/core/cpp/util.hpp @@ -9,6 +9,7 @@ #include #include #include "luastack.hpp" +#include "spookyv2.hpp" namespace util { @@ -29,6 +30,9 @@ std::string id_vector_debug_string(const IdVector &idv); // Unions and sorts two ID vectors. IdVector sort_union_id_vectors(const IdVector &v1, const IdVector &v2); +// Get a 64-bit hashvalue for an ID vector. +HashValue hash_id_vector(const IdVector &idv); + // Split a string into multiple strings StringVec split(const std::string &s, char sep); diff --git a/luprex/core/cpp/world.cpp b/luprex/core/cpp/world.cpp index 4b716b02..0117ae2f 100644 --- a/luprex/core/cpp/world.cpp +++ b/luprex/core/cpp/world.cpp @@ -185,7 +185,7 @@ std::string World::tangible_ids_debug_string() const { } -util::IdVector World::get_near(int64_t player_id, float radius, bool exclude_nowhere) const { +util::IdVector World::get_near_unsorted(int64_t player_id, float radius, bool exclude_nowhere) const { const Tangible *player = tangible_get(player_id); if (player == nullptr) { return IdVector(); @@ -200,6 +200,12 @@ util::IdVector World::get_near(int64_t player_id, float radius, bool exclude_now return plane_map_.scan_radius(aqback.plane(), aqback.xyz().x, aqback.xyz().y, radius, player_id); } +util::IdVector World::get_near(int64_t player_id, float radius, bool exclude_nowhere) const { + util::IdVector v = get_near_unsorted(player_id, radius, exclude_nowhere); + std::sort(v.begin(), v.end()); + return v; +} + Tangible *World::tangible_make(lua_State *L, int64_t id, bool pushdb) { // Get a state if we don't already have one. if (L == nullptr) { @@ -550,8 +556,8 @@ void World::difference_transmit(int64_t actor_id, const World *master, StreamBuf // Get the list of tangibles visible in either model. // Some tangibles may be missing in the master, some may be missing in the sync. util::IdVector visible = util::sort_union_id_vectors( - master->get_near(actor_id, 100.0, true), - this->get_near(actor_id, 100.0, true)); + master->get_near_unsorted(actor_id, RadiusVisibility, true), + get_near_unsorted(actor_id, RadiusVisibility, true)); TanVector m_visible = master->tangible_get_all(visible); TanVector s_visible = tangible_get_all(visible); assert(m_visible.size() == s_visible.size()); @@ -575,12 +581,24 @@ void World::difference_transmit(int64_t actor_id, const World *master, StreamBuf } } + // Obtain the list of tangibles whose tables we're going to transmit. + util::IdVector closetans = get_near(actor_id, RadiusClose, true); + assert(closetans == master->get_near(actor_id, RadiusClose, true)); + util::HashValue closehash = util::hash_id_vector(closetans); + + // Confirm the close tangibles with the client. + sb->write_hashvalue(closehash); + assert(tsb.at_eof()); } void World::apply_differences(StreamBuffer *sb) { - patch_actor_essentials(sb); + int64_t actor_id = patch_actor_essentials(sb); patch_visible_animations(sb); + util::IdVector closetans = get_near(actor_id, RadiusClose, true); + util::HashValue closehash = util::hash_id_vector(closetans); + util::HashValue m_closehash = sb->read_hashvalue(); + assert(closehash == m_closehash); } void World::diff_actor_essentials(const Tangible *m_actor, const Tangible *s_actor, StreamBuffer *sb) { @@ -594,7 +612,7 @@ void World::diff_actor_essentials(const Tangible *m_actor, const Tangible *s_act } } -void World::patch_actor_essentials(StreamBuffer *sb) { +int64_t World::patch_actor_essentials(StreamBuffer *sb) { int64_t actor_id = sb->read_int64(); Tangible *s_actor = tangible_get(actor_id); if (s_actor == nullptr) { @@ -606,6 +624,7 @@ void World::patch_actor_essentials(StreamBuffer *sb) { s_actor->anim_queue_.patch(sb); } s_actor->update_plane_item(); + return actor_id; } void World::diff_visible_animations(const TanVector &mvis, const TanVector &svis, StreamBuffer *sb) { diff --git a/luprex/core/cpp/world.hpp b/luprex/core/cpp/world.hpp index 950a2d22..48825004 100644 --- a/luprex/core/cpp/world.hpp +++ b/luprex/core/cpp/world.hpp @@ -80,6 +80,8 @@ public: using IdVector = util::IdVector; using TanVector = std::vector; using Redirects = std::map; + const float RadiusVisibility = 100.0; + const float RadiusClose = 10.0; // Constructor. // @@ -103,9 +105,11 @@ public: // get_near // // Get a list of the tangibles that are near the player. If 'exclude_nowhere' is - // true, exclude any tangibles on the nowhere plane. + // true, exclude any tangibles on the nowhere plane. The unsorted version returns + // the tangibles in an unpredictable order. // IdVector get_near(int64_t player_id, float radius, bool exclude_nowhere) const; + IdVector get_near_unsorted(int64_t player_id, float radius, bool exclude_nowhere) const; // Make a tangible. // @@ -247,7 +251,7 @@ private: // We also update the actor's ID allocation pipeline. // static void diff_actor_essentials(const Tangible *mactor, const Tangible *sactor, StreamBuffer *sb); - void patch_actor_essentials(StreamBuffer *sb); + int64_t patch_actor_essentials(StreamBuffer *sb); // Pass 2 of difference transmission: visible animations. //