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