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