]> git.localhorst.tv Git - blank.git/blob - src/server/net.cpp
store players in world save
[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         if (server.GetWorldSave().Exists(player)) {
399                 server.GetWorldSave().Read(player);
400         }
401
402         old_base = PlayerChunks().Base();
403         Chunk::Pos begin = PlayerChunks().CoordsBegin();
404         Chunk::Pos end = PlayerChunks().CoordsEnd();
405         for (Chunk::Pos pos = begin; pos.z < end.z; ++pos.z) {
406                 for (pos.y = begin.y; pos.y < end.y; ++pos.y) {
407                         for (pos.x = begin.x; pos.x < end.x; ++pos.x) {
408                                 chunk_queue.push_back(pos);
409                         }
410                 }
411         }
412         if (HasPlayerModel()) {
413                 GetPlayerModel().Instantiate(PlayerEntity().GetModel());
414         }
415
416         cout << "player \"" << player.Name() << "\" joined" << endl;
417 }
418
419 void ClientConnection::DetachPlayer() {
420         if (!HasPlayer()) return;
421         cout << "player \"" << input->GetPlayer().Name() << "\" left" << endl;
422         server.GetWorldSave().Write(input->GetPlayer());
423         PlayerEntity().Kill();
424         PlayerEntity().UnRef();
425         input.reset();
426         transmitter.Abort();
427         chunk_queue.clear();
428         old_actions = 0;
429 }
430
431 void ClientConnection::SetPlayerModel(const CompositeModel &m) noexcept {
432         player_model = &m;
433         if (HasPlayer()) {
434                 m.Instantiate(PlayerEntity().GetModel());
435         }
436 }
437
438 bool ClientConnection::HasPlayerModel() const noexcept {
439         return player_model;
440 }
441
442 const CompositeModel &ClientConnection::GetPlayerModel() const noexcept {
443         return *player_model;
444 }
445
446 void ClientConnection::OnPacketReceived(uint16_t seq) {
447         if (transmitter.Waiting()) {
448                 transmitter.Ack(seq);
449         }
450         if (!confirm_wait) return;
451         for (auto iter = spawns.begin(), end = spawns.end(); iter != end; ++iter) {
452                 if (seq == iter->spawn_pack) {
453                         iter->spawn_pack = -1;
454                         --confirm_wait;
455                         return;
456                 }
457                 if (seq == iter->despawn_pack) {
458                         spawns.erase(iter);
459                         --confirm_wait;
460                         return;
461                 }
462         }
463 }
464
465 void ClientConnection::OnPacketLost(uint16_t seq) {
466         if (transmitter.Waiting()) {
467                 transmitter.Nack(seq);
468         }
469         if (!confirm_wait) return;
470         for (SpawnStatus &status : spawns) {
471                 if (seq == status.spawn_pack) {
472                         status.spawn_pack = -1;
473                         --confirm_wait;
474                         SendSpawn(status);
475                         return;
476                 }
477                 if (seq == status.despawn_pack) {
478                         status.despawn_pack = -1;
479                         --confirm_wait;
480                         SendDespawn(status);
481                         return;
482                 }
483         }
484 }
485
486 void ClientConnection::On(const Packet::Login &pack) {
487         string name;
488         pack.ReadPlayerName(name);
489
490         Player *new_player = server.GetWorld().AddPlayer(name);
491
492         if (new_player) {
493                 // success!
494                 AttachPlayer(*new_player);
495                 cout << "accepted login from player \"" << name << '"' << endl;
496                 auto response = Prepare<Packet::Join>();
497                 response.WritePlayer(new_player->GetEntity());
498                 response.WriteWorldName(server.GetWorld().Name());
499                 Send();
500                 // set up update tracking
501                 player_update_state = new_player->GetEntity().GetState();
502                 player_update_pack = pack.Seq();
503                 player_update_timer.Reset();
504                 player_update_timer.Start();
505         } else {
506                 // aw no :(
507                 cout << "rejected login from player \"" << name << '"' << endl;
508                 Prepare<Packet::Part>();
509                 Send();
510                 conn.Close();
511         }
512 }
513
514 void ClientConnection::On(const Packet::Part &) {
515         conn.Close();
516 }
517
518 void ClientConnection::On(const Packet::PlayerUpdate &pack) {
519         if (!HasPlayer()) return;
520         int pack_diff = int16_t(pack.Seq()) - int16_t(player_update_pack);
521         bool overdue = player_update_timer.HitOnce();
522         player_update_timer.Reset();
523         if (pack_diff <= 0 && !overdue) {
524                 // drop old packets if we have a fairly recent state
525                 return;
526         }
527         glm::vec3 movement(0.0f);
528         float pitch = 0.0f;
529         float yaw = 0.0f;
530         uint8_t new_actions;
531         uint8_t slot;
532
533         player_update_pack = pack.Seq();
534         pack.ReadPredictedState(player_update_state);
535         pack.ReadMovement(movement);
536         pack.ReadPitch(pitch);
537         pack.ReadYaw(yaw);
538         pack.ReadActions(new_actions);
539         pack.ReadSlot(slot);
540
541         input->SetMovement(movement);
542         input->TurnHead(pitch - input->GetPitch(), yaw - input->GetYaw());
543         input->SelectInventory(slot);
544
545         if ((new_actions & 0x01) && !(old_actions & 0x01)) {
546                 input->StartPrimaryAction();
547         } else if (!(new_actions & 0x01) && (old_actions & 0x01)) {
548                 input->StopPrimaryAction();
549         }
550         if ((new_actions & 0x02) && !(old_actions & 0x02)) {
551                 input->StartSecondaryAction();
552         } else if (!(new_actions & 0x02) && (old_actions & 0x02)) {
553                 input->StopSecondaryAction();
554         }
555         if ((new_actions & 0x04) && !(old_actions & 0x04)) {
556                 input->StartTertiaryAction();
557         } else if (!(new_actions & 0x04) && (old_actions & 0x04)) {
558                 input->StopTertiaryAction();
559         }
560         old_actions = new_actions;
561 }
562
563 bool ClientConnection::ChunkInRange(const glm::ivec3 &pos) const noexcept {
564         return HasPlayer() && PlayerChunks().InRange(pos);
565 }
566
567
568 Server::Server(const Config::Network &conf, World &world, const WorldSave &save)
569 : serv_sock(nullptr)
570 , serv_pack{ -1, nullptr, 0 }
571 , clients()
572 , world(world)
573 , save(save)
574 , player_model(nullptr) {
575         serv_sock = SDLNet_UDP_Open(conf.port);
576         if (!serv_sock) {
577                 throw NetError("SDLNet_UDP_Open");
578         }
579
580         serv_pack.data = new Uint8[sizeof(Packet)];
581         serv_pack.maxlen = sizeof(Packet);
582 }
583
584 Server::~Server() {
585         delete[] serv_pack.data;
586         SDLNet_UDP_Close(serv_sock);
587 }
588
589
590 void Server::Handle() {
591         int result = SDLNet_UDP_Recv(serv_sock, &serv_pack);
592         while (result > 0) {
593                 HandlePacket(serv_pack);
594                 result = SDLNet_UDP_Recv(serv_sock, &serv_pack);
595         }
596         if (result == -1) {
597                 // a boo boo happened
598                 throw NetError("SDLNet_UDP_Recv");
599         }
600 }
601
602 void Server::HandlePacket(const UDPpacket &udp_pack) {
603         if (udp_pack.len < int(sizeof(Packet::Header))) {
604                 // packet too small, drop
605                 return;
606         }
607         const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
608         if (pack.header.tag != Packet::TAG) {
609                 // mistagged packet, drop
610                 return;
611         }
612
613         ClientConnection &client = GetClient(udp_pack.address);
614         client.GetConnection().Received(udp_pack);
615 }
616
617 ClientConnection &Server::GetClient(const IPaddress &addr) {
618         for (ClientConnection &client : clients) {
619                 if (client.Matches(addr)) {
620                         return client;
621                 }
622         }
623         clients.emplace_back(*this, addr);
624         if (HasPlayerModel()) {
625                 clients.back().SetPlayerModel(GetPlayerModel());
626         }
627         return clients.back();
628 }
629
630 void Server::Update(int dt) {
631         for (list<ClientConnection>::iterator client(clients.begin()), end(clients.end()); client != end;) {
632                 client->Update(dt);
633                 if (client->Disconnected()) {
634                         client = clients.erase(client);
635                 } else {
636                         ++client;
637                 }
638         }
639 }
640
641 void Server::SetPlayerModel(const CompositeModel &m) noexcept {
642         player_model = &m;
643         for (ClientConnection &client : clients) {
644                 client.SetPlayerModel(m);
645         }
646 }
647
648 bool Server::HasPlayerModel() const noexcept {
649         return player_model;
650 }
651
652 const CompositeModel &Server::GetPlayerModel() const noexcept {
653         return *player_model;
654 }
655
656 void Server::SetBlock(Chunk &chunk, int index, const Block &block) {
657         chunk.SetBlock(index, block);
658         // TODO: batch chunk changes
659         auto pack = Packet::Make<Packet::BlockUpdate>(GetPacket());
660         pack.WriteChunkCoords(chunk.Position());
661         pack.WriteBlockCount(uint32_t(1));
662         pack.WriteIndex(index, 0);
663         pack.WriteBlock(chunk.BlockAt(index), 0);
664         GetPacket().len = sizeof(Packet::Header) + Packet::BlockUpdate::GetSize(1);
665         for (ClientConnection &client : clients) {
666                 if (client.ChunkInRange(chunk.Position())) {
667                         client.Send();
668                 }
669         }
670 }
671
672 }
673 }