]> git.localhorst.tv Git - blank.git/blobdiff - src/client/net.cpp
(shabby) client side handling of messages
[blank.git] / src / client / net.cpp
index ed2dd20d3e332b13c1905da3d6d4e8ee3245734d..36d598bd9b2ba76681c509483a052f22717d6a6b 100644 (file)
@@ -1,11 +1,15 @@
 #include "ChunkReceiver.hpp"
 #include "ChunkTransmission.hpp"
 #include "Client.hpp"
+#include "NetworkedInput.hpp"
 
 #include "../app/init.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>
@@ -18,8 +22,9 @@ namespace blank {
 namespace client {
 
 
-ChunkReceiver::ChunkReceiver(ChunkStore &store)
+ChunkReceiver::ChunkReceiver(ChunkStore &store, const WorldSave &save)
 : store(store)
+, save(save)
 , transmissions()
 , timer(5000) {
        timer.Start();
@@ -45,8 +50,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;
+
+       Chunk::Pos 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);
@@ -123,6 +170,7 @@ void ChunkReceiver::Commit(ChunkTransmission &trans) {
        } else {
                memcpy(dst, src, min(src_len, dst_len));
        }
+       chunk->Invalidate();
        trans.Clear();
 }
 
@@ -176,7 +224,7 @@ IPaddress client_resolve(const char *host, Uint16 port) {
 
 }
 
-Client::Client(const Config &conf)
+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 } {
@@ -235,9 +283,21 @@ uint16_t Client::SendLogin(const string &name) {
        return conn.Send(client_pack, client_sock);
 }
 
-uint16_t Client::SendPlayerUpdate(const Entity &player) {
+uint16_t Client::SendPlayerUpdate(
+       const EntityState &prediction,
+       const glm::vec3 &movement,
+       float pitch,
+       float yaw,
+       uint8_t actions,
+       uint8_t slot
+) {
        auto pack = Packet::Make<Packet::PlayerUpdate>(client_pack);
-       pack.WritePlayer(player);
+       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);
 }
 
@@ -246,5 +306,145 @@ uint16_t Client::SendPart() {
        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()
+, actions(0) {
+
+}
+
+void NetworkedInput::Update(int dt) {
+       Invalidate();
+       UpdatePlayer();
+}
+
+void NetworkedInput::PushPlayerUpdate(int dt) {
+       const EntityState &state = GetPlayer().GetEntity().GetState();
+
+       uint16_t packet = client.SendPlayerUpdate(
+               state,
+               GetMovement(),
+               GetPitch(),
+               GetYaw(),
+               actions,
+               InventorySlot()
+       );
+       if (player_hist.size() < 16) {
+               player_hist.emplace_back(state, GetPlayer().GetEntity().TargetVelocity(), dt * 0.001f, packet);
+       } else {
+               auto entry = player_hist.begin();
+               entry->state = state;
+               entry->tgt_vel = GetPlayer().GetEntity().TargetVelocity();
+               entry->delta_t = dt * 0.001f;
+               entry->packet = packet;
+               player_hist.splice(player_hist.end(), player_hist, entry);
+       }
+}
+
+void NetworkedInput::MergePlayerCorrection(uint16_t seq, const EntityState &corrected_state) {
+       if (player_hist.empty()) return;
+
+       auto entry = player_hist.begin();
+       auto end = player_hist.end();
+
+       // we may have received an older packet
+       int pack_diff = int16_t(seq) - int16_t(entry->packet);
+       if (pack_diff < 0) {
+               // indeed we have, just ignore it
+               return;
+       }
+
+       // drop anything older than the fix
+       while (entry != end) {
+               pack_diff = int16_t(seq) - int16_t(entry->packet);
+               if (pack_diff > 0) {
+                       entry = player_hist.erase(entry);
+               } else {
+                       break;
+               }
+       }
+
+       EntityState &player_state = GetPlayer().GetEntity().GetState();
+       Entity replay(GetPlayer().GetEntity());
+       replay.SetState(corrected_state);
+
+       if (entry != end) {
+               entry->state.chunk_pos = replay.GetState().chunk_pos;
+               entry->state.block_pos = replay.GetState().block_pos;
+               ++entry;
+       }
+
+       vector<WorldCollision> col;
+       while (entry != end) {
+               replay.Velocity(entry->state.velocity);
+               replay.TargetVelocity(entry->tgt_vel);
+               GetWorld().Update(replay, entry->delta_t);
+               entry->state.chunk_pos = replay.GetState().chunk_pos;
+               entry->state.block_pos = replay.GetState().block_pos;
+               ++entry;
+       }
+
+       glm::vec3 displacement(replay.GetState().Diff(player_state));
+       const float disp_squared = dot(displacement, displacement);
+
+       if (disp_squared < 16.0f * numeric_limits<float>::epsilon()) {
+               return;
+       }
+
+       // if offset > 10cm, warp the player
+       // otherwise, move at most 1cm per frame towards
+       // the fixed position (160ms, so shouldn't be too noticeable)
+       constexpr float warp_thresh = 0.01f; // (1/10)^2
+       constexpr float max_disp = 0.0001f; // (1/100)^2
+
+       if (disp_squared > warp_thresh) {
+               player_state.chunk_pos = replay.GetState().chunk_pos;
+               player_state.block_pos = replay.GetState().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 NetworkedInput::StartPrimaryAction() {
+       actions |= 0x01;
+}
+
+void NetworkedInput::StopPrimaryAction() {
+       actions &= ~0x01;
+}
+
+void NetworkedInput::StartSecondaryAction() {
+       actions |= 0x02;
+}
+
+void NetworkedInput::StopSecondaryAction() {
+       actions &= ~0x02;
+}
+
+void NetworkedInput::StartTertiaryAction() {
+       actions |= 0x04;
+}
+
+void NetworkedInput::StopTertiaryAction() {
+       actions &= ~0x04;
+}
+
 }
 }