From: Daniel Karbach Date: Tue, 15 Sep 2015 15:47:15 +0000 (+0200) Subject: some experiements with state sync X-Git-Url: https://git.localhorst.tv/?a=commitdiff_plain;h=896c4c0ba2efd6774894fd8308cc097b7f4123e3;p=blank.git some experiements with state sync --- diff --git a/src/app/IntervalTimer.hpp b/src/app/IntervalTimer.hpp index 06caa5e..5240f5d 100644 --- a/src/app/IntervalTimer.hpp +++ b/src/app/IntervalTimer.hpp @@ -40,9 +40,15 @@ public: int Elapsed() const noexcept { return value; } + int Interval() const noexcept { + return intv; + } int Iteration() const noexcept { return value / intv; } + void PopIteration() noexcept { + value -= intv; + } void Update(int dt) noexcept { value += dt * speed; diff --git a/src/app/ServerState.cpp b/src/app/ServerState.cpp index 0be5653..d980cbc 100644 --- a/src/app/ServerState.cpp +++ b/src/app/ServerState.cpp @@ -24,11 +24,13 @@ ServerState::ServerState( , skeletons() , spawner(world, skeletons, gc.seed) , server(sc, world) +, loop_timer(16) , push_timer(16) { TextureIndex tex_index; env.loader.LoadBlockTypes("default", block_types, tex_index); skeletons.LoadHeadless(); + loop_timer.Start(); push_timer.Start(); std::cout << "listening on UDP port " << sc.port << std::endl; @@ -43,10 +45,14 @@ void ServerState::Handle(const SDL_Event &event) { void ServerState::Update(int dt) { + loop_timer.Update(dt); push_timer.Update(dt); server.Handle(); - spawner.Update(dt); - world.Update(dt); + while (loop_timer.HitOnce()) { + spawner.Update(loop_timer.Interval()); + world.Update(loop_timer.Interval()); + loop_timer.PopIteration(); + } chunk_loader.Update(dt); if (push_timer.Hit()) { server.Update(dt); diff --git a/src/app/ServerState.hpp b/src/app/ServerState.hpp index 0deb65a..afa7840 100644 --- a/src/app/ServerState.hpp +++ b/src/app/ServerState.hpp @@ -42,6 +42,7 @@ private: Skeletons skeletons; Spawner spawner; Server server; + IntervalTimer loop_timer; IntervalTimer push_timer; }; diff --git a/src/client/InteractiveState.hpp b/src/client/InteractiveState.hpp index 91a5944..eaca4c9 100644 --- a/src/client/InteractiveState.hpp +++ b/src/client/InteractiveState.hpp @@ -49,14 +49,14 @@ private: Interface interface; ChunkRenderer chunk_renderer; Skeletons skeletons; + IntervalTimer loop_timer; IntervalTimer update_timer; struct PlayerHistory { EntityState state; - int timestamp; std::uint16_t packet; - PlayerHistory(EntityState s, int t, std::uint16_t p) - : state(s), timestamp(t), packet(p) { } + PlayerHistory(EntityState s, std::uint16_t p) + : state(s), packet(p) { } }; std::list player_hist; diff --git a/src/client/client.cpp b/src/client/client.cpp index 4a48b45..dbdaa73 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -56,6 +56,7 @@ InteractiveState::InteractiveState(MasterState &master, uint32_t player_id) ) , chunk_renderer(*interface.GetPlayer().chunks) , skeletons() +, loop_timer(16) , update_timer(16) , player_hist() { TextureIndex tex_index; @@ -65,6 +66,7 @@ InteractiveState::InteractiveState(MasterState &master, uint32_t player_id) skeletons.Load(); // TODO: better solution for initializing HUD interface.SelectNext(); + loop_timer.Start(); update_timer.Start(); } @@ -101,14 +103,17 @@ void InteractiveState::Handle(const SDL_Event &event) { } void InteractiveState::Update(int dt) { + loop_timer.Update(dt); + update_timer.Update(dt); master.Update(dt); interface.Update(dt); - world.Update(dt); + while (loop_timer.HitOnce()) { + world.Update(loop_timer.Interval()); + loop_timer.PopIteration(); + } chunk_renderer.Update(dt); - update_timer.Update(dt); - Entity &player = *interface.GetPlayer().entity; if (update_timer.Hit()) { @@ -126,11 +131,10 @@ void InteractiveState::Update(int dt) { void InteractiveState::PushPlayerUpdate(const Entity &player) { std::uint16_t packet = master.GetClient().SendPlayerUpdate(player); if (player_hist.size() < 16) { - player_hist.emplace_back(player.GetState(), update_timer.Elapsed(), packet); + player_hist.emplace_back(player.GetState(), packet); } else { auto entry = player_hist.begin(); entry->state = player.GetState(); - entry->timestamp = update_timer.Elapsed(); entry->packet = packet; player_hist.splice(player_hist.end(), player_hist, entry); } @@ -145,13 +149,49 @@ void InteractiveState::MergePlayerCorrection(uint16_t seq, const EntityState &co // drop anything older than the fix while (entry != end) { int pack_diff = int16_t(seq) - int16_t(entry->packet); - if (pack_diff < 0) { + if (pack_diff > 0) { entry = player_hist.erase(entry); } else { break; } } - if (entry == end) return; + + EntityState replay_state(corrected_state); + EntityState &player_state = interface.GetPlayer().entity->GetState(); + + if (entry != end) { + entry->state.chunk_pos = replay_state.chunk_pos; + entry->state.block_pos = replay_state.block_pos; + ++entry; + } + + while (entry != end) { + replay_state.velocity = entry->state.velocity; + replay_state.Update(16); + entry->state.chunk_pos = replay_state.chunk_pos; + entry->state.block_pos = replay_state.block_pos; + ++entry; + } + + glm::vec3 displacement(replay_state.Diff(player_state)); + const float disp_squared = dot(displacement, displacement); + + if (disp_squared < 16.0f * numeric_limits::epsilon()) { + return; + } + + constexpr float warp_thresh = 1.0f; + constexpr float max_disp = 0.0001f; // (1/100)^2 + + if (disp_squared > warp_thresh) { + player_state.chunk_pos = replay_state.chunk_pos; + player_state.block_pos = replay_state.block_pos; + } else if (disp_squared < max_disp) { + player_state.block_pos += displacement; + } else { + displacement *= 0.01f / sqrt(disp_squared); + player_state.block_pos += displacement; + } } void InteractiveState::Render(Viewport &viewport) { diff --git a/src/net/net.cpp b/src/net/net.cpp index 3f4bf74..98954bf 100644 --- a/src/net/net.cpp +++ b/src/net/net.cpp @@ -348,7 +348,10 @@ void ClientConnection::On(const Packet::PlayerUpdate &pack) { if (pack_diff > 0 || overdue) { player_update_pack = pack.Seq(); // TODO: do client input validation here - pack.ReadPlayerState(Player().GetState()); + EntityState new_state; + pack.ReadPlayerState(new_state); + Player().Velocity(new_state.velocity); + Player().Orientation(new_state.orient); } }