]> git.lizzy.rs Git - dragonfireclient.git/blob - src/server.h
Breath cheat fix: server side
[dragonfireclient.git] / src / server.h
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #ifndef SERVER_HEADER
21 #define SERVER_HEADER
22
23 #include "network/connection.h"
24 #include "irr_v3d.h"
25 #include "map.h"
26 #include "hud.h"
27 #include "gamedef.h"
28 #include "serialization.h" // For SER_FMT_VER_INVALID
29 #include "mods.h"
30 #include "inventorymanager.h"
31 #include "subgame.h"
32 #include "util/numeric.h"
33 #include "util/thread.h"
34 #include "util/basic_macros.h"
35 #include "environment.h"
36 #include "chat_interface.h"
37 #include "clientiface.h"
38 #include "remoteplayer.h"
39 #include "network/networkpacket.h"
40 #include <string>
41 #include <list>
42 #include <map>
43 #include <vector>
44
45 class IWritableItemDefManager;
46 class IWritableNodeDefManager;
47 class IWritableCraftDefManager;
48 class BanManager;
49 class EventManager;
50 class Inventory;
51 class PlayerSAO;
52 class IRollbackManager;
53 struct RollbackAction;
54 class EmergeManager;
55 class GameScripting;
56 class ServerEnvironment;
57 struct SimpleSoundSpec;
58 class ServerThread;
59
60 enum ClientDeletionReason {
61         CDR_LEAVE,
62         CDR_TIMEOUT,
63         CDR_DENY
64 };
65
66 class MapEditEventAreaIgnorer
67 {
68 public:
69         MapEditEventAreaIgnorer(VoxelArea *ignorevariable, const VoxelArea &a):
70                 m_ignorevariable(ignorevariable)
71         {
72                 if(m_ignorevariable->getVolume() == 0)
73                         *m_ignorevariable = a;
74                 else
75                         m_ignorevariable = NULL;
76         }
77
78         ~MapEditEventAreaIgnorer()
79         {
80                 if(m_ignorevariable)
81                 {
82                         assert(m_ignorevariable->getVolume() != 0);
83                         *m_ignorevariable = VoxelArea();
84                 }
85         }
86
87 private:
88         VoxelArea *m_ignorevariable;
89 };
90
91 struct MediaInfo
92 {
93         std::string path;
94         std::string sha1_digest;
95
96         MediaInfo(const std::string &path_="",
97                   const std::string &sha1_digest_=""):
98                 path(path_),
99                 sha1_digest(sha1_digest_)
100         {
101         }
102 };
103
104 struct ServerSoundParams
105 {
106         float gain;
107         std::string to_player;
108         enum Type{
109                 SSP_LOCAL=0,
110                 SSP_POSITIONAL=1,
111                 SSP_OBJECT=2
112         } type;
113         v3f pos;
114         u16 object;
115         float max_hear_distance;
116         bool loop;
117
118         ServerSoundParams():
119                 gain(1.0),
120                 to_player(""),
121                 type(SSP_LOCAL),
122                 pos(0,0,0),
123                 object(0),
124                 max_hear_distance(32*BS),
125                 loop(false)
126         {}
127
128         v3f getPos(ServerEnvironment *env, bool *pos_exists) const;
129 };
130
131 struct ServerPlayingSound
132 {
133         ServerSoundParams params;
134         UNORDERED_SET<u16> clients; // peer ids
135 };
136
137 class Server : public con::PeerHandler, public MapEventReceiver,
138                 public InventoryManager, public IGameDef
139 {
140 public:
141         /*
142                 NOTE: Every public method should be thread-safe
143         */
144
145         Server(
146                 const std::string &path_world,
147                 const SubgameSpec &gamespec,
148                 bool simple_singleplayer_mode,
149                 bool ipv6,
150                 ChatInterface *iface = NULL
151         );
152         ~Server();
153         void start(Address bind_addr);
154         void stop();
155         // This is mainly a way to pass the time to the server.
156         // Actual processing is done in an another thread.
157         void step(float dtime);
158         // This is run by ServerThread and does the actual processing
159         void AsyncRunStep(bool initial_step=false);
160         void Receive();
161         PlayerSAO* StageTwoClientInit(u16 peer_id);
162
163         /*
164          * Command Handlers
165          */
166
167         void handleCommand(NetworkPacket* pkt);
168
169         void handleCommand_Null(NetworkPacket* pkt) {};
170         void handleCommand_Deprecated(NetworkPacket* pkt);
171         void handleCommand_Init(NetworkPacket* pkt);
172         void handleCommand_Init_Legacy(NetworkPacket* pkt);
173         void handleCommand_Init2(NetworkPacket* pkt);
174         void handleCommand_RequestMedia(NetworkPacket* pkt);
175         void handleCommand_ReceivedMedia(NetworkPacket* pkt);
176         void handleCommand_ClientReady(NetworkPacket* pkt);
177         void handleCommand_GotBlocks(NetworkPacket* pkt);
178         void handleCommand_PlayerPos(NetworkPacket* pkt);
179         void handleCommand_DeletedBlocks(NetworkPacket* pkt);
180         void handleCommand_InventoryAction(NetworkPacket* pkt);
181         void handleCommand_ChatMessage(NetworkPacket* pkt);
182         void handleCommand_Damage(NetworkPacket* pkt);
183         void handleCommand_Password(NetworkPacket* pkt);
184         void handleCommand_PlayerItem(NetworkPacket* pkt);
185         void handleCommand_Respawn(NetworkPacket* pkt);
186         void handleCommand_Interact(NetworkPacket* pkt);
187         void handleCommand_RemovedSounds(NetworkPacket* pkt);
188         void handleCommand_NodeMetaFields(NetworkPacket* pkt);
189         void handleCommand_InventoryFields(NetworkPacket* pkt);
190         void handleCommand_FirstSrp(NetworkPacket* pkt);
191         void handleCommand_SrpBytesA(NetworkPacket* pkt);
192         void handleCommand_SrpBytesM(NetworkPacket* pkt);
193
194         void ProcessData(NetworkPacket *pkt);
195
196         void Send(NetworkPacket* pkt);
197
198         // Helper for handleCommand_PlayerPos and handleCommand_Interact
199         void process_PlayerPos(RemotePlayer *player, PlayerSAO *playersao,
200                 NetworkPacket *pkt);
201
202         // Both setter and getter need no envlock,
203         // can be called freely from threads
204         void setTimeOfDay(u32 time);
205
206         /*
207                 Shall be called with the environment locked.
208                 This is accessed by the map, which is inside the environment,
209                 so it shouldn't be a problem.
210         */
211         void onMapEditEvent(MapEditEvent *event);
212
213         /*
214                 Shall be called with the environment and the connection locked.
215         */
216         Inventory* getInventory(const InventoryLocation &loc);
217         void setInventoryModified(const InventoryLocation &loc, bool playerSend = true);
218
219         // Connection must be locked when called
220         std::wstring getStatusString();
221         inline double getUptime() const { return m_uptime.m_value; }
222
223         // read shutdown state
224         inline bool getShutdownRequested() const { return m_shutdown_requested; }
225
226         // request server to shutdown
227         void requestShutdown(const std::string &msg, bool reconnect)
228         {
229                 m_shutdown_requested = true;
230                 m_shutdown_msg = msg;
231                 m_shutdown_ask_reconnect = reconnect;
232         }
233
234         // Returns -1 if failed, sound handle on success
235         // Envlock
236         s32 playSound(const SimpleSoundSpec &spec, const ServerSoundParams &params);
237         void stopSound(s32 handle);
238
239         // Envlock
240         std::set<std::string> getPlayerEffectivePrivs(const std::string &name);
241         bool checkPriv(const std::string &name, const std::string &priv);
242         void reportPrivsModified(const std::string &name=""); // ""=all
243         void reportInventoryFormspecModified(const std::string &name);
244
245         void setIpBanned(const std::string &ip, const std::string &name);
246         void unsetIpBanned(const std::string &ip_or_name);
247         std::string getBanDescription(const std::string &ip_or_name);
248
249         void notifyPlayer(const char *name, const std::wstring &msg);
250         void notifyPlayers(const std::wstring &msg);
251         void spawnParticle(const std::string &playername,
252                 v3f pos, v3f velocity, v3f acceleration,
253                 float expirationtime, float size,
254                 bool collisiondetection, bool collision_removal,
255                 bool vertical, const std::string &texture);
256
257         u32 addParticleSpawner(u16 amount, float spawntime,
258                 v3f minpos, v3f maxpos,
259                 v3f minvel, v3f maxvel,
260                 v3f minacc, v3f maxacc,
261                 float minexptime, float maxexptime,
262                 float minsize, float maxsize,
263                 bool collisiondetection, bool collision_removal,
264                 ServerActiveObject *attached,
265                 bool vertical, const std::string &texture,
266                 const std::string &playername);
267
268         void deleteParticleSpawner(const std::string &playername, u32 id);
269
270         // Creates or resets inventory
271         Inventory* createDetachedInventory(const std::string &name, const std::string &player="");
272
273         // Envlock and conlock should be locked when using scriptapi
274         GameScripting *getScriptIface(){ return m_script; }
275
276         // actions: time-reversed list
277         // Return value: success/failure
278         bool rollbackRevertActions(const std::list<RollbackAction> &actions,
279                         std::list<std::string> *log);
280
281         // IGameDef interface
282         // Under envlock
283         virtual IItemDefManager* getItemDefManager();
284         virtual INodeDefManager* getNodeDefManager();
285         virtual ICraftDefManager* getCraftDefManager();
286         virtual ITextureSource* getTextureSource();
287         virtual IShaderSource* getShaderSource();
288         virtual u16 allocateUnknownNodeId(const std::string &name);
289         virtual ISoundManager* getSoundManager();
290         virtual MtEventManager* getEventManager();
291         virtual scene::ISceneManager* getSceneManager();
292         virtual IRollbackManager *getRollbackManager() { return m_rollback; }
293         virtual EmergeManager *getEmergeManager() { return m_emerge; }
294
295         IWritableItemDefManager* getWritableItemDefManager();
296         IWritableNodeDefManager* getWritableNodeDefManager();
297         IWritableCraftDefManager* getWritableCraftDefManager();
298
299         const std::vector<ModSpec> &getMods() const { return m_mods; }
300         const ModSpec* getModSpec(const std::string &modname) const;
301         void getModNames(std::vector<std::string> &modlist);
302         std::string getBuiltinLuaPath();
303         inline std::string getWorldPath() const { return m_path_world; }
304
305         inline bool isSingleplayer()
306                         { return m_simple_singleplayer_mode; }
307
308         inline void setAsyncFatalError(const std::string &error)
309                         { m_async_fatal_error.set(error); }
310
311         bool showFormspec(const char *name, const std::string &formspec, const std::string &formname);
312         Map & getMap() { return m_env->getMap(); }
313         ServerEnvironment & getEnv() { return *m_env; }
314
315         u32 hudAdd(RemotePlayer *player, HudElement *element);
316         bool hudRemove(RemotePlayer *player, u32 id);
317         bool hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *value);
318         bool hudSetFlags(RemotePlayer *player, u32 flags, u32 mask);
319         bool hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount);
320         s32 hudGetHotbarItemcount(RemotePlayer *player) const
321                         { return player->getHotbarItemcount(); }
322         void hudSetHotbarImage(RemotePlayer *player, std::string name);
323         std::string hudGetHotbarImage(RemotePlayer *player);
324         void hudSetHotbarSelectedImage(RemotePlayer *player, std::string name);
325         const std::string &hudGetHotbarSelectedImage(RemotePlayer *player) const
326         {
327                 return player->getHotbarSelectedImage();
328         }
329
330         inline Address getPeerAddress(u16 peer_id)
331                         { return m_con.GetPeerAddress(peer_id); }
332
333         bool setLocalPlayerAnimations(RemotePlayer *player, v2s32 animation_frames[4],
334                         f32 frame_speed);
335         bool setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third);
336
337         bool setSky(RemotePlayer *player, const video::SColor &bgcolor,
338                         const std::string &type, const std::vector<std::string> &params);
339
340         bool overrideDayNightRatio(RemotePlayer *player, bool do_override, float brightness);
341
342         /* con::PeerHandler implementation. */
343         void peerAdded(con::Peer *peer);
344         void deletingPeer(con::Peer *peer, bool timeout);
345
346         void DenySudoAccess(u16 peer_id);
347         void DenyAccessVerCompliant(u16 peer_id, u16 proto_ver, AccessDeniedCode reason,
348                 const std::string &str_reason = "", bool reconnect = false);
349         void DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason="");
350         void acceptAuth(u16 peer_id, bool forSudoMode);
351         void DenyAccess_Legacy(u16 peer_id, const std::wstring &reason);
352         bool getClientConInfo(u16 peer_id, con::rtt_stat_type type,float* retval);
353         bool getClientInfo(u16 peer_id,ClientState* state, u32* uptime,
354                         u8* ser_vers, u16* prot_vers, u8* major, u8* minor, u8* patch,
355                         std::string* vers_string);
356
357         void printToConsoleOnly(const std::string &text);
358
359         void SendPlayerHPOrDie(PlayerSAO *player);
360         void SendPlayerBreath(PlayerSAO *sao);
361         void SendInventory(PlayerSAO* playerSAO);
362         void SendMovePlayer(u16 peer_id);
363
364         // Bind address
365         Address m_bind_addr;
366
367         // Environment mutex (envlock)
368         Mutex m_env_mutex;
369
370 private:
371
372         friend class EmergeThread;
373         friend class RemoteClient;
374
375         void SendMovement(u16 peer_id);
376         void SendHP(u16 peer_id, u8 hp);
377         void SendBreath(u16 peer_id, u16 breath);
378         void SendAccessDenied(u16 peer_id, AccessDeniedCode reason,
379                 const std::string &custom_reason, bool reconnect = false);
380         void SendAccessDenied_Legacy(u16 peer_id, const std::wstring &reason);
381         void SendDeathscreen(u16 peer_id,bool set_camera_point_target, v3f camera_point_target);
382         void SendItemDef(u16 peer_id,IItemDefManager *itemdef, u16 protocol_version);
383         void SendNodeDef(u16 peer_id,INodeDefManager *nodedef, u16 protocol_version);
384
385         /* mark blocks not sent for all clients */
386         void SetBlocksNotSent(std::map<v3s16, MapBlock *>& block);
387
388
389         void SendChatMessage(u16 peer_id, const std::wstring &message);
390         void SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed);
391         void SendPlayerHP(u16 peer_id);
392
393         void SendLocalPlayerAnimations(u16 peer_id, v2s32 animation_frames[4], f32 animation_speed);
394         void SendEyeOffset(u16 peer_id, v3f first, v3f third);
395         void SendPlayerPrivileges(u16 peer_id);
396         void SendPlayerInventoryFormspec(u16 peer_id);
397         void SendShowFormspecMessage(u16 peer_id, const std::string &formspec, const std::string &formname);
398         void SendHUDAdd(u16 peer_id, u32 id, HudElement *form);
399         void SendHUDRemove(u16 peer_id, u32 id);
400         void SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value);
401         void SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask);
402         void SendHUDSetParam(u16 peer_id, u16 param, const std::string &value);
403         void SendSetSky(u16 peer_id, const video::SColor &bgcolor,
404                         const std::string &type, const std::vector<std::string> &params);
405         void SendOverrideDayNightRatio(u16 peer_id, bool do_override, float ratio);
406
407         /*
408                 Send a node removal/addition event to all clients except ignore_id.
409                 Additionally, if far_players!=NULL, players further away than
410                 far_d_nodes are ignored and their peer_ids are added to far_players
411         */
412         // Envlock and conlock should be locked when calling these
413         void sendRemoveNode(v3s16 p, u16 ignore_id=0,
414                         std::vector<u16> *far_players=NULL, float far_d_nodes=100);
415         void sendAddNode(v3s16 p, MapNode n, u16 ignore_id=0,
416                         std::vector<u16> *far_players=NULL, float far_d_nodes=100,
417                         bool remove_metadata=true);
418         void setBlockNotSent(v3s16 p);
419
420         // Environment and Connection must be locked when called
421         void SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version);
422
423         // Sends blocks to clients (locks env and con on its own)
424         void SendBlocks(float dtime);
425
426         void fillMediaCache();
427         void sendMediaAnnouncement(u16 peer_id);
428         void sendRequestedMedia(u16 peer_id,
429                         const std::vector<std::string> &tosend);
430
431         void sendDetachedInventory(const std::string &name, u16 peer_id);
432         void sendDetachedInventories(u16 peer_id);
433
434         // Adds a ParticleSpawner on peer with peer_id (PEER_ID_INEXISTENT == all)
435         void SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime,
436                 v3f minpos, v3f maxpos,
437                 v3f minvel, v3f maxvel,
438                 v3f minacc, v3f maxacc,
439                 float minexptime, float maxexptime,
440                 float minsize, float maxsize,
441                 bool collisiondetection, bool collision_removal,
442                 u16 attached_id,
443                 bool vertical, const std::string &texture, u32 id);
444
445         void SendDeleteParticleSpawner(u16 peer_id, u32 id);
446
447         // Spawns particle on peer with peer_id (PEER_ID_INEXISTENT == all)
448         void SendSpawnParticle(u16 peer_id,
449                 v3f pos, v3f velocity, v3f acceleration,
450                 float expirationtime, float size,
451                 bool collisiondetection, bool collision_removal,
452                 bool vertical, const std::string &texture);
453
454         u32 SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas);
455         void SendActiveObjectMessages(u16 peer_id, const std::string &datas, bool reliable = true);
456         /*
457                 Something random
458         */
459
460         void DiePlayer(u16 peer_id);
461         void RespawnPlayer(u16 peer_id);
462         void DeleteClient(u16 peer_id, ClientDeletionReason reason);
463         void UpdateCrafting(RemotePlayer *player);
464
465         void handleChatInterfaceEvent(ChatEvent *evt);
466
467         // This returns the answer to the sender of wmessage, or "" if there is none
468         std::wstring handleChat(const std::string &name, const std::wstring &wname,
469                 const std::wstring &wmessage,
470                 bool check_shout_priv = false,
471                 RemotePlayer *player = NULL);
472         void handleAdminChat(const ChatEventChat *evt);
473
474         v3f findSpawnPos();
475
476         // When called, connection mutex should be locked
477         RemoteClient* getClient(u16 peer_id,ClientState state_min=CS_Active);
478         RemoteClient* getClientNoEx(u16 peer_id,ClientState state_min=CS_Active);
479
480         // When called, environment mutex should be locked
481         std::string getPlayerName(u16 peer_id);
482         PlayerSAO* getPlayerSAO(u16 peer_id);
483
484         /*
485                 Get a player from memory or creates one.
486                 If player is already connected, return NULL
487                 Does not verify/modify auth info and password.
488
489                 Call with env and con locked.
490         */
491         PlayerSAO *emergePlayer(const char *name, u16 peer_id, u16 proto_version);
492
493         void handlePeerChanges();
494
495         /*
496                 Variables
497         */
498
499         // World directory
500         std::string m_path_world;
501         // Subgame specification
502         SubgameSpec m_gamespec;
503         // If true, do not allow multiple players and hide some multiplayer
504         // functionality
505         bool m_simple_singleplayer_mode;
506         u16 m_max_chatmessage_length;
507
508         // Thread can set; step() will throw as ServerError
509         MutexedVariable<std::string> m_async_fatal_error;
510
511         // Some timers
512         float m_liquid_transform_timer;
513         float m_liquid_transform_every;
514         float m_masterserver_timer;
515         float m_emergethread_trigger_timer;
516         float m_savemap_timer;
517         IntervalLimiter m_map_timer_and_unload_interval;
518
519         // Environment
520         ServerEnvironment *m_env;
521
522         // server connection
523         con::Connection m_con;
524
525         // Ban checking
526         BanManager *m_banmanager;
527
528         // Rollback manager (behind m_env_mutex)
529         IRollbackManager *m_rollback;
530         bool m_enable_rollback_recording; // Updated once in a while
531
532         // Emerge manager
533         EmergeManager *m_emerge;
534
535         // Scripting
536         // Envlock and conlock should be locked when using Lua
537         GameScripting *m_script;
538
539         // Item definition manager
540         IWritableItemDefManager *m_itemdef;
541
542         // Node definition manager
543         IWritableNodeDefManager *m_nodedef;
544
545         // Craft definition manager
546         IWritableCraftDefManager *m_craftdef;
547
548         // Event manager
549         EventManager *m_event;
550
551         // Mods
552         std::vector<ModSpec> m_mods;
553
554         /*
555                 Threads
556         */
557
558         // A buffer for time steps
559         // step() increments and AsyncRunStep() run by m_thread reads it.
560         float m_step_dtime;
561         Mutex m_step_dtime_mutex;
562
563         // current server step lag counter
564         float m_lag;
565
566         // The server mainly operates in this thread
567         ServerThread *m_thread;
568
569         /*
570                 Time related stuff
571         */
572
573         // Timer for sending time of day over network
574         float m_time_of_day_send_timer;
575         // Uptime of server in seconds
576         MutexedVariable<double> m_uptime;
577
578         /*
579          Client interface
580          */
581         ClientInterface m_clients;
582
583         /*
584                 Peer change queue.
585                 Queues stuff from peerAdded() and deletingPeer() to
586                 handlePeerChanges()
587         */
588         std::queue<con::PeerChange> m_peer_change_queue;
589
590         /*
591                 Random stuff
592         */
593
594         bool m_shutdown_requested;
595         std::string m_shutdown_msg;
596         bool m_shutdown_ask_reconnect;
597
598         ChatInterface *m_admin_chat;
599         std::string m_admin_nick;
600
601         /*
602                 Map edit event queue. Automatically receives all map edits.
603                 The constructor of this class registers us to receive them through
604                 onMapEditEvent
605
606                 NOTE: Should these be moved to actually be members of
607                 ServerEnvironment?
608         */
609
610         /*
611                 Queue of map edits from the environment for sending to the clients
612                 This is behind m_env_mutex
613         */
614         std::queue<MapEditEvent*> m_unsent_map_edit_queue;
615         /*
616                 Set to true when the server itself is modifying the map and does
617                 all sending of information by itself.
618                 This is behind m_env_mutex
619         */
620         bool m_ignore_map_edit_events;
621         /*
622                 If a non-empty area, map edit events contained within are left
623                 unsent. Done at map generation time to speed up editing of the
624                 generated area, as it will be sent anyway.
625                 This is behind m_env_mutex
626         */
627         VoxelArea m_ignore_map_edit_events_area;
628         /*
629                 If set to !=0, the incoming MapEditEvents are modified to have
630                 this peed id as the disabled recipient
631                 This is behind m_env_mutex
632         */
633         u16 m_ignore_map_edit_events_peer_id;
634
635         // media files known to server
636         UNORDERED_MAP<std::string, MediaInfo> m_media;
637
638         /*
639                 Sounds
640         */
641         UNORDERED_MAP<s32, ServerPlayingSound> m_playing_sounds;
642         s32 m_next_sound_id;
643
644         /*
645                 Detached inventories (behind m_env_mutex)
646         */
647         // key = name
648         std::map<std::string, Inventory*> m_detached_inventories;
649         // value = "" (visible to all players) or player name
650         std::map<std::string, std::string> m_detached_inventories_player;
651
652         DISABLE_CLASS_COPY(Server);
653 };
654
655 /*
656         Runs a simple dedicated server loop.
657
658         Shuts down when kill is set to true.
659 */
660 void dedicated_server_loop(Server &server, bool &kill);
661
662 #endif
663