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