]> icculus.org git repositories - divverent/darkplaces.git/blob - net_master.c
added -gl_driver commandline option, it's useless though (crashs when I try it on...
[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 <malloc.h>
23 #include "quakedef.h"
24
25 cvar_t sv_masters [] =
26 {
27         {CVAR_SAVE, "sv_master1", ""},
28         {CVAR_SAVE, "sv_master2", ""},
29         {CVAR_SAVE, "sv_master3", ""},
30         {CVAR_SAVE, "sv_master4", ""}
31 };
32
33
34 /*
35 ====================
36 Master_BuildGetServers
37
38 Build a getservers request for a master server
39 ====================
40 */
41 char* Master_BuildGetServers (void)
42 {
43         static int nextmaster = 0;
44         cvar_t* sv_master;
45         char request [256];
46         
47         if (nextmaster >= sizeof (sv_masters) / sizeof (sv_masters[0]))
48         {
49                 nextmaster = 0;
50                 return NULL;
51         }
52
53         sv_master = &sv_masters[nextmaster++];
54
55         // No master, no heartbeat
56         if (sv_master->string[0] == '\0')
57         {
58                 nextmaster = 0;
59                 return NULL;
60         }
61         
62         // Build the heartbeat
63         snprintf (request, sizeof (request), "getservers %s %u empty full\x0A", gamename, NET_PROTOCOL_VERSION);
64         SZ_Clear (&net_message);
65         MSG_WriteLong (&net_message, -1);
66         MSG_WriteString (&net_message, request);
67
68         net_message.cursize--;  // we don't send the trailing '\0'
69
70         return sv_master->string;
71 }
72
73
74 /*
75 ====================
76 Master_BuildHeartbeat
77
78 Build an heartbeat for a master server
79 ====================
80 */
81 char* Master_BuildHeartbeat (void)
82 {
83         static int nextmaster = 0;
84         cvar_t* sv_master;
85         
86         if (nextmaster >= sizeof (sv_masters) / sizeof (sv_masters[0]))
87         {
88                 nextmaster = 0;
89                 return NULL;
90         }
91
92         sv_master = &sv_masters[nextmaster++];
93
94         // No master, no heartbeat
95         if (sv_master->string[0] == '\0')
96         {
97                 nextmaster = 0;
98                 return NULL;
99         }
100         
101         // Build the heartbeat
102         SZ_Clear (&net_message);
103         MSG_WriteLong (&net_message, -1);
104         MSG_WriteString (&net_message, "heartbeat DarkPlaces\x0A");
105
106         net_message.cursize--;  // we don't send the trailing '\0'
107
108         return sv_master->string;
109 }
110
111
112 /*
113 ====================
114 Master_HandleMessage
115
116 Handle the master server messages
117 ====================
118 */
119 int Master_HandleMessage (void)
120 {
121         const char* string = MSG_ReadString ();
122
123         // If it's a "getinfo" request
124         if (!strncmp (string, "getinfo", 7))
125         {
126                 char response [512];
127                 size_t length;
128
129                 length = snprintf (response, sizeof (response), "infoResponse\x0A"
130                                         "\\gamename\\%s\\modname\\%s\\sv_maxclients\\%d"
131                                         "\\clients\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d",
132                                         gamename, com_modname, svs.maxclients, net_activeconnections,
133                                         sv.name, hostname.string, NET_PROTOCOL_VERSION);
134
135                 // Too long to fit into the buffer?
136                 if (length >= sizeof (response))
137                         return -1;
138
139                 // If there was a challenge in the getinfo message
140                 if (string[7] == ' ')
141                 {
142                         string += 8;  // skip the header and the space
143
144                         // If the challenge wouldn't fit into the buffer
145                         if (length + 11 + strlen (string) >= sizeof (response))
146                                 return -1;
147
148                         sprintf (response + length, "\\challenge\\%s", string);
149                 }
150
151                 SZ_Clear (&net_message);
152                 MSG_WriteLong (&net_message, -1);
153                 MSG_WriteString (&net_message, response);
154
155                 return net_message.cursize - 1;
156         }
157
158         return 0;
159 }
160
161
162 /*
163 ====================
164 Master_Init
165
166 Initialize the code that handles master server requests and reponses
167 ====================
168 */
169 void Master_Init (void)
170 {
171         unsigned int ind;
172         for (ind = 0; ind < sizeof (sv_masters) / sizeof (sv_masters[0]); ind++)
173                 Cvar_RegisterVariable (&sv_masters[ind]);
174 }
175
176
177 /*
178 ====================
179 Master_ParseServerList
180
181 Parse getserverResponse messages
182 ====================
183 */
184 void Master_ParseServerList (net_landriver_t* dfunc)
185 {
186         int control;
187         qbyte* servers;
188         int ipaddr;
189         struct qsockaddr svaddr;
190         char ipstring [32];
191
192         if (net_message.cursize < sizeof(int))
193                 return;
194
195         // is the cache full?
196         if (hostCacheCount == HOSTCACHESIZE)
197                 return;
198
199         MSG_BeginReading ();
200         control = BigLong(*((int *)net_message.data));
201         MSG_ReadLong();
202         if (control != -1)
203                 return;
204
205         if (strncmp (net_message.data + 4, "getserversResponse\\", 19)) 
206                 return;
207
208         // Skip the next 18 bytes
209         MSG_ReadLong(); MSG_ReadLong(); MSG_ReadLong(); MSG_ReadLong();
210         MSG_ReadShort(); MSG_ReadByte();
211
212         servers = alloca (net_message.cursize - 23);
213         memcpy (servers , net_message.data + 23, net_message.cursize - 23);
214
215         // Extract the IP addresses
216         while ((ipaddr = (servers[3] << 24) + (servers[2] << 16) + (servers[1] << 8) + servers[0]) != -1)
217         {
218                 int port = (servers[5] << 8) + servers[4];
219
220                 if (port == -1 || port == 0)
221                         break;
222
223                 port = ((port >> 8) & 0xFF) + ((port & 0xFF) << 8);
224                 sprintf (ipstring, "%u.%u.%u.%u:%hu",
225                                         ipaddr & 0xFF, (ipaddr >> 8) & 0xFF,
226                                         (ipaddr >> 16) & 0xFF, ipaddr >> 24,
227                                         port);
228                 dfunc->GetAddrFromName (ipstring, &svaddr);
229
230                 Con_DPrintf("Requesting info from server %s\n", ipstring);
231                 // Send a request at this address
232                 SZ_Clear(&net_message);
233                 MSG_WriteLong(&net_message, 0);  // save space for the header, filled in later
234                 MSG_WriteByte(&net_message, CCREQ_SERVER_INFO);
235                 MSG_WriteString(&net_message, "QUAKE");
236                 MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
237                 *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
238                 dfunc->Write(dfunc->controlSock, net_message.data, net_message.cursize, &svaddr);
239                 SZ_Clear(&net_message);
240
241                 if (servers[6] != '\\')
242                         break;
243                 servers += 7;
244         }
245 }