]> git.localhorst.tv Git - blank.git/blob - src/client/net.cpp
split geometry lib
[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(ChunkStore &store, const WorldSave &save)
27 : store(store)
28 , save(save)
29 , transmissions()
30 , timer(5000) {
31         timer.Start();
32 }
33
34 ChunkReceiver::~ChunkReceiver() {
35
36 }
37
38 void ChunkReceiver::Update(int dt) {
39         timer.Update(dt);
40         for (ChunkTransmission &trans : transmissions) {
41                 if (trans.active && (timer.Elapsed() - trans.last_update) > timer.Interval()) {
42                         cout << "timeout for transmission of chunk " << trans.coords << endl;
43                         trans.Clear();
44                 }
45         }
46         if (transmissions.size() > 3) {
47                 for (auto iter = transmissions.begin(), end = transmissions.end(); iter != end; ++iter) {
48                         if (!iter->active) {
49                                 transmissions.erase(iter);
50                                 break;
51                         }
52                 }
53         }
54         LoadN(10);
55         StoreN(10);
56 }
57
58 int ChunkReceiver::ToLoad() const noexcept {
59         return store.EstimateMissing();
60 }
61
62 void ChunkReceiver::LoadOne() {
63         if (!store.HasMissing()) return;
64
65         Chunk::Pos pos = store.NextMissing();
66         Chunk *chunk = store.Allocate(pos);
67         if (!chunk) {
68                 // chunk store corrupted?
69                 return;
70         }
71
72         if (save.Exists(pos)) {
73                 save.Read(*chunk);
74         }
75 }
76
77 void ChunkReceiver::LoadN(size_t n) {
78         size_t end = min(n, size_t(ToLoad()));
79         for (size_t i = 0; i < end && store.HasMissing(); ++i) {
80                 LoadOne();
81         }
82 }
83
84 void ChunkReceiver::StoreN(size_t n) {
85         size_t saved = 0;
86         for (Chunk &chunk : store) {
87                 if (chunk.ShouldUpdateSave()) {
88                         save.Write(chunk);
89                         ++saved;
90                         if (saved >= n) {
91                                 break;
92                         }
93                 }
94         }
95 }
96
97
98 void ChunkReceiver::Handle(const Packet::ChunkBegin &pack) {
99         uint32_t id;
100         pack.ReadTransmissionId(id);
101         ChunkTransmission &trans = GetTransmission(id);
102         pack.ReadFlags(trans.flags);
103         pack.ReadChunkCoords(trans.coords);
104         pack.ReadDataSize(trans.data_size);
105         trans.last_update = timer.Elapsed();
106         trans.header_received = true;
107         Commit(trans);
108 }
109
110 void ChunkReceiver::Handle(const Packet::ChunkData &pack) {
111         uint32_t id, pos, size;
112         pack.ReadTransmissionId(id);
113         pack.ReadDataOffset(pos);
114         if (pos >= sizeof(ChunkTransmission::buffer)) {
115                 cout << "received chunk data offset outside of buffer size" << endl;
116                 return;
117         }
118         pack.ReadDataSize(size);
119         ChunkTransmission &trans = GetTransmission(id);
120         size_t len = min(size_t(size), sizeof(ChunkTransmission::buffer) - pos);
121         pack.ReadData(&trans.buffer[pos], len);
122         // TODO: this method breaks when a packet arrives twice
123         trans.data_received += len;
124         trans.last_update = timer.Elapsed();
125         Commit(trans);
126 }
127
128 ChunkTransmission &ChunkReceiver::GetTransmission(uint32_t id) {
129         // search for ongoing
130         for (ChunkTransmission &trans : transmissions) {
131                 if (trans.active && trans.id == id) {
132                         return trans;
133                 }
134         }
135         // search for unused
136         for (ChunkTransmission &trans : transmissions) {
137                 if (!trans.active) {
138                         trans.active = true;
139                         trans.id = id;
140                         return trans;
141                 }
142         }
143         // allocate new
144         transmissions.emplace_back();
145         transmissions.back().active = true;
146         transmissions.back().id = id;
147         return transmissions.back();
148 }
149
150 void ChunkReceiver::Commit(ChunkTransmission &trans) {
151         if (!trans.Complete()) return;
152
153         Chunk *chunk = store.Allocate(trans.coords);
154         if (!chunk) {
155                 // chunk no longer of interes, just drop the data
156                 // it should probably be cached to disk, but not now :P
157                 trans.Clear();
158                 return;
159         }
160
161         const Byte *src = &trans.buffer[0];
162         uLong src_len = min(size_t(trans.data_size), sizeof(ChunkTransmission::buffer));
163         Byte *dst = reinterpret_cast<Byte *>(chunk->BlockData());
164         uLong dst_len = Chunk::BlockSize();
165
166         if (trans.Compressed()) {
167                 if (uncompress(dst, &dst_len, src, src_len) != Z_OK) {
168                         // omg, now what?
169                         cout << "got corruped chunk data for " << trans.coords << endl;
170                 }
171         } else {
172                 memcpy(dst, src, min(src_len, dst_len));
173         }
174         chunk->Invalidate();
175         trans.Clear();
176 }
177
178 ChunkTransmission::ChunkTransmission()
179 : id(0)
180 , flags(0)
181 , coords()
182 , data_size(0)
183 , data_received(0)
184 , last_update(0)
185 , header_received(false)
186 , active(false)
187 , buffer() {
188
189 }
190
191 void ChunkTransmission::Clear() noexcept {
192         data_size = 0;
193         data_received = 0;
194         last_update = 0;
195         header_received = false;
196         active = false;
197 }
198
199 bool ChunkTransmission::Complete() const noexcept {
200         return header_received && data_received == data_size;
201 }
202
203 bool ChunkTransmission::Compressed() const noexcept {
204         return flags & 1;
205 }
206
207
208 namespace {
209
210 UDPsocket client_bind(Uint16 port) {
211         UDPsocket sock = SDLNet_UDP_Open(port);
212         if (!sock) {
213                 throw NetError("SDLNet_UDP_Open");
214         }
215         return sock;
216 }
217
218 IPaddress client_resolve(const char *host, Uint16 port) {
219         IPaddress addr;
220         if (SDLNet_ResolveHost(&addr, host, port) != 0) {
221                 throw NetError("SDLNet_ResolveHost");
222         }
223         return addr;
224 }
225
226 }
227
228 Client::Client(const Config::Network &conf)
229 : conn(client_resolve(conf.host.c_str(), conf.port))
230 , client_sock(client_bind(0))
231 , client_pack{ -1, nullptr, 0 } {
232         client_pack.data = new Uint8[sizeof(Packet)];
233         client_pack.maxlen = sizeof(Packet);
234         // establish connection
235         SendPing();
236 }
237
238 Client::~Client() {
239         delete[] client_pack.data;
240         SDLNet_UDP_Close(client_sock);
241 }
242
243
244 void Client::Handle() {
245         int result = SDLNet_UDP_Recv(client_sock, &client_pack);
246         while (result > 0) {
247                 HandlePacket(client_pack);
248                 result = SDLNet_UDP_Recv(client_sock, &client_pack);
249         }
250         if (result == -1) {
251                 // a boo boo happened
252                 throw NetError("SDLNet_UDP_Recv");
253         }
254 }
255
256 void Client::HandlePacket(const UDPpacket &udp_pack) {
257         if (!conn.Matches(udp_pack.address)) {
258                 // packet came from somewhere else, drop
259                 return;
260         }
261         const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
262         if (pack.header.tag != Packet::TAG) {
263                 // mistagged packet, drop
264                 return;
265         }
266
267         conn.Received(udp_pack);
268 }
269
270 void Client::Update(int dt) {
271         conn.Update(dt);
272         if (conn.ShouldPing()) {
273                 SendPing();
274         }
275 }
276
277 uint16_t Client::SendPing() {
278         return conn.SendPing(client_pack, client_sock);
279 }
280
281 uint16_t Client::SendLogin(const string &name) {
282         auto pack = Packet::Make<Packet::Login>(client_pack);
283         pack.WritePlayerName(name);
284         return conn.Send(client_pack, client_sock);
285 }
286
287 uint16_t Client::SendPlayerUpdate(
288         const EntityState &prediction,
289         const glm::vec3 &movement,
290         float pitch,
291         float yaw,
292         uint8_t actions,
293         uint8_t slot
294 ) {
295         auto pack = Packet::Make<Packet::PlayerUpdate>(client_pack);
296         pack.WritePredictedState(prediction);
297         pack.WriteMovement(movement);
298         pack.WriteActions(actions);
299         pack.WriteSlot(slot);
300         return conn.Send(client_pack, client_sock);
301 }
302
303 uint16_t Client::SendPart() {
304         Packet::Make<Packet::Part>(client_pack);
305         return conn.Send(client_pack, client_sock);
306 }
307
308 uint16_t Client::SendMessage(
309         uint8_t type,
310         uint32_t ref,
311         const string &msg
312 ) {
313         auto pack = Packet::Make<Packet::Message>(client_pack);
314         pack.WriteType(type);
315         pack.WriteReferral(ref);
316         pack.WriteMessage(msg);
317         client_pack.len = sizeof(Packet::Header) + Packet::Message::GetSize(msg);
318         return conn.Send(client_pack, client_sock);
319 }
320
321 NetworkedInput::NetworkedInput(World &world, Player &player, Client &client)
322 : PlayerController(world, player)
323 , client(client)
324 , player_hist()
325 , old_movement(0.0f)
326 , old_actions(0)
327 , actions(0) {
328
329 }
330
331 bool NetworkedInput::UpdateImportant() const noexcept {
332         return old_actions != actions || !iszero(old_movement - GetMovement());
333 }
334
335 void NetworkedInput::Update(Entity &, float dt) {
336         Invalidate();
337         UpdatePlayer();
338 }
339
340 void NetworkedInput::PushPlayerUpdate(int dt) {
341         const EntityState &state = GetPlayer().GetEntity().GetState();
342
343         uint16_t packet = client.SendPlayerUpdate(
344                 state,
345                 GetMovement(),
346                 GetPitch(),
347                 GetYaw(),
348                 actions,
349                 InventorySlot()
350         );
351         if (player_hist.size() < 16) {
352                 player_hist.emplace_back(state, GetMovement(), dt * 0.001f, packet);
353         } else {
354                 auto entry = player_hist.begin();
355                 entry->state = state;
356                 entry->movement = GetMovement();
357                 entry->delta_t = dt * 0.001f;
358                 entry->packet = packet;
359                 player_hist.splice(player_hist.end(), player_hist, entry);
360         }
361         old_movement = GetMovement();
362         old_actions = actions;
363 }
364
365 void NetworkedInput::MergePlayerCorrection(uint16_t seq, const EntityState &corrected_state) {
366         if (player_hist.empty()) return;
367
368         auto entry = player_hist.begin();
369         auto end = player_hist.end();
370
371         // we may have received an older packet
372         int pack_diff = int16_t(seq) - int16_t(entry->packet);
373         if (pack_diff < 0) {
374                 // indeed we have, just ignore it
375                 return;
376         }
377
378         // drop anything older than the fix
379         while (entry != end) {
380                 pack_diff = int16_t(seq) - int16_t(entry->packet);
381                 if (pack_diff > 0) {
382                         entry = player_hist.erase(entry);
383                 } else {
384                         break;
385                 }
386         }
387
388         glm::vec3 restore_movement(GetMovement());
389
390         EntityState player_state = GetPlayer().GetEntity().GetState();
391         Entity replay(GetPlayer().GetEntity());
392         replay.SetState(corrected_state);
393
394         if (entry != end) {
395                 entry->state.chunk_pos = replay.GetState().chunk_pos;
396                 entry->state.block_pos = replay.GetState().block_pos;
397                 ++entry;
398         }
399
400         vector<WorldCollision> col;
401         while (entry != end) {
402                 SetMovement(entry->movement);
403                 GetWorld().Update(replay, entry->delta_t);
404                 entry->state.chunk_pos = replay.GetState().chunk_pos;
405                 entry->state.block_pos = replay.GetState().block_pos;
406                 ++entry;
407         }
408
409         glm::vec3 displacement(replay.GetState().Diff(player_state));
410         const float disp_squared = dot(displacement, displacement);
411
412         if (disp_squared < 16.0f * numeric_limits<float>::epsilon()) {
413                 SetMovement(restore_movement);
414                 return;
415         }
416
417         // if offset > 10cm, warp the player
418         // otherwise, move at most 1cm per frame towards
419         // the fixed position (160ms, so shouldn't be too noticeable)
420         constexpr float warp_thresh = 0.01f; // (1/10)^2
421         constexpr float max_disp = 0.0001f; // (1/100)^2
422
423         if (disp_squared > warp_thresh) {
424                 player_state.chunk_pos = replay.GetState().chunk_pos;
425                 player_state.block_pos = replay.GetState().block_pos;
426         } else if (disp_squared < max_disp) {
427                 player_state.block_pos += displacement;
428         } else {
429                 displacement *= 0.01f / sqrt(disp_squared);
430                 player_state.block_pos += displacement;
431         }
432         GetPlayer().GetEntity().SetState(player_state);
433         SetMovement(restore_movement);
434 }
435
436 void NetworkedInput::StartPrimaryAction() {
437         actions |= 0x01;
438 }
439
440 void NetworkedInput::StopPrimaryAction() {
441         actions &= ~0x01;
442 }
443
444 void NetworkedInput::StartSecondaryAction() {
445         actions |= 0x02;
446 }
447
448 void NetworkedInput::StopSecondaryAction() {
449         actions &= ~0x02;
450 }
451
452 void NetworkedInput::StartTertiaryAction() {
453         actions |= 0x04;
454 }
455
456 void NetworkedInput::StopTertiaryAction() {
457         actions &= ~0x04;
458 }
459
460 }
461 }