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