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