#include "Client.hpp"
#include "NetworkedInput.hpp"
-#include "../app/init.hpp"
+#include "../app/error.hpp"
+#include "../geometry/distance.hpp"
+#include "../io/WorldSave.hpp"
#include "../net/Packet.hpp"
#include "../world/Chunk.hpp"
#include "../world/ChunkStore.hpp"
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();
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) {
}
}
}
+ 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);
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));
}
+ chunk->ScanActive();
chunk->Invalidate();
trans.Clear();
}
}
-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;
}
}
+// 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
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);
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();
}
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(),
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) {
}
}
- 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.GetState().chunk_pos;
- entry->state.block_pos = replay.GetState().block_pos;
+ entry->state.pos = replay.GetState().pos;
++entry;
}
vector<WorldCollision> col;
while (entry != end) {
- replay.Velocity(entry->state.velocity);
- replay.Update(entry->delta_t);
- if (GetWorld().Intersection(replay, col)) {
- GetWorld().Resolve(replay, col);
- col.clear();
- }
- entry->state.chunk_pos = replay.GetState().chunk_pos;
- entry->state.block_pos = replay.GetState().block_pos;
+ SetMovement(entry->movement);
+ replay.Update(GetWorld(), 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);
+ const float disp_squared = glm::dot(displacement, displacement);
if (disp_squared < 16.0f * numeric_limits<float>::epsilon()) {
+ SetMovement(restore_movement);
return;
}
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;
+ 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() {