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