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