1 #include "ChunkReceiver.hpp"
2 #include "ChunkTransmission.hpp"
4 #include "NetworkedInput.hpp"
6 #include "../app/error.hpp"
7 #include "../geometry/distance.hpp"
8 #include "../io/WorldSave.hpp"
9 #include "../net/Packet.hpp"
10 #include "../world/Chunk.hpp"
11 #include "../world/ChunkStore.hpp"
12 #include "../world/Player.hpp"
13 #include "../world/World.hpp"
17 #include <glm/gtx/io.hpp>
26 ChunkReceiver::ChunkReceiver(Client &client, ChunkStore &store, const WorldSave &save)
35 ChunkReceiver::~ChunkReceiver() {
39 void ChunkReceiver::Update(int dt) {
41 for (ChunkTransmission &trans : transmissions) {
42 if (trans.active && (timer.Elapsed() - trans.last_update) > timer.Interval()) {
43 cout << "timeout for transmission of chunk " << trans.coords << endl;
44 if (trans.header_received) {
45 client.SendChunkRequest(trans.coords);
47 trans.last_update = timer.Elapsed();
54 if (transmissions.size() > 3) {
55 for (auto iter = transmissions.begin(), end = transmissions.end(); iter != end; ++iter) {
57 transmissions.erase(iter);
66 int ChunkReceiver::ToLoad() const noexcept {
67 return store.EstimateMissing();
70 void ChunkReceiver::LoadOne() {
71 if (!store.HasMissing()) return;
73 ExactLocation::Coarse pos = store.NextMissing();
74 Chunk *chunk = store.Allocate(pos);
76 // chunk store corrupted?
80 if (save.Exists(pos)) {
85 void ChunkReceiver::LoadN(size_t n) {
86 size_t end = min(n, size_t(ToLoad()));
87 for (size_t i = 0; i < end && store.HasMissing(); ++i) {
92 void ChunkReceiver::StoreN(size_t n) {
94 for (Chunk &chunk : store) {
95 if (chunk.ShouldUpdateSave()) {
106 void ChunkReceiver::Handle(const Packet::ChunkBegin &pack) {
108 pack.ReadTransmissionId(id);
109 ChunkTransmission &trans = GetTransmission(id);
110 pack.ReadFlags(trans.flags);
111 pack.ReadChunkCoords(trans.coords);
112 pack.ReadDataSize(trans.data_size);
113 trans.last_update = timer.Elapsed();
114 trans.header_received = true;
118 void ChunkReceiver::Handle(const Packet::ChunkData &pack) {
119 uint32_t id, pos, size;
120 pack.ReadTransmissionId(id);
121 pack.ReadDataOffset(pos);
122 if (pos >= sizeof(ChunkTransmission::buffer)) {
123 cout << "received chunk data offset outside of buffer size" << endl;
126 pack.ReadDataSize(size);
127 ChunkTransmission &trans = GetTransmission(id);
128 size_t len = min(size_t(size), sizeof(ChunkTransmission::buffer) - pos);
129 pack.ReadData(&trans.buffer[pos], len);
130 // TODO: this method breaks when a packet arrives twice
131 trans.data_received += len;
132 trans.last_update = timer.Elapsed();
136 ChunkTransmission &ChunkReceiver::GetTransmission(uint32_t id) {
137 // search for ongoing
138 for (ChunkTransmission &trans : transmissions) {
139 if (trans.active && trans.id == id) {
144 for (ChunkTransmission &trans : transmissions) {
152 transmissions.emplace_back();
153 transmissions.back().active = true;
154 transmissions.back().id = id;
155 return transmissions.back();
158 void ChunkReceiver::Commit(ChunkTransmission &trans) {
159 if (!trans.Complete()) return;
161 Chunk *chunk = store.Allocate(trans.coords);
163 // chunk no longer of interest, just drop the data
164 // it should probably be cached to disk, but not now :P
169 const Byte *src = &trans.buffer[0];
170 uLong src_len = min(size_t(trans.data_size), sizeof(ChunkTransmission::buffer));
171 Byte *dst = reinterpret_cast<Byte *>(chunk->BlockData());
172 uLong dst_len = Chunk::BlockSize();
174 if (trans.Compressed()) {
175 if (uncompress(dst, &dst_len, src, src_len) != Z_OK) {
177 cout << "got corruped chunk data for " << trans.coords << endl;
178 client.SendChunkRequest(trans.coords);
180 // chunk data can, and probably will, contain invalid block IDs, so
181 // zero it to be safe
182 memset(dst, 0, dst_len);
186 memcpy(dst, src, min(src_len, dst_len));
193 ChunkTransmission::ChunkTransmission()
200 , header_received(false)
206 void ChunkTransmission::Reset() noexcept {
210 header_received = false;
213 void ChunkTransmission::Clear() noexcept {
218 bool ChunkTransmission::Complete() const noexcept {
219 return header_received && data_received == data_size;
222 bool ChunkTransmission::Compressed() const noexcept {
229 UDPsocket client_bind(Uint16 port) {
230 UDPsocket sock = SDLNet_UDP_Open(port);
232 throw NetError("SDLNet_UDP_Open");
237 IPaddress client_resolve(const char *host, Uint16 port) {
239 if (SDLNet_ResolveHost(&addr, host, port) != 0) {
240 throw NetError("SDLNet_ResolveHost");
247 // relying on {} zero intitialization for UDPpacket, because
248 // the type and number of fields is not well defined
249 #pragma GCC diagnostic push
250 #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
251 Client::Client(const Config::Network &conf)
252 : conn(client_resolve(conf.host.c_str(), conf.port))
253 , client_sock(client_bind(0))
254 , client_pack{ -1, nullptr, 0 } {
255 #pragma GCC diagnostic pop
256 client_pack.data = new Uint8[sizeof(Packet)];
257 client_pack.maxlen = sizeof(Packet);
258 // establish connection
263 delete[] client_pack.data;
264 SDLNet_UDP_Close(client_sock);
268 void Client::Handle() {
269 int result = SDLNet_UDP_Recv(client_sock, &client_pack);
271 HandlePacket(client_pack);
272 result = SDLNet_UDP_Recv(client_sock, &client_pack);
275 // a boo boo happened
276 throw NetError("SDLNet_UDP_Recv");
280 void Client::HandlePacket(const UDPpacket &udp_pack) {
281 if (!conn.Matches(udp_pack.address)) {
282 // packet came from somewhere else, drop
285 const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
286 if (pack.header.tag != Packet::TAG) {
287 // mistagged packet, drop
291 conn.Received(udp_pack);
294 void Client::Update(int dt) {
296 if (conn.ShouldPing()) {
301 uint16_t Client::SendPing() {
302 return conn.SendPing(client_pack, client_sock);
305 uint16_t Client::SendLogin(const string &name) {
306 auto pack = Packet::Make<Packet::Login>(client_pack);
307 pack.WritePlayerName(name);
308 return conn.Send(client_pack, client_sock);
311 uint16_t Client::SendPlayerUpdate(
312 const EntityState &prediction,
313 const glm::vec3 &movement,
319 auto pack = Packet::Make<Packet::PlayerUpdate>(client_pack);
320 pack.WritePredictedState(prediction);
321 pack.WriteMovement(movement);
322 pack.WriteActions(actions);
323 pack.WriteSlot(slot);
324 return conn.Send(client_pack, client_sock);
327 uint16_t Client::SendPart() {
328 Packet::Make<Packet::Part>(client_pack);
329 return conn.Send(client_pack, client_sock);
332 uint16_t Client::SendChunkRequest(
333 const glm::ivec3 &coords
335 auto pack = Packet::Make<Packet::ChunkBegin>(client_pack);
336 pack.WriteChunkCoords(coords);
337 return conn.Send(client_pack, client_sock);
340 uint16_t Client::SendMessage(
345 auto pack = Packet::Make<Packet::Message>(client_pack);
346 pack.WriteType(type);
347 pack.WriteReferral(ref);
348 pack.WriteMessage(msg);
349 client_pack.len = sizeof(Packet::Header) + Packet::Message::GetSize(msg);
350 return conn.Send(client_pack, client_sock);
353 NetworkedInput::NetworkedInput(World &world, Player &player, Client &client)
354 : PlayerController(world, player)
363 bool NetworkedInput::UpdateImportant() const noexcept {
364 return old_actions != actions || !iszero(old_movement - GetMovement());
367 void NetworkedInput::Update(Entity &, float) {
372 void NetworkedInput::PushPlayerUpdate(int dt) {
373 const EntityState &state = GetPlayer().GetEntity().GetState();
375 uint16_t packet = client.SendPlayerUpdate(
383 if (player_hist.size() < 16) {
384 player_hist.emplace_back(state, GetMovement(), dt * 0.001f, packet);
386 auto entry = player_hist.begin();
387 entry->state = state;
388 entry->movement = GetMovement();
389 entry->delta_t = dt * 0.001f;
390 entry->packet = packet;
391 player_hist.splice(player_hist.end(), player_hist, entry);
393 old_movement = GetMovement();
394 old_actions = actions;
397 void NetworkedInput::MergePlayerCorrection(uint16_t seq, const EntityState &corrected_state) {
398 if (player_hist.empty()) return;
400 auto entry = player_hist.begin();
401 auto end = player_hist.end();
403 // we may have received an older packet
404 int pack_diff = int16_t(seq) - int16_t(entry->packet);
406 // indeed we have, just ignore it
410 // drop anything older than the fix
411 while (entry != end) {
412 pack_diff = int16_t(seq) - int16_t(entry->packet);
414 entry = player_hist.erase(entry);
420 glm::vec3 restore_movement(GetMovement());
422 EntityState player_state = GetPlayer().GetEntity().GetState();
423 Entity replay(GetPlayer().GetEntity());
424 replay.SetState(corrected_state);
427 entry->state.pos = replay.GetState().pos;
431 vector<WorldCollision> col;
432 while (entry != end) {
433 SetMovement(entry->movement);
434 replay.Update(GetWorld(), entry->delta_t);
435 entry->state.pos = replay.GetState().pos;
439 glm::vec3 displacement(replay.GetState().Diff(player_state));
440 const float disp_squared = glm::dot(displacement, displacement);
442 if (disp_squared < 16.0f * numeric_limits<float>::epsilon()) {
443 SetMovement(restore_movement);
447 // if offset > 10cm, warp the player
448 // otherwise, move at most 1cm per frame towards
449 // the fixed position (160ms, so shouldn't be too noticeable)
450 constexpr float warp_thresh = 0.01f; // (1/10)^2
451 constexpr float max_disp = 0.0001f; // (1/100)^2
453 if (disp_squared > warp_thresh) {
454 player_state.pos = replay.GetState().pos;
455 } else if (disp_squared < max_disp) {
456 player_state.pos.block += displacement;
458 displacement *= 0.01f / sqrt(disp_squared);
459 player_state.pos.block += displacement;
461 GetPlayer().GetEntity().SetState(player_state);
462 SetMovement(restore_movement);
465 void NetworkedInput::StartPrimaryAction() {
469 void NetworkedInput::StopPrimaryAction() {
473 void NetworkedInput::StartSecondaryAction() {
477 void NetworkedInput::StopSecondaryAction() {
481 void NetworkedInput::StartTertiaryAction() {
485 void NetworkedInput::StopTertiaryAction() {