]> icculus.org git repositories - divverent/darkplaces.git/blob - net_main.c
some cleanups to hostcache (got rid of driver/address stuff and expanded the strings...
[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 Address        Name/Description                   Map             Users\n");
340         Con_Printf("--------------------- ---------------------------------- --------------- -----\n");
341         slistLastShown = 0;
342 }
343
344
345 static void PrintSlist(void)
346 {
347         int n;
348         for (n = slistLastShown; n < hostCacheCount; n++)
349                 Con_Printf("%-21.21s %-34.34s %-15.15s %2u/%2u\n", hostcache[n].cname, hostcache[n].name, hostcache[n].map, hostcache[n].users, hostcache[n].maxusers);
350         slistLastShown = n;
351 }
352
353
354 static void PrintSlistTrailer(void)
355 {
356         if (hostCacheCount)
357                 Con_Printf("== end list ==\n\n");
358         else
359                 Con_Printf("No %s servers found.\n\n", gamename);
360 }
361
362
363 void NET_SlistCommon (PollProcedure *sendProcedure, PollProcedure *pollProcedure)
364 {
365         if (slistInProgress)
366                 return;
367
368         if (! slistSilent)
369         {
370                 Con_Printf("Looking for %s servers...\n", gamename);
371                 PrintSlistHeader();
372         }
373
374         slistInProgress = true;
375         slistStartTime = Sys_DoubleTime();
376
377         SchedulePollProcedure(sendProcedure, 0.0);
378         SchedulePollProcedure(pollProcedure, 0.1);
379
380         hostCacheCount = 0;
381 }
382
383
384 void NET_Slist_f (void)
385 {
386         NET_SlistCommon (&slistSendProcedure, &slistPollProcedure);
387 }
388
389
390 void NET_InetSlist_f (void)
391 {
392         NET_SlistCommon (&inetSlistSendProcedure, &inetSlistPollProcedure);
393 }
394
395
396 static void Slist_Send(void)
397 {
398         for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++)
399         {
400                 if (!slistLocal && net_driverlevel == 0)
401                         continue;
402                 if (net_drivers[net_driverlevel].initialized == false)
403                         continue;
404                 dfunc.SearchForHosts (true);
405         }
406
407         if ((Sys_DoubleTime() - slistStartTime) < 0.5)
408                 SchedulePollProcedure(&slistSendProcedure, 0.75);
409 }
410
411
412 static void Slist_Poll(void)
413 {
414         for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++)
415         {
416                 if (!slistLocal && net_driverlevel == 0)
417                         continue;
418                 if (net_drivers[net_driverlevel].initialized == false)
419                         continue;
420                 dfunc.SearchForHosts (false);
421         }
422
423         if (! slistSilent)
424                 PrintSlist();
425
426         if ((Sys_DoubleTime() - slistStartTime) < 1.5)
427         {
428                 SchedulePollProcedure(&slistPollProcedure, 0.1);
429                 return;
430         }
431
432         if (! slistSilent)
433                 PrintSlistTrailer();
434         slistInProgress = false;
435         slistSilent = false;
436         slistLocal = true;
437 }
438
439
440 static void InetSlist_Send(void)
441 {
442         const char* host;
443
444         if (!slistInProgress)
445                 return;
446
447         while ((host = Master_BuildGetServers ()) != NULL)
448         {
449                 for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++)
450                 {
451                         if (!slistLocal && net_driverlevel == 0)
452                                 continue;
453                         if (net_drivers[net_driverlevel].initialized == false)
454                                 continue;
455                         dfunc.SearchForInetHosts (host);
456                 }
457         }
458
459         if ((Sys_DoubleTime() - slistStartTime) < 3.5)
460                 SchedulePollProcedure(&inetSlistSendProcedure, 1.0);
461 }
462
463
464 static void InetSlist_Poll(void)
465 {
466         for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++)
467         {
468                 if (!slistLocal && net_driverlevel == 0)
469                         continue;
470                 if (net_drivers[net_driverlevel].initialized == false)
471                         continue;
472                 // We stop as soon as we have one answer (FIXME: bad...)
473                 if (dfunc.SearchForInetHosts (NULL))
474                         slistInProgress = false;
475         }
476
477         if (! slistSilent)
478                 PrintSlist();
479
480         if (slistInProgress && (Sys_DoubleTime() - slistStartTime) < 4.0)
481         {
482                 SchedulePollProcedure(&inetSlistPollProcedure, 0.1);
483                 return;
484         }
485
486         if (! slistSilent)
487                 PrintSlistTrailer();
488         slistInProgress = false;
489         slistSilent = false;
490         slistLocal = true;
491 }
492
493
494 /*
495 ===================
496 NET_Connect
497 ===================
498 */
499
500 int hostCacheCount = 0;
501 hostcache_t hostcache[HOSTCACHESIZE];
502
503 qsocket_t *NET_Connect (char *host)
504 {
505         qsocket_t *ret;
506
507         if (host == NULL || *host == 0)
508                 return NULL;
509
510         SetNetTime();
511
512         if (host && strcasecmp (host, "local") == 0)
513         {
514                 net_driverlevel = 0;
515                 return dfunc.Connect (host);
516         }
517
518         for (net_driverlevel = 0;net_driverlevel < net_numdrivers;net_driverlevel++)
519         {
520                 if (net_drivers[net_driverlevel].initialized == false)
521                         continue;
522                 ret = dfunc.Connect (host);
523                 if (ret)
524                         return ret;
525         }
526
527         return NULL;
528 }
529
530
531 /*
532 ===================
533 NET_CheckNewConnections
534 ===================
535 */
536
537 qsocket_t *NET_CheckNewConnections (void)
538 {
539         qsocket_t       *ret;
540
541         SetNetTime();
542
543         for (net_driverlevel=0 ; net_driverlevel<net_numdrivers; net_driverlevel++)
544         {
545                 if (net_drivers[net_driverlevel].initialized == false)
546                         continue;
547                 if (net_driverlevel && listening == false)
548                         continue;
549                 ret = dfunc.CheckNewConnections ();
550                 if (ret)
551                         return ret;
552         }
553
554         return NULL;
555 }
556
557 /*
558 ===================
559 NET_Close
560 ===================
561 */
562 void NET_Close (qsocket_t *sock)
563 {
564         if (!sock)
565                 return;
566
567         if (sock->disconnected)
568                 return;
569
570         SetNetTime();
571
572         // call the driver_Close function
573         sfunc.Close (sock);
574
575         NET_FreeQSocket(sock);
576 }
577
578
579 /*
580 =================
581 NET_GetMessage
582
583 If there is a complete message, return it in net_message
584
585 returns 0 if no data is waiting
586 returns 1 if a message was received
587 returns -1 if connection is invalid
588 =================
589 */
590
591 extern void PrintStats(qsocket_t *s);
592
593 int     NET_GetMessage (qsocket_t *sock)
594 {
595         int ret;
596
597         if (!sock)
598                 return -1;
599
600         if (sock->disconnected)
601         {
602                 Con_Printf("NET_GetMessage: disconnected socket\n");
603                 return -1;
604         }
605
606         SetNetTime();
607
608         ret = sfunc.QGetMessage(sock);
609
610         // see if this connection has timed out
611         if (ret == 0 && sock->driver)
612         {
613                 if (net_time - sock->lastMessageTime > net_messagetimeout.value)
614                 {
615                         NET_Close(sock);
616                         return -1;
617                 }
618         }
619
620
621         if (ret > 0)
622         {
623                 if (sock->driver)
624                 {
625                         sock->lastMessageTime = net_time;
626                         if (ret == 1)
627                                 messagesReceived++;
628                         else if (ret == 2)
629                                 unreliableMessagesReceived++;
630                 }
631         }
632
633         return ret;
634 }
635
636
637 /*
638 ==================
639 NET_SendMessage
640
641 Try to send a complete length+message unit over the reliable stream.
642 returns 0 if the message cannot be delivered reliably, but the connection
643                 is still considered valid
644 returns 1 if the message was sent properly
645 returns -1 if the connection died
646 ==================
647 */
648 int NET_SendMessage (qsocket_t *sock, sizebuf_t *data)
649 {
650         int             r;
651
652         if (!sock)
653                 return -1;
654
655         if (sock->disconnected)
656         {
657                 Con_Printf("NET_SendMessage: disconnected socket\n");
658                 return -1;
659         }
660
661         SetNetTime();
662         r = sfunc.QSendMessage(sock, data);
663         if (r == 1 && sock->driver)
664                 messagesSent++;
665
666         return r;
667 }
668
669
670 int NET_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data)
671 {
672         int             r;
673
674         if (!sock)
675                 return -1;
676
677         if (sock->disconnected)
678         {
679                 Con_Printf("NET_SendMessage: disconnected socket\n");
680                 return -1;
681         }
682
683         SetNetTime();
684         r = sfunc.SendUnreliableMessage(sock, data);
685         if (r == 1 && sock->driver)
686                 unreliableMessagesSent++;
687
688         return r;
689 }
690
691
692 /*
693 ==================
694 NET_CanSendMessage
695
696 Returns true or false if the given qsocket can currently accept a
697 message to be transmitted.
698 ==================
699 */
700 qboolean NET_CanSendMessage (qsocket_t *sock)
701 {
702         int             r;
703
704         if (!sock)
705                 return false;
706
707         if (sock->disconnected)
708                 return false;
709
710         SetNetTime();
711
712         r = sfunc.CanSendMessage(sock);
713
714         return r;
715 }
716
717
718 /*
719 ====================
720 NET_Heartbeat
721
722 Send an heartbeat to the master server(s)
723 ====================
724 */
725 void NET_Heartbeat (int priority)
726 {
727         const char* host;
728
729         if (! Master_AllowHeartbeat (priority))
730                 return;
731
732         while ((host = Master_BuildHeartbeat ()) != NULL)
733         {
734                 for (net_driverlevel=0 ; net_driverlevel<net_numdrivers; net_driverlevel++)
735                 {
736                         if (net_drivers[net_driverlevel].initialized == false)
737                                 continue;
738                         if (net_driverlevel && listening == false)
739                                 continue;
740                         dfunc.Heartbeat (host);
741                 }
742         }
743 }
744
745
746 int NET_SendToAll(sizebuf_t *data, int blocktime)
747 {
748         double          start;
749         int                     i;
750         int                     count = 0;
751         qbyte           state [MAX_SCOREBOARD];
752
753         for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
754         {
755                 state[i] = 2;
756                 if (host_client->netconnection && host_client->active)
757                 {
758                         if (host_client->netconnection->driver == 0)
759                                 NET_SendMessage(host_client->netconnection, data);
760                         else
761                                 state[i] = 0;
762                 }
763         }
764
765         // for every player (simultaneously) wait for the first CanSendMessage
766         // and send the message, then wait for a second CanSendMessage (verifying
767         // it was received)
768         start = Sys_DoubleTime();
769         do
770         {
771                 count = 0;
772                 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
773                 {
774                         if (state[i] < 2)
775                         {
776                                 count++;
777                                 // need to send to this one
778                                 if (NET_CanSendMessage (host_client->netconnection))
779                                 {
780                                         if (state[i] == 0 && NET_SendMessage (host_client->netconnection, data) == -1)
781                                                 state[i] = 2; // connection lost
782                                         state[i]++;
783                                 }
784                                 else
785                                         NET_GetMessage (host_client->netconnection);
786                         }
787                 }
788         }
789         while (count && (Sys_DoubleTime() - start) < blocktime);
790         return count;
791 }
792
793
794 //=============================================================================
795
796 /*
797 ====================
798 NET_Init
799 ====================
800 */
801
802 void NET_Init (void)
803 {
804         int                     i;
805         int                     controlSocket;
806
807         i = COM_CheckParm ("-port");
808         if (!i)
809                 i = COM_CheckParm ("-udpport");
810         if (!i)
811                 i = COM_CheckParm ("-ipxport");
812
813         if (i)
814         {
815                 if (i < com_argc-1)
816                         DEFAULTnet_hostport = atoi (com_argv[i+1]);
817                 else
818                         Sys_Error ("NET_Init: you must specify a number after -port");
819         }
820         net_hostport = DEFAULTnet_hostport;
821
822         if (COM_CheckParm("-listen") || cls.state == ca_dedicated || gamemode == GAME_TRANSFUSION)
823                 listening = true;
824
825         SetNetTime();
826
827         net_mempool = Mem_AllocPool("qsocket");
828
829         // allocate space for network message buffer
830         SZ_Alloc (&net_message, NET_MAXMESSAGE, "net_message");
831
832         Cvar_RegisterVariable (&net_messagetimeout);
833         Cvar_RegisterVariable (&hostname);
834         Cvar_RegisterVariable (&developer_networking);
835
836         Cmd_AddCommand ("net_slist", NET_Slist_f);
837         Cmd_AddCommand ("net_inetslist", NET_InetSlist_f);
838         Cmd_AddCommand ("listen", NET_Listen_f);
839         Cmd_AddCommand ("maxplayers", MaxPlayers_f);
840         Cmd_AddCommand ("port", NET_Port_f);
841         Cmd_AddCommand ("heartbeat", NET_Heartbeat_f);
842
843         // initialize all the drivers
844         for (net_driverlevel=0 ; net_driverlevel<net_numdrivers ; net_driverlevel++)
845                 {
846                 controlSocket = net_drivers[net_driverlevel].Init();
847                 if (controlSocket == -1)
848                         continue;
849                 net_drivers[net_driverlevel].initialized = true;
850                 net_drivers[net_driverlevel].controlSock = controlSocket;
851                 if (listening)
852                         net_drivers[net_driverlevel].Listen (true);
853                 }
854
855         if (*my_ipx_address)
856                 Con_DPrintf("IPX address %s\n", my_ipx_address);
857         if (*my_tcpip_address)
858                 Con_DPrintf("TCP/IP address %s\n", my_tcpip_address);
859
860         Master_Init ();
861 }
862
863 /*
864 ====================
865 NET_Shutdown
866 ====================
867 */
868
869 void NET_Shutdown (void)
870 {
871         SetNetTime();
872
873         while (net_activeSockets)
874                 NET_Close(net_activeSockets);
875
876 //
877 // shutdown the drivers
878 //
879         for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++)
880         {
881                 if (net_drivers[net_driverlevel].initialized == true)
882                 {
883                         net_drivers[net_driverlevel].Shutdown ();
884                         net_drivers[net_driverlevel].initialized = false;
885                 }
886         }
887
888         Mem_FreePool(&net_mempool);
889 }
890
891
892 static PollProcedure *pollProcedureList = NULL;
893
894 void NET_Poll(void)
895 {
896         PollProcedure *pp;
897
898         if (!configRestored)
899                 configRestored = true;
900
901         SetNetTime();
902
903         for (pp = pollProcedureList; pp; pp = pp->next)
904         {
905                 if (pp->nextTime > net_time)
906                         break;
907                 pollProcedureList = pp->next;
908                 pp->procedure(pp->arg);
909         }
910 }
911
912
913 void SchedulePollProcedure(PollProcedure *proc, double timeOffset)
914 {
915         PollProcedure *pp, *prev;
916
917         proc->nextTime = Sys_DoubleTime() + timeOffset;
918         for (pp = pollProcedureList, prev = NULL; pp; pp = pp->next)
919         {
920                 if (pp->nextTime >= proc->nextTime)
921                         break;
922                 prev = pp;
923         }
924
925         if (prev == NULL)
926         {
927                 proc->next = pollProcedureList;
928                 pollProcedureList = proc;
929                 return;
930         }
931
932         proc->next = pp;
933         prev->next = proc;
934 }
935