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