1 // client processing of the incoming network stream
2
3 #include "cube.h"
4 #include "bot/bot.h"
5
6 VARP(networkdebug, 0, 0, 1);
7 #define DEBUGCOND (networkdebug==1)
8
9 extern bool watchingdemo;
10 extern string clientpassword;
11
12 packetqueue pktlogger;
13
neterr(const char * s)14 void neterr(const char *s)
15 {
16 conoutf("\f3illegal network message (%s)", s);
17
18 // might indicate a client/server communication bug, create error report
19 pktlogger.flushtolog("packetlog.txt");
20 conoutf("\f3wrote a network error report to packetlog.txt, please post this file to the bugtracker now!");
21
22 disconnect();
23 }
24
25 VARP(autogetmap, 0, 1, 1); // only if the client doesn't have that map
26 VARP(autogetnewmaprevisions, 0, 1, 1);
27
28 bool localwrongmap = false;
29 int MA = 0, Hhits = 0; // flowtron: moved here
changemapserv(char * name,int mode,int download,int revision)30 bool changemapserv(char *name, int mode, int download, int revision) // forced map change from the server
31 {
32 MA = Hhits = 0; // reset for checkarea()
33 gamemode = mode;
34 if(m_demo) return true;
35 if(m_coop)
36 {
37 if(!name[0] || !load_world(name)) empty_world(0, true);
38 return true;
39 }
40 else if(player1->state==CS_EDITING) { /*conoutf("SANITY drop from EDITING");*/ toggleedit(true); } // fix stuck-in-editmode bug
41 bool loaded = load_world(name);
42 if(download > 0)
43 {
44 bool revmatch = hdr.maprevision == revision || revision == 0;
45 if(watchingdemo)
46 {
47 if(!revmatch) conoutf(_("%c3demo was recorded on map revision %d, you have map revision %d"), CC, revision, hdr.maprevision);
48 }
49 else
50 {
51 if(securemapcheck(name, false)) return true;
52 bool sizematch = maploaded == download || download < 10;
53 if(loaded && sizematch && revmatch) return true;
54 bool getnewrev = autogetnewmaprevisions && revision > hdr.maprevision;
55 if(autogetmap || getnewrev)
56 {
57 if(!loaded || getnewrev) getmap(); // no need to ask
58 else
59 {
60 defformatstring(msg)("map '%s' revision: local %d, provided by server %d", name, hdr.maprevision, revision);
61 alias("__getmaprevisions", msg);
62 showmenu("getmap");
63 }
64 }
65 else
66 {
67 if(!loaded || download < 10) conoutf(_("\"getmap\" to download the current map from the server"));
68 else conoutf(_("\"getmap\" to download a %s version of the current map from the server"),
69 revision == 0 ? _("different") : (revision > hdr.maprevision ? _("newer") : _("older")));
70 }
71 }
72 }
73 else return true;
74 return false;
75 }
76
77 // update the position of other clients in the game in our world
78 // don't care if he's in the scenery or other players,
79 // just don't overlap with our client
80
updatepos(playerent * d)81 void updatepos(playerent *d)
82 {
83 const float r = player1->radius+d->radius;
84 const float dx = player1->o.x-d->o.x;
85 const float dy = player1->o.y-d->o.y;
86 const float dz = player1->o.z-d->o.z;
87 const float rz = player1->aboveeye+d->eyeheight;
88 const float fx = (float)fabs(dx), fy = (float)fabs(dy), fz = (float)fabs(dz);
89 if(fx<r && fy<r && fz<rz && d->state!=CS_DEAD)
90 {
91 if(fx<fy) d->o.y += dy<0 ? r-fy : -(r-fy); // push aside
92 else d->o.x += dx<0 ? r-fx : -(r-fx);
93 }
94 }
95
updatelagtime(playerent * d)96 void updatelagtime(playerent *d)
97 {
98 int lagtime = totalmillis-d->lastupdate;
99 if(lagtime)
100 {
101 if(d->state!=CS_SPAWNING && d->lastupdate) d->plag = (d->plag*5+lagtime)/6;
102 d->lastupdate = totalmillis;
103 }
104 }
105
106 extern void trydisconnect();
107
108 VARP(maxrollremote, 0, 0, 20); // bound remote "roll" values by our maxroll?!
109
parsepositions(ucharbuf & p)110 void parsepositions(ucharbuf &p)
111 {
112 int type;
113 while(p.remaining()) switch(type = getint(p))
114 {
115 case SV_POS: // position of another client
116 case SV_POSC:
117 {
118 int cn, f, g;
119 vec o, vel;
120 float yaw, pitch, roll = 0;
121 bool scoping;//, shoot;
122 if(type == SV_POSC)
123 {
124 bitbuf<ucharbuf> q(p);
125 cn = q.getbits(5);
126 int usefactor = q.getbits(2) + 7;
127 o.x = q.getbits(usefactor + 4) / DMF;
128 o.y = q.getbits(usefactor + 4) / DMF;
129 yaw = q.getbits(9) * 360.0f / 512;
130 pitch = (q.getbits(8) - 128) * 90.0f / 127;
131 roll = !q.getbits(1) ? (q.getbits(6) - 32) * 20.0f / 31 : 0.0f;
132 if(!q.getbits(1))
133 {
134 vel.x = (q.getbits(4) - 8) / DVELF;
135 vel.y = (q.getbits(4) - 8) / DVELF;
136 vel.z = (q.getbits(4) - 8) / DVELF;
137 }
138 else vel.x = vel.y = vel.z = 0.0f;
139 f = q.getbits(8);
140 int negz = q.getbits(1);
141 int full = q.getbits(1);
142 int s = q.rembits();
143 if(s < 3) s += 8;
144 if(full) s = 11;
145 int z = q.getbits(s);
146 if(negz) z = -z;
147 o.z = z / DMF;
148 scoping = ( q.getbits(1) ? true : false );
149 q.getbits(1);//shoot = ( q.getbits(1) ? true : false );
150 }
151 else
152 {
153 cn = getint(p);
154 o.x = getuint(p)/DMF;
155 o.y = getuint(p)/DMF;
156 o.z = getuint(p)/DMF;
157 yaw = (float)getuint(p);
158 pitch = (float)getint(p);
159 g = getuint(p);
160 if ((g>>3) & 1) roll = (float)(getint(p)*20.0f/125.0f);
161 if (g & 1) vel.x = getint(p)/DVELF; else vel.x = 0;
162 if ((g>>1) & 1) vel.y = getint(p)/DVELF; else vel.y = 0;
163 if ((g>>2) & 1) vel.z = getint(p)/DVELF; else vel.z = 0;
164 scoping = ( (g>>4) & 1 ? true : false );
165 //shoot = ( (g>>5) & 1 ? true : false ); // we are not using this yet
166 f = getuint(p);
167 }
168 int seqcolor = (f>>6)&1;
169 playerent *d = getclient(cn);
170 if(!d || seqcolor!=(d->lifesequence&1)) continue;
171 vec oldpos(d->o);
172 float oldyaw = d->yaw, oldpitch = d->pitch;
173 loopi(3)
174 {
175 float dr = o.v[i] - d->o.v[i] + ( i == 2 ? d->eyeheight : 0);
176 if ( !dr ) d->vel.v[i] = 0.0f;
177 else if ( d->vel.v[i] ) d->vel.v[i] = dr * 0.05f + d->vel.v[i] * 0.95f;
178 d->vel.v[i] += vel.v[i];
179 if ( i==2 && d->onfloor && d->vel.v[i] < 0.0f ) d->vel.v[i] = 0.0f;
180 }
181 d->o = o;
182 d->o.z += d->eyeheight;
183 d->yaw = yaw;
184 d->pitch = pitch;
185 if(d->weaponsel->type == GUN_SNIPER)
186 {
187 sniperrifle *sr = (sniperrifle *)d->weaponsel;
188 sr->scoped = d->scoping = scoping;
189 }
190 d->roll = roll;
191 d->strafe = (f&3)==3 ? -1 : f&3;
192 f >>= 2;
193 d->move = (f&3)==3 ? -1 : f&3;
194 f >>= 2;
195 d->onfloor = f&1;
196 f >>= 1;
197 d->onladder = f&1;
198 f >>= 2;
199 d->last_pos = totalmillis;
200 updatecrouch(d, f&1);
201 updatepos(d);
202 updatelagtime(d);
203 extern int smoothmove, smoothdist;
204 if(d->state==CS_DEAD)
205 {
206 d->resetinterp();
207 d->smoothmillis = 0;
208 }
209 else if(smoothmove && d->smoothmillis>=0 && oldpos.dist(d->o) < smoothdist)
210 {
211 d->newpos = d->o;
212 d->newpos.z -= d->eyeheight;
213 d->newyaw = d->yaw;
214 d->newpitch = d->pitch;
215 d->o = oldpos;
216 d->yaw = oldyaw;
217 d->pitch = oldpitch;
218 oldpos.z -= d->eyeheight;
219 (d->deltapos = oldpos).sub(d->newpos);
220 d->deltayaw = oldyaw - d->newyaw;
221 if(d->deltayaw > 180) d->deltayaw -= 360;
222 else if(d->deltayaw < -180) d->deltayaw += 360;
223 d->deltapitch = oldpitch - d->newpitch;
224 d->smoothmillis = lastmillis;
225 }
226 else d->smoothmillis = 0;
227 if(d->state==CS_LAGGED || d->state==CS_SPAWNING) d->state = CS_ALIVE;
228 // when playing a demo spectate first player we know about
229 if(player1->isspectating() && player1->spectatemode==SM_NONE) togglespect();
230 extern void clamproll(physent *pl);
231 if(maxrollremote) clamproll((physent *) d);
232 break;
233 }
234
235 default:
236 neterr("type");
237 return;
238 }
239 }
240
241 extern int checkarea(int maplayout_factor, char *maplayout);
242 char *mlayout = NULL;
243 int Mv = 0, Ma = 0, F2F = 1000 * MINFF; // moved up:, MA = 0;
244 float Mh = 0;
245 extern int connected;
246 extern int lastpm;
247 extern bool noflags;
248 bool item_fail = false;
249 int map_quality = MAP_IS_EDITABLE;
250
251 /// TODO: many functions and variables are redundant between client and server... someone should redo the entire server code and unify client and server.
good_map()252 bool good_map() // call this function only at startmap
253 {
254 if (mlayout) MA = checkarea(sfactor, mlayout);
255
256 F2F = 1000 * MINFF;
257 if(m_flags)
258 {
259 flaginfo &f0 = flaginfos[0];
260 flaginfo &f1 = flaginfos[1];
261 #define DIST(x) (f0.pos.x - f1.pos.x)
262 F2F = (!numflagspawn[0] || !numflagspawn[1]) ? 1000 * MINFF : DIST(x)*DIST(x)+DIST(y)*DIST(y);
263 #undef DIST
264 }
265
266 item_fail = false;
267 loopv(ents)
268 {
269 entity &e1 = ents[i];
270 if (e1.type < I_CLIPS || e1.type > I_AKIMBO) continue;
271 float density = 0, hdensity = 0;
272 loopvj(ents)
273 {
274 entity &e2 = ents[j];
275 if (e2.type < I_CLIPS || e2.type > I_AKIMBO || i == j) continue;
276 #define DIST(x) (e1.x - e2.x)
277 #define DIST_ATT ((e1.z + e1.attr1) - (e2.z + e2.attr1))
278 float r2 = DIST(x)*DIST(x) + DIST(y)*DIST(y) + DIST_ATT*DIST_ATT;
279 #undef DIST_ATT
280 #undef DIST
281 if ( r2 == 0.0f ) { conoutf("\f3MAP CHECK FAIL: Items too close %s %s (%hd,%hd)", entnames[e1.type], entnames[e2.type],e1.x,e1.y); item_fail = true; break; }
282 r2 = 1/r2;
283 if (r2 < 0.0025f) continue;
284 if (e1.type != e2.type)
285 {
286 hdensity += r2;
287 continue;
288 }
289 density += r2;
290 }
291 if ( hdensity > 0.5f ) { conoutf("\f3MAP CHECK FAIL: Items too close %s %.2f (%hd,%hd)", entnames[e1.type],hdensity,e1.x,e1.y); item_fail = true; break; }
292 switch(e1.type)
293 {
294 #define LOGTHISSWITCH(X) if( density > X ) { conoutf("\f3MAP CHECK FAIL: Items too close %s %.2f (%hd,%hd)", entnames[e1.type],density,e1.x,e1.y); item_fail = true; break; }
295 case I_CLIPS:
296 case I_HEALTH: LOGTHISSWITCH(0.24f); break;
297 case I_AMMO: LOGTHISSWITCH(0.04f); break;
298 case I_HELMET: LOGTHISSWITCH(0.02f); break;
299 case I_ARMOUR:
300 case I_GRENADE:
301 case I_AKIMBO: LOGTHISSWITCH(0.005f); break;
302 default: break;
303 #undef LOGTHISSWITCH
304 }
305 }
306
307 map_quality = (!item_fail && F2F > MINFF && MA < MAXMAREA && Mh < MAXMHEIGHT && Hhits < MAXHHITS) ? MAP_IS_GOOD : MAP_IS_BAD;
308 if ( (!connected || gamemode == GMODE_COOPEDIT) && map_quality == MAP_IS_BAD ) map_quality = MAP_IS_EDITABLE;
309 return map_quality > 0;
310 }
311
312 VARP(hudextras, 0, 0, 3);
313
314 int teamworkid = -1;
315
showhudextras(char hudextras,char value)316 void showhudextras(char hudextras, char value){
317 void (*outf)(const char *s, ...) = (hudextras > 1 ? hudoutf : conoutf);
318 bool caps = hudextras < 3 ? false : true;
319 switch(value)
320 {
321 case HE_COMBO:
322 case HE_COMBO2:
323 case HE_COMBO3:
324 case HE_COMBO4:
325 case HE_COMBO5:
326 {
327 int n = value - HE_COMBO;
328 if (n > 3) outf("\f3%s",strcaps("monster combo!!!",caps)); // I expect to never see this one
329 else if (!n) outf("\f5%s",strcaps("combo", caps));
330 else outf("\f5%s x%d",strcaps("multi combo", caps),n+1);
331 break;
332 }
333 case HE_TEAMWORK:
334 outf("\f5%s",strcaps("teamwork done", caps)); break;
335 case HE_FLAGDEFENDED:
336 outf("\f5%s",strcaps("you defended the flag", caps)); break;
337 case HE_FLAGCOVERED:
338 outf("\f5%s",strcaps("you covered the flag", caps)); break;
339 case HE_COVER:
340 if (teamworkid >= 0)
341 {
342 playerent *p = getclient(teamworkid);
343 if (!p || p == player1) teamworkid = -1;
344 else outf("\f5you covered %s",p->name); break;
345 }
346 default:
347 {
348 if (value >= HE_NUM)
349 {
350 teamworkid = value - HE_NUM;
351 playerent *p = getclient(teamworkid);
352 if (!p || p == player1) teamworkid = -1;
353 else outf("\f4you replied to %s",p->name);
354 }
355 else outf("\f3Update your client!");
356 break;
357 }
358 }
359 #undef SSPAM
360 }
361
362 int lastspawn = 0;
363
onCallVote(int type,int vcn,char * text,char * a)364 void onCallVote(int type, int vcn, char *text, char *a)
365 {
366 if(identexists("onCallVote"))
367 {
368 defformatstring(runas)("%s %d %d [%s] [%s]", "onCallVote", type, vcn, text, a);
369 execute(runas);
370 }
371 }
372
onChangeVote(int mod,int id)373 void onChangeVote(int mod, int id)
374 {
375 if(identexists("onChangeVote"))
376 {
377 defformatstring(runas)("%s %d %d", "onChangeVote", mod, id);
378 execute(runas);
379 }
380 }
381
382 VARP(voicecomsounds, 0, 1, 2);
383 bool medals_arrived=0;
384 medalsst a_medals[END_MDS];
parsemessages(int cn,playerent * d,ucharbuf & p,bool demo=false)385 void parsemessages(int cn, playerent *d, ucharbuf &p, bool demo = false)
386 {
387 static char text[MAXTRANS];
388 int type, joining = 0;
389 bool demoplayback = false;
390
391 while(p.remaining())
392 {
393 type = getint(p);
394
395 if(demo && watchingdemo && demoprotocol == 1132)
396 {
397 if(type > SV_IPLIST) --type; // SV_WHOIS removed
398 if(type >= SV_TEXTPRIVATE) ++type; // SV_TEXTPRIVATE added
399 if(type == SV_SWITCHNAME) // SV_SPECTCN removed
400 {
401 getint(p);
402 continue;
403 }
404 else if(type > SV_SWITCHNAME) --type;
405 }
406
407 #ifdef _DEBUG
408 if(type!=SV_POS && type!=SV_CLIENTPING && type!=SV_PING && type!=SV_PONG && type!=SV_CLIENT)
409 {
410 DEBUGVAR(d);
411 ASSERT(type>=0 && type<SV_NUM);
412 DEBUGVAR(messagenames[type]);
413 protocoldebug(true);
414 }
415 else protocoldebug(false);
416 #endif
417
418 switch(type)
419 {
420 case SV_SERVINFO: // welcome message from the server
421 {
422 int mycn = getint(p), prot = getint(p);
423 if(prot!=CUR_PROTOCOL_VERSION && !(watchingdemo && prot == -PROTOCOL_VERSION))
424 {
425 conoutf(_("%c3incompatible game protocol (local protocol: %d :: server protocol: %d)"), CC, CUR_PROTOCOL_VERSION, prot);
426 conoutf("\f3if this occurs a lot, obtain an upgrade from \f1http://assault.cubers.net");
427 if(watchingdemo) conoutf("breaking loop : \f3this demo is using a different protocol\f5 : end it now!"); // SVN-WiP-bug: causes endless retry loop else!
428 else disconnect();
429 return;
430 }
431 sessionid = getint(p);
432 player1->clientnum = mycn;
433 if(getint(p) > 0) conoutf(_("INFO: this server is password protected"));
434 sendintro();
435 break;
436 }
437
438 case SV_WELCOME:
439 joining = getint(p);
440 player1->resetspec();
441 resetcamera();
442 break;
443
444 case SV_CLIENT:
445 {
446 int cn = getint(p), len = getuint(p);
447 ucharbuf q = p.subbuf(len);
448 parsemessages(cn, getclient(cn), q, demo);
449 break;
450 }
451
452 case SV_SOUND:
453 audiomgr.playsound(getint(p), d);
454 break;
455
456 case SV_VOICECOMTEAM:
457 {
458 playerent *d = getclient(getint(p));
459 if(d) d->lastvoicecom = lastmillis;
460 int t = getint(p);
461 if(!d || !(d->muted || d->ignored))
462 {
463 if ( voicecomsounds == 1 || (voicecomsounds == 2 && m_teammode) ) audiomgr.playsound(t, SP_HIGH);
464 }
465 break;
466 }
467 case SV_VOICECOM:
468 {
469 int t = getint(p);
470 if(!d || !(d->muted || d->ignored))
471 {
472 if ( voicecomsounds == 1 ) audiomgr.playsound(t, SP_HIGH);
473 }
474 if(d) d->lastvoicecom = lastmillis;
475 break;
476 }
477
478 case SV_TEAMTEXTME:
479 case SV_TEAMTEXT:
480 {
481 int cn = getint(p);
482 getstring(text, p);
483 filtertext(text, text);
484 playerent *d = getclient(cn);
485 if(!d) break;
486 if(d->ignored) clientlogf("ignored: %s%s %s", colorname(d), type == SV_TEAMTEXT ? ":" : "", text);
487 else
488 {
489 if(m_teammode) conoutf(type == SV_TEAMTEXTME ? "\f1%s %s" : "%s:\f1 %s", colorname(d), highlight(text));
490 else conoutf(type == SV_TEAMTEXTME ? "\f0%s %s" : "%s:\f0 %s", colorname(d), highlight(text));
491 }
492 break;
493 }
494
495 case SV_TEXTME:
496 case SV_TEXT:
497 if(cn == -1)
498 {
499 getstring(text, p);
500 conoutf("MOTD:");
501 conoutf("\f4%s", text);
502 }
503 else if(d)
504 {
505 getstring(text, p);
506 filtertext(text, text);
507 if(d->ignored && d->clientrole != CR_ADMIN) clientlogf("ignored: %s%s %s", colorname(d), type == SV_TEXT ? ":" : "", text);
508 else conoutf(type == SV_TEXTME ? "\f0%s %s" : "%s:\f0 %s", colorname(d), highlight(text));
509 }
510 else return;
511 break;
512
513 case SV_TEXTPRIVATE:
514 {
515 int cn = getint(p);
516 getstring(text, p);
517 filtertext(text, text);
518 playerent *d = getclient(cn);
519 if(!d) break;
520 if(d->ignored) clientlogf("ignored: pm %s %s", colorname(d), text);
521 else
522 {
523 conoutf("%s (PM):\f9 %s", colorname(d), highlight(text));
524 lastpm = d->clientnum;
525 if(identexists("onPM"))
526 {
527 defformatstring(onpm)("onPM %d [%s]", d->clientnum, text);
528 execute(onpm);
529 }
530 }
531 break;
532 }
533
534 case SV_MAPCHANGE:
535 {
536 extern int spawnpermission;
537 spawnpermission = SP_SPECT;
538 getstring(text, p);
539 int mode = getint(p);
540 int downloadable = getint(p);
541 int revision = getint(p);
542 localwrongmap = !changemapserv(text, mode, downloadable, revision);
543 if(m_arena && joining>2) deathstate(player1);
544 break;
545 }
546
547 case SV_ITEMLIST:
548 {
549 int n;
550 resetspawns();
551 while((n = getint(p))!=-1) setspawn(n, true);
552 break;
553 }
554
555 case SV_MAPIDENT:
556 {
557 loopi(2) getint(p);
558 break;
559 }
560
561 case SV_SWITCHNAME:
562 getstring(text, p);
563 filtertext(text, text, 0, MAXNAMELEN);
564 if(!text[0]) copystring(text, "unarmed");
565 if(d)
566 {
567 if(strcmp(d->name, text))
568 conoutf(_("%s is now known as %s"), colorname(d), colorname(d, text));
569 if(identexists("onNameChange"))
570 {
571 defformatstring(onnamechange)("onNameChange %d \"%s\"", d->clientnum, text);
572 execute(onnamechange);
573 }
574 copystring(d->name, text, MAXNAMELEN+1);
575 updateclientname(d);
576 }
577 break;
578
579 case SV_SWITCHTEAM:
580 getint(p);
581 break;
582
583 case SV_SWITCHSKIN:
584 loopi(2)
585 {
586 int skin = getint(p);
587 if(d) d->setskin(i, skin);
588 }
589 break;
590
591 case SV_INITCLIENT: // another client either connected or changed name/team
592 {
593 int cn = getint(p);
594 playerent *d = newclient(cn);
595 if(!d)
596 {
597 getstring(text, p);
598 loopi(2) getint(p);
599 getint(p);
600 if(!demo || !watchingdemo || demoprotocol > 1132) getint(p);
601 break;
602 }
603 getstring(text, p);
604 filtertext(text, text, 0, MAXNAMELEN);
605 if(!text[0]) copystring(text, "unarmed");
606 if(d->name[0]) // already connected
607 {
608 if(strcmp(d->name, text))
609 conoutf(_("%s is now known as %s"), colorname(d), colorname(d, text));
610 }
611 else // new client
612 {
613 conoutf(_("connected: %s"), colorname(d, text));
614 }
615 copystring(d->name, text, MAXNAMELEN+1);
616 if(identexists("onConnect"))
617 {
618 defformatstring(onconnect)("onConnect %d", d->clientnum);
619 execute(onconnect);
620 }
621 loopi(2) d->setskin(i, getint(p));
622 d->team = getint(p);
623
624 if(!demo || !watchingdemo || demoprotocol > 1132) d->address = getint(p); // partial IP address
625
626 if(m_flags) loopi(2)
627 {
628 flaginfo &f = flaginfos[i];
629 if(!f.actor) f.actor = getclient(f.actor_cn);
630 }
631 updateclientname(d);
632 break;
633 }
634
635 case SV_CDIS:
636 {
637 int cn = getint(p);
638 playerent *d = getclient(cn);
639 if(!d) break;
640 if(d->name[0]) conoutf(_("player %s disconnected"), colorname(d));
641 zapplayer(players[cn]);
642 if(identexists("onDisconnect"))
643 {
644 defformatstring(ondisconnect)("onDisconnect %d", d->clientnum);
645 execute(ondisconnect);
646 }
647 break;
648 }
649
650 case SV_EDITMODE:
651 {
652 int val = getint(p);
653 if(!d) break;
654 if(val) d->state = CS_EDITING;
655 else
656 {
657 //2011oct16:flowtron:keep spectator state
658 //specators shouldn't be allowed to toggle editmode for themselves. they're ghosts!
659 d->state = d->state==CS_SPECTATE?CS_SPECTATE:CS_ALIVE;
660 }
661 break;
662 }
663
664 case SV_SPAWN:
665 {
666 playerent *s = d;
667 if(!s) { static playerent dummy; s = &dummy; }
668 s->respawn();
669 s->lifesequence = getint(p);
670 s->health = getint(p);
671 s->armour = getint(p);
672 int gunselect = getint(p);
673 s->setprimary(gunselect);
674 s->selectweapon(gunselect);
675 loopi(NUMGUNS) s->ammo[i] = getint(p);
676 loopi(NUMGUNS) s->mag[i] = getint(p);
677 s->state = CS_SPAWNING;
678 if(s->lifesequence==0) s->resetstats(); //NEW
679 break;
680 }
681
682 case SV_SPAWNSTATE:
683 {
684 if ( map_quality == MAP_IS_BAD )
685 {
686 loopi(6+2*NUMGUNS) getint(p);
687 conoutf(_("map deemed unplayable - fix it before you can spawn"));
688 break;
689 }
690
691 if(editmode) toggleedit(true);
692 showscores(false);
693 setscope(false);
694 setburst(false);
695 player1->respawn();
696 player1->lifesequence = getint(p);
697 player1->health = getint(p);
698 player1->armour = getint(p);
699 player1->setprimary(getint(p));
700 player1->selectweapon(getint(p));
701 int arenaspawn = getint(p);
702 loopi(NUMGUNS) player1->ammo[i] = getint(p);
703 loopi(NUMGUNS) player1->mag[i] = getint(p);
704 player1->state = CS_ALIVE;
705 lastspawn = lastmillis;
706 findplayerstart(player1, false, arenaspawn);
707 arenaintermission = 0;
708 if(m_arena && !localwrongmap)
709 {
710 closemenu(NULL);
711 conoutf(_("new round starting... fight!"));
712 hudeditf(HUDMSG_TIMER, "FIGHT!");
713 if(m_botmode) BotManager.RespawnBots();
714 }
715 addmsg(SV_SPAWN, "rii", player1->lifesequence, player1->weaponsel->type);
716 player1->weaponswitch(player1->primweap);
717 player1->weaponchanging -= weapon::weaponchangetime/2;
718 if(player1->lifesequence==0) player1->resetstats(); //NEW
719 break;
720 }
721
722 case SV_SHOTFX:
723 {
724 int scn = getint(p), gun = getint(p);
725 vec from, to;
726 loopk(3) to[k] = getint(p)/DMF;
727 playerent *s = getclient(scn);
728 if(!s || !weapon::valid(gun)) break;
729 loopk(3) from[k] = s->o.v[k];
730 if(gun==GUN_SHOTGUN) createrays(from, to);
731 s->lastaction = lastmillis;
732 s->weaponchanging = 0;
733 s->mag[gun]--;
734 if(s->weapons[gun])
735 {
736 s->lastattackweapon = s->weapons[gun];
737 s->weapons[gun]->gunwait = s->weapons[gun]->info.attackdelay;
738 s->weapons[gun]->attackfx(from, to, -1);
739 s->weapons[gun]->reloading = 0;
740 }
741 s->pstatshots[gun]++; //NEW
742 break;
743 }
744
745 case SV_THROWNADE:
746 {
747 vec from, to;
748 loopk(3) from[k] = getint(p)/DMF;
749 loopk(3) to[k] = getint(p)/DMF;
750 int nademillis = getint(p);
751 if(!d) break;
752 d->lastaction = lastmillis;
753 d->weaponchanging = 0;
754 d->lastattackweapon = d->weapons[GUN_GRENADE];
755 if(d->weapons[GUN_GRENADE])
756 {
757 d->weapons[GUN_GRENADE]->attackfx(from, to, nademillis);
758 d->weapons[GUN_GRENADE]->reloading = 0;
759 }
760 if(d!=player1) d->pstatshots[GUN_GRENADE]++; //NEW
761 break;
762 }
763
764 case SV_RELOAD:
765 {
766 int cn = getint(p), gun = getint(p);
767 playerent *p = getclient(cn);
768 if(p && p!=player1) p->weapons[gun]->reload(false);
769 break;
770 }
771
772 // for AUTH: WIP
773
774 case SV_AUTHREQ:
775 {
776 extern int autoauth;
777 getstring(text, p);
778 if(autoauth && text[0] && tryauth(text)) conoutf("server requested authkey \"%s\"", text);
779 break;
780 }
781
782 case SV_AUTHCHAL:
783 {
784 getstring(text, p);
785 authkey *a = findauthkey(text);
786 uint id = (uint)getint(p);
787 getstring(text, p);
788 if(a && a->lastauth && lastmillis - a->lastauth < 60*1000)
789 {
790 vector<char> buf;
791 answerchallenge(a->key, text, buf);
792 //conoutf("answering %u, challenge %s with %s", id, text, buf.getbuf());
793 addmsg(SV_AUTHANS, "rsis", a->desc, id, buf.getbuf());
794 }
795 break;
796 }
797
798 // :for AUTH
799
800 case SV_GIBDAMAGE:
801 case SV_DAMAGE:
802 {
803 int tcn = getint(p),
804 acn = getint(p),
805 gun = getint(p),
806 damage = getint(p),
807 armour = getint(p),
808 health = getint(p);
809 playerent *target = getclient(tcn), *actor = getclient(acn);
810 if(!target || !actor) break;
811 target->armour = armour;
812 target->health = health;
813 dodamage(damage, target, actor, -1, type==SV_GIBDAMAGE, false);
814 actor->pstatdamage[gun]+=damage; //NEW
815 break;
816 }
817
818 case SV_POINTS:
819 {
820 int count = getint(p);
821 if ( count > 0 ) {
822 loopi(count){
823 int pcn = getint(p); int score = getint(p);
824 playerent *ppl = getclient(pcn);
825 if (!ppl) break;
826 ppl->points += score;
827 }
828 } else {
829 int medals = getint(p);
830 if(medals > 0) {
831 // medals_arrived=1;
832 loopi(medals) {
833 int mcn=getint(p); int mtype=getint(p); int mitem=getint(p);
834 a_medals[mtype].assigned=1;
835 a_medals[mtype].cn=mcn;
836 a_medals[mtype].item=mitem;
837 }
838 }
839 }
840 break;
841 }
842
843 case SV_HUDEXTRAS:
844 {
845 char value = getint(p);
846 if (hudextras) showhudextras(hudextras, value);
847 break;
848 }
849
850 case SV_HITPUSH:
851 {
852 int gun = getint(p), damage = getint(p);
853 vec dir;
854 loopk(3) dir[k] = getint(p)/DNF;
855 player1->hitpush(damage, dir, NULL, gun);
856 break;
857 }
858
859 case SV_GIBDIED:
860 case SV_DIED:
861 {
862 int vcn = getint(p), acn = getint(p), frags = getint(p), gun = getint(p);
863 playerent *victim = getclient(vcn), *actor = getclient(acn);
864 if(!actor) break;
865 if ( m_mp(gamemode) ) actor->frags = frags;
866 if(!victim) break;
867 dokill(victim, actor, type==SV_GIBDIED, gun);
868 break;
869 }
870
871 case SV_RESUME:
872 {
873 loopi(MAXCLIENTS)
874 {
875 int cn = getint(p);
876 if(p.overread() || cn<0) break;
877 int state = getint(p), lifesequence = getint(p), primary = getint(p), gunselect = getint(p), flagscore = getint(p), frags = getint(p), deaths = getint(p), health = getint(p), armour = getint(p), points = getint(p);
878 int teamkills = 0;
879 if(!demo || !watchingdemo || demoprotocol > 1132) teamkills = getint(p);
880 int ammo[NUMGUNS], mag[NUMGUNS];
881 loopi(NUMGUNS) ammo[i] = getint(p);
882 loopi(NUMGUNS) mag[i] = getint(p);
883 playerent *d = (cn == getclientnum() ? player1 : newclient(cn));
884 if(!d) continue;
885 if(d!=player1) d->state = state;
886 d->lifesequence = lifesequence;
887 d->flagscore = flagscore;
888 d->frags = frags;
889 d->deaths = deaths;
890 d->points = points;
891 d->tks = teamkills;
892 if(d!=player1)
893 {
894 d->setprimary(primary);
895 d->selectweapon(gunselect);
896 d->health = health;
897 d->armour = armour;
898 memcpy(d->ammo, ammo, sizeof(ammo));
899 memcpy(d->mag, mag, sizeof(mag));
900 if(d->lifesequence==0) d->resetstats(); //NEW
901 }
902 }
903 break;
904 }
905
906 case SV_DISCSCORES:
907 {
908 discscores.shrink(0);
909 int team;
910 while((team = getint(p)) >= 0)
911 {
912 discscore &ds = discscores.add();
913 ds.team = team;
914 getstring(text, p);
915 filtertext(ds.name, text, 0, MAXNAMELEN);
916 ds.flags = getint(p);
917 ds.frags = getint(p);
918 ds.deaths = getint(p);
919 ds.points = getint(p);
920 }
921 break;
922 }
923 case SV_ITEMSPAWN:
924 {
925 int i = getint(p);
926 setspawn(i, true);
927 break;
928 }
929
930 case SV_ITEMACC:
931 {
932 int i = getint(p), cn = getint(p);
933 playerent *d = getclient(cn);
934 pickupeffects(i, d);
935 break;
936 }
937
938 case SV_EDITH: // coop editing messages, should be extended to include all possible editing ops
939 case SV_EDITT:
940 case SV_EDITS:
941 case SV_EDITD:
942 case SV_EDITE:
943 {
944 int x = getint(p);
945 int y = getint(p);
946 int xs = getint(p);
947 int ys = getint(p);
948 int v = getint(p);
949 block b = { x, y, xs, ys };
950 switch(type)
951 {
952 case SV_EDITH: editheightxy(v!=0, getint(p), b); break;
953 case SV_EDITT: edittexxy(v, getint(p), b); break;
954 case SV_EDITS: edittypexy(v, b); break;
955 case SV_EDITD: setvdeltaxy(v, b); break;
956 case SV_EDITE: editequalisexy(v!=0, b); break;
957 }
958 break;
959 }
960
961 case SV_NEWMAP:
962 {
963 int size = getint(p);
964 if(size>=0) empty_world(size, true);
965 else empty_world(-1, true);
966 if(d && d!=player1)
967 conoutf(size>=0 ? _("%s started a new map of size %d") : _("%s enlarged the map to size %d"), colorname(d), sfactor);
968 break;
969 }
970
971 case SV_EDITENT: // coop edit of ent
972 {
973 uint i = getint(p);
974 while((uint)ents.length()<=i) ents.add().type = NOTUSED;
975 int to = ents[i].type;
976 if(ents[i].type==SOUND)
977 {
978 entity &e = ents[i];
979
980 entityreference entref(&e);
981 location *loc = audiomgr.locations.find(e.attr1, &entref, mapsounds);
982
983 if(loc)
984 loc->drop();
985 }
986
987 ents[i].type = getint(p);
988 ents[i].x = getint(p);
989 ents[i].y = getint(p);
990 ents[i].z = getint(p);
991 ents[i].attr1 = getint(p);
992 ents[i].attr2 = getint(p);
993 ents[i].attr3 = getint(p);
994 ents[i].attr4 = getint(p);
995 ents[i].spawned = false;
996 if(ents[i].type==LIGHT || to==LIGHT) calclight();
997 if(ents[i].type==SOUND) audiomgr.preloadmapsound(ents[i]);
998 break;
999 }
1000
1001 case SV_PONG:
1002 {
1003 int millis = getint(p);
1004 addmsg(SV_CLIENTPING, "i", player1->ping = max(0, (player1->ping*5+totalmillis-millis)/6));
1005 break;
1006 }
1007
1008 case SV_CLIENTPING:
1009 if(!d) return;
1010 d->ping = getint(p);
1011 break;
1012
1013 case SV_GAMEMODE:
1014 nextmode = getint(p);
1015 if (nextmode >= GMODE_NUM) nextmode -= GMODE_NUM;
1016 break;
1017
1018 case SV_TIMEUP:
1019 {
1020 int curgamemillis = getint(p);
1021 int curgamelimit = getint(p);
1022 timeupdate(curgamemillis, curgamelimit);
1023 break;
1024 }
1025
1026 case SV_WEAPCHANGE:
1027 {
1028 int gun = getint(p);
1029 if(d) d->selectweapon(gun);
1030 break;
1031 }
1032
1033 case SV_SERVMSG:
1034 getstring(text, p);
1035 conoutf("%s", text);
1036 break;
1037
1038 case SV_FLAGINFO:
1039 {
1040 int flag = getint(p);
1041 if(flag<0 || flag>1) return;
1042 flaginfo &f = flaginfos[flag];
1043 f.state = getint(p);
1044 switch(f.state)
1045 {
1046 case CTFF_STOLEN:
1047 flagstolen(flag, getint(p));
1048 break;
1049 case CTFF_DROPPED:
1050 {
1051 float x = getuint(p)/DMF;
1052 float y = getuint(p)/DMF;
1053 float z = getuint(p)/DMF;
1054 flagdropped(flag, x, y, z);
1055 break;
1056 }
1057 case CTFF_INBASE:
1058 flaginbase(flag);
1059 break;
1060 case CTFF_IDLE:
1061 flagidle(flag);
1062 break;
1063 }
1064 break;
1065 }
1066
1067 case SV_FLAGMSG:
1068 {
1069 int flag = getint(p);
1070 int message = getint(p);
1071 int actor = getint(p);
1072 int flagtime = message == FM_KTFSCORE ? getint(p) : -1;
1073 flagmsg(flag, message, actor, flagtime);
1074 break;
1075 }
1076
1077 case SV_FLAGCNT:
1078 {
1079 int fcn = getint(p);
1080 int flags = getint(p);
1081 playerent *p = getclient(fcn);
1082 if(p) p->flagscore = flags;
1083 break;
1084 }
1085
1086 case SV_ARENAWIN:
1087 {
1088 int acn = getint(p);
1089 playerent *alive = getclient(acn);
1090 conoutf(_("the round is over! next round in 5 seconds..."));
1091 if(m_botmode && acn==-2) hudoutf(_("the bots have won the round!"));
1092 else if(!alive) hudoutf(_("everyone died!"));
1093 else if(m_teammode) hudoutf(_("team %s has won the round!"), team_string(alive->team));
1094 else if(alive==player1) hudoutf(_("you are the survivor!"));
1095 else hudoutf(_("%s is the survivor!"), colorname(alive));
1096 arenaintermission = lastmillis;
1097 break;
1098 }
1099
1100 case SV_SPAWNDENY:
1101 {
1102 extern int spawnpermission;
1103 spawnpermission = getint(p);
1104 if(spawnpermission == SP_REFILLMATCH) hudoutf("\f3You can now spawn to refill your team.");
1105 break;
1106 }
1107 case SV_FORCEDEATH:
1108 {
1109 int cn = getint(p);
1110 playerent *d = cn==getclientnum() ? player1 : newclient(cn);
1111 if(!d) break;
1112 deathstate(d);
1113 break;
1114 }
1115
1116 case SV_SERVOPINFO:
1117 {
1118 loopv(players) { if(players[i]) players[i]->clientrole = CR_DEFAULT; }
1119 player1->clientrole = CR_DEFAULT;
1120
1121 int cl = getint(p), r = getint(p);
1122 if(cl >= 0 && r >= 0)
1123 {
1124 playerent *pl = (cl == getclientnum() ? player1 : newclient(cl));
1125 if(pl)
1126 {
1127 pl->clientrole = r;
1128 if(pl->name[0])
1129 {
1130 // two messages required to allow for proper german translation - is there a better way to do it?
1131 if(pl==player1) conoutf(_("you claimed %s status"), r == CR_ADMIN ? "admin" : "master");
1132 else conoutf(_("%s claimed %s status"), colorname(pl), r == CR_ADMIN ? "admin" : "master");
1133 }
1134 }
1135 }
1136 break;
1137 }
1138
1139 case SV_TEAMDENY:
1140 {
1141 int t = getint(p);
1142 if(m_teammode)
1143 {
1144 if(team_isvalid(t)) conoutf(_("you can't change to team %s"), team_string(t));
1145 }
1146 else
1147 {
1148 conoutf(_("you can't change to %s mode"), team_isspect(t) ? _("spectate") : _("active"));
1149 }
1150 break;
1151 }
1152
1153 case SV_SETTEAM:
1154 {
1155 int fpl = getint(p), fnt = getint(p), ftr = fnt >> 4;
1156 fnt &= 0x0f;
1157 playerent *d = (fpl == getclientnum() ? player1 : newclient(fpl));
1158 if(d)
1159 {
1160 const char *nts = team_string(fnt);
1161 bool you = fpl == player1->clientnum;
1162 if(m_teammode || team_isspect(fnt))
1163 {
1164 if(d->team == fnt)
1165 {
1166 if(you && ftr == FTR_AUTOTEAM) hudoutf("you stay in team %s", nts);
1167 }
1168 else
1169 {
1170 if(you && !watchingdemo)
1171 {
1172 switch(ftr)
1173 {
1174 case FTR_PLAYERWISH:
1175 conoutf(_("you're now in team %s"), nts);
1176 break;
1177 case FTR_AUTOTEAM:
1178 hudoutf(_("the server forced you to team %s"), nts);
1179 break;
1180 }
1181 }
1182 else
1183 {
1184 const char *pls = colorname(d);
1185 bool et = team_base(player1->team) != team_base(fnt);
1186 switch(ftr)
1187 {
1188 case FTR_PLAYERWISH:
1189 conoutf(_("player %s switched to team %s"), pls, nts); // new message
1190 break;
1191 case FTR_AUTOTEAM:
1192 if(watchingdemo) conoutf(_("the server forced %s to team %s"), colorname(d), nts);
1193 else hudoutf(_("the server forced %s to %s team"), colorname(d), et ? _("the enemy") : _("your"));
1194 break;
1195 }
1196 }
1197 if(you && !team_isspect(d->team) && team_isspect(fnt) && d->state == CS_DEAD) spectatemode(SM_FLY);
1198 }
1199 }
1200 else if(d->team != fnt && ftr == FTR_PLAYERWISH) conoutf(_("%s changed to active play"), you ? _("you") : colorname(d));
1201 d->team = fnt;
1202 if(team_isspect(d->team)) d->state = CS_SPECTATE;
1203 }
1204 break;
1205 }
1206
1207 case SV_SERVERMODE:
1208 {
1209 int sm = getint(p);
1210 servstate.autoteam = sm & 1;
1211 servstate.mastermode = (sm >> 2) & MM_MASK;
1212 servstate.matchteamsize = sm >> 4;
1213 //if(sm & AT_SHUFFLE) playsound(TEAMSHUFFLE); // TODO
1214 break;
1215 }
1216
1217 case SV_CALLVOTE:
1218 {
1219 int type = getint(p);
1220 int vcn = -1, n_yes = 0, n_no = 0;
1221 if ( type == -1 )
1222 {
1223 d = getclient(vcn = getint(p));
1224 n_yes = getint(p);
1225 n_no = getint(p);
1226 type = getint(p);
1227 }
1228 if (type == SA_MAP && d == NULL) d = player1; // gonext uses this
1229 if( type < 0 || type >= SA_NUM || !d ) return;
1230 votedisplayinfo *v = NULL;
1231 string a1, a2;
1232 switch(type)
1233 {
1234 case SA_MAP:
1235 getstring(text, p);
1236 filtertext(text, text);
1237 itoa(a1, getint(p));
1238 defformatstring(t)("%d", getint(p));
1239 v = newvotedisplayinfo(d, type, text, a1, t);
1240 break;
1241 case SA_KICK:
1242 case SA_BAN:
1243 {
1244 itoa(a1, getint(p));
1245 getstring(text, p);
1246 filtertext(text, text);
1247 v = newvotedisplayinfo(d, type, a1, text);
1248 break;
1249 }
1250 case SA_SERVERDESC:
1251 getstring(text, p);
1252 filtertext(text, text);
1253 v = newvotedisplayinfo(d, type, text, NULL);
1254 break;
1255 case SA_STOPDEMO:
1256 // compatibility
1257 break;
1258 case SA_REMBANS:
1259 case SA_SHUFFLETEAMS:
1260 v = newvotedisplayinfo(d, type, NULL, NULL);
1261 break;
1262 case SA_FORCETEAM:
1263 itoa(a1, getint(p));
1264 itoa(a2, getint(p));
1265 v = newvotedisplayinfo(d, type, a1, a2);
1266 break;
1267 default:
1268 itoa(a1, getint(p));
1269 v = newvotedisplayinfo(d, type, a1, NULL);
1270 break;
1271 }
1272 displayvote(v);
1273 onCallVote(type, v->owner->clientnum, text, a1);
1274 if (vcn >= 0)
1275 {
1276 loopi(n_yes) votecount(VOTE_YES);
1277 loopi(n_no) votecount(VOTE_NO);
1278 }
1279 extern int vote(int);
1280 if (d == player1) vote(VOTE_YES);
1281 break;
1282 }
1283
1284 case SV_CALLVOTESUC:
1285 {
1286 callvotesuc();
1287 onChangeVote( 0, -1);
1288 break;
1289 }
1290
1291 case SV_CALLVOTEERR:
1292 {
1293 int errn = getint(p);
1294 callvoteerr(errn);
1295 onChangeVote( 1, errn);
1296 break;
1297 }
1298
1299 case SV_VOTE:
1300 {
1301 int vote = getint(p);
1302 votecount(vote);
1303 onChangeVote( 2, vote);
1304 break;
1305 }
1306
1307 case SV_VOTERESULT:
1308 {
1309 int vres = getint(p);
1310 voteresult(vres);
1311 onChangeVote( 3, vres);
1312 break;
1313 }
1314
1315 case SV_IPLIST:
1316 {
1317 int cn;
1318 while((cn = getint(p)) >= 0 && !p.overread())
1319 {
1320 playerent *pl = getclient(cn);
1321 int ip = getint(p);
1322 if(!pl) continue;
1323 else pl->address = ip;
1324 }
1325 break;
1326 }
1327
1328 case SV_SENDDEMOLIST:
1329 {
1330 int demos = getint(p);
1331 if(!demos) conoutf(_("no demos available"));
1332 else loopi(demos)
1333 {
1334 getstring(text, p);
1335 conoutf("%d. %s", i+1, text);
1336 }
1337 break;
1338 }
1339
1340 case SV_DEMOPLAYBACK:
1341 {
1342 string demofile;
1343 extern char *curdemofile;
1344 if(demo && watchingdemo && demoprotocol == 1132)
1345 {
1346 watchingdemo = demoplayback = getint(p)!=0;
1347 copystring(demofile, "n/a");
1348 }
1349 else
1350 {
1351 getstring(demofile, p, MAXSTRLEN);
1352 watchingdemo = demoplayback = demofile[0] != '\0';
1353 }
1354 DELETEA(curdemofile);
1355 if(demoplayback)
1356 {
1357 curdemofile = newstring(demofile);
1358 player1->resetspec();
1359 player1->state = CS_SPECTATE;
1360 player1->team = TEAM_SPECT;
1361 }
1362 else
1363 {
1364 // cleanups
1365 curdemofile = newstring("n/a");
1366 loopv(players) zapplayer(players[i]);
1367 clearvote();
1368 player1->state = CS_ALIVE;
1369 player1->resetspec();
1370 }
1371 player1->clientnum = getint(p);
1372 break;
1373 }
1374
1375 default:
1376 neterr("type");
1377 return;
1378 }
1379 }
1380
1381 #ifdef _DEBUG
1382 protocoldebug(false);
1383 #endif
1384 }
1385
setDemoFilenameFormat(char * fmt)1386 void setDemoFilenameFormat(char *fmt)
1387 {
1388 extern string demofilenameformat;
1389 if(fmt && fmt[0]!='\0')
1390 {
1391 copystring(demofilenameformat, fmt);
1392 } else copystring(demofilenameformat, DEFDEMOFILEFMT); // reset to default if passed empty string - or should we output the current value in this case?
1393 }
1394 COMMANDN(demonameformat, setDemoFilenameFormat, "s");
setDemoTimestampFormat(char * fmt)1395 void setDemoTimestampFormat(char *fmt)
1396 {
1397 extern string demotimestampformat;
1398 if(fmt && fmt[0]!='\0')
1399 {
1400 copystring(demotimestampformat, fmt);
1401 } else copystring(demotimestampformat, DEFDEMOTIMEFMT); // reset to default if passed empty string - or should we output the current value in this case?
1402 }
1403 COMMANDN(demotimeformat, setDemoTimestampFormat, "s");
setDemoTimeLocal(int * truth)1404 void setDemoTimeLocal(int *truth)
1405 {
1406 extern int demotimelocal;
1407 demotimelocal = *truth == 0 ? 0 : 1;
1408 }
1409 COMMANDN(demotimelocal, setDemoTimeLocal, "i");
getdemonameformat()1410 void getdemonameformat() { extern string demofilenameformat; result(demofilenameformat); } COMMAND(getdemonameformat, "");
getdemotimeformat()1411 void getdemotimeformat() { extern string demotimestampformat; result(demotimestampformat); } COMMAND(getdemotimeformat, "");
getdemotimelocal()1412 void getdemotimelocal() { extern int demotimelocal; intret(demotimelocal); } COMMAND(getdemotimelocal, "");
1413
1414
parseDemoFilename(char * srvfinfo)1415 const char *parseDemoFilename(char *srvfinfo)
1416 {
1417 int gmode = 0; //-314;
1418 int mplay = 0;
1419 int mdrop = 0;
1420 int stamp = 0;
1421 string srvmap;
1422 if(srvfinfo && srvfinfo[0])
1423 {
1424 int fip = 0;
1425 char sep[] = ":";
1426 char *pch;
1427 pch = strtok (srvfinfo,sep);
1428 while (pch != NULL && fip < 4)
1429 {
1430 fip++;
1431 switch(fip)
1432 {
1433 case 1: gmode = atoi(pch); break;
1434 case 2: mplay = atoi(pch); break;
1435 case 3: mdrop = atoi(pch); break;
1436 case 4: stamp = atoi(pch); break;
1437 default: break;
1438 }
1439 pch = strtok (NULL, sep);
1440 }
1441 copystring(srvmap, pch);
1442 }
1443 extern const char *getDemoFilename(int gmode, int mplay, int mdrop, int tstamp, char *srvmap);
1444 return getDemoFilename(gmode, mplay, mdrop, stamp, srvmap);
1445 }
1446
receivefile(uchar * data,int len)1447 void receivefile(uchar *data, int len)
1448 {
1449 static char text[MAXTRANS];
1450 ucharbuf p(data, len);
1451 int type = getint(p);
1452 data += p.length();
1453 len -= p.length();
1454 switch(type)
1455 {
1456 case SV_SENDDEMO:
1457 {
1458 getstring(text, p);
1459 extern string demosubpath;
1460 defformatstring(demofn)("%s", parseDemoFilename(text));
1461 defformatstring(fname)("demos/%s%s.dmo", demosubpath, demofn);
1462 copystring(demosubpath, "");
1463 data += strlen(text);
1464 int demosize = getint(p);
1465 if(p.remaining() < demosize)
1466 {
1467 p.forceoverread();
1468 break;
1469 }
1470 path(fname);
1471 stream *demo = openrawfile(fname, "wb");
1472 if(!demo)
1473 {
1474 conoutf(_("failed writing to \"%s\""), fname);
1475 return;
1476 }
1477 conoutf(_("received demo \"%s\""), fname);
1478 demo->write(&p.buf[p.len], demosize);
1479 delete demo;
1480 break;
1481 }
1482
1483 case SV_RECVMAP:
1484 {
1485 getstring(text, p);
1486 conoutf(_("received map \"%s\" from server, reloading.."), text);
1487 int mapsize = getint(p);
1488 int cfgsize = getint(p);
1489 int cfgsizegz = getint(p);
1490 /* int revision = */ getint(p);
1491 int size = mapsize + cfgsizegz;
1492 if(MAXMAPSENDSIZE < mapsize + cfgsizegz || cfgsize > MAXCFGFILESIZE) { // sam's suggestion
1493 conoutf(_("map %s is too large to receive"), text);
1494 } else {
1495 if(p.remaining() < size)
1496 {
1497 p.forceoverread();
1498 break;
1499 }
1500 if(securemapcheck(text))
1501 {
1502 p.len += size;
1503 break;
1504 }
1505 writemap(path(text), mapsize, &p.buf[p.len]);
1506 p.len += mapsize;
1507 writecfggz(path(text), cfgsize, cfgsizegz, &p.buf[p.len]);
1508 p.len += cfgsizegz;
1509 }
1510 break;
1511 }
1512
1513 default:
1514 p.len = 0;
1515 parsemessages(-1, NULL, p);
1516 break;
1517 }
1518 }
1519
servertoclient(int chan,uchar * buf,int len,bool demo)1520 void servertoclient(int chan, uchar *buf, int len, bool demo) // processes any updates from the server
1521 {
1522 ucharbuf p(buf, len);
1523 switch(chan)
1524 {
1525 case 0: parsepositions(p); break;
1526 case 1: parsemessages(-1, NULL, p, demo); break;
1527 case 2: receivefile(p.buf, p.maxlen); break;
1528 }
1529 }
1530
localservertoclient(int chan,uchar * buf,int len,bool demo)1531 void localservertoclient(int chan, uchar *buf, int len, bool demo) // processes any updates from the server
1532 {
1533 // pktlogger.queue(enet_packet_create (buf, len, 0)); // log local & demo packets
1534 servertoclient(chan, buf, len, demo);
1535 }
1536