fixed all the signed/unsigned mismatch warnings
[divverent/darkplaces.git] / net_master.c
1 /*
2 Copyright (C) 2002 Mathieu Olivier
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_master.c
21
22 #include "quakedef.h"
23
24
25 cvar_t sv_public = {0, "sv_public", "0"};
26 cvar_t sv_heartbeatperiod = {CVAR_SAVE, "sv_heartbeatperiod", "180"};
27
28 cvar_t sv_masters [] =
29 {
30         {0, "sv_masterextra1", "68.102.242.12"},
31         {CVAR_SAVE, "sv_master1", ""},
32         {CVAR_SAVE, "sv_master2", ""},
33         {CVAR_SAVE, "sv_master3", ""},
34         {CVAR_SAVE, "sv_master4", ""}
35 };
36
37 static double nextheartbeattime = 0;
38
39
40 /*
41 ====================
42 Master_AllowHeartbeat
43
44 Allow (or not) NET_Heartbeat to proceed depending on various factors
45 ====================
46 */
47 qboolean Master_AllowHeartbeat (int priority)
48 {
49         // LordHavoc: make advertising optional
50         if (!sv_public.integer)
51                 return false;
52         // LordHavoc: don't advertise singleplayer games
53         if (svs.maxclients < 2)
54                 return false;
55         // if it's a state change (client connected), limit next heartbeat to no
56         // more than 30 sec in the future
57         if (priority == 1 && nextheartbeattime > realtime + 30.0)
58                 nextheartbeattime = realtime + 30.0;
59         if (priority <= 1 && realtime < nextheartbeattime)
60                 return false;
61         // limit heartbeatperiod to 30 to 270 second range,
62         // lower limit is to avoid abusing master servers with excess traffic,
63         // upper limit is to avoid timing out on the master server (which uses
64         // 300 sec timeout)
65         if (sv_heartbeatperiod.value < 30)
66                 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
67         if (sv_heartbeatperiod.value > 270)
68                 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
69         // send a heartbeat as often as the admin wants
70         nextheartbeattime = realtime + sv_heartbeatperiod.value;
71         return true;
72 }
73
74
75 /*
76 ====================
77 Master_BuildGetServers
78
79 Build a getservers request for a master server
80 ====================
81 */
82 const char* Master_BuildGetServers (void)
83 {
84         static int nextmaster = 0;
85         cvar_t* sv_master;
86         char request [256];
87
88         if (nextmaster >= (int)(sizeof (sv_masters) / sizeof (sv_masters[0])))
89         {
90                 nextmaster = 0;
91                 return NULL;
92         }
93
94         sv_master = &sv_masters[nextmaster++];
95
96         // No master, no heartbeat
97         if (sv_master->string[0] == '\0')
98         {
99                 nextmaster = 0;
100                 return NULL;
101         }
102
103         // Build the heartbeat
104         snprintf (request, sizeof (request), "getservers %s %u empty full\x0A", gamename, NET_PROTOCOL_VERSION);
105         SZ_Clear (&net_message);
106         MSG_WriteLong (&net_message, -1);
107         MSG_WriteString (&net_message, request);
108
109         net_message.cursize--;  // we don't send the trailing '\0'
110
111         return sv_master->string;
112 }
113
114
115 /*
116 ====================
117 Master_BuildHeartbeat
118
119 Build an heartbeat for a master server
120 ====================
121 */
122 const char* Master_BuildHeartbeat (void)
123 {
124         static int nextmaster = 0;
125         cvar_t* sv_master;
126
127         if (nextmaster >= (int)(sizeof (sv_masters) / sizeof (sv_masters[0])))
128         {
129                 nextmaster = 0;
130                 return NULL;
131         }
132
133         sv_master = &sv_masters[nextmaster++];
134
135         // No master, no heartbeat
136         if (sv_master->string[0] == '\0')
137         {
138                 nextmaster = 0;
139                 return NULL;
140         }
141
142         // Build the heartbeat
143         SZ_Clear (&net_message);
144         MSG_WriteLong (&net_message, -1);
145         MSG_WriteString (&net_message, "heartbeat DarkPlaces\x0A");
146
147         net_message.cursize--;  // we don't send the trailing '\0'
148
149         return sv_master->string;
150 }
151
152
153 /*
154 ====================
155 Master_HandleMessage
156
157 Handle the master server messages
158 ====================
159 */
160 int Master_HandleMessage (void)
161 {
162         const char* string = MSG_ReadString ();
163
164         // If it's a "getinfo" request
165         if (!strncmp (string, "getinfo", 7))
166         {
167                 char response [512];
168                 size_t length;
169
170                 length = snprintf (response, sizeof (response), "infoResponse\x0A"
171                                         "\\gamename\\%s\\modname\\%s\\sv_maxclients\\%d"
172                                         "\\clients\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d",
173                                         gamename, com_modname, svs.maxclients, net_activeconnections,
174                                         sv.name, hostname.string, NET_PROTOCOL_VERSION);
175
176                 // Too long to fit into the buffer?
177                 if (length >= sizeof (response))
178                         return -1;
179
180                 // If there was a challenge in the getinfo message
181                 if (string[7] == ' ')
182                 {
183                         string += 8;  // skip the header and the space
184
185                         // If the challenge wouldn't fit into the buffer
186                         if (length + 11 + strlen (string) >= sizeof (response))
187                                 return -1;
188
189                         sprintf (response + length, "\\challenge\\%s", string);
190                 }
191
192                 SZ_Clear (&net_message);
193                 MSG_WriteLong (&net_message, -1);
194                 MSG_WriteString (&net_message, response);
195
196                 return net_message.cursize - 1;
197         }
198
199         return 0;
200 }
201
202
203 /*
204 ====================
205 Master_Init
206
207 Initialize the code that handles master server requests and reponses
208 ====================
209 */
210 void Master_Init (void)
211 {
212         unsigned int ind;
213         Cvar_RegisterVariable (&sv_public);
214         Cvar_RegisterVariable (&sv_heartbeatperiod);
215         for (ind = 0; ind < sizeof (sv_masters) / sizeof (sv_masters[0]); ind++)
216                 Cvar_RegisterVariable (&sv_masters[ind]);
217 }
218
219
220 /*
221 ====================
222 Master_ParseServerList
223
224 Parse getserverResponse messages
225 ====================
226 */
227 void Master_ParseServerList (net_landriver_t* dfunc)
228 {
229         int control;
230         qbyte* servers;
231         qbyte* crtserver;
232         int ipaddr;
233         struct qsockaddr svaddr;
234         char ipstring [32];
235
236         if (net_message.cursize < (int)sizeof(int))
237                 return;
238
239         // is the cache full?
240         if (hostCacheCount == HOSTCACHESIZE)
241                 return;
242
243         MSG_BeginReading ();
244         control = BigLong(*((int *)net_message.data));
245         MSG_ReadLong();
246         if (control != -1)
247                 return;
248
249         if (strncmp (net_message.data + 4, "getserversResponse\\", 19)) 
250                 return;
251
252         // Skip the next 19 bytes
253         MSG_ReadLong(); MSG_ReadLong(); MSG_ReadLong(); MSG_ReadLong();
254         MSG_ReadShort(); MSG_ReadByte();
255
256         crtserver = servers = Z_Malloc (net_message.cursize - 23);
257         memcpy (servers , net_message.data + 23, net_message.cursize - 23);
258
259         // Extract the IP addresses
260         while ((ipaddr = (crtserver[3] << 24) | (crtserver[2] << 16) | (crtserver[1] << 8) | crtserver[0]) != -1)
261         {
262                 int port = (crtserver[5] << 8) | crtserver[4];
263
264                 if (port == -1 || port == 0)
265                         break;
266
267                 port = ((port >> 8) & 0xFF) + ((port & 0xFF) << 8);
268                 sprintf (ipstring, "%u.%u.%u.%u:%hu",
269                                         ipaddr & 0xFF, (ipaddr >> 8) & 0xFF,
270                                         (ipaddr >> 16) & 0xFF, ipaddr >> 24,
271                                         port);
272                 dfunc->GetAddrFromName (ipstring, &svaddr);
273
274                 Con_DPrintf("Requesting info from server %s\n", ipstring);
275                 // Send a request at this address
276                 SZ_Clear(&net_message);
277                 MSG_WriteLong(&net_message, 0);  // save space for the header, filled in later
278                 MSG_WriteByte(&net_message, CCREQ_SERVER_INFO);
279                 MSG_WriteString(&net_message, "QUAKE");
280                 MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
281                 *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
282                 dfunc->Write(dfunc->controlSock, net_message.data, net_message.cursize, &svaddr);
283                 SZ_Clear(&net_message);
284
285                 if (crtserver[6] != '\\')
286                         break;
287                 crtserver += 7;
288         }
289
290         Z_Free (servers);
291 }