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