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