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