From fa73e3600dee78063ab95e1903172ab339dcd4bd Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Fri, 13 Nov 2015 12:31:27 +0100 Subject: [PATCH] re-request incomplete or corrupted chunk transfers --- doc/protocol | 4 ++++ src/client/ChunkReceiver.hpp | 6 +++++- src/client/ChunkTransmission.hpp | 1 + src/client/Client.hpp | 2 ++ src/client/client.cpp | 2 +- src/client/net.cpp | 36 +++++++++++++++++++++++++++----- src/server/ClientConnection.hpp | 1 + src/server/net.cpp | 9 ++++++++ 8 files changed, 54 insertions(+), 7 deletions(-) diff --git a/doc/protocol b/doc/protocol index 8e26949..f194d77 100644 --- a/doc/protocol +++ b/doc/protocol @@ -170,6 +170,10 @@ Chunk Begin ----------- Sent by the server to inform the client of an upcoming chunk transmission. +The client may send this packet to the server to re-request a chunk +transmission. In that case fields other than the chunk coordinates are +ignored. Also, the server may choose not to resend the chunk (e.g. if the +player is too far away from it). Code: 9 Payload: diff --git a/src/client/ChunkReceiver.hpp b/src/client/ChunkReceiver.hpp index 57c1ceb..d682d1e 100644 --- a/src/client/ChunkReceiver.hpp +++ b/src/client/ChunkReceiver.hpp @@ -16,11 +16,12 @@ class WorldSave; namespace client { class ChunkTransmission; +class Client; class ChunkReceiver { public: - ChunkReceiver(ChunkStore &, const WorldSave &); + ChunkReceiver(Client &, ChunkStore &, const WorldSave &); ~ChunkReceiver(); void Update(int dt); @@ -39,7 +40,10 @@ private: ChunkTransmission &GetTransmission(std::uint32_t id); void Commit(ChunkTransmission &); + void ReRequest(ChunkTransmission &); + private: + Client &client; ChunkStore &store; const WorldSave &save; std::list transmissions; diff --git a/src/client/ChunkTransmission.hpp b/src/client/ChunkTransmission.hpp index f54c7d8..7e801ee 100644 --- a/src/client/ChunkTransmission.hpp +++ b/src/client/ChunkTransmission.hpp @@ -28,6 +28,7 @@ struct ChunkTransmission { ChunkTransmission(); + void Reset() noexcept; void Clear() noexcept; bool Complete() const noexcept; diff --git a/src/client/Client.hpp b/src/client/Client.hpp index e124ae5..b09001c 100644 --- a/src/client/Client.hpp +++ b/src/client/Client.hpp @@ -37,6 +37,8 @@ public: float yaw, std::uint8_t actions, std::uint8_t slot); + std::uint16_t SendChunkRequest( + const glm::ivec3 &); std::uint16_t SendMessage( std::uint8_t type, std::uint32_t ref, diff --git a/src/client/client.cpp b/src/client/client.cpp index 5a34976..232ba25 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -56,7 +56,7 @@ InteractiveState::InteractiveState(MasterState &master, uint32_t player_id) , manip(master.GetEnv().audio, sounds, player.GetEntity()) , input(world, player, master.GetClient()) , interface(master.GetConfig(), master.GetEnv().keymap, input, *this) -, chunk_receiver(world.Chunks(), save) +, chunk_receiver(master.GetClient(), world.Chunks(), save) , chunk_renderer(player.GetChunks()) , loop_timer(16) , stat_timer(1000) diff --git a/src/client/net.cpp b/src/client/net.cpp index d7a2661..75865be 100644 --- a/src/client/net.cpp +++ b/src/client/net.cpp @@ -23,8 +23,9 @@ namespace blank { namespace client { -ChunkReceiver::ChunkReceiver(ChunkStore &store, const WorldSave &save) -: store(store) +ChunkReceiver::ChunkReceiver(Client &client, ChunkStore &store, const WorldSave &save) +: client(client) +, store(store) , save(save) , transmissions() , timer(5000) { @@ -40,7 +41,14 @@ void ChunkReceiver::Update(int dt) { for (ChunkTransmission &trans : transmissions) { if (trans.active && (timer.Elapsed() - trans.last_update) > timer.Interval()) { cout << "timeout for transmission of chunk " << trans.coords << endl; - trans.Clear(); + if (trans.header_received) { + client.SendChunkRequest(trans.coords); + trans.Reset(); + trans.last_update = timer.Elapsed(); + } else { + // well shit + trans.Clear(); + } } } if (transmissions.size() > 3) { @@ -152,7 +160,7 @@ void ChunkReceiver::Commit(ChunkTransmission &trans) { Chunk *chunk = store.Allocate(trans.coords); if (!chunk) { - // chunk no longer of interes, just drop the data + // chunk no longer of interest, just drop the data // it should probably be cached to disk, but not now :P trans.Clear(); return; @@ -167,6 +175,12 @@ void ChunkReceiver::Commit(ChunkTransmission &trans) { if (uncompress(dst, &dst_len, src, src_len) != Z_OK) { // omg, now what? cout << "got corruped chunk data for " << trans.coords << endl; + client.SendChunkRequest(trans.coords); + trans.Reset(); + // chunk data can, and probably will, contain invalid block IDs, so + // zero it to be safe + memset(dst, 0, dst_len); + return; } } else { memcpy(dst, src, min(src_len, dst_len)); @@ -188,11 +202,15 @@ ChunkTransmission::ChunkTransmission() } -void ChunkTransmission::Clear() noexcept { +void ChunkTransmission::Reset() noexcept { data_size = 0; data_received = 0; last_update = 0; header_received = false; +} + +void ChunkTransmission::Clear() noexcept { + Reset(); active = false; } @@ -305,6 +323,14 @@ uint16_t Client::SendPart() { return conn.Send(client_pack, client_sock); } +uint16_t Client::SendChunkRequest( + const glm::ivec3 &coords +) { + auto pack = Packet::Make(client_pack); + pack.WriteChunkCoords(coords); + return conn.Send(client_pack, client_sock); +} + uint16_t Client::SendMessage( uint8_t type, uint32_t ref, diff --git a/src/server/ClientConnection.hpp b/src/server/ClientConnection.hpp index bd42241..57a34e4 100644 --- a/src/server/ClientConnection.hpp +++ b/src/server/ClientConnection.hpp @@ -83,6 +83,7 @@ private: void On(const Packet::Login &) override; void On(const Packet::Part &) override; void On(const Packet::PlayerUpdate &) override; + void On(const Packet::ChunkBegin &) override; void On(const Packet::Message &) override; void CheckEntities(); diff --git a/src/server/net.cpp b/src/server/net.cpp index 7947c22..d160889 100644 --- a/src/server/net.cpp +++ b/src/server/net.cpp @@ -401,6 +401,7 @@ void ClientConnection::CheckChunkQueue() { } old_base = PlayerChunks().Base(); sort(chunk_queue.begin(), chunk_queue.end(), QueueCompare(old_base)); + chunk_queue.erase(unique(chunk_queue.begin(), chunk_queue.end()), chunk_queue.end()); } // don't push entity updates and chunk data in the same tick if (chunk_blocks_skipped >= NetStat().SuggestedPacketHold() && !SendingUpdates()) { @@ -612,6 +613,14 @@ bool ClientConnection::ChunkInRange(const glm::ivec3 &pos) const noexcept { return HasPlayer() && PlayerChunks().InRange(pos); } +void ClientConnection::On(const Packet::ChunkBegin &pack) { + glm::ivec3 pos; + pack.ReadChunkCoords(pos); + if (ChunkInRange(pos)) { + chunk_queue.push_front(pos); + } +} + void ClientConnection::On(const Packet::Message &pack) { uint8_t type; uint32_t ref; -- 2.39.2