1 // misc useful functions used by the server
2
3 #include "cube.h"
4
5 #ifdef _DEBUG
6 bool protocoldbg = false;
protocoldebug(bool enable)7 void protocoldebug(bool enable) { protocoldbg = enable; }
8 #define DEBUGCOND (protocoldbg)
9 #endif
10
11 // all network traffic is in 32bit ints, which are then compressed using the following simple scheme (assumes that most values are small).
12
13 template<class T>
putint_(T & p,int n)14 static inline void putint_(T &p, int n)
15 {
16 DEBUGVAR(n);
17 if(n<128 && n>-127) p.put(n);
18 else if(n<0x8000 && n>=-0x8000) { p.put(0x80); p.put(n); p.put(n>>8); }
19 else { p.put(0x81); p.put(n); p.put(n>>8); p.put(n>>16); p.put(n>>24); }
20 }
putint(ucharbuf & p,int n)21 void putint(ucharbuf &p, int n) { putint_(p, n); }
putint(packetbuf & p,int n)22 void putint(packetbuf &p, int n) { putint_(p, n); }
putint(vector<uchar> & p,int n)23 void putint(vector<uchar> &p, int n) { putint_(p, n); }
24
getint(ucharbuf & p)25 int getint(ucharbuf &p)
26 {
27 int c = (char)p.get();
28 if(c==-128) { int n = p.get(); n |= char(p.get())<<8; DEBUGVAR(n); return n; }
29 else if(c==-127) { int n = p.get(); n |= p.get()<<8; n |= p.get()<<16; n |= (p.get()<<24); DEBUGVAR(n); return n; }
30 else
31 {
32 DEBUGVAR(c);
33 return c;
34 }
35 }
36
37 // much smaller encoding for unsigned integers up to 28 bits, but can handle signed
38 template<class T>
putuint_(T & p,int n)39 static inline void putuint_(T &p, int n)
40 {
41 DEBUGVAR(n);
42 if(n < 0 || n >= (1<<21))
43 {
44 p.put(0x80 | (n & 0x7F));
45 p.put(0x80 | ((n >> 7) & 0x7F));
46 p.put(0x80 | ((n >> 14) & 0x7F));
47 p.put(n >> 21);
48 }
49 else if(n < (1<<7)) p.put(n);
50 else if(n < (1<<14))
51 {
52 p.put(0x80 | (n & 0x7F));
53 p.put(n >> 7);
54 }
55 else
56 {
57 p.put(0x80 | (n & 0x7F));
58 p.put(0x80 | ((n >> 7) & 0x7F));
59 p.put(n >> 14);
60 }
61 }
putuint(ucharbuf & p,int n)62 void putuint(ucharbuf &p, int n) { putuint_(p, n); }
putuint(packetbuf & p,int n)63 void putuint(packetbuf &p, int n) { putuint_(p, n); }
putuint(vector<uchar> & p,int n)64 void putuint(vector<uchar> &p, int n) { putuint_(p, n); }
65
getuint(ucharbuf & p)66 int getuint(ucharbuf &p)
67 {
68 int n = p.get();
69 if(n & 0x80)
70 {
71 n += (p.get() << 7) - 0x80;
72 if(n & (1<<14)) n += (p.get() << 14) - (1<<14);
73 if(n & (1<<21)) n += (p.get() << 21) - (1<<21);
74 if(n & (1<<28)) n |= 0xF0000000;
75 }
76 DEBUGVAR(n);
77 return n;
78 }
79
80 template<class T>
putfloat_(T & p,float f)81 static inline void putfloat_(T &p, float f)
82 {
83 lilswap(&f, 1);
84 p.put((uchar *)&f, sizeof(float));
85 }
putfloat(ucharbuf & p,float f)86 void putfloat(ucharbuf &p, float f) { putfloat_(p, f); }
putfloat(packetbuf & p,float f)87 void putfloat(packetbuf &p, float f) { putfloat_(p, f); }
putfloat(vector<uchar> & p,float f)88 void putfloat(vector<uchar> &p, float f) { putfloat_(p, f); }
89
getfloat(ucharbuf & p)90 float getfloat(ucharbuf &p)
91 {
92 float f;
93 p.get((uchar *)&f, sizeof(float));
94 return lilswap(f);
95 }
96
97 template<class T>
sendstring_(const char * text,T & p)98 static inline void sendstring_(const char *text, T &p)
99 {
100 const char *t = text;
101 if(t) { while(*t) putint(p, *t++); }
102 putint(p, 0);
103 DEBUGVAR(text);
104 }
sendstring(const char * t,ucharbuf & p)105 void sendstring(const char *t, ucharbuf &p) { sendstring_(t, p); }
sendstring(const char * t,packetbuf & p)106 void sendstring(const char *t, packetbuf &p) { sendstring_(t, p); }
sendstring(const char * t,vector<uchar> & p)107 void sendstring(const char *t, vector<uchar> &p) { sendstring_(t, p); }
108
getstring(char * text,ucharbuf & p,int len)109 void getstring(char *text, ucharbuf &p, int len)
110 {
111 char *t = text;
112 do
113 {
114 if(t>=&text[len]) { text[len-1] = 0; return; }
115 if(!p.remaining()) { *t = 0; return; }
116 *t = getint(p);
117 }
118 while(*t++);
119 DEBUGVAR(text);
120 }
121
122 #define follower(x) (((x) & 0xc0) == 0x80)
123
getutf8char(const uchar * & s)124 int getutf8char(const uchar *&s)
125 {
126 int res = *s++;
127 if(res < 0x80) return res;
128 if((res & 0xe0) == 0xc0)
129 { // 2-Byte
130 if(follower(s[0]))
131 {
132 res &= 0x1f;
133 res <<= 6;
134 res |= *s++ & 0x3f;
135 return res;
136 }
137 return -1;
138 }
139 if((res & 0xf0) == 0xe0)
140 { // 3-Byte
141 if(follower(s[0]) && follower(s[1]))
142 {
143 res &= 0x0f;
144 loopi(2)
145 {
146 res <<= 6;
147 res |= *s++ & 0x3f;
148 }
149 return res;
150 }
151 return -1;
152 }
153 if((res & 0xf8) == 0xf0)
154 { // 4-Byte
155 if(follower(s[0]) && follower(s[1]) && follower(s[2]))
156 {
157 res &= 0x07;
158 loopi(3)
159 {
160 res <<= 6;
161 res |= *s++ & 0x3f;
162 }
163 return res;
164 }
165 return -1;
166 }
167 return -1;
168 }
169
pututf8char(uchar * & d,int s)170 int pututf8char(uchar *&d, int s)
171 {
172 if(s < 0 || s > 0x1fffff) return 0;
173 if(s < 0x80)
174 {
175 *d++ = s;
176 return 1;
177 }
178 if(s < 0x800)
179 {
180 *d++ = ((s >> 6) & 0x1f) | 0xc0;
181 *d++ = (s & 0x3f) | 0x80;
182 return 2;
183 }
184 if(s < 0x10000)
185 {
186 *d++ = ((s >> 12) & 0x0f) | 0xe0;
187 *d++ = ((s >> 6) & 0x3f) | 0x80;
188 *d++ = (s & 0x3f) | 0x80;
189 return 3;
190 }
191 *d++ = ((s >> 18) & 0x07) | 0xf0;
192 *d++ = ((s >> 12) & 0x3f) | 0x80;
193 *d++ = ((s >> 6) & 0x3f) | 0x80;
194 *d++ = (s & 0x3f) | 0x80;
195 return 4;
196 }
197
filtertext(char * dst,const char * src,int whitespace,int len)198 void filtertext(char *dst, const char *src, int whitespace, int len)
199 { // whitespace: no whitespace at all (0), blanks only (1), blanks & newline (2), spaces for underlines (-1)
200 for(int c = *src; c; c = *++src)
201 {
202 c &= 0x7F; // 7-bit ascii
203 if(c == '\f')
204 {
205 if(!*++src) break;
206 continue;
207 }
208 if(isspace(c) ? whitespace && (whitespace>1 || c == ' '): isprint(c))
209 {
210 if ( whitespace < 0 && c == ' ' ) c = '_';
211 *dst++ = c;
212 if(!--len) break;
213 }
214 }
215 *dst = '\0';
216 }
217
filterrichtext(char * dst,const char * src,int len)218 void filterrichtext(char *dst, const char *src, int len)
219 {
220 int b, c;
221 unsigned long ul;
222 for(c = *src; c; c = *++src)
223 {
224 c &= 0x7F; // 7-bit ascii
225 if(c == '\\')
226 {
227 b = 0;
228 c = *++src;
229 switch(c)
230 {
231 case '\0': --src; continue;
232 case 'f': c = '\f'; break;
233 case 'n': c = '\n'; break;
234 case 'x':
235 b = 16;
236 c = *++src;
237 default:
238 if(isspace(c)) continue;
239 if(b == 0 && !isdigit(c)) break;
240 ul = strtoul(src, (char **) &src, b);
241 --src;
242 c = (int) ul;
243 if(!c) continue; // number conversion failed
244 break;
245 }
246 }
247 *dst++ = c;
248 if(!--len || !*src) break;
249 }
250 *dst = '\0';
251 }
252
filterservdesc(char * dst,const char * src,int len)253 void filterservdesc(char *dst, const char *src, int len)
254 { // only colors and spaces allowed
255 for(int c = *src; c; c = *++src)
256 {
257 c &= 0x7F; // 7-bit ascii
258 if((!isspace(c) && isprint(c)) || c == ' ' || c == '\f')
259 {
260 *dst++ = c;
261 if(!--len) break;
262 }
263 }
264 *dst = '\0';
265 }
266
filterlang(char * d,const char * s)267 void filterlang(char *d, const char *s)
268 {
269 if(strlen(s) == 2)
270 {
271 loopi(2) d[i] = tolower(s[i]);
272 d[2] = '\0';
273 if(islower(d[0]) && islower(d[1])) return;
274 }
275 *d = '\0';
276 }
277
trimtrailingwhitespace(char * s)278 void trimtrailingwhitespace(char *s)
279 {
280 for(int n = (int)strlen(s) - 1; n >= 0 && isspace(s[n]); n--)
281 s[n] = '\0';
282 }
283
cutcolorstring(char * text,int len)284 void cutcolorstring(char *text, int len)
285 { // limit string length, ignore color codes
286 while(*text)
287 {
288 if(*text == '\f' && text[1]) text++;
289 else len--;
290 if(len < 0) { *text = '\0'; break; }
291 text++;
292 }
293 }
294
295 const char *modefullnames[] =
296 {
297 "demo playback",
298 "team deathmatch", "coopedit", "deathmatch", "survivor",
299 "team survivor", "ctf", "pistol frenzy", "bot team deathmatch", "bot deathmatch", "last swiss standing",
300 "one shot, one kill", "team one shot, one kill", "bot one shot, one kill", "hunt the flag", "team keep the flag",
301 "keep the flag", "team pistol frenzy", "team last swiss standing", "bot pistol frenzy", "bot last swiss standing", "bot team survivor", "bot team one shot, one kill"
302 };
303
304 const char *modeacronymnames[] =
305 {
306 "DEMO",
307 "TDM", "coop", "DM", "SURV", "TSURV", "CTF", "PF", "BTDM", "BDM", "LSS",
308 "OSOK", "TOSOK", "BOSOK", "HTF", "TKTF", "KTF", "TPF", "TLSS", "BPF", "BLSS", "BTSURV", "BTOSOK"
309 };
310
311 const char *voteerrors[] = { "voting is currently disabled", "there is already a vote pending", "already voted", "can't vote that often", "this vote is not allowed in the current environment (singleplayer/multiplayer)", "no permission", "invalid vote", "server denied your call", "the next map/mode is already set" };
312 const char *mmfullnames[] = { "open", "private", "match" };
313
fullmodestr(int n)314 const char *fullmodestr(int n) { return (n>=-1 && size_t(n+1) < sizeof(modefullnames)/sizeof(modefullnames[0])) ? modefullnames[n+1] : "unknown"; }
acronymmodestr(int n)315 const char *acronymmodestr(int n) { return (n>=-1 && size_t(n+1) < sizeof(modeacronymnames)/sizeof(modeacronymnames[0])) ? modeacronymnames[n+1] : "UNK"; } // 'n/a' bad on *nix filesystem (demonameformat)
modestr(int n,bool acronyms)316 const char *modestr(int n, bool acronyms) { return acronyms ? acronymmodestr (n) : fullmodestr(n); }
voteerrorstr(int n)317 const char *voteerrorstr(int n) { return (n>=0 && (size_t)n < sizeof(voteerrors)/sizeof(voteerrors[0])) ? voteerrors[n] : "unknown"; }
mmfullname(int n)318 const char *mmfullname(int n) { return (n>=0 && n < MM_NUM) ? mmfullnames[n] : "unknown"; }
319
defaultgamelimit(int gamemode)320 int defaultgamelimit(int gamemode) { return m_teammode ? 15 : 10; }
321
322 static const int msgsizes[] = // size inclusive message token, 0 for variable or not-checked sizes
323 {
324 SV_SERVINFO, 5, SV_WELCOME, 2, SV_INITCLIENT, 0, SV_POS, 0, SV_POSC, 0, SV_POSN, 0, SV_TEXT, 0, SV_TEAMTEXT, 0, SV_TEXTME, 0, SV_TEAMTEXTME, 0, SV_TEXTPRIVATE, 0,
325 SV_SOUND, 2, SV_VOICECOM, 2, SV_VOICECOMTEAM, 2, SV_CDIS, 2,
326 SV_SHOOT, 0, SV_EXPLODE, 0, SV_SUICIDE, 1, SV_AKIMBO, 2, SV_RELOAD, 3, SV_AUTHT, 0, SV_AUTHREQ, 0, SV_AUTHTRY, 0, SV_AUTHANS, 0, SV_AUTHCHAL, 0,
327 SV_GIBDIED, 5, SV_DIED, 5, SV_GIBDAMAGE, 7, SV_DAMAGE, 7, SV_HITPUSH, 6, SV_SHOTFX, 6, SV_THROWNADE, 8,
328 SV_TRYSPAWN, 1, SV_SPAWNSTATE, 23, SV_SPAWN, 3, SV_SPAWNDENY, 2, SV_FORCEDEATH, 2, SV_RESUME, 0,
329 SV_DISCSCORES, 0, SV_TIMEUP, 3, SV_EDITENT, 10, SV_ITEMACC, 2,
330 SV_MAPCHANGE, 0, SV_ITEMSPAWN, 2, SV_ITEMPICKUP, 2,
331 SV_PING, 2, SV_PONG, 2, SV_CLIENTPING, 2, SV_GAMEMODE, 2,
332 SV_EDITMODE, 2, SV_EDITH, 7, SV_EDITT, 7, SV_EDITS, 6, SV_EDITD, 6, SV_EDITE, 6, SV_NEWMAP, 2,
333 SV_SENDMAP, 0, SV_RECVMAP, 1, SV_REMOVEMAP, 0,
334 SV_SERVMSG, 0, SV_ITEMLIST, 0, SV_WEAPCHANGE, 2, SV_PRIMARYWEAP, 2,
335 SV_FLAGACTION, 3, SV_FLAGINFO, 0, SV_FLAGMSG, 0, SV_FLAGCNT, 3,
336 SV_ARENAWIN, 2,
337 SV_SETADMIN, 0, SV_SERVOPINFO, 3,
338 SV_CALLVOTE, 0, SV_CALLVOTESUC, 1, SV_CALLVOTEERR, 2, SV_VOTE, 2, SV_VOTERESULT, 2,
339 SV_SETTEAM, 3, SV_TEAMDENY, 2, SV_SERVERMODE, 2,
340 SV_IPLIST, 0,
341 SV_LISTDEMOS, 1, SV_SENDDEMOLIST, 0, SV_GETDEMO, 2, SV_SENDDEMO, 0, SV_DEMOPLAYBACK, 3,
342 SV_CONNECT, 0,
343 SV_SWITCHNAME, 0, SV_SWITCHSKIN, 0, SV_SWITCHTEAM, 0,
344 SV_CLIENT, 0,
345 SV_EXTENSION, 0,
346 SV_MAPIDENT, 3, SV_HUDEXTRAS, 2, SV_POINTS, 0,
347 -1
348 };
349
msgsizelookup(int msg)350 int msgsizelookup(int msg)
351 {
352 static int sizetable[SV_NUM] = { -1 };
353 if(sizetable[0] < 0)
354 {
355 memset(sizetable, -1, sizeof(sizetable));
356 for(const int *p = msgsizes; *p >= 0; p += 2) sizetable[p[0]] = p[1];
357 }
358 return msg >= 0 && msg < SV_NUM ? sizetable[msg] : -1;
359 }
360
361