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