1 #include "ChunkReceiver.hpp"
2 #include "ChunkTransmission.hpp"
4 #include "NetworkedInput.hpp"
6 #include "../app/init.hpp"
7 #include "../io/WorldSave.hpp"
8 #include "../net/Packet.hpp"
9 #include "../world/Chunk.hpp"
10 #include "../world/ChunkStore.hpp"
11 #include "../world/Player.hpp"
12 #include "../world/World.hpp"
16 #include <glm/gtx/io.hpp>
25 ChunkReceiver::ChunkReceiver(ChunkStore &store, const WorldSave &save)
33 ChunkReceiver::~ChunkReceiver() {
37 void ChunkReceiver::Update(int dt) {
39 for (ChunkTransmission &trans : transmissions) {
40 if (trans.active && (timer.Elapsed() - trans.last_update) > timer.Interval()) {
41 cout << "timeout for transmission of chunk " << trans.coords << endl;
45 if (transmissions.size() > 3) {
46 for (auto iter = transmissions.begin(), end = transmissions.end(); iter != end; ++iter) {
48 transmissions.erase(iter);
57 int ChunkReceiver::ToLoad() const noexcept {
58 return store.EstimateMissing();
61 void ChunkReceiver::LoadOne() {
62 if (!store.HasMissing()) return;
64 Chunk::Pos pos = store.NextMissing();
65 Chunk *chunk = store.Allocate(pos);
67 // chunk store corrupted?
71 if (save.Exists(pos)) {
76 void ChunkReceiver::LoadN(size_t n) {
77 size_t end = min(n, size_t(ToLoad()));
78 for (size_t i = 0; i < end && store.HasMissing(); ++i) {
83 void ChunkReceiver::StoreN(size_t n) {
85 for (Chunk &chunk : store) {
86 if (chunk.ShouldUpdateSave()) {
97 void ChunkReceiver::Handle(const Packet::ChunkBegin &pack) {
99 pack.ReadTransmissionId(id);
100 ChunkTransmission &trans = GetTransmission(id);
101 pack.ReadFlags(trans.flags);
102 pack.ReadChunkCoords(trans.coords);
103 pack.ReadDataSize(trans.data_size);
104 trans.last_update = timer.Elapsed();
105 trans.header_received = true;
109 void ChunkReceiver::Handle(const Packet::ChunkData &pack) {
110 uint32_t id, pos, size;
111 pack.ReadTransmissionId(id);
112 pack.ReadDataOffset(pos);
113 if (pos >= sizeof(ChunkTransmission::buffer)) {
114 cout << "received chunk data offset outside of buffer size" << endl;
117 pack.ReadDataSize(size);
118 ChunkTransmission &trans = GetTransmission(id);
119 size_t len = min(size_t(size), sizeof(ChunkTransmission::buffer) - pos);
120 pack.ReadData(&trans.buffer[pos], len);
121 // TODO: this method breaks when a packet arrives twice
122 trans.data_received += len;
123 trans.last_update = timer.Elapsed();
127 ChunkTransmission &ChunkReceiver::GetTransmission(uint32_t id) {
128 // search for ongoing
129 for (ChunkTransmission &trans : transmissions) {
130 if (trans.active && trans.id == id) {
135 for (ChunkTransmission &trans : transmissions) {
143 transmissions.emplace_back();
144 transmissions.back().active = true;
145 transmissions.back().id = id;
146 return transmissions.back();
149 void ChunkReceiver::Commit(ChunkTransmission &trans) {
150 if (!trans.Complete()) return;
152 Chunk *chunk = store.Allocate(trans.coords);
154 // chunk no longer of interes, just drop the data
155 // it should probably be cached to disk, but not now :P
160 const Byte *src = &trans.buffer[0];
161 uLong src_len = min(size_t(trans.data_size), sizeof(ChunkTransmission::buffer));
162 Byte *dst = reinterpret_cast<Byte *>(chunk->BlockData());
163 uLong dst_len = Chunk::BlockSize();
165 if (trans.Compressed()) {
166 if (uncompress(dst, &dst_len, src, src_len) != Z_OK) {
168 cout << "got corruped chunk data for " << trans.coords << endl;
171 memcpy(dst, src, min(src_len, dst_len));
177 ChunkTransmission::ChunkTransmission()
184 , header_received(false)
190 void ChunkTransmission::Clear() noexcept {
194 header_received = false;
198 bool ChunkTransmission::Complete() const noexcept {
199 return header_received && data_received == data_size;
202 bool ChunkTransmission::Compressed() const noexcept {
209 UDPsocket client_bind(Uint16 port) {
210 UDPsocket sock = SDLNet_UDP_Open(port);
212 throw NetError("SDLNet_UDP_Open");
217 IPaddress client_resolve(const char *host, Uint16 port) {
219 if (SDLNet_ResolveHost(&addr, host, port) != 0) {
220 throw NetError("SDLNet_ResolveHost");
227 Client::Client(const Config::Network &conf)
228 : conn(client_resolve(conf.host.c_str(), conf.port))
229 , client_sock(client_bind(0))
230 , client_pack{ -1, nullptr, 0 } {
231 client_pack.data = new Uint8[sizeof(Packet)];
232 client_pack.maxlen = sizeof(Packet);
233 // establish connection
238 delete[] client_pack.data;
239 SDLNet_UDP_Close(client_sock);
243 void Client::Handle() {
244 int result = SDLNet_UDP_Recv(client_sock, &client_pack);
246 HandlePacket(client_pack);
247 result = SDLNet_UDP_Recv(client_sock, &client_pack);
250 // a boo boo happened
251 throw NetError("SDLNet_UDP_Recv");
255 void Client::HandlePacket(const UDPpacket &udp_pack) {
256 if (!conn.Matches(udp_pack.address)) {
257 // packet came from somewhere else, drop
260 const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
261 if (pack.header.tag != Packet::TAG) {
262 // mistagged packet, drop
266 conn.Received(udp_pack);
269 void Client::Update(int dt) {
271 if (conn.ShouldPing()) {
276 uint16_t Client::SendPing() {
277 return conn.SendPing(client_pack, client_sock);
280 uint16_t Client::SendLogin(const string &name) {
281 auto pack = Packet::Make<Packet::Login>(client_pack);
282 pack.WritePlayerName(name);
283 return conn.Send(client_pack, client_sock);
286 uint16_t Client::SendPlayerUpdate(
287 const EntityState &prediction,
288 const glm::vec3 &movement,
294 auto pack = Packet::Make<Packet::PlayerUpdate>(client_pack);
295 pack.WritePredictedState(prediction);
296 pack.WriteMovement(movement);
297 pack.WriteActions(actions);
298 pack.WriteSlot(slot);
299 return conn.Send(client_pack, client_sock);
302 uint16_t Client::SendPart() {
303 Packet::Make<Packet::Part>(client_pack);
304 return conn.Send(client_pack, client_sock);
307 uint16_t Client::SendMessage(
312 auto pack = Packet::Make<Packet::Message>(client_pack);
313 pack.WriteType(type);
314 pack.WriteReferral(ref);
315 pack.WriteMessage(msg);
316 client_pack.len = sizeof(Packet::Header) + Packet::Message::GetSize(msg);
317 return conn.Send(client_pack, client_sock);
320 NetworkedInput::NetworkedInput(World &world, Player &player, Client &client)
321 : PlayerController(world, player)
330 bool NetworkedInput::UpdateImportant() const noexcept {
331 return old_actions != actions || !iszero(old_movement - GetMovement());
334 void NetworkedInput::Update(Entity &, float dt) {
339 void NetworkedInput::PushPlayerUpdate(int dt) {
340 const EntityState &state = GetPlayer().GetEntity().GetState();
342 uint16_t packet = client.SendPlayerUpdate(
350 if (player_hist.size() < 16) {
351 player_hist.emplace_back(state, GetMovement(), dt * 0.001f, packet);
353 auto entry = player_hist.begin();
354 entry->state = state;
355 entry->movement = GetMovement();
356 entry->delta_t = dt * 0.001f;
357 entry->packet = packet;
358 player_hist.splice(player_hist.end(), player_hist, entry);
360 old_movement = GetMovement();
361 old_actions = actions;
364 void NetworkedInput::MergePlayerCorrection(uint16_t seq, const EntityState &corrected_state) {
365 if (player_hist.empty()) return;
367 auto entry = player_hist.begin();
368 auto end = player_hist.end();
370 // we may have received an older packet
371 int pack_diff = int16_t(seq) - int16_t(entry->packet);
373 // indeed we have, just ignore it
377 // drop anything older than the fix
378 while (entry != end) {
379 pack_diff = int16_t(seq) - int16_t(entry->packet);
381 entry = player_hist.erase(entry);
387 glm::vec3 restore_movement(GetMovement());
389 EntityState player_state = GetPlayer().GetEntity().GetState();
390 Entity replay(GetPlayer().GetEntity());
391 replay.SetState(corrected_state);
394 entry->state.chunk_pos = replay.GetState().chunk_pos;
395 entry->state.block_pos = replay.GetState().block_pos;
399 vector<WorldCollision> col;
400 while (entry != end) {
401 SetMovement(entry->movement);
402 GetWorld().Update(replay, entry->delta_t);
403 entry->state.chunk_pos = replay.GetState().chunk_pos;
404 entry->state.block_pos = replay.GetState().block_pos;
408 glm::vec3 displacement(replay.GetState().Diff(player_state));
409 const float disp_squared = dot(displacement, displacement);
411 if (disp_squared < 16.0f * numeric_limits<float>::epsilon()) {
412 SetMovement(restore_movement);
416 // if offset > 10cm, warp the player
417 // otherwise, move at most 1cm per frame towards
418 // the fixed position (160ms, so shouldn't be too noticeable)
419 constexpr float warp_thresh = 0.01f; // (1/10)^2
420 constexpr float max_disp = 0.0001f; // (1/100)^2
422 if (disp_squared > warp_thresh) {
423 player_state.chunk_pos = replay.GetState().chunk_pos;
424 player_state.block_pos = replay.GetState().block_pos;
425 } else if (disp_squared < max_disp) {
426 player_state.block_pos += displacement;
428 displacement *= 0.01f / sqrt(disp_squared);
429 player_state.block_pos += displacement;
431 GetPlayer().GetEntity().SetState(player_state);
432 SetMovement(restore_movement);
435 void NetworkedInput::StartPrimaryAction() {
439 void NetworkedInput::StopPrimaryAction() {
443 void NetworkedInput::StartSecondaryAction() {
447 void NetworkedInput::StopSecondaryAction() {
451 void NetworkedInput::StartTertiaryAction() {
455 void NetworkedInput::StopTertiaryAction() {