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