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