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