]> git.localhorst.tv Git - blank.git/blobdiff - src/client/net.cpp
make gcc nag more
[blank.git] / src / client / net.cpp
index 6e8d00cc69de3922c063d912fdf34b82b46927b1..f1e555336699738a4434470c1bd1955ad8bea4d4 100644 (file)
@@ -4,10 +4,13 @@
 #include "NetworkedInput.hpp"
 
 #include "../app/init.hpp"
+#include "../geometry/distance.hpp"
+#include "../io/WorldSave.hpp"
 #include "../net/Packet.hpp"
 #include "../world/Chunk.hpp"
 #include "../world/ChunkStore.hpp"
 #include "../world/Player.hpp"
+#include "../world/World.hpp"
 
 #include <iostream>
 #include <zlib.h>
@@ -20,8 +23,10 @@ namespace blank {
 namespace client {
 
 
-ChunkReceiver::ChunkReceiver(ChunkStore &store)
-: store(store)
+ChunkReceiver::ChunkReceiver(Client &client, ChunkStore &store, const WorldSave &save)
+: client(client)
+, store(store)
+, save(save)
 , transmissions()
 , timer(5000) {
        timer.Start();
@@ -36,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) {
@@ -47,8 +59,50 @@ void ChunkReceiver::Update(int dt) {
                        }
                }
        }
+       LoadN(10);
+       StoreN(10);
+}
+
+int ChunkReceiver::ToLoad() const noexcept {
+       return store.EstimateMissing();
+}
+
+void ChunkReceiver::LoadOne() {
+       if (!store.HasMissing()) return;
+
+       ExactLocation::Coarse pos = store.NextMissing();
+       Chunk *chunk = store.Allocate(pos);
+       if (!chunk) {
+               // chunk store corrupted?
+               return;
+       }
+
+       if (save.Exists(pos)) {
+               save.Read(*chunk);
+       }
+}
+
+void ChunkReceiver::LoadN(size_t n) {
+       size_t end = min(n, size_t(ToLoad()));
+       for (size_t i = 0; i < end && store.HasMissing(); ++i) {
+               LoadOne();
+       }
 }
 
+void ChunkReceiver::StoreN(size_t n) {
+       size_t saved = 0;
+       for (Chunk &chunk : store) {
+               if (chunk.ShouldUpdateSave()) {
+                       save.Write(chunk);
+                       ++saved;
+                       if (saved >= n) {
+                               break;
+                       }
+               }
+       }
+}
+
+
 void ChunkReceiver::Handle(const Packet::ChunkBegin &pack) {
        uint32_t id;
        pack.ReadTransmissionId(id);
@@ -106,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;
@@ -121,10 +175,17 @@ 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));
        }
+       chunk->ScanActive();
        chunk->Invalidate();
        trans.Clear();
 }
@@ -142,11 +203,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;
 }
 
@@ -179,10 +244,15 @@ IPaddress client_resolve(const char *host, Uint16 port) {
 
 }
 
+// relying on {} zero intitialization for UDPpacket, because
+// the type and number of fields is not well defined
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
 Client::Client(const Config::Network &conf)
 : conn(client_resolve(conf.host.c_str(), conf.port))
 , client_sock(client_bind(0))
 , client_pack{ -1, nullptr, 0 } {
+#pragma GCC diagnostic pop
        client_pack.data = new Uint8[sizeof(Packet)];
        client_pack.maxlen = sizeof(Packet);
        // establish connection
@@ -241,16 +311,14 @@ uint16_t Client::SendLogin(const string &name) {
 uint16_t Client::SendPlayerUpdate(
        const EntityState &prediction,
        const glm::vec3 &movement,
-       float pitch,
-       float yaw,
-       std::uint8_t actions,
-       std::uint8_t slot
+       float,
+       float,
+       uint8_t actions,
+       uint8_t slot
 ) {
        auto pack = Packet::Make<Packet::PlayerUpdate>(client_pack);
        pack.WritePredictedState(prediction);
        pack.WriteMovement(movement);
-       pack.WritePitch(pitch);
-       pack.WriteYaw(yaw);
        pack.WriteActions(actions);
        pack.WriteSlot(slot);
        return conn.Send(client_pack, client_sock);
@@ -261,16 +329,42 @@ uint16_t Client::SendPart() {
        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,
+       const string &msg
+) {
+       auto pack = Packet::Make<Packet::Message>(client_pack);
+       pack.WriteType(type);
+       pack.WriteReferral(ref);
+       pack.WriteMessage(msg);
+       client_pack.len = sizeof(Packet::Header) + Packet::Message::GetSize(msg);
+       return conn.Send(client_pack, client_sock);
+}
 
 NetworkedInput::NetworkedInput(World &world, Player &player, Client &client)
 : PlayerController(world, player)
 , client(client)
 , player_hist()
+, old_movement(0.0f)
+, old_actions(0)
 , actions(0) {
 
 }
 
-void NetworkedInput::Update(int dt) {
+bool NetworkedInput::UpdateImportant() const noexcept {
+       return old_actions != actions || !iszero(old_movement - GetMovement());
+}
+
+void NetworkedInput::Update(Entity &, float) {
        Invalidate();
        UpdatePlayer();
 }
@@ -278,7 +372,7 @@ void NetworkedInput::Update(int dt) {
 void NetworkedInput::PushPlayerUpdate(int dt) {
        const EntityState &state = GetPlayer().GetEntity().GetState();
 
-       std::uint16_t packet = client.SendPlayerUpdate(
+       uint16_t packet = client.SendPlayerUpdate(
                state,
                GetMovement(),
                GetPitch(),
@@ -287,14 +381,17 @@ void NetworkedInput::PushPlayerUpdate(int dt) {
                InventorySlot()
        );
        if (player_hist.size() < 16) {
-               player_hist.emplace_back(state, dt, packet);
+               player_hist.emplace_back(state, GetMovement(), dt * 0.001f, packet);
        } else {
                auto entry = player_hist.begin();
                entry->state = state;
-               entry->delta_t = dt;
+               entry->movement = GetMovement();
+               entry->delta_t = dt * 0.001f;
                entry->packet = packet;
                player_hist.splice(player_hist.end(), player_hist, entry);
        }
+       old_movement = GetMovement();
+       old_actions = actions;
 }
 
 void NetworkedInput::MergePlayerCorrection(uint16_t seq, const EntityState &corrected_state) {
@@ -320,27 +417,30 @@ void NetworkedInput::MergePlayerCorrection(uint16_t seq, const EntityState &corr
                }
        }
 
-       EntityState replay_state(corrected_state);
-       EntityState &player_state = GetPlayer().GetEntity().GetState();
+       glm::vec3 restore_movement(GetMovement());
+
+       EntityState player_state = GetPlayer().GetEntity().GetState();
+       Entity replay(GetPlayer().GetEntity());
+       replay.SetState(corrected_state);
 
        if (entry != end) {
-               entry->state.chunk_pos = replay_state.chunk_pos;
-               entry->state.block_pos = replay_state.block_pos;
+               entry->state.pos = replay.GetState().pos;
                ++entry;
        }
 
+       vector<WorldCollision> col;
        while (entry != end) {
-               replay_state.velocity = entry->state.velocity;
-               replay_state.Update(entry->delta_t);
-               entry->state.chunk_pos = replay_state.chunk_pos;
-               entry->state.block_pos = replay_state.block_pos;
+               SetMovement(entry->movement);
+               replay.Update(GetWorld(), entry->delta_t);
+               entry->state.pos = replay.GetState().pos;
                ++entry;
        }
 
-       glm::vec3 displacement(replay_state.Diff(player_state));
-       const float disp_squared = dot(displacement, displacement);
+       glm::vec3 displacement(replay.GetState().Diff(player_state));
+       const float disp_squared = glm::dot(displacement, displacement);
 
        if (disp_squared < 16.0f * numeric_limits<float>::epsilon()) {
+               SetMovement(restore_movement);
                return;
        }
 
@@ -351,14 +451,15 @@ void NetworkedInput::MergePlayerCorrection(uint16_t seq, const EntityState &corr
        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;
+               player_state.pos = replay.GetState().pos;
        } else if (disp_squared < max_disp) {
-               player_state.block_pos += displacement;
+               player_state.pos.block += displacement;
        } else {
                displacement *= 0.01f / sqrt(disp_squared);
-               player_state.block_pos += displacement;
+               player_state.pos.block += displacement;
        }
+       GetPlayer().GetEntity().SetState(player_state);
+       SetMovement(restore_movement);
 }
 
 void NetworkedInput::StartPrimaryAction() {