1
// misc useful functions used by the server
8
bool protocoldbg = false;
9
void protocoldebug(bool enable) { protocoldbg = enable; }
10
#define DEBUGCOND (protocoldbg)
13
// all network traffic is in 32bit ints, which are then compressed using the following simple scheme (assumes that most values are small).
15
void putint(ucharbuf &p, int n)
18
if(n<128 && n>-127) p.put(n);
19
else if(n<0x8000 && n>=-0x8000) { p.put(0x80); p.put(n); p.put(n>>8); }
20
else { p.put(0x81); p.put(n); p.put(n>>8); p.put(n>>16); p.put(n>>24); }
23
int getint(ucharbuf &p)
25
int c = (char)p.get();
26
if(c==-128) { int n = p.get(); n |= char(p.get())<<8; DEBUGVAR(n); return n; }
27
else if(c==-127) { int n = p.get(); n |= p.get()<<8; n |= p.get()<<16; n |= (p.get()<<24); DEBUGVAR(n); return n; }
35
// much smaller encoding for unsigned integers up to 28 bits, but can handle signed
36
void putuint(ucharbuf &p, int n)
39
if(n < 0 || n >= (1<<21))
41
p.put(0x80 | (n & 0x7F));
42
p.put(0x80 | ((n >> 7) & 0x7F));
43
p.put(0x80 | ((n >> 14) & 0x7F));
46
else if(n < (1<<7)) p.put(n);
49
p.put(0x80 | (n & 0x7F));
54
p.put(0x80 | (n & 0x7F));
55
p.put(0x80 | ((n >> 7) & 0x7F));
60
int getuint(ucharbuf &p)
65
n += (p.get() << 7) - 0x80;
66
if(n & (1<<14)) n += (p.get() << 14) - (1<<14);
67
if(n & (1<<21)) n += (p.get() << 21) - (1<<21);
68
if(n & (1<<28)) n |= 0xF0000000;
74
void sendstring(const char *text, ucharbuf &p)
77
while(*t) putint(p, *t++);
82
void getstring(char *text, ucharbuf &p, int len)
87
if(t>=&text[len]) { text[len-1] = 0; return; }
88
if(!p.remaining()) { *t = 0; return; }
95
void filtertext(char *dst, const char *src, int whitespace, int len)
96
{ // whitespace: no whitespace at all (0), blanks only (1), blanks & newline (2)
97
for(int c = *src; c; c = *++src)
99
c &= 0x7F; // 7-bit ascii
102
case '\f': ++src; continue;
104
if(isspace(c) ? whitespace && (whitespace>1 || c == ' ') : isprint(c))
113
void filterrichtext(char *dst, const char *src, int len)
117
for(c = *src; c; c = *++src)
119
c &= 0x7F; // 7-bit ascii
126
case '\0': --src; continue;
127
case 'f': c = '\f'; break;
128
case 'n': c = '\n'; break;
133
if(isspace(c)) continue;
134
if(b == 0 && !isdigit(c)) break;
135
ul = strtoul(src, (char **) &src, b);
138
if(!c) continue; // number conversion failed
143
if(!--len || !*src) break;
148
void filterservdesc(char *dst, const char *src, int len)
149
{ // only colors and spaces allowed
150
for(int c = *src; c; c = *++src)
152
c &= 0x7F; // 7-bit ascii
153
if((!isspace(c) && isprint(c)) || c == ' ' || c == '\f')
162
void cutcolorstring(char *text, int len)
163
{ // limit string length, ignore color codes
166
if(*text == '\f' && text[1]) text++;
168
if(len < 0) { *text = '\0'; break; }
173
const char *modefullnames[] =
176
"team deathmatch", "coopedit", "deathmatch", "survivor",
177
"team survivor", "ctf", "pistol frenzy", "bot team deathmatch", "bot deathmatch", "last swiss standing",
178
"one shot, one kill", "team one shot, one kill", "bot one shot, one kill", "hunt the flag", "team keep the flag", "keep the flag"
181
const char *modeacronymnames[] =
184
"TDM", "coop", "DM", "SURV", "TSURV", "CTF", "PF", "BTDM", "BDM", "LSS",
185
"OSOK", "TOSOK", "BOSOK", "HTF", "TKTF", "KTF"
188
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" };
189
const char *mmfullnames[] = { "open", "private" };
191
const char *fullmodestr(int n) { return (n>=-1 && size_t(n+1) < sizeof(modefullnames)/sizeof(modefullnames[0])) ? modefullnames[n+1] : "unknown"; }
192
const char *acronymmodestr(int n) { return (n>=-1 && size_t(n+1) < sizeof(modeacronymnames)/sizeof(modeacronymnames[0])) ? modeacronymnames[n+1] : "n/a"; }
193
const char *modestr(int n, bool acronyms) { return acronyms ? acronymmodestr (n) : fullmodestr(n); }
194
const char *voteerrorstr(int n) { return (n>=0 && (size_t)n < sizeof(voteerrors)/sizeof(voteerrors[0])) ? voteerrors[n] : "unknown"; }
195
const char *mmfullname(int n) { return (n>=0 && n < MM_NUM) ? mmfullnames[n] : "unknown"; }
197
char msgsizesl[] = // size inclusive message token, 0 for variable or not-checked sizes
199
SV_INITS2C, 5, SV_WELCOME, 2, SV_INITC2S, 0, SV_POS, 0, SV_TEXT, 0, SV_TEAMTEXT, 0, SV_SOUND, 2, SV_VOICECOM, 2, SV_VOICECOMTEAM, 2, SV_CDIS, 2,
200
SV_SHOOT, 0, SV_EXPLODE, 0, SV_SUICIDE, 1, SV_AKIMBO, 2, SV_RELOAD, 3,
201
SV_GIBDIED, 4, SV_DIED, 4, SV_GIBDAMAGE, 6, SV_DAMAGE, 6, SV_HITPUSH, 6, SV_SHOTFX, 9, SV_THROWNADE, 8,
202
SV_TRYSPAWN, 1, SV_SPAWNSTATE, 23, SV_SPAWN, 3, SV_FORCEDEATH, 2, SV_RESUME, 0,
203
SV_TIMEUP, 2, SV_EDITENT, 10, SV_MAPRELOAD, 2, SV_NEXTMAP, 0, SV_ITEMACC, 2,
204
SV_MAPCHANGE, 0, SV_ITEMSPAWN, 2, SV_ITEMPICKUP, 2,
205
SV_PING, 2, SV_PONG, 2, SV_CLIENTPING, 2, SV_GAMEMODE, 2,
206
SV_EDITMODE, 2, SV_EDITH, 7, SV_EDITT, 7, SV_EDITS, 6, SV_EDITD, 6, SV_EDITE, 6, SV_NEWMAP, 2,
207
SV_SENDMAP, 0, SV_RECVMAP, 1, SV_SERVMSG, 0, SV_ITEMLIST, 0, SV_WEAPCHANGE, 2, SV_PRIMARYWEAP, 2,
209
SV_FLAGACTION, 3, SV_FLAGINFO, 0, SV_FLAGMSG, 0, SV_FLAGCNT, 3,
211
SV_SETADMIN, 0, SV_SERVOPINFO, 3,
212
SV_CALLVOTE, 0, SV_CALLVOTESUC, 1, SV_CALLVOTEERR, 2, SV_VOTE, 2, SV_VOTERESULT, 2,
213
SV_FORCETEAM, 3, SV_AUTOTEAM, 2, SV_CHANGETEAM, 1,
214
SV_WHOIS, 2, SV_WHOISINFO, 3,
215
SV_LISTDEMOS, 1, SV_SENDDEMOLIST, 0, SV_GETDEMO, 2, SV_SENDDEMO, 0, SV_DEMOPLAYBACK, 3,
219
SV_SPAWNLIST, 0, SV_FORCENOTIFY, 3,
223
char msgsizelookup(int msg)
225
for(char *p = msgsizesl; *p>=0; p += 2) if(*p==msg) return p[1];
229
const char *genpwdhash(const char *name, const char *pwd, int salt)
232
s_sprintf(temp)("%s %d %s %s %d", pwd, salt, name, pwd, abs(PROTOCOL_VERSION));
234
tiger::hash((uchar *)temp, (int)strlen(temp), hash);
235
s_sprintf(temp)("%llx %llx %llx", hash.chunks[0], hash.chunks[1], hash.chunks[2]);