]> git.localhorst.tv Git - blank.git/blob - src/server/net.cpp
fixed int rollover in client entity update
[blank.git] / src / server / net.cpp
1 #include "ClientConnection.hpp"
2 #include "ChunkTransmitter.hpp"
3 #include "Server.hpp"
4
5 #include "../app/init.hpp"
6 #include "../world/ChunkIndex.hpp"
7 #include "../world/Entity.hpp"
8 #include "../world/World.hpp"
9
10 #include <iostream>
11 #include <zlib.h>
12
13 using namespace std;
14
15
16 namespace blank {
17 namespace server {
18
19 ChunkTransmitter::ChunkTransmitter(ClientConnection &conn)
20 : conn(conn)
21 , current(nullptr)
22 , buffer_size(Chunk::BlockSize() + 10)
23 , buffer(new uint8_t[buffer_size])
24 , buffer_len(0)
25 , packet_len(Packet::ChunkData::MAX_DATA_LEN)
26 , cursor(0)
27 , num_packets(0)
28 , begin_packet(-1)
29 , data_packets()
30 , confirm_wait(0)
31 , trans_id(0)
32 , compressed(false) {
33
34 }
35
36 ChunkTransmitter::~ChunkTransmitter() {
37         Abort();
38 }
39
40 bool ChunkTransmitter::Idle() const noexcept {
41         return !Transmitting() && !Waiting();
42 }
43
44 bool ChunkTransmitter::Transmitting() const noexcept {
45         return cursor < num_packets;
46 }
47
48 void ChunkTransmitter::Transmit() {
49         if (cursor < num_packets) {
50                 SendData(cursor);
51                 ++cursor;
52         }
53 }
54
55 bool ChunkTransmitter::Waiting() const noexcept {
56         return confirm_wait > 0;
57 }
58
59 void ChunkTransmitter::Ack(uint16_t seq) {
60         if (!Waiting()) {
61                 return;
62         }
63         if (seq == begin_packet) {
64                 begin_packet = -1;
65                 --confirm_wait;
66                 if (Idle()) {
67                         Release();
68                 }
69                 return;
70         }
71         for (int i = 0, end = data_packets.size(); i < end; ++i) {
72                 if (seq == data_packets[i]) {
73                         data_packets[i] = -1;
74                         --confirm_wait;
75                         if (Idle()) {
76                                 Release();
77                         }
78                         return;
79                 }
80         }
81 }
82
83 void ChunkTransmitter::Nack(uint16_t seq) {
84         if (!Waiting()) {
85                 return;
86         }
87         if (seq == begin_packet) {
88                 SendBegin();
89                 return;
90         }
91         for (size_t i = 0, end = data_packets.size(); i < end; ++i) {
92                 if (seq == data_packets[i]) {
93                         SendData(i);
94                         return;
95                 }
96         }
97 }
98
99 void ChunkTransmitter::Abort() {
100         if (!current) return;
101
102         Release();
103
104         begin_packet = -1;
105         data_packets.clear();
106         confirm_wait = 0;
107 }
108
109 void ChunkTransmitter::Send(Chunk &chunk) {
110         // abort current chunk, if any
111         Abort();
112
113         current = &chunk;
114         current->Ref();
115
116         // load new chunk data
117         compressed = true;
118         buffer_len = buffer_size;
119         if (compress(buffer.get(), &buffer_len, reinterpret_cast<const Bytef *>(chunk.BlockData()), Chunk::BlockSize()) != Z_OK) {
120                 // compression failed, send it uncompressed
121                 buffer_len = Chunk::BlockSize();
122                 memcpy(buffer.get(), chunk.BlockData(), buffer_len);
123                 compressed = false;
124         }
125         cursor = 0;
126         num_packets = (buffer_len / packet_len) + (buffer_len % packet_len != 0);
127         data_packets.resize(num_packets, -1);
128
129         ++trans_id;
130         SendBegin();
131 }
132
133 void ChunkTransmitter::SendBegin() {
134         uint32_t flags = compressed;
135         auto pack = conn.Prepare<Packet::ChunkBegin>();
136         pack.WriteTransmissionId(trans_id);
137         pack.WriteFlags(flags);
138         pack.WriteChunkCoords(current->Position());
139         pack.WriteDataSize(buffer_len);
140         if (begin_packet == -1) {
141                 ++confirm_wait;
142         }
143         begin_packet = conn.Send();
144 }
145
146 void ChunkTransmitter::SendData(size_t i) {
147         int pos = i * packet_len;
148         int len = min(packet_len, buffer_len - pos);
149         const uint8_t *data = &buffer[pos];
150
151         auto pack = conn.Prepare<Packet::ChunkData>();
152         pack.WriteTransmissionId(trans_id);
153         pack.WriteDataOffset(pos);
154         pack.WriteDataSize(len);
155         pack.WriteData(data, len);
156
157         if (data_packets[i] == -1) {
158                 ++confirm_wait;
159         }
160         data_packets[i] = conn.Send();
161 }
162
163 void ChunkTransmitter::Release() {
164         if (current) {
165                 current->UnRef();
166                 current = nullptr;
167         }
168 }
169
170
171 ClientConnection::ClientConnection(Server &server, const IPaddress &addr)
172 : server(server)
173 , conn(addr)
174 , player(nullptr, nullptr)
175 , spawns()
176 , confirm_wait(0)
177 , player_update_state()
178 , player_update_pack(0)
179 , player_update_timer(1500)
180 , transmitter(*this)
181 , chunk_queue()
182 , old_base() {
183         conn.SetHandler(this);
184 }
185
186 ClientConnection::~ClientConnection() {
187         DetachPlayer();
188 }
189
190 void ClientConnection::Update(int dt) {
191         conn.Update(dt);
192         if (Disconnected()) {
193                 return;
194         }
195         if (HasPlayer()) {
196                 // sync entities
197                 auto global_iter = server.GetWorld().Entities().begin();
198                 auto global_end = server.GetWorld().Entities().end();
199                 auto local_iter = spawns.begin();
200                 auto local_end = spawns.end();
201
202                 while (global_iter != global_end && local_iter != local_end) {
203                         if (global_iter->ID() == local_iter->entity->ID()) {
204                                 // they're the same
205                                 if (CanDespawn(*global_iter)) {
206                                         SendDespawn(*local_iter);
207                                 } else {
208                                         // update
209                                         SendUpdate(*local_iter);
210                                 }
211                                 ++global_iter;
212                                 ++local_iter;
213                         } else if (global_iter->ID() < local_iter->entity->ID()) {
214                                 // global entity was inserted
215                                 if (CanSpawn(*global_iter)) {
216                                         auto spawned = spawns.emplace(local_iter, *global_iter);
217                                         SendSpawn(*spawned);
218                                 }
219                                 ++global_iter;
220                         } else {
221                                 // global entity was removed
222                                 SendDespawn(*local_iter);
223                                 ++local_iter;
224                         }
225                 }
226
227                 // leftover spawns
228                 while (global_iter != global_end) {
229                         if (CanSpawn(*global_iter)) {
230                                 spawns.emplace_back(*global_iter);
231                                 SendSpawn(spawns.back());
232                         }
233                         ++global_iter;
234                 }
235
236                 // leftover despawns
237                 while (local_iter != local_end) {
238                         SendDespawn(*local_iter);
239                         ++local_iter;
240                 }
241
242                 CheckPlayerFix();
243                 CheckChunkQueue();
244         }
245         if (conn.ShouldPing()) {
246                 conn.SendPing(server.GetPacket(), server.GetSocket());
247         }
248 }
249
250 ClientConnection::SpawnStatus::SpawnStatus(Entity &e)
251 : entity(&e)
252 , spawn_pack(-1)
253 , despawn_pack(-1) {
254         entity->Ref();
255 }
256
257 ClientConnection::SpawnStatus::~SpawnStatus() {
258         entity->UnRef();
259 }
260
261 bool ClientConnection::CanSpawn(const Entity &e) const noexcept {
262         return
263                 &e != player.entity &&
264                 !e.Dead() &&
265                 manhattan_radius(e.ChunkCoords() - PlayerEntity().ChunkCoords()) < 7;
266 }
267
268 bool ClientConnection::CanDespawn(const Entity &e) const noexcept {
269         return
270                 e.Dead() ||
271                 manhattan_radius(e.ChunkCoords() - PlayerEntity().ChunkCoords()) > 7;
272 }
273
274 uint16_t ClientConnection::Send() {
275         return conn.Send(server.GetPacket(), server.GetSocket());
276 }
277
278 uint16_t ClientConnection::Send(size_t len) {
279         server.GetPacket().len = len;
280         return Send();
281 }
282
283 void ClientConnection::SendSpawn(SpawnStatus &status) {
284         // don't double spawn
285         if (status.spawn_pack != -1) return;
286
287         auto pack = Prepare<Packet::SpawnEntity>();
288         pack.WriteEntity(*status.entity);
289         status.spawn_pack = Send();
290         ++confirm_wait;
291 }
292
293 void ClientConnection::SendDespawn(SpawnStatus &status) {
294         // don't double despawn
295         if (status.despawn_pack != -1) return;
296
297         auto pack = Prepare<Packet::DespawnEntity>();
298         pack.WriteEntityID(status.entity->ID());
299         status.despawn_pack = Send();
300         ++confirm_wait;
301 }
302
303 void ClientConnection::SendUpdate(SpawnStatus &status) {
304         // don't send updates while spawn not ack'd or despawn sent
305         if (status.spawn_pack != -1 || status.despawn_pack != -1) return;
306
307         // TODO: pack entity updates
308         auto pack = Prepare<Packet::EntityUpdate>();
309         pack.WriteEntityCount(1);
310         pack.WriteEntity(*status.entity, 0);
311         Send(Packet::EntityUpdate::GetSize(1));
312 }
313
314 void ClientConnection::CheckPlayerFix() {
315         // player_update_state's position holds the client's most recent prediction
316         glm::vec3 diff = player_update_state.Diff(PlayerEntity().GetState());
317         float dist_squared = dot(diff, diff);
318
319         // if client's prediction is off by more than 1cm, send
320         // our (authoritative) state back so it can fix it
321         constexpr float fix_thresh = 0.0001f;
322
323         if (dist_squared > fix_thresh) {
324                 auto pack = Prepare<Packet::PlayerCorrection>();
325                 pack.WritePacketSeq(player_update_pack);
326                 pack.WritePlayer(PlayerEntity());
327                 Send();
328         }
329 }
330
331 void ClientConnection::CheckChunkQueue() {
332         if (PlayerChunks().Base() != old_base) {
333                 Chunk::Pos begin = PlayerChunks().CoordsBegin();
334                 Chunk::Pos end = PlayerChunks().CoordsEnd();
335                 for (Chunk::Pos pos = begin; pos.z < end.z; ++pos.z) {
336                         for (pos.y = begin.y; pos.y < end.y; ++pos.y) {
337                                 for (pos.x = begin.x; pos.x < end.x; ++pos.x) {
338                                         if (manhattan_radius(pos - old_base) > PlayerChunks().Extent()) {
339                                                 chunk_queue.push_back(pos);
340                                         }
341                                 }
342                         }
343                 }
344                 old_base = PlayerChunks().Base();
345         }
346         if (transmitter.Transmitting()) {
347                 transmitter.Transmit();
348                 return;
349         }
350         if (transmitter.Idle()) {
351                 int count = 0;
352                 constexpr int max = 64;
353                 while (count < max && !chunk_queue.empty()) {
354                         Chunk::Pos pos = chunk_queue.front();
355                         chunk_queue.pop_front();
356                         if (PlayerChunks().InRange(pos)) {
357                                 Chunk *chunk = PlayerChunks().Get(pos);
358                                 if (chunk) {
359                                         transmitter.Send(*chunk);
360                                         return;
361                                 } else {
362                                         chunk_queue.push_back(pos);
363                                 }
364                                 ++count;
365                         }
366                 }
367         }
368 }
369
370 void ClientConnection::AttachPlayer(const Player &new_player) {
371         DetachPlayer();
372         player = new_player;
373         player.entity->Ref();
374
375         old_base = player.chunks->Base();
376         Chunk::Pos begin = player.chunks->CoordsBegin();
377         Chunk::Pos end = player.chunks->CoordsEnd();
378         for (Chunk::Pos pos = begin; pos.z < end.z; ++pos.z) {
379                 for (pos.y = begin.y; pos.y < end.y; ++pos.y) {
380                         for (pos.x = begin.x; pos.x < end.x; ++pos.x) {
381                                 chunk_queue.push_back(pos);
382                         }
383                 }
384         }
385
386         cout << "player \"" << player.entity->Name() << "\" joined" << endl;
387 }
388
389 void ClientConnection::DetachPlayer() {
390         if (!HasPlayer()) return;
391         cout << "player \"" << player.entity->Name() << "\" left" << endl;
392         player.entity->Kill();
393         player.entity->UnRef();
394         player.entity = nullptr;
395         player.chunks = nullptr;
396         transmitter.Abort();
397         chunk_queue.clear();
398 }
399
400 void ClientConnection::OnPacketReceived(uint16_t seq) {
401         if (transmitter.Waiting()) {
402                 transmitter.Ack(seq);
403         }
404         if (!confirm_wait) return;
405         for (auto iter = spawns.begin(), end = spawns.end(); iter != end; ++iter) {
406                 if (seq == iter->spawn_pack) {
407                         iter->spawn_pack = -1;
408                         --confirm_wait;
409                         return;
410                 }
411                 if (seq == iter->despawn_pack) {
412                         spawns.erase(iter);
413                         --confirm_wait;
414                         return;
415                 }
416         }
417 }
418
419 void ClientConnection::OnPacketLost(uint16_t seq) {
420         if (transmitter.Waiting()) {
421                 transmitter.Nack(seq);
422         }
423         if (!confirm_wait) return;
424         for (SpawnStatus &status : spawns) {
425                 if (seq == status.spawn_pack) {
426                         status.spawn_pack = -1;
427                         --confirm_wait;
428                         SendSpawn(status);
429                         return;
430                 }
431                 if (seq == status.despawn_pack) {
432                         status.despawn_pack = -1;
433                         --confirm_wait;
434                         SendDespawn(status);
435                         return;
436                 }
437         }
438 }
439
440 void ClientConnection::On(const Packet::Login &pack) {
441         string name;
442         pack.ReadPlayerName(name);
443
444         Player new_player = server.GetWorld().AddPlayer(name);
445
446         if (new_player.entity) {
447                 // success!
448                 AttachPlayer(new_player);
449                 cout << "accepted login from player \"" << name << '"' << endl;
450                 auto response = Prepare<Packet::Join>();
451                 response.WritePlayer(*new_player.entity);
452                 response.WriteWorldName(server.GetWorld().Name());
453                 Send();
454                 // set up update tracking
455                 player_update_state = new_player.entity->GetState();
456                 player_update_pack = pack.Seq();
457                 player_update_timer.Reset();
458                 player_update_timer.Start();
459         } else {
460                 // aw no :(
461                 cout << "rejected login from player \"" << name << '"' << endl;
462                 Prepare<Packet::Part>();
463                 Send();
464                 conn.Close();
465         }
466 }
467
468 void ClientConnection::On(const Packet::Part &) {
469         conn.Close();
470 }
471
472 void ClientConnection::On(const Packet::PlayerUpdate &pack) {
473         if (!HasPlayer()) return;
474         int pack_diff = int16_t(pack.Seq()) - int16_t(player_update_pack);
475         bool overdue = player_update_timer.HitOnce();
476         player_update_timer.Reset();
477         if (pack_diff > 0 || overdue) {
478                 player_update_pack = pack.Seq();
479                 pack.ReadPlayerState(player_update_state);
480                 // accept velocity and orientation as "user input"
481                 PlayerEntity().Velocity(player_update_state.velocity);
482                 PlayerEntity().Orientation(player_update_state.orient);
483         }
484 }
485
486
487 Server::Server(const Config &conf, World &world)
488 : serv_sock(nullptr)
489 , serv_pack{ -1, nullptr, 0 }
490 , clients()
491 , world(world) {
492         serv_sock = SDLNet_UDP_Open(conf.port);
493         if (!serv_sock) {
494                 throw NetError("SDLNet_UDP_Open");
495         }
496
497         serv_pack.data = new Uint8[sizeof(Packet)];
498         serv_pack.maxlen = sizeof(Packet);
499 }
500
501 Server::~Server() {
502         delete[] serv_pack.data;
503         SDLNet_UDP_Close(serv_sock);
504 }
505
506
507 void Server::Handle() {
508         int result = SDLNet_UDP_Recv(serv_sock, &serv_pack);
509         while (result > 0) {
510                 HandlePacket(serv_pack);
511                 result = SDLNet_UDP_Recv(serv_sock, &serv_pack);
512         }
513         if (result == -1) {
514                 // a boo boo happened
515                 throw NetError("SDLNet_UDP_Recv");
516         }
517 }
518
519 void Server::HandlePacket(const UDPpacket &udp_pack) {
520         if (udp_pack.len < int(sizeof(Packet::Header))) {
521                 // packet too small, drop
522                 return;
523         }
524         const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
525         if (pack.header.tag != Packet::TAG) {
526                 // mistagged packet, drop
527                 return;
528         }
529
530         ClientConnection &client = GetClient(udp_pack.address);
531         client.GetConnection().Received(udp_pack);
532 }
533
534 ClientConnection &Server::GetClient(const IPaddress &addr) {
535         for (ClientConnection &client : clients) {
536                 if (client.Matches(addr)) {
537                         return client;
538                 }
539         }
540         clients.emplace_back(*this, addr);
541         return clients.back();
542 }
543
544 void Server::Update(int dt) {
545         for (list<ClientConnection>::iterator client(clients.begin()), end(clients.end()); client != end;) {
546                 client->Update(dt);
547                 if (client->Disconnected()) {
548                         client = clients.erase(client);
549                 } else {
550                         ++client;
551                 }
552         }
553 }
554
555 }
556 }