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