-----------
 
 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:
 
 namespace client {
 
 class ChunkTransmission;
+class Client;
 
 class ChunkReceiver {
 
 public:
-       ChunkReceiver(ChunkStore &, const WorldSave &);
+       ChunkReceiver(Client &, ChunkStore &, const WorldSave &);
        ~ChunkReceiver();
 
        void Update(int dt);
        ChunkTransmission &GetTransmission(std::uint32_t id);
        void Commit(ChunkTransmission &);
 
+       void ReRequest(ChunkTransmission &);
+
 private:
+       Client &client;
        ChunkStore &store;
        const WorldSave &save;
        std::list<ChunkTransmission> transmissions;
 
 
        ChunkTransmission();
 
+       void Reset() noexcept;
        void Clear() noexcept;
 
        bool Complete() const noexcept;
 
                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,
 
 , 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)
 
 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) {
        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) {
 
        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;
                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));
 
 }
 
-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;
 }
 
        return conn.Send(client_pack, client_sock);
 }
 
+uint16_t Client::SendChunkRequest(
+       const glm::ivec3 &coords
+) {
+       auto pack = Packet::Make<Packet::ChunkBegin>(client_pack);
+       pack.WriteChunkCoords(coords);
+       return conn.Send(client_pack, client_sock);
+}
+
 uint16_t Client::SendMessage(
        uint8_t type,
        uint32_t ref,
 
        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();
 
                }
                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()) {
        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;