X-Git-Url: http://git.localhorst.tv/?a=blobdiff_plain;f=src%2Fclient%2Fnet.cpp;h=bd2a43abd002a20224e8019ff36f203c08989061;hb=ed3bdc028edc0ecb5835d1c0bf18dbc59b342daf;hp=a5feaf5f7cddd4ac4038d2f5886432fa9d49686c;hpb=b066e776622f96e906600a0c4a08de392bd03676;p=blank.git diff --git a/src/client/net.cpp b/src/client/net.cpp index a5feaf5..bd2a43a 100644 --- a/src/client/net.cpp +++ b/src/client/net.cpp @@ -1,11 +1,16 @@ #include "ChunkReceiver.hpp" #include "ChunkTransmission.hpp" #include "Client.hpp" +#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 #include @@ -18,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(); @@ -34,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) { @@ -45,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); @@ -104,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; @@ -119,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(); } @@ -140,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; } @@ -236,9 +303,19 @@ 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(client_pack); - pack.WritePlayer(player); + pack.WritePredictedState(prediction); + pack.WriteMovement(movement); + pack.WriteActions(actions); + pack.WriteSlot(slot); return conn.Send(client_pack, client_sock); } @@ -247,5 +324,162 @@ 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, + const string &msg +) { + auto pack = Packet::Make(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) { + +} + +bool NetworkedInput::UpdateImportant() const noexcept { + return old_actions != actions || !iszero(old_movement - GetMovement()); +} + +void NetworkedInput::Update(Entity &, float 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, GetMovement(), dt * 0.001f, packet); + } else { + auto entry = player_hist.begin(); + entry->state = state; + 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) { + 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; + } + } + + glm::vec3 restore_movement(GetMovement()); + + EntityState player_state = GetPlayer().GetEntity().GetState(); + Entity replay(GetPlayer().GetEntity()); + replay.SetState(corrected_state); + + if (entry != end) { + entry->state.pos = replay.GetState().pos; + ++entry; + } + + vector col; + while (entry != end) { + SetMovement(entry->movement); + GetWorld().Update(replay, entry->delta_t); + entry->state.pos = replay.GetState().pos; + ++entry; + } + + glm::vec3 displacement(replay.GetState().Diff(player_state)); + const float disp_squared = dot(displacement, displacement); + + if (disp_squared < 16.0f * numeric_limits::epsilon()) { + SetMovement(restore_movement); + 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.pos = replay.GetState().pos; + } else if (disp_squared < max_disp) { + player_state.pos.block += displacement; + } else { + displacement *= 0.01f / sqrt(disp_squared); + player_state.pos.block += displacement; + } + GetPlayer().GetEntity().SetState(player_state); + SetMovement(restore_movement); +} + +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; +} + } }