]> git.localhorst.tv Git - blank.git/blob - src/client/net.cpp
make gcc nag more
[blank.git] / src / client / net.cpp
1 #include "ChunkReceiver.hpp"
2 #include "ChunkTransmission.hpp"
3 #include "Client.hpp"
4 #include "NetworkedInput.hpp"
5
6 #include "../app/init.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"
14
15 #include <iostream>
16 #include <zlib.h>
17 #include <glm/gtx/io.hpp>
18
19 using namespace std;
20
21
22 namespace blank {
23 namespace client {
24
25
26 ChunkReceiver::ChunkReceiver(Client &client, ChunkStore &store, const WorldSave &save)
27 : client(client)
28 , store(store)
29 , save(save)
30 , transmissions()
31 , timer(5000) {
32         timer.Start();
33 }
34
35 ChunkReceiver::~ChunkReceiver() {
36
37 }
38
39 void ChunkReceiver::Update(int dt) {
40         timer.Update(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);
46                                 trans.Reset();
47                                 trans.last_update = timer.Elapsed();
48                         } else {
49                                 // well shit
50                                 trans.Clear();
51                         }
52                 }
53         }
54         if (transmissions.size() > 3) {
55                 for (auto iter = transmissions.begin(), end = transmissions.end(); iter != end; ++iter) {
56                         if (!iter->active) {
57                                 transmissions.erase(iter);
58                                 break;
59                         }
60                 }
61         }
62         LoadN(10);
63         StoreN(10);
64 }
65
66 int ChunkReceiver::ToLoad() const noexcept {
67         return store.EstimateMissing();
68 }
69
70 void ChunkReceiver::LoadOne() {
71         if (!store.HasMissing()) return;
72
73         ExactLocation::Coarse pos = store.NextMissing();
74         Chunk *chunk = store.Allocate(pos);
75         if (!chunk) {
76                 // chunk store corrupted?
77                 return;
78         }
79
80         if (save.Exists(pos)) {
81                 save.Read(*chunk);
82         }
83 }
84
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) {
88                 LoadOne();
89         }
90 }
91
92 void ChunkReceiver::StoreN(size_t n) {
93         size_t saved = 0;
94         for (Chunk &chunk : store) {
95                 if (chunk.ShouldUpdateSave()) {
96                         save.Write(chunk);
97                         ++saved;
98                         if (saved >= n) {
99                                 break;
100                         }
101                 }
102         }
103 }
104
105
106 void ChunkReceiver::Handle(const Packet::ChunkBegin &pack) {
107         uint32_t id;
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;
115         Commit(trans);
116 }
117
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;
124                 return;
125         }
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();
133         Commit(trans);
134 }
135
136 ChunkTransmission &ChunkReceiver::GetTransmission(uint32_t id) {
137         // search for ongoing
138         for (ChunkTransmission &trans : transmissions) {
139                 if (trans.active && trans.id == id) {
140                         return trans;
141                 }
142         }
143         // search for unused
144         for (ChunkTransmission &trans : transmissions) {
145                 if (!trans.active) {
146                         trans.active = true;
147                         trans.id = id;
148                         return trans;
149                 }
150         }
151         // allocate new
152         transmissions.emplace_back();
153         transmissions.back().active = true;
154         transmissions.back().id = id;
155         return transmissions.back();
156 }
157
158 void ChunkReceiver::Commit(ChunkTransmission &trans) {
159         if (!trans.Complete()) return;
160
161         Chunk *chunk = store.Allocate(trans.coords);
162         if (!chunk) {
163                 // chunk no longer of interest, just drop the data
164                 // it should probably be cached to disk, but not now :P
165                 trans.Clear();
166                 return;
167         }
168
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();
173
174         if (trans.Compressed()) {
175                 if (uncompress(dst, &dst_len, src, src_len) != Z_OK) {
176                         // omg, now what?
177                         cout << "got corruped chunk data for " << trans.coords << endl;
178                         client.SendChunkRequest(trans.coords);
179                         trans.Reset();
180                         // chunk data can, and probably will, contain invalid block IDs, so
181                         // zero it to be safe
182                         memset(dst, 0, dst_len);
183                         return;
184                 }
185         } else {
186                 memcpy(dst, src, min(src_len, dst_len));
187         }
188         chunk->ScanActive();
189         chunk->Invalidate();
190         trans.Clear();
191 }
192
193 ChunkTransmission::ChunkTransmission()
194 : id(0)
195 , flags(0)
196 , coords()
197 , data_size(0)
198 , data_received(0)
199 , last_update(0)
200 , header_received(false)
201 , active(false)
202 , buffer() {
203
204 }
205
206 void ChunkTransmission::Reset() noexcept {
207         data_size = 0;
208         data_received = 0;
209         last_update = 0;
210         header_received = false;
211 }
212
213 void ChunkTransmission::Clear() noexcept {
214         Reset();
215         active = false;
216 }
217
218 bool ChunkTransmission::Complete() const noexcept {
219         return header_received && data_received == data_size;
220 }
221
222 bool ChunkTransmission::Compressed() const noexcept {
223         return flags & 1;
224 }
225
226
227 namespace {
228
229 UDPsocket client_bind(Uint16 port) {
230         UDPsocket sock = SDLNet_UDP_Open(port);
231         if (!sock) {
232                 throw NetError("SDLNet_UDP_Open");
233         }
234         return sock;
235 }
236
237 IPaddress client_resolve(const char *host, Uint16 port) {
238         IPaddress addr;
239         if (SDLNet_ResolveHost(&addr, host, port) != 0) {
240                 throw NetError("SDLNet_ResolveHost");
241         }
242         return addr;
243 }
244
245 }
246
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
259         SendPing();
260 }
261
262 Client::~Client() {
263         delete[] client_pack.data;
264         SDLNet_UDP_Close(client_sock);
265 }
266
267
268 void Client::Handle() {
269         int result = SDLNet_UDP_Recv(client_sock, &client_pack);
270         while (result > 0) {
271                 HandlePacket(client_pack);
272                 result = SDLNet_UDP_Recv(client_sock, &client_pack);
273         }
274         if (result == -1) {
275                 // a boo boo happened
276                 throw NetError("SDLNet_UDP_Recv");
277         }
278 }
279
280 void Client::HandlePacket(const UDPpacket &udp_pack) {
281         if (!conn.Matches(udp_pack.address)) {
282                 // packet came from somewhere else, drop
283                 return;
284         }
285         const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
286         if (pack.header.tag != Packet::TAG) {
287                 // mistagged packet, drop
288                 return;
289         }
290
291         conn.Received(udp_pack);
292 }
293
294 void Client::Update(int dt) {
295         conn.Update(dt);
296         if (conn.ShouldPing()) {
297                 SendPing();
298         }
299 }
300
301 uint16_t Client::SendPing() {
302         return conn.SendPing(client_pack, client_sock);
303 }
304
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);
309 }
310
311 uint16_t Client::SendPlayerUpdate(
312         const EntityState &prediction,
313         const glm::vec3 &movement,
314         float,
315         float,
316         uint8_t actions,
317         uint8_t slot
318 ) {
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);
325 }
326
327 uint16_t Client::SendPart() {
328         Packet::Make<Packet::Part>(client_pack);
329         return conn.Send(client_pack, client_sock);
330 }
331
332 uint16_t Client::SendChunkRequest(
333         const glm::ivec3 &coords
334 ) {
335         auto pack = Packet::Make<Packet::ChunkBegin>(client_pack);
336         pack.WriteChunkCoords(coords);
337         return conn.Send(client_pack, client_sock);
338 }
339
340 uint16_t Client::SendMessage(
341         uint8_t type,
342         uint32_t ref,
343         const string &msg
344 ) {
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);
351 }
352
353 NetworkedInput::NetworkedInput(World &world, Player &player, Client &client)
354 : PlayerController(world, player)
355 , client(client)
356 , player_hist()
357 , old_movement(0.0f)
358 , old_actions(0)
359 , actions(0) {
360
361 }
362
363 bool NetworkedInput::UpdateImportant() const noexcept {
364         return old_actions != actions || !iszero(old_movement - GetMovement());
365 }
366
367 void NetworkedInput::Update(Entity &, float) {
368         Invalidate();
369         UpdatePlayer();
370 }
371
372 void NetworkedInput::PushPlayerUpdate(int dt) {
373         const EntityState &state = GetPlayer().GetEntity().GetState();
374
375         uint16_t packet = client.SendPlayerUpdate(
376                 state,
377                 GetMovement(),
378                 GetPitch(),
379                 GetYaw(),
380                 actions,
381                 InventorySlot()
382         );
383         if (player_hist.size() < 16) {
384                 player_hist.emplace_back(state, GetMovement(), dt * 0.001f, packet);
385         } else {
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);
392         }
393         old_movement = GetMovement();
394         old_actions = actions;
395 }
396
397 void NetworkedInput::MergePlayerCorrection(uint16_t seq, const EntityState &corrected_state) {
398         if (player_hist.empty()) return;
399
400         auto entry = player_hist.begin();
401         auto end = player_hist.end();
402
403         // we may have received an older packet
404         int pack_diff = int16_t(seq) - int16_t(entry->packet);
405         if (pack_diff < 0) {
406                 // indeed we have, just ignore it
407                 return;
408         }
409
410         // drop anything older than the fix
411         while (entry != end) {
412                 pack_diff = int16_t(seq) - int16_t(entry->packet);
413                 if (pack_diff > 0) {
414                         entry = player_hist.erase(entry);
415                 } else {
416                         break;
417                 }
418         }
419
420         glm::vec3 restore_movement(GetMovement());
421
422         EntityState player_state = GetPlayer().GetEntity().GetState();
423         Entity replay(GetPlayer().GetEntity());
424         replay.SetState(corrected_state);
425
426         if (entry != end) {
427                 entry->state.pos = replay.GetState().pos;
428                 ++entry;
429         }
430
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;
436                 ++entry;
437         }
438
439         glm::vec3 displacement(replay.GetState().Diff(player_state));
440         const float disp_squared = glm::dot(displacement, displacement);
441
442         if (disp_squared < 16.0f * numeric_limits<float>::epsilon()) {
443                 SetMovement(restore_movement);
444                 return;
445         }
446
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
452
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;
457         } else {
458                 displacement *= 0.01f / sqrt(disp_squared);
459                 player_state.pos.block += displacement;
460         }
461         GetPlayer().GetEntity().SetState(player_state);
462         SetMovement(restore_movement);
463 }
464
465 void NetworkedInput::StartPrimaryAction() {
466         actions |= 0x01;
467 }
468
469 void NetworkedInput::StopPrimaryAction() {
470         actions &= ~0x01;
471 }
472
473 void NetworkedInput::StartSecondaryAction() {
474         actions |= 0x02;
475 }
476
477 void NetworkedInput::StopSecondaryAction() {
478         actions &= ~0x02;
479 }
480
481 void NetworkedInput::StartTertiaryAction() {
482         actions |= 0x04;
483 }
484
485 void NetworkedInput::StopTertiaryAction() {
486         actions &= ~0x04;
487 }
488
489 }
490 }