1 /*
2 * qstat
3 * by Steve Jankowski
4 *
5 * Cube 2 / Sauerbraten protocol
6 * Copyright 2011 NoisyB
7 *
8 * Licensed under the Artistic License, see LICENSE.txt for license terms
9 */
10 #include <string.h>
11 #include <stdlib.h>
12 #include <stdio.h>
13
14 #include "debug.h"
15 #include "qstat.h"
16 #include "packet_manip.h"
17
18 struct offset {
19 unsigned char *d;
20 int pos;
21 int len;
22 };
23
24 //#define SB_MASTER_SERVER "http://sauerbraten.org/masterserver/retrieve.do?item=list"
25 #define SB_PROTOCOL 258
26 #define MIN(a, b) ((a) < (b) ? (a) : (b))
27 #define MAX_ATTR 255
28 #define MAX_STRING 1024
29
30 static int
getint(struct offset * d)31 getint(struct offset *d)
32 {
33 int val = 0;
34
35 if (d->pos >= d->len) {
36 return (0);
37 }
38
39 val = d->d[d->pos++] & 0xff;// 8 bit value
40
41 // except...
42 if ((val == 0x80) && (d->pos < d->len - 2)) { // 16 bit value
43 val = (d->d[d->pos++] & 0xff);
44 val |= (d->d[d->pos++] & 0xff) << 8;
45 } else if ((val == 0x81) && (d->pos < d->len - 4)) { // 32 bit value
46 val = (d->d[d->pos++] & 0xff);
47 val |= (d->d[d->pos++] & 0xff) << 8;
48 val |= (d->d[d->pos++] & 0xff) << 16;
49 val |= (d->d[d->pos++] & 0xff) << 24;
50 }
51
52 return (val);
53 }
54
55
56 static char *
getstr(char * dest,int dest_len,struct offset * d)57 getstr(char *dest, int dest_len, struct offset *d)
58 {
59 int len = 0;
60
61 if (d->pos >= d->len) {
62 return (NULL);
63 }
64
65 len = MIN(dest_len, d->len - d->pos);
66 strncpy(dest, (const char *)d->d + d->pos, len)[len - 1] = 0;
67 d->pos += strlen(dest) + 1;
68
69 return (dest);
70 }
71
72
73 static char *
sb_getversion_s(int n)74 sb_getversion_s(int n)
75 {
76 static char *version_s[] =
77 {
78 "Justice",
79 "CTF",
80 "Assassin",
81 "Summer",
82 "Spring",
83 "Gui",
84 "Water",
85 "Normalmap",
86 "Sp",
87 "Occlusion",
88 "Shader",
89 "Physics",
90 "Mp",
91 "",
92 "Agc",
93 "Quakecon",
94 "Independence"
95 };
96
97 n = SB_PROTOCOL - n;
98 if ((n >= 0) && ((size_t)n < sizeof(version_s) / sizeof(version_s[0]))) {
99 return (version_s[n]);
100 }
101 return ("unknown");
102 }
103
104
105 static char *
sb_getmode_s(int n)106 sb_getmode_s(int n)
107 {
108 static char *mode_s[] =
109 {
110 "slowmo SP",
111 "slowmo DMSP",
112 "demo",
113 "SP",
114 "DMSP",
115 "ffa/default",
116 "coopedit",
117 "ffa/duel",
118 "teamplay",
119 "instagib",
120 "instagib team",
121 "efficiency",
122 "efficiency team",
123 "insta arena",
124 "insta clan arena",
125 "tactics arena",
126 "tactics clan arena",
127 "capture",
128 "insta capture",
129 "regen capture",
130 "assassin",
131 "insta assassin",
132 "ctf",
133 "insta ctf"
134 };
135
136 n += 6;
137 if ((n >= 0) && ((size_t)n < sizeof(mode_s) / sizeof(mode_s[0]))) {
138 return (mode_s[n]);
139 }
140 return ("unknown");
141 }
142
143
144 query_status_t
send_cube2_request_packet(struct qserver * server)145 send_cube2_request_packet(struct qserver *server)
146 {
147 return (send_packet(server, server->type->status_packet, server->type->status_len));
148 }
149
150
151 query_status_t
deal_with_cube2_packet(struct qserver * server,char * rawpkt,int pktlen)152 deal_with_cube2_packet(struct qserver *server, char *rawpkt, int pktlen)
153 {
154 // skip unimplemented ack, crc, etc
155 int i;
156 int numattr;
157 int attr[MAX_ATTR];
158 char buf[MAX_STRING];
159
160 enum {
161 MM_OPEN = 0,
162 MM_VETO,
163 MM_LOCKED,
164 MM_PRIVATE
165 };
166
167 struct offset d;
168 d.d = (unsigned char *)rawpkt;
169 d.pos = 0;
170 d.len = pktlen;
171
172 server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
173 getint(&d); // we have the ping already
174 server->num_players = getint(&d);
175 numattr = getint(&d);
176 for (i = 0; i < numattr && i < MAX_ATTR; i++) {
177 attr[i] = getint(&d);
178 }
179
180 server->protocol_version = attr[0];
181
182 sprintf(buf, "%d %s", attr[0], sb_getversion_s(attr[0]));
183 add_rule(server, "version", buf, NO_FLAGS);
184
185 sprintf(buf, "%d %s", attr[1], sb_getmode_s(attr[1]));
186 add_rule(server, "mode", buf, NO_FLAGS);
187
188 sprintf(buf, "%d", attr[2]);
189 add_rule(server, "seconds_left", buf, NO_FLAGS);
190
191 server->max_players = attr[3];
192
193 switch (attr[5]) {
194 case MM_OPEN:
195 sprintf(buf, "%d open", attr[5]);
196 break;
197
198 case MM_VETO:
199 sprintf(buf, "%d veto", attr[5]);
200 break;
201
202 case MM_LOCKED:
203 sprintf(buf, "%d locked", attr[5]);
204 break;
205
206 case MM_PRIVATE:
207 sprintf(buf, "%d private", attr[5]);
208 break;
209
210 default:
211 sprintf(buf, "%d unknown", attr[5]);
212 }
213 add_rule(server, "mm", buf, NO_FLAGS);
214
215 for (i = 0; i < numattr && i < MAX_ATTR; i++) {
216 char buf2[MAX_STRING];
217 sprintf(buf, "attr%d", i);
218 sprintf(buf2, "%d", attr[i]);
219 add_rule(server, buf, buf2, NO_FLAGS);
220 }
221
222 getstr(buf, MAX_STRING, &d);
223 server->map_name = strdup(buf);
224 getstr(buf, MAX_STRING, &d);
225 server->server_name = strdup(buf);
226
227 return (DONE_FORCE);
228 }
229