]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - net_main.c
refactored NET_SendToAll, it is debatable whether it is more readable or not, but...
[xonotic/darkplaces.git] / net_main.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // net_main.c
21
22 #include "quakedef.h"
23 #include "net_master.h"
24
25 qsocket_t *net_activeSockets = NULL;
26 mempool_t *net_mempool;
27
28 qboolean        ipxAvailable = false;
29 qboolean        tcpipAvailable = false;
30
31 int                     net_hostport;
32 int                     DEFAULTnet_hostport = 26000;
33
34 char            my_ipx_address[NET_NAMELEN];
35 char            my_tcpip_address[NET_NAMELEN];
36
37 static qboolean listening = false;
38
39 qboolean        slistInProgress = false;
40 qboolean        slistSilent = false;
41 qboolean        slistLocal = true;
42 static double   slistStartTime;
43 static int              slistLastShown;
44
45 static void Slist_Send(void);
46 static void Slist_Poll(void);
47 PollProcedure   slistSendProcedure = {NULL, 0.0, Slist_Send};
48 PollProcedure   slistPollProcedure = {NULL, 0.0, Slist_Poll};
49
50 static void InetSlist_Send(void);
51 static void InetSlist_Poll(void);
52 PollProcedure   inetSlistSendProcedure = {NULL, 0.0, InetSlist_Send};
53 PollProcedure   inetSlistPollProcedure = {NULL, 0.0, InetSlist_Poll};
54
55
56 sizebuf_t               net_message;
57 int                             net_activeconnections = 0;
58
59 int messagesSent = 0;
60 int messagesReceived = 0;
61 int unreliableMessagesSent = 0;
62 int unreliableMessagesReceived = 0;
63
64 cvar_t  net_messagetimeout = {0, "net_messagetimeout","300"};
65 cvar_t  hostname = {CVAR_SAVE, "hostname", "UNNAMED"};
66
67 qboolean        configRestored = false;
68
69 // these two macros are to make the code more readable
70 #define sfunc   net_drivers[sock->driver]
71 #define dfunc   net_drivers[net_driverlevel]
72
73 int     net_driverlevel;
74
75 /*
76 #define SLSERVERS 1024
77 #define SLNAME 40
78 #define SLMAPNAME 16
79 #define SLMODNAME 16
80 typedef struct slserver_s
81 {
82         unsigned int ipaddr;
83         unsigned short port;
84         unsigned short ping;
85         char name[SLNAME];
86         char mapname[SLMAPNAME];
87         char modname[SLMODNAME];
88 }
89 slserver_t;
90
91 slserver_t sl_server[SLSERVERS];
92 int sl_numservers = 0;
93
94 void SL_ClearServers(void)
95 {
96         sl_numservers = 0;
97 }
98
99 slserver_t *SL_FindServer(unsigned int ipaddr, unsigned short port)
100 {
101         int i;
102         slserver_t *sl;
103         for (i = 0, sl = sl_server;i < sl_numservers;i++, sl++)
104                 if (sl->ipaddr == ipaddr && sl->port == port)
105                         return;
106 }
107
108 void SL_AddServer(unsigned int ipaddr, unsigned short port)
109 {
110         if (SL_FindServer(ipaddr, port))
111                 return;
112         memset(sl_server + sl_numservers, 0, sizeof(slserver_t));
113         sl_server[sl_numservers].ipaddr = ipaddr;
114         sl_server[sl_numservers].port = port;
115         sl_server[sl_numservers].ping = 0xFFFF;
116         sl_numservers++;
117 }
118
119 void SL_UpdateServerName(unsigned int ipaddr, unsigned short port, const char *name);
120 {
121         int namelen;
122         slserver_t *sl;
123         sl = SL_FindServer(ipaddr, port);
124         if (sl == NULL)
125                 return;
126         memset(sl->name, 0, sizeof(sl->name));
127         namelen = strlen(name);
128         if (namelen > sizeof(sl->name) - 1)
129                 namelen = sizeof(sl->name) - 1;
130         if (namelen)
131                 memcpy(sl->name, name, namelen);
132 }
133
134 void SL_UpdateServerModName(unsigned int ipaddr, unsigned short port, const char *name);
135 {
136         int namelen;
137         slserver_t *sl;
138         sl = SL_FindServer(ipaddr, port);
139         if (sl == NULL)
140                 return;
141         memset(sl->modname, 0, sizeof(sl->modname));
142         namelen = strlen(name);
143         if (namelen > sizeof(sl->modname) - 1)
144                 namelen = sizeof(sl->modname) - 1;
145         if (namelen)
146                 memcpy(sl->modname, name, namelen);
147 }
148
149 void SL_UpdateServerMapName(unsigned int ipaddr, unsigned short port, const char *name);
150 {
151         int namelen;
152         slserver_t *sl;
153         sl = SL_FindServer(ipaddr, port);
154         if (sl == NULL)
155                 return;
156         memset(sl->mapname, 0, sizeof(sl->mapname));
157         namelen = strlen(name);
158         if (namelen > sizeof(sl->mapname) - 1)
159                 namelen = sizeof(sl->mapname) - 1;
160         if (namelen)
161                 memcpy(sl->mapname, name, namelen);
162 }
163
164 void SL_UpdateServerPing(unsigned int ipaddr, unsigned short port, float ping);
165 {
166         int i;
167         slserver_t *sl;
168         sl = SL_FindServer(ipaddr, port);
169         if (sl == NULL)
170                 return;
171         i = ping * 1000.0;
172         sl->ping = bound(0, i, 9999);
173 }
174 */
175
176
177 double                  net_time;
178
179 double SetNetTime(void)
180 {
181         net_time = Sys_DoubleTime();
182         return net_time;
183 }
184
185
186 /*
187 ===================
188 NET_NewQSocket
189
190 Called by drivers when a new communications endpoint is required
191 The sequence and buffer fields will be filled in properly
192 ===================
193 */
194 qsocket_t *NET_NewQSocket (void)
195 {
196         qsocket_t       *sock;
197
198         if (net_activeconnections >= svs.maxclients)
199                 return NULL;
200
201         sock = Mem_Alloc(net_mempool, sizeof(qsocket_t));
202
203         // add it to active list
204         sock->next = net_activeSockets;
205         net_activeSockets = sock;
206
207         sock->disconnected = false;
208         sock->connecttime = net_time;
209         strcpy (sock->address,"UNSET ADDRESS");
210         sock->driver = net_driverlevel;
211         sock->socket = 0;
212         sock->driverdata = NULL;
213         sock->canSend = true;
214         sock->sendNext = false;
215         sock->lastMessageTime = net_time;
216         sock->ackSequence = 0;
217         sock->sendSequence = 0;
218         sock->unreliableSendSequence = 0;
219         sock->sendMessageLength = 0;
220         sock->receiveSequence = 0;
221         sock->unreliableReceiveSequence = 0;
222         sock->receiveMessageLength = 0;
223
224         return sock;
225 }
226
227
228 void NET_FreeQSocket(qsocket_t *sock)
229 {
230         qsocket_t       *s;
231
232         // remove it from active list
233         if (sock == net_activeSockets)
234                 net_activeSockets = net_activeSockets->next;
235         else
236         {
237                 for (s = net_activeSockets; s; s = s->next)
238                         if (s->next == sock)
239                         {
240                                 s->next = sock->next;
241                                 break;
242                         }
243                 if (!s)
244                         Sys_Error ("NET_FreeQSocket: not active\n");
245         }
246
247         Mem_Free(sock);
248 }
249
250
251 static void NET_Listen_f (void)
252 {
253         if (Cmd_Argc () != 2)
254         {
255                 Con_Printf ("\"listen\" is \"%u\"\n", listening ? 1 : 0);
256                 return;
257         }
258
259         listening = atoi(Cmd_Argv(1)) ? true : false;
260
261         for (net_driverlevel=0 ; net_driverlevel<net_numdrivers; net_driverlevel++)
262         {
263                 if (net_drivers[net_driverlevel].initialized == false)
264                         continue;
265                 dfunc.Listen (listening);
266         }
267 }
268
269
270 static void MaxPlayers_f (void)
271 {
272         int n;
273
274         if (Cmd_Argc () != 2)
275         {
276                 Con_Printf ("\"maxplayers\" is \"%u\"\n", svs.maxclients);
277                 return;
278         }
279
280         if (sv.active)
281         {
282                 Con_Printf ("maxplayers can not be changed while a server is running.\n");
283                 return;
284         }
285
286         n = atoi(Cmd_Argv(1));
287         n = bound(1, n, MAX_SCOREBOARD);
288         if (svs.maxclients != n)
289                 Con_Printf ("\"maxplayers\" set to \"%u\"\n", n);
290
291         if ((n == 1) && listening)
292                 Cbuf_AddText ("listen 0\n");
293
294         if ((n > 1) && (!listening))
295                 Cbuf_AddText ("listen 1\n");
296
297         SV_SetMaxClients(n);
298 }
299
300
301 static void NET_Port_f (void)
302 {
303         int     n;
304
305         if (Cmd_Argc () != 2)
306         {
307                 Con_Printf ("\"port\" is \"%u\"\n", net_hostport);
308                 return;
309         }
310
311         n = atoi(Cmd_Argv(1));
312         if (n < 1 || n > 65534)
313         {
314                 Con_Printf ("Bad value, must be between 1 and 65534\n");
315                 return;
316         }
317
318         DEFAULTnet_hostport = n;
319         net_hostport = n;
320
321         if (listening)
322         {
323                 // force a change to the new port
324                 Cbuf_AddText ("listen 0\n");
325                 Cbuf_AddText ("listen 1\n");
326         }
327 }
328
329
330 static void NET_Heartbeat_f (void)
331 {
332         NET_Heartbeat (2);
333 }
334
335
336 static void PrintSlistHeader(void)
337 {
338         Con_Printf("Server          Map             Users\n");
339         Con_Printf("--------------- --------------- -----\n");
340         slistLastShown = 0;
341 }
342
343
344 static void PrintSlist(void)
345 {
346         int n;
347
348         for (n = slistLastShown; n < hostCacheCount; n++)
349         {
350                 if (hostcache[n].maxusers)
351                         Con_Printf("%-15.15s %-15.15s %2u/%2u\n", hostcache[n].name, hostcache[n].map, hostcache[n].users, hostcache[n].maxusers);
352                 else
353                         Con_Printf("%-15.15s %-15.15s\n", hostcache[n].name, hostcache[n].map);
354         }
355         slistLastShown = n;
356 }
357
358
359 static void PrintSlistTrailer(void)
360 {
361         if (hostCacheCount)
362                 Con_Printf("== end list ==\n\n");
363         else
364         {
365                 if (gamemode == GAME_TRANSFUSION)
366                         Con_Printf("No Transfusion servers found.\n\n");
367                 else
368                         Con_Printf("No Quake servers found.\n\n");
369         }
370 }
371
372
373 void NET_SlistCommon (PollProcedure *sendProcedure, PollProcedure *pollProcedure)
374 {
375         if (slistInProgress)
376                 return;
377
378         if (! slistSilent)
379         {
380                 if (gamemode == GAME_TRANSFUSION)
381                         Con_Printf("Looking for Transfusion servers...\n");
382                 else
383                         Con_Printf("Looking for Quake servers...\n");
384                 PrintSlistHeader();
385         }
386
387         slistInProgress = true;
388         slistStartTime = Sys_DoubleTime();
389
390         SchedulePollProcedure(sendProcedure, 0.0);
391         SchedulePollProcedure(pollProcedure, 0.1);
392
393         hostCacheCount = 0;
394 }
395
396
397 void NET_Slist_f (void)
398 {
399         NET_SlistCommon (&slistSendProcedure, &slistPollProcedure);
400 }
401
402
403 void NET_InetSlist_f (void)
404 {
405         NET_SlistCommon (&inetSlistSendProcedure, &inetSlistPollProcedure);
406 }
407
408
409 static void Slist_Send(void)
410 {
411         for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++)
412         {
413                 if (!slistLocal && net_driverlevel == 0)
414                         continue;
415                 if (net_drivers[net_driverlevel].initialized == false)
416                         continue;
417                 dfunc.SearchForHosts (true);
418         }
419
420         if ((Sys_DoubleTime() - slistStartTime) < 0.5)
421                 SchedulePollProcedure(&slistSendProcedure, 0.75);
422 }
423
424
425 static void Slist_Poll(void)
426 {
427         for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++)
428         {
429                 if (!slistLocal && net_driverlevel == 0)
430                         continue;
431                 if (net_drivers[net_driverlevel].initialized == false)
432                         continue;
433                 dfunc.SearchForHosts (false);
434         }
435
436         if (! slistSilent)
437                 PrintSlist();
438
439         if ((Sys_DoubleTime() - slistStartTime) < 1.5)
440         {
441                 SchedulePollProcedure(&slistPollProcedure, 0.1);
442                 return;
443         }
444
445         if (! slistSilent)
446                 PrintSlistTrailer();
447         slistInProgress = false;
448         slistSilent = false;
449         slistLocal = true;
450 }
451
452
453 static void InetSlist_Send(void)
454 {
455         const char* host;
456
457         if (!slistInProgress)
458                 return;
459
460         while ((host = Master_BuildGetServers ()) != NULL)
461         {
462                 for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++)
463                 {
464                         if (!slistLocal && net_driverlevel == 0)
465                                 continue;
466                         if (net_drivers[net_driverlevel].initialized == false)
467                                 continue;
468                         dfunc.SearchForInetHosts (host);
469                 }
470         }
471
472         if ((Sys_DoubleTime() - slistStartTime) < 3.5)
473                 SchedulePollProcedure(&inetSlistSendProcedure, 1.0);
474 }
475
476
477 static void InetSlist_Poll(void)
478 {
479         for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++)
480         {
481                 if (!slistLocal && net_driverlevel == 0)
482                         continue;
483                 if (net_drivers[net_driverlevel].initialized == false)
484                         continue;
485                 // We stop as soon as we have one answer (FIXME: bad...)
486                 if (dfunc.SearchForInetHosts (NULL))
487                         slistInProgress = false;
488         }
489
490         if (! slistSilent)
491                 PrintSlist();
492
493         if (slistInProgress && (Sys_DoubleTime() - slistStartTime) < 4.0)
494         {
495                 SchedulePollProcedure(&inetSlistPollProcedure, 0.1);
496                 return;
497         }
498
499         if (! slistSilent)
500                 PrintSlistTrailer();
501         slistInProgress = false;
502         slistSilent = false;
503         slistLocal = true;
504 }
505
506
507 /*
508 ===================
509 NET_Connect
510 ===================
511 */
512
513 int hostCacheCount = 0;
514 hostcache_t hostcache[HOSTCACHESIZE];
515
516 qsocket_t *NET_Connect (char *host)
517 {
518         qsocket_t               *ret;
519         int                             n;
520
521         SetNetTime();
522
523         if (host && *host == 0)
524                 host = NULL;
525
526         if (host)
527         {
528                 if (strcasecmp (host, "local") == 0)
529                 {
530                         net_driverlevel = 0;
531                         return dfunc.Connect (host);
532                 }
533
534                 if (hostCacheCount)
535                 {
536                         for (n = 0; n < hostCacheCount; n++)
537                                 if (strcasecmp (host, hostcache[n].name) == 0)
538                                 {
539                                         host = hostcache[n].cname;
540                                         break;
541                                 }
542                         if (n < hostCacheCount)
543                                 goto JustDoIt;
544                 }
545         }
546
547         slistSilent = host ? true : false;
548         NET_Slist_f ();
549
550         while(slistInProgress)
551                 NET_Poll();
552
553         if (host == NULL)
554         {
555                 if (hostCacheCount != 1)
556                         return NULL;
557                 host = hostcache[0].cname;
558                 Con_Printf("Connecting to...\n%s @ %s\n\n", hostcache[0].name, host);
559         }
560
561         if (hostCacheCount)
562                 for (n = 0; n < hostCacheCount; n++)
563                         if (strcasecmp (host, hostcache[n].name) == 0)
564                         {
565                                 host = hostcache[n].cname;
566                                 break;
567                         }
568
569 JustDoIt:
570         for (net_driverlevel = 0;net_driverlevel < net_numdrivers;net_driverlevel++)
571         {
572                 if (net_drivers[net_driverlevel].initialized == false)
573                         continue;
574                 ret = dfunc.Connect (host);
575                 if (ret)
576                         return ret;
577         }
578
579         if (host)
580         {
581                 Con_Printf("\n");
582                 PrintSlistHeader();
583                 PrintSlist();
584                 PrintSlistTrailer();
585         }
586
587         return NULL;
588 }
589
590
591 /*
592 ===================
593 NET_CheckNewConnections
594 ===================
595 */
596
597 qsocket_t *NET_CheckNewConnections (void)
598 {
599         qsocket_t       *ret;
600
601         SetNetTime();
602
603         for (net_driverlevel=0 ; net_driverlevel<net_numdrivers; net_driverlevel++)
604         {
605                 if (net_drivers[net_driverlevel].initialized == false)
606                         continue;
607                 if (net_driverlevel && listening == false)
608                         continue;
609                 ret = dfunc.CheckNewConnections ();
610                 if (ret)
611                         return ret;
612         }
613
614         return NULL;
615 }
616
617 /*
618 ===================
619 NET_Close
620 ===================
621 */
622 void NET_Close (qsocket_t *sock)
623 {
624         if (!sock)
625                 return;
626
627         if (sock->disconnected)
628                 return;
629
630         SetNetTime();
631
632         // call the driver_Close function
633         sfunc.Close (sock);
634
635         NET_FreeQSocket(sock);
636 }
637
638
639 /*
640 =================
641 NET_GetMessage
642
643 If there is a complete message, return it in net_message
644
645 returns 0 if no data is waiting
646 returns 1 if a message was received
647 returns -1 if connection is invalid
648 =================
649 */
650
651 extern void PrintStats(qsocket_t *s);
652
653 int     NET_GetMessage (qsocket_t *sock)
654 {
655         int ret;
656
657         if (!sock)
658                 return -1;
659
660         if (sock->disconnected)
661         {
662                 Con_Printf("NET_GetMessage: disconnected socket\n");
663                 return -1;
664         }
665
666         SetNetTime();
667
668         ret = sfunc.QGetMessage(sock);
669
670         // see if this connection has timed out
671         if (ret == 0 && sock->driver)
672         {
673                 if (net_time - sock->lastMessageTime > net_messagetimeout.value)
674                 {
675                         NET_Close(sock);
676                         return -1;
677                 }
678         }
679
680
681         if (ret > 0)
682         {
683                 if (sock->driver)
684                 {
685                         sock->lastMessageTime = net_time;
686                         if (ret == 1)
687                                 messagesReceived++;
688                         else if (ret == 2)
689                                 unreliableMessagesReceived++;
690                 }
691         }
692
693         return ret;
694 }
695
696
697 /*
698 ==================
699 NET_SendMessage
700
701 Try to send a complete length+message unit over the reliable stream.
702 returns 0 if the message cannot be delivered reliably, but the connection
703                 is still considered valid
704 returns 1 if the message was sent properly
705 returns -1 if the connection died
706 ==================
707 */
708 int NET_SendMessage (qsocket_t *sock, sizebuf_t *data)
709 {
710         int             r;
711
712         if (!sock)
713                 return -1;
714
715         if (sock->disconnected)
716         {
717                 Con_Printf("NET_SendMessage: disconnected socket\n");
718                 return -1;
719         }
720
721         SetNetTime();
722         r = sfunc.QSendMessage(sock, data);
723         if (r == 1 && sock->driver)
724                 messagesSent++;
725
726         return r;
727 }
728
729
730 int NET_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data)
731 {
732         int             r;
733
734         if (!sock)
735                 return -1;
736
737         if (sock->disconnected)
738         {
739                 Con_Printf("NET_SendMessage: disconnected socket\n");
740                 return -1;
741         }
742
743         SetNetTime();
744         r = sfunc.SendUnreliableMessage(sock, data);
745         if (r == 1 && sock->driver)
746                 unreliableMessagesSent++;
747
748         return r;
749 }
750
751
752 /*
753 ==================
754 NET_CanSendMessage
755
756 Returns true or false if the given qsocket can currently accept a
757 message to be transmitted.
758 ==================
759 */
760 qboolean NET_CanSendMessage (qsocket_t *sock)
761 {
762         int             r;
763
764         if (!sock)
765                 return false;
766
767         if (sock->disconnected)
768                 return false;
769
770         SetNetTime();
771
772         r = sfunc.CanSendMessage(sock);
773
774         return r;
775 }
776
777
778 /*
779 ====================
780 NET_Heartbeat
781
782 Send an heartbeat to the master server(s)
783 ====================
784 */
785 void NET_Heartbeat (int priority)
786 {
787         const char* host;
788
789         if (! Master_AllowHeartbeat (priority))
790                 return;
791
792         while ((host = Master_BuildHeartbeat ()) != NULL)
793         {
794                 for (net_driverlevel=0 ; net_driverlevel<net_numdrivers; net_driverlevel++)
795                 {
796                         if (net_drivers[net_driverlevel].initialized == false)
797                                 continue;
798                         if (net_driverlevel && listening == false)
799                                 continue;
800                         dfunc.Heartbeat (host);
801                 }
802         }
803 }
804
805
806 int NET_SendToAll(sizebuf_t *data, int blocktime)
807 {
808         double          start;
809         int                     i;
810         int                     count = 0;
811         qbyte           state [MAX_SCOREBOARD];
812
813         for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
814                 state[i] = (host_client->netconnection && host_client->active) ? 0 : 2;
815
816         // for every player (simultaneously) wait for the first CanSendMessage
817         // and send the message, then wait for a second CanSendMessage (verifying
818         // it was received)
819         start = Sys_DoubleTime();
820         do
821         {
822                 count = 0;
823                 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
824                 {
825                         if (state[i] < 2)
826                         {
827                                 // need to send to this one
828                                 if (NET_CanSendMessage (host_client->netconnection))
829                                 {
830                                         if (state[i] == 0)
831                                         {
832                                                 if (NET_SendMessage (host_client->netconnection, data) == 1)
833                                                         state[i] = 2; // connection lost
834                                                 else
835                                                         count++;
836                                         }
837                                         state[i]++;
838                                 }
839                                 else
840                                 {
841                                         NET_GetMessage (host_client->netconnection);
842                                         count++;
843                                 }
844                         }
845                 }
846         }
847         while (count && (Sys_DoubleTime() - start) < blocktime);
848         return count;
849 }
850
851
852 //=============================================================================
853
854 /*
855 ====================
856 NET_Init
857 ====================
858 */
859
860 void NET_Init (void)
861 {
862         int                     i;
863         int                     controlSocket;
864
865         i = COM_CheckParm ("-port");
866         if (!i)
867                 i = COM_CheckParm ("-udpport");
868         if (!i)
869                 i = COM_CheckParm ("-ipxport");
870
871         if (i)
872         {
873                 if (i < com_argc-1)
874                         DEFAULTnet_hostport = atoi (com_argv[i+1]);
875                 else
876                         Sys_Error ("NET_Init: you must specify a number after -port");
877         }
878         net_hostport = DEFAULTnet_hostport;
879
880         if (COM_CheckParm("-listen") || cls.state == ca_dedicated || gamemode == GAME_TRANSFUSION)
881                 listening = true;
882
883         SetNetTime();
884
885         net_mempool = Mem_AllocPool("qsocket");
886
887         // allocate space for network message buffer
888         SZ_Alloc (&net_message, NET_MAXMESSAGE, "net_message");
889
890         Cvar_RegisterVariable (&net_messagetimeout);
891         Cvar_RegisterVariable (&hostname);
892
893         Cmd_AddCommand ("net_slist", NET_Slist_f);
894         Cmd_AddCommand ("net_inetslist", NET_InetSlist_f);
895         Cmd_AddCommand ("listen", NET_Listen_f);
896         Cmd_AddCommand ("maxplayers", MaxPlayers_f);
897         Cmd_AddCommand ("port", NET_Port_f);
898         Cmd_AddCommand ("heartbeat", NET_Heartbeat_f);
899
900         // initialize all the drivers
901         for (net_driverlevel=0 ; net_driverlevel<net_numdrivers ; net_driverlevel++)
902                 {
903                 controlSocket = net_drivers[net_driverlevel].Init();
904                 if (controlSocket == -1)
905                         continue;
906                 net_drivers[net_driverlevel].initialized = true;
907                 net_drivers[net_driverlevel].controlSock = controlSocket;
908                 if (listening)
909                         net_drivers[net_driverlevel].Listen (true);
910                 }
911
912         if (*my_ipx_address)
913                 Con_DPrintf("IPX address %s\n", my_ipx_address);
914         if (*my_tcpip_address)
915                 Con_DPrintf("TCP/IP address %s\n", my_tcpip_address);
916
917         Master_Init ();
918 }
919
920 /*
921 ====================
922 NET_Shutdown
923 ====================
924 */
925
926 void NET_Shutdown (void)
927 {
928         SetNetTime();
929
930         while (net_activeSockets)
931                 NET_Close(net_activeSockets);
932
933 //
934 // shutdown the drivers
935 //
936         for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++)
937         {
938                 if (net_drivers[net_driverlevel].initialized == true)
939                 {
940                         net_drivers[net_driverlevel].Shutdown ();
941                         net_drivers[net_driverlevel].initialized = false;
942                 }
943         }
944
945         Mem_FreePool(&net_mempool);
946 }
947
948
949 static PollProcedure *pollProcedureList = NULL;
950
951 void NET_Poll(void)
952 {
953         PollProcedure *pp;
954
955         if (!configRestored)
956                 configRestored = true;
957
958         SetNetTime();
959
960         for (pp = pollProcedureList; pp; pp = pp->next)
961         {
962                 if (pp->nextTime > net_time)
963                         break;
964                 pollProcedureList = pp->next;
965                 pp->procedure(pp->arg);
966         }
967 }
968
969
970 void SchedulePollProcedure(PollProcedure *proc, double timeOffset)
971 {
972         PollProcedure *pp, *prev;
973
974         proc->nextTime = Sys_DoubleTime() + timeOffset;
975         for (pp = pollProcedureList, prev = NULL; pp; pp = pp->next)
976         {
977                 if (pp->nextTime >= proc->nextTime)
978                         break;
979                 prev = pp;
980         }
981
982         if (prev == NULL)
983         {
984                 proc->next = pollProcedureList;
985                 pollProcedureList = proc;
986                 return;
987         }
988
989         proc->next = pp;
990         prev->next = proc;
991 }
992