clients2c.cpp 57 KB

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