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