// client processing of the incoming network stream
#include "cube.h"
#include "bot/bot.h"
VARP(networkdebug, 0, 0, 1);
#define DEBUGCOND (networkdebug==1)
extern bool watchingdemo;
extern string clientpassword;
packetqueue pktlogger;
void neterr(const char *s)
{
conoutf("\f3illegal network message (%s)", s);
// might indicate a client/server communication bug, create error report
pktlogger.flushtolog("packetlog.txt");
conoutf("\f3wrote a network error report to packetlog.txt, please post this file to the bugtracker now!");
disconnect();
}
VARP(autogetmap, 0, 1, 1); // only if the client doesn't have that map
VARP(autogetnewmaprevisions, 0, 1, 1);
bool localwrongmap = false;
int MA = 0, Hhits = 0; // flowtron: moved here
bool changemapserv(char *name, int mode, int muts, int download, int revision) // forced map change from the server
{
MA = Hhits = 0; // reset for checkarea()
modecheck(gamemode = mode, mutators = muts);
render_void = m_void(gamemode, mutators);
if(m_demo(gamemode)) return true;
if(m_edit(gamemode))
{
if(!name[0] || !load_world(name)) empty_world(0, true);
return true;
}
else if(player1->state==CS_EDITING) { /*conoutf("SANITY drop from EDITING");*/ toggleedit(true); } // fix stuck-in-editmode bug
bool loaded = load_world(name);
if(download > 0)
{
bool revmatch = hdr.maprevision == revision || revision == 0;
if(watchingdemo)
{
if(!revmatch) conoutf(_("%c3demo was recorded on map revision %d, you have map revision %d"), CC, revision, hdr.maprevision);
}
else
{
if(securemapcheck(name, false)) return true;
bool sizematch = maploaded == download || download < 10;
if(loaded && sizematch && revmatch) return true;
bool getnewrev = autogetnewmaprevisions && revision > hdr.maprevision;
if(autogetmap || getnewrev)
{
if(!loaded || getnewrev) getmap(); // no need to ask
else
{
defformatstring(msg)("map '%s' revision: local %d, provided by server %d", name, hdr.maprevision, revision);
alias("__getmaprevisions", msg);
showmenu("getmap");
}
}
else
{
if(!loaded || download < 10) conoutf(_("\"getmap\" to download the current map from the server"));
else conoutf(_("\"getmap\" to download a %s version of the current map from the server"),
revision == 0 ? _("different") : (revision > hdr.maprevision ? _("newer") : _("older")));
}
}
}
else return true;
return false;
}
// update the position of other clients in the game in our world
// don't care if he's in the scenery or other players,
// just don't overlap with our client
void updatepos(playerent *d)
{
const float r = player1->radius+d->radius;
const float dx = player1->o.x-d->o.x;
const float dy = player1->o.y-d->o.y;
const float dz = player1->o.z-d->o.z;
const float rz = player1->aboveeye+d->eyeheight;
const float fx = (float)fabs(dx), fy = (float)fabs(dy), fz = (float)fabs(dz);
if(fx<r && fy<r && fz<rz && d->state!=CS_DEAD)
{
if(fx<fy) d->o.y += dy<0 ? r-fy : -(r-fy); // push aside
else d->o.x += dx<0 ? r-fx : -(r-fx);
}
}
void updatelagtime(playerent *d)
{
int lagtime = totalmillis-d->lastupdate;
if(lagtime)
{
if(d->lastupdate) d->plag = (d->plag*5+lagtime)/6;
d->lastupdate = totalmillis;
}
}
extern void trydisconnect();
VARP(maxrollremote, 0, 0, 20); // bound remote "roll" values by our maxroll?!
void parsepositions(ucharbuf &p)
{
int type;
while(p.remaining()) switch(type = getint(p))
{
case SV_POS: // position of another client
case SV_POSC:
{
int cn, f, g;
vec o, vel;
float yaw, pitch, roll = 0;
bool scoping, sprinting;//, shoot;
if(type == SV_POSC)
{
bitbuf<ucharbuf> q(p);
cn = q.getbits(5);
int usefactor = q.getbits(2) + 7;
o.x = q.getbits(usefactor + 4) / DMF;
o.y = q.getbits(usefactor + 4) / DMF;
yaw = q.getbits(9) * 360.0f / 512;
pitch = (q.getbits(8) - 128) * 90.0f / 127;
roll = !q.getbits(1) ? (q.getbits(6) - 32) * 20.0f / 31 : 0.0f;
if(!q.getbits(1))
{
vel.x = (q.getbits(4) - 8) / DVELF;
vel.y = (q.getbits(4) - 8) / DVELF;
vel.z = (q.getbits(4) - 8) / DVELF;
}
else vel.x = vel.y = vel.z = 0.0f;
f = q.getbits(8);
int negz = q.getbits(1);
int full = q.getbits(1);
int s = q.rembits();
if(s < 3) s += 8;
if(full) s = 11;
int z = q.getbits(s);
if(negz) z = -z;
o.z = z / DMF;
scoping = ( q.getbits(1) ? true : false );
q.getbits(1);//shoot = ( q.getbits(1) ? true : false );
sprinting = q.getbits(1) ? true : false;
}
else
{
cn = getint(p);
o.x = getuint(p)/DMF;
o.y = getuint(p)/DMF;
o.z = getuint(p)/DMF;
yaw = (float)getuint(p);
pitch = (float)getint(p);
g = getuint(p);
if ((g>>3) & 1) roll = (float)(getint(p)*20.0f/125.0f);
if (g & 1) vel.x = getint(p)/DVELF; else vel.x = 0;
if ((g>>1) & 1) vel.y = getint(p)/DVELF; else vel.y = 0;
if ((g>>2) & 1) vel.z = getint(p)/DVELF; else vel.z = 0;
scoping = ( (g>>4) & 1 ? true : false );
//shoot = ( (g>>5) & 1 ? true : false ); // we are not using this yet
sprinting = ((g >> 6) & 1 ? true : false);
f = getuint(p);
}
int seqcolor = (f>>6)&1;
playerent *d = getclient(cn);
if(!d || seqcolor!=(d->lifesequence&1)) continue;
vec oldpos(d->o);
float oldyaw = d->yaw, oldpitch = d->pitch;
loopi(3)
{
float dr = o.v[i] - d->o.v[i] + ( i == 2 ? d->eyeheight : 0);
if ( !dr ) d->vel.v[i] = 0.0f;
else if ( d->vel.v[i] ) d->vel.v[i] = dr * 0.05f + d->vel.v[i] * 0.95f;
d->vel.v[i] += vel.v[i];
if ( i==2 && d->onfloor && d->vel.v[i] < 0.0f ) d->vel.v[i] = 0.0f;
}
d->o = o;
d->o.z += d->eyeheight;
d->yaw = yaw;
d->pitch = pitch;
d->scoping = scoping;
d->roll = roll;
d->strafe = (f&3)==3 ? -1 : f&3;
f >>= 2;
d->move = (f&3)==3 ? -1 : f&3;
f >>= 2;
d->onfloor = f&1;
f >>= 1;
d->onladder = f&1;
f >>= 2;
d->last_pos = totalmillis;
updatecrouch(d, f&1);
d->sprinting = sprinting;
updatepos(d);
updatelagtime(d);
extern int smoothmove, smoothdist;
if(d->state==CS_DEAD)
{
d->resetinterp();
d->smoothmillis = 0;
}
else if(smoothmove && d->smoothmillis>=0 && oldpos.dist(d->o) < smoothdist)
{
d->newpos = d->o;
d->newpos.z -= d->eyeheight;
d->newyaw = d->yaw;
d->newpitch = d->pitch;
d->o = oldpos;
d->yaw = oldyaw;
d->pitch = oldpitch;
oldpos.z -= d->eyeheight;
(d->deltapos = oldpos).sub(d->newpos);
d->deltayaw = oldyaw - d->newyaw;
if(d->deltayaw > 180) d->deltayaw -= 360;
else if(d->deltayaw < -180) d->deltayaw += 360;
d->deltapitch = oldpitch - d->newpitch;
d->smoothmillis = lastmillis;
}
else d->smoothmillis = 0;
if (d->state == CS_WAITING) d->state = CS_ALIVE;
// when playing a demo spectate first player we know about
if(player1->isspectating() && player1->spectatemode==SM_NONE) togglespect();
extern void clamproll(physent *pl);
if(maxrollremote) clamproll((physent *) d);
break;
}
default:
neterr("type");
return;
}
}
extern int checkarea(int maplayout_factor, char *maplayout);
char *mlayout = NULL;
int Mv = 0, Ma = 0, F2F = 1000 * MINFF; // moved up:, MA = 0;
float Mh = 0;
extern int connected;
extern bool noflags;
bool item_fail = false;
int map_quality = MAP_IS_EDITABLE;
/// TODO: many functions and variables are redundant between client and server... someone should redo the entire server code and unify client and server.
bool good_map() // call this function only at startmap
{
if (mlayout) MA = checkarea(sfactor, mlayout);
F2F = 1000 * MINFF;
if(m_flags(gamemode))
{
flaginfo &f0 = flaginfos[0];
flaginfo &f1 = flaginfos[1];
#define DIST(x) (f0.pos.x - f1.pos.x)
F2F = (!numflagspawn[0] || !numflagspawn[1]) ? 1000 * MINFF : DIST(x)*DIST(x)+DIST(y)*DIST(y);
#undef DIST
}
item_fail = false;
loopv(ents)
{
entity &e1 = ents[i];
if (e1.type < I_CLIPS || e1.type > I_AKIMBO) continue;
float density = 0, hdensity = 0;
loopvj(ents)
{
entity &e2 = ents[j];
if (e2.type < I_CLIPS || e2.type > I_AKIMBO || i == j) continue;
#define DIST(x) (e1.x - e2.x)
#define DIST_ATT ((e1.z + e1.attr1) - (e2.z + e2.attr1))
float r2 = DIST(x)*DIST(x) + DIST(y)*DIST(y) + DIST_ATT*DIST_ATT;
#undef DIST_ATT
#undef DIST
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; }
r2 = 1/r2;
if (r2 < 0.0025f) continue;
if (e1.type != e2.type)
{
hdensity += r2;
continue;
}
density += r2;
}
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; }
switch(e1.type)
{
#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; }
case I_CLIPS:
case I_HEALTH: LOGTHISSWITCH(0.24f); break;
case I_AMMO: LOGTHISSWITCH(0.04f); break;
case I_HELMET: LOGTHISSWITCH(0.02f); break;
case I_ARMOUR:
case I_GRENADE:
case I_AKIMBO: LOGTHISSWITCH(0.005f); break;
default: break;
#undef LOGTHISSWITCH
}
}
map_quality = (!item_fail && F2F > MINFF && MA < MAXMAREA && Mh < MAXMHEIGHT && Hhits < MAXHHITS) ? MAP_IS_GOOD : MAP_IS_BAD;
if ( (!connected || m_edit(gamemode)) && map_quality == MAP_IS_BAD ) map_quality = MAP_IS_EDITABLE;
return map_quality > 0;
}
void onCallVote(int type, int vcn, const votedata &vote)
{
if(identexists("onCallVote"))
{
defformatstring(runas)("onCallVote %d %d %d %d [%s]", type, vcn, vote.int1, vote.int2, vote.str1);
execute(runas);
}
}
void onChangeVote(int mod, int id, int cn)
{
if(identexists("onChangeVote"))
{
defformatstring(runas)("onChangeVote %d %d %d", mod, id, cn);
execute(runas);
}
}
extern votedisplayinfo *curvote;
void parsemessages(int cn, playerent *d, ucharbuf &p, bool demo = false)
{
static char text[MAXTRANS];
int type, joining = 0;
bool demoplayback = false;
while(p.remaining())
{
type = getint(p);
#ifdef _DEBUG
if(type!=SV_POS && type!=SV_CLIENTPING && type!=SV_PINGPONG && type!=SV_CLIENT)
{
DEBUGVAR(d);
ASSERT(type>=0 && type<SV_NUM);
DEBUGVAR(messagenames[type]);
protocoldebug(true);
}
else protocoldebug(false);
#endif
switch(type)
{
case SV_SERVINFO: // welcome message from the server
{
int mycn = getint(p), prot = getint(p);
if (prot != PROTOCOL_VERSION && !(watchingdemo && prot == -PROTOCOL_VERSION))
{
conoutf(_("%c3incompatible game protocol (local protocol: %d :: server protocol: %d)"), CC, PROTOCOL_VERSION, prot);
conoutf("\f3if this occurs a lot, obtain an upgrade from \f1http://acr.victorz.ca");
if(watchingdemo) conoutf("breaking loop : \f3this demo is using a different protocol\f5 : end it now!"); // SVN-WiP-bug: causes endless retry loop else!
else disconnect();
return;
}
sessionid = getint(p);
player1->clientnum = mycn;
if(getint(p) > 0) conoutf(_("INFO: this server is password protected"));
sendintro();
break;
}
case SV_WELCOME:
joining = getint(p);
// Sync weapon info on connect
loopi(NUMGUNS)
{
#define GETWEAPSTAT(s) guns[i].s = getint(p);
GETWEAPSTAT(reloadtime)
GETWEAPSTAT(attackdelay)
//GETWEAPSTAT(damage)
//GETWEAPSTAT(projspeed)
//GETWEAPSTAT(part)
GETWEAPSTAT(spread)
GETWEAPSTAT(spreadrem)
GETWEAPSTAT(kick)
GETWEAPSTAT(addsize)
GETWEAPSTAT(magsize)
//GETWEAPSTAT(mdl_kick_rot)
//GETWEAPSTAT(mdl_kick_back)
GETWEAPSTAT(recoil)
GETWEAPSTAT(maxrecoil)
GETWEAPSTAT(recoilangle)
GETWEAPSTAT(pushfactor)
#undef GETWEAPSTAT
}
player1->resetspec();
resetcamera();
break;
case SV_CLIENT:
{
int cn = getint(p);//, len = getuint(p);
d = getclient(cn);
/*
ucharbuf q = p.subbuf(p.remaining());
parsemessages(cn, getclient(cn), q, demo);
*/
break;
}
case SV_SOUND:
{
playerent *d = getclient(getint(p));
const int snd = getint(p);
if(!d || d == player1 || isowned(d)) break;
switch(snd)
{
case S_NOAMMO:
case S_JUMP:
case S_SOFTLAND:
case S_HARDLAND:
audiomgr.playsound(snd, d);
break;
}
break;
}
case SV_TEXT:
{
int cn = getint(p), voice = getint(p), flags = getint(p);
getstring(text, p);
if(cn >= 0)
{
playerent *d = getclient(cn);
if(!d) break;
filtertext(text, text);
saytext(d, text, flags, voice);
}
else
{
filterrichtext(text, text);
chatoutf(cn == -1 ? "\f4MOTD: %s" : "\f5[\f1CONSOLE\f5] \f2%s", text);
}
break;
}
case SV_TYPING:
{
const int cn = getint(p), typing = getint(p);
playerent *d = getclient(cn);
if (d)
d->typing = (typing != 0);
break;
}
case SV_MAPCHANGE:
{
// get map info
getstring(text, p);
int mode = getint(p), muts = getint(p);
int downloadable = getint(p);
int revision = getint(p);
localwrongmap = !changemapserv(text, mode, muts, downloadable, revision);
if(m_duke(gamemode, mutators) && joining>2) deathstate(player1);
// get item spawns
int n;
resetspawns();
while (!p.overread())
{
n = getint(p);
if (n == -1) break;
setspawn(n);
}
// get knives
n = getint(p); // reuse
knives.setsize(0);
loopi(n)
{
cknife &k = knives.add();
k.id = getint(p);
k.millis = totalmillis + getint(p);
loopi(3) k.o[i] = getint(p)/DMF;
}
// get confirms
n = getint(p); // more reuse
confirms.setsize(0);
loopi(n)
{
cconfirm &c = confirms.add();
c.id = getint(p);
c.team = getint(p);
loopi(3) c.o[i] = getint(p)/DMF;
}
break;
}
case SV_KNIFEADD:
{
cknife &k = knives.add_limit<1000>();
k.id = getint(p);
k.millis = totalmillis + KNIFETTL;
loopi(3) k.o[i] = getint(p)/DMF;
break;
}
case SV_KNIFEREMOVE:
{
int id = getint(p);
loopv(knives) if (knives[i].id == id) knives.remove(i--);
break;
}
case SV_CONFIRMADD:
{
cconfirm &k = confirms.add_limit<10000>();
k.id = getint(p);
k.team = getint(p);
loopi(3) k.o[i] = getint(p)/DMF;
break;
}
case SV_CONFIRMREMOVE:
{
int id = getint(p);
loopv(confirms) if (confirms[i].id == id) confirms.remove(i--);
break;
}
case SV_MAPIDENT:
{
conoutf(_("%c3please %c1get the map %c2by typing %c0/getmap"), CC, CC, CC, CC);
break;
}
case SV_SWITCHNAME:
getstring(text, p);
filtername(text, text);
if(!text[0]) copystring(text, "unarmed");
if(d)
{
if(strcmp(d->name, text))
conoutf(_("%s is now known as %s"), colorname(d), text);
if(identexists("onNameChange"))
{
defformatstring(onnamechange)("onNameChange %d \"%s\"", d->clientnum, text);
execute(onnamechange);
}
copystring(d->name, text, MAXNAMELEN+1);
updateclientname(d);
}
break;
case SV_SWITCHSKIN:
loopi(2)
{
int skin = getint(p);
if(d) d->setskin(i, skin);
}
break;
case SV_THIRDPERSON:
case SV_LEVEL:
{
playerent *d = getclient(getint(p));
int info = getint(p);
if (!d || d == player1) break;
switch (type)
{
case SV_THIRDPERSON:
d->thirdperson = info;
break;
case SV_LEVEL:
info = clamp(info, 1, MAXLEVEL);
d->level = info;
if (d->pBot) d->pBot->MakeSkill(info);
break;
}
break;
}
case SV_INITCLIENT: // another client either connected or changed name/team
{
int cn = getint(p);
playerent *d = newclient(cn);
getstring(text, p);
if(!d || d == player1)
{
loopi(6) getint(p);
break;
}
filtername(text, text);
if(!text[0]) copystring(text, "unarmed");
copystring(d->name, text, MAXNAMELEN+1);
conoutf(_("connected: %s"), colorname(d));
chatonlyf(_("%s %c0joined %c2the %c1game"), colorname(d), CC, CC, CC);
if(identexists("onConnect"))
{
defformatstring(onconnect)("onConnect %d", d->clientnum);
execute(onconnect);
}
loopi(2) d->setskin(i, getint(p));
d->level = getint(p);
d->team = getint(p);
d->build = getint(p);
d->thirdperson = getint(p);
if(m_flags(gamemode)) loopi(2)
{
flaginfo &f = flaginfos[i];
if(!f.actor) f.actor = getclient(f.actor_cn);
}
updateclientname(d);
break;
}
case SV_INITAI:
{
const int cn = getint(p);
playerent *d = newclient(cn);
if(!d || d == player1)
{
// this should never happen!
loopi(6) getint(p);
break;
}
d->ownernum = getint(p);
BotManager.GetBotName(getint(p), d);
loopi(2) d->setskin(i, getint(p));
d->team = getint(p);
d->level = getint(p); // skill for bots
if(m_flags(gamemode)) loopi(2)
{
flaginfo &f = flaginfos[i];
if(!f.actor) f.actor = getclient(f.actor_cn);
}
updateclientname(d);
break;
}
case SV_REASSIGNAI:
{
playerent *d = getclient(getint(p));
const int newowner = getint(p);
getstring(text, p);
if(!d || d == player1) break;
if(isowned(d) && newowner != getclientnum())
d->removeai();
d->ownernum = newowner;
//if(d->state == CS_WAITING) d->state = CS_ALIVE; // the server will now force death before reassigning
d->plag = 0;
break;
}
case SV_CDIS:
{
const int cn = getint(p);
const int reason = getint(p);
playerent *d = getclient(cn);
if(!d || d == player1) break;
if(d->name[0])
{
extern const char *disc_reason(int reason);
conoutf(_("player %s disconnected (%s)"), colorname(d), disc_reason(reason));
chatonlyf(_("%s %c3left %c2the %c1game"), colorname(d), CC, CC, CC);
}
zapplayer(players[cn]);
if(identexists("onDisconnect"))
{
defformatstring(ondisconnect)("onDisconnect %d", d->clientnum);
execute(ondisconnect);
}
break;
}
case SV_DELAI:
{
int cn = getint(p);
playerent *d = getclient(cn);
if(!d || d == player1) break;
zapplayer(players[cn]);
break;
}
case SV_EDITMODE:
{
int val = getint(p);
if(!d) break;
d->state = val ? CS_EDITING : CS_ALIVE;
break;
}
case SV_TRYSPAWN:
{
const int enqueued = getint(p);
extern bool spawnenqueued;
spawnenqueued = (enqueued > 0);
if (enqueued) player1->respawnoffset = lastmillis - SPAWNDELAY + enqueued;
break;
}
case SV_SPAWN:
{
playerent *d = getclient(getint(p));
if(!d || d == player1 || isowned(d)) { static playerent dummy; d = &dummy; }
d->respawn(gamemode, mutators);
d->lifesequence = getint(p);
d->health = getint(p);
d->armour = getint(p);
d->perk1 = getint(p);
d->perk2 = getint(p);
d->primary = getint(p);
d->selectweapon(d->primary);
d->secondary = getint(p);
loopi(NUMGUNS) d->ammo[i] = getint(p);
loopi(NUMGUNS) d->mag[i] = getint(p);
loopi(3) d->o[i] = getint(p) / DMF;
d->yaw = getint(p);
d->state = CS_ALIVE;
d->lastspawn = lastmillis;
if (identexists("onSpawn"))
{
defformatstring(onspawn)("onSpawn %d", d->clientnum);
execute(onspawn);
}
if(d->lifesequence==0) d->resetstats(); //NEW
break;
}
case SV_SPAWNSTATE:
{
playerent *d = getclient(getint(p));
if(!d || (d != player1 && !isowned(d))) { static playerent dummy; d = &dummy; }
if ( map_quality == MAP_IS_BAD )
{
loopi(7+2*NUMGUNS+4) getint(p);
conoutf(_("map deemed unplayable - fix it before you can spawn"));
break;
}
if(d == player1)
{
if(editmode) toggleedit(true);
showscores(false);
setscope(false);
setburst(false);
}
d->respawn(gamemode, mutators);
d->lifesequence = getint(p);
d->health = getint(p);
d->armour = getint(p);
d->perk1 = getint(p);
d->perk2 = getint(p);
d->primary = getint(p);
d->selectweapon(d->primary);
d->secondary = getint(p);
loopi(NUMGUNS) d->ammo[i] = getint(p);
loopi(NUMGUNS) d->mag[i] = getint(p);
d->state = CS_ALIVE;
d->lastspawn = lastmillis;
loopi(3) d->o[i] = getint(p) / DMF;
d->yaw = getint(p);
d->pitch = d->roll = 0;
entinmap(d); // client may adjust spawn position a little
if(d == player1 && m_duke(gamemode, mutators) && !localwrongmap)
{
if (!m_zombie(gamemode) && !m_convert(gamemode, mutators)) arenaintermission = 0;
//closemenu(NULL);
conoutf(_("new round starting... fight!"));
hudeditf(HUDMSG_TIMER, "FIGHT!");
}
addmsg(SV_SPAWN, "ri5", d->clientnum, d->lifesequence, (int)(d->o.x*DMF), (int)(d->o.y*DMF), (int)(d->o.z*DMF));
d->weaponswitch(d->weapons[d->primary]);
d->weaponchanging -= SWITCHTIME(d->perk1 == PERK_TIME) / 2;
if (identexists("onSpawn"))
{
defformatstring(onspawn)("onSpawn %d", d->clientnum);
execute(onspawn);
}
if(d->lifesequence==0) d->resetstats(); //NEW
break;
}
case SV_BLEED:
{
playerent *d = getclient(getint(p));
if (d) d->addicon(eventicon::BLEED);
break;
}
case SV_HEADSHOT:
{
// make bloody stain
vec from, to;
loopi(3) from[i] = getint(p) / DMF;
loopi(3) to[i] = getint(p) / DMF;
addheadshot(from, to, getint(p));
break;
}
case SV_EXPLODE:
{
int cn = getint(p), weap = getint(p), dmg = getint(p);
playerent *d = getclient(cn);
vec o;
loopi(3) o[i] = getint(p) / DMF;
if (!d) break;
// hit effect
if (d->weapons[weap])
{
if (explosive_weap(weap) && dmg);
else if (melee_weap(weap) && dmg < 20 * HEALTHSCALE);
else d->weapons[weap]->attackhit(o);
}
// blood
if (dmg) damageeffect(dmg, o);
break;
}
case SV_SG:
{
extern vec sg[SGRAYS];
loopi(SGRAYS)
{
sg[i].x = getint(p) / DMF;
sg[i].y = getint(p) / DMF;
sg[i].z = getint(p) / DMF;
}
break;
}
case SV_SHOOT:
case SV_SHOOTC:
case SV_RICOCHET:
{
int scn = getint(p), gun = getint(p);
vec from, to;
if (type == SV_SHOOTC)
from = to = vec(0, 0, 0);
else
{
loopk(3) from[k] = getint(p) / DMF;
loopk(3) to[k] = getint(p) / DMF;
}
playerent *s = getclient(scn);
if (!s || !weapon::valid(gun) || !s->weapons[gun]) break;
if (s == player1 && (type == SV_SHOOTC || gun == GUN_GRENADE)) break;
// if it's somebody else's players, remove a bit of ammo
if (type != SV_RICOCHET && s != player1 && !isowned(s))
{
s->lastaction = lastmillis;
s->weaponchanging = 0;
s->mag[gun]--;
s->lastattackweapon = s->weapons[gun];
s->weapons[gun]->gunwait = s->weapons[gun]->info.attackdelay;
s->weapons[gun]->reloading = 0;
}
// all have to do is attackfx
s->weapons[gun]->attackfx(from, to, type == SV_RICOCHET ? -2 : -1);
s->pstatshots[gun]++; //NEW
break;
}
case SV_THROWNADE:
{
playerent *d = getclient(getint(p));
vec from, to;
loopk(3) from[k] = getint(p)/DMF;
loopk(3) to[k] = getint(p)/DNF;
int nademillis = getint(p);
if(!d) break;
d->lastaction = lastmillis;
d->weaponchanging = 0;
d->lastattackweapon = d->weapons[GUN_GRENADE];
if(d->weapons[GUN_GRENADE])
{
d->weapons[GUN_GRENADE]->attackfx(from, to, nademillis);
d->weapons[GUN_GRENADE]->reloading = 0;
}
if(d!=player1) d->pstatshots[GUN_GRENADE]++; //NEW
break;
}
case SV_THROWKNIFE:
{
playerent *d = getclient(getint(p));
vec from, to;
loopk(3) from[k] = getint(p) / DMF;
loopk(3) to[k] = getint(p) / DNF;
if (!d || d == player1 || isowned(d)) break;
d->lastaction = lastmillis;
d->lastattackweapon = d->weapons[GUN_KNIFE];
if (d->weapons[GUN_KNIFE]) d->weapons[GUN_KNIFE]->attackfx(from, to, 1);
break;
}
case SV_STREAKREADY:
{
playerent *d = getclient(getint(p));
const int streak = getint(p);
if (!d) break;
switch (streak)
{
case STREAK_AIRSTRIKE:
d->addicon(eventicon::AIRSTRIKE);
++d->airstrikes;
break;
case STREAK_DROPNADE:
d->addicon(eventicon::DROPNADE);
break;
case STREAK_REVENGE:
d->addicon(eventicon::SUICIDEBOMB);
break;
}
break;
}
case SV_STREAKUSE:
{
playerent *d = getclient(getint(p));
const int streak = getint(p), info = getint(p);
if (!d) break;
switch (streak)
{
case STREAK_AIRSTRIKE:
// may be delayed? in the future?
d->airstrikes = info;
break;
case STREAK_RADAR:
d->radarearned = lastmillis + info;
d->addicon(eventicon::RADAR);
break;
case STREAK_NUKE:
if (info > 0) // deploy nuke
{
d->nukemillis = lastmillis + info;
d->addicon(eventicon::NUKE);
audiomgr.playsound(S_CALLVOTE, SP_HIGHEST);
// add voice?
chatoutf("\f2%s is deploying a nuke! \f%s!", colorname(d), d == player1 ? "0Stay alive" : isteam(d, player1) ? "1Defend" : "3Stop it");
}
else if (!info) // nuke deployed
{
// gg...
d->nukemillis = 0;
chatoutf("\f3%s deployed a nuke!", colorname(d));
audiomgr.playsound(S_VOTEPASS, SP_HIGHEST);
}
else if (info == -2) // nuke cancelled
{
d->nukemillis = 0;
chatoutf("\f2%s lost the nuke!", colorname(d));
// add icon?
audiomgr.playsound(S_VOTEFAIL, SP_HIGHEST);
}
break;
case STREAK_JUG:
d->health = info;
d->addicon(eventicon::JUGGERNAUT);
addobit(d, OBIT_JUG, FRAG_NONE, false, NULL);
break;
case STREAK_DROPNADE:
case STREAK_REVENGE:
{
grenadeent *g = new grenadeent(d, NADETTL - MARTYRDOMTTL);
bounceents.add(g);
g->id = info;
g->nadestate = /* NS_THROWN */ 1;
g->o = d->o;
g->moveoutsidebbox((g->vel = vec(0, 0, 0)), d);
g->resetinterp();
g->inwater = hdr.waterlevel > g->o.z;
break;
}
}
break;
}
case SV_RELOAD:
{
int cn = getint(p), gun = getint(p), mag = getint(p), ammo = getint(p);
playerent *p = getclient(cn);
if (!p || gun < 0 || gun >= NUMGUNS) break;
if (p != player1 && !isowned(p) && p->weapons[gun])
p->weapons[gun]->reload(false);
p->ammo[gun] = ammo;
p->mag[gun] = mag;
if (gun == GUN_KNIFE) p->addicon(eventicon::PICKUP);
break;
}
case SV_TEAMSCORE:
{
const int team = getint(p),
points = getint(p),
flags = getint(p),
frags = getint(p),
assist = getint(p),
death = getint(p);
if (!team_isactive(team)) break;
teamscore &t = teamscores[team];
t.points = points;
t.flagscore = flags;
t.frags = frags;
t.assists = assist;
t.deaths = death;
break;
}
case SV_SCORE:
{
const int cn = getint(p),
score = getint(p),
flags = getint(p),
frags = getint(p),
assists = getint(p),
deaths = getint(p),
pointstreak = getint(p),
deathstreak = getint(p);
playerent *d = getclient(cn);
if (!d) break;
d->points = score;
d->flagscore = flags;
d->frags = frags;
d->assists = assists;
d->deaths = deaths;
d->pointstreak = pointstreak;
d->deathstreak = deathstreak;
break;
}
case SV_POINTS:
{
const int reason = getint(p), points = getint(p);
addexp(points);
if (reason < 0 || reason >= PR_MAX) break;
const char *pointreason_names[PR_MAX] =
{
"",
_("Assist"),
_("SPLAT!"),
_("HEADSHOT!"),
_("Kill Confirmed"),
_("Kill Denied"),
_("Healed Self"),
_("Healed Teammate"),
_("%c3Healed Enemy"),
_("Prevented Bleedout!"),
_("Teammate saved you!"), // Bleedout prevented by teammate
_("%c0You won!"),
_("%c1Your team wins!"),
_("%c3You lost!"),
_("%c1Domination bonus"),
_("%c0Flag secured!"),
_("%c3Flag overthrown!"),
_("%c0Buzzkill!"),
_("%c3Buzzkilled!"),
_("%c1Got own tags!"),
_("%c3Kill Denied"),
_("%c0Pro Kill"),
_("%c3Killed by Pro weapon"),
};
formatstring(text)(pointreason_names[reason], CC);
expreason(text);
break;
}
case SV_REGEN:
case SV_HEAL:
{
playerent *healer = type == SV_HEAL ? getclient(getint(p)) : NULL;
const int cn = getint(p), health = getint(p);
playerent *d = getclient(cn);
if (!d) break;
d->health = health;
d->lastregen = lastmillis;
if (!healer) break;
addobit(healer, OBIT_REVIVE, FRAG_NONE, false, d);
if (d == player1) hudoutf("\fs\f1REVIVED \f2by \fr%s", colorname(healer));
break;
}
case SV_KILL:
{
int vcn = getint(p),
acn = getint(p),
gun = getint(p),
style = getint(p),
damage = getint(p),
combo = getint(p),
assist = getint(p);
vec src; loopi(3) src[i] = getint(p)/DMF;
float killdist = getint(p) / DMF;
playerent *victim = getclient(vcn), *actor = getclient(acn);
if (!victim) break;
victim->health -= damage;
if (!actor) break;
dodamage(damage, victim, actor, gun, style, src);
victim->deathcamsrc = src;
dokill(victim, actor, gun, style, damage, combo, assist, killdist);
break;
}
case SV_DAMAGE:
{
int tcn = getint(p),
acn = getint(p),
damage = getint(p),
armour = getint(p),
health = getint(p),
gun = getint(p),
style = getint(p);
vec src; loopi(3) src[i] = getint(p)/DMF;
playerent *target = getclient(tcn), *actor = getclient(acn);
if (!target || !actor) break;
target->armour = armour;
target->health = health;
dodamage(damage, target, actor, gun, style, src);
actor->pstatdamage[gun] += damage; //NEW
break;
}
case SV_DAMAGEOBJECTIVE:
{
const int cn = getint(p);
playerent *d = getclient(cn);
if (d)
{
d->lasthit = lastmillis;
if (d == focus)
{
extern int hitsound, lasthit;
if (hitsound && lasthit != lastmillis) audiomgr.playsound(S_HITSOUND, SP_HIGH);
lasthit = lastmillis;
}
}
break;
}
case SV_RESUME:
{
loopi(MAXCLIENTS)
{
int cn = getint(p);
if(p.overread() || cn<0) break;
const int state = getint(p),
lifesequence = getint(p),
primary = getint(p),
secondary = getint(p),
perk1 = getint(p),
perk2 = getint(p),
gunselect = getint(p),
flagscore = getint(p),
frags = getint(p),
points = getint(p),
assists = getint(p),
deaths = getint(p),
health = getint(p),
armour = getint(p),
pointstreak = getint(p),
deathstreak = getint(p),
airstrikes = getint(p),
radarearned = getint(p),
nukemillis = getint(p);
int ammo[NUMGUNS], mag[NUMGUNS];
loopi(NUMGUNS) ammo[i] = getint(p);
loopi(NUMGUNS) mag[i] = getint(p);
playerent *d = newclient(cn);
if(!d) continue;
if(d!=player1) d->state = state;
d->lifesequence = lifesequence;
d->flagscore = flagscore;
d->frags = frags;
d->points = points;
d->assists = assists;
d->deaths = deaths;
d->pointstreak = pointstreak;
d->deathstreak = deathstreak;
d->airstrikes = airstrikes;
d->radarearned = lastmillis + radarearned;
d->nukemillis = lastmillis + nukemillis;
if(d!=player1)
{
d->primary = primary;
d->secondary = secondary;
d->selectweapon(gunselect);
d->health = health;
d->armour = armour;
d->perk1 = perk1;
d->perk2 = perk2;
memcpy(d->ammo, ammo, sizeof(ammo));
memcpy(d->mag, mag, sizeof(mag));
if(d->lifesequence==0) d->resetstats(); //NEW
}
}
break;
}
case SV_DISCSCORES:
{
discscores.shrink(0);
int team;
while((team = getint(p)) >= 0 && !p.overread())
{
discscore &ds = discscores.add();
ds.team = team;
getstring(text, p);
filtername(ds.name, text);
ds.flags = getint(p);
ds.frags = getint(p);
ds.assists = getint(p);
ds.deaths = getint(p);
ds.points = getint(p);
}
break;
}
case SV_ITEMSPAWN:
setspawn(getint(p));
break;
case SV_ITEMACC:
{
int i = getint(p), cn = getint(p), spawntime = getint(p);
playerent *d = getclient(cn);
pickupeffects(i, d, spawntime);
break;
}
case SV_EDITH: // coop editing messages, should be extended to include all possible editing ops
case SV_EDITT:
case SV_EDITS:
case SV_EDITD:
case SV_EDITE:
{
int x = getint(p);
int y = getint(p);
int xs = getint(p);
int ys = getint(p);
int v = getint(p);
block b = { x, y, xs, ys };
switch(type)
{
case SV_EDITH: editheightxy(v!=0, getint(p), b); break;
case SV_EDITT: edittexxy(v, getint(p), b); break;
case SV_EDITS: edittypexy(v, b); break;
case SV_EDITD: setvdeltaxy(v, b); break;
case SV_EDITE: editequalisexy(v!=0, b); break;
}
break;
}
case SV_EDITW:
{
const int newwaterlevel = getint(p);
loopi(4) hdr.watercolor[i] = getint(p);
if (newwaterlevel == hdr.waterlevel) break;
hdr.waterlevel = newwaterlevel;
if (d && d != player1)
conoutf(_("%s changed the water-level to %d"), colorname(d), hdr.waterlevel);
break;
}
case SV_NEWMAP:
{
int size = getint(p);
if(size>=0) empty_world(size, true);
else empty_world(-1, true);
if(d && d!=player1)
conoutf(size>=0 ? _("%s started a new map of size %d") : _("%s enlarged the map to size %d"), colorname(d), sfactor);
break;
}
case SV_EDITENT: // coop edit of ent
{
uint i = getint(p);
while((uint)ents.length()<=i) ents.add().type = NOTUSED;
int to = ents[i].type;
if(ents[i].type==SOUND)
{
entity &e = ents[i];
entityreference entref(&e);
location *loc = audiomgr.locations.find(e.attr1, &entref, mapsounds);
if(loc)
loc->drop();
}
ents[i].type = getint(p);
ents[i].x = getint(p);
ents[i].y = getint(p);
ents[i].z = getint(p);
ents[i].attr1 = getint(p);
ents[i].attr2 = getint(p);
ents[i].attr3 = getint(p);
ents[i].attr4 = getint(p);
ents[i].spawned = false;
if(ents[i].type==LIGHT || to==LIGHT) calclight();
if(ents[i].type==SOUND) audiomgr.preloadmapsound(ents[i]);
break;
}
case SV_PINGPONG:
{
addmsg(SV_CLIENTPING, "i", totalmillis - getint(p));
break;
}
case SV_CLIENTPING:
{
int cn = getint(p), ping = getint(p);
if(cn == getclientnum())
player1->ping = ping;
loopv(players)
if(players[i] && (i == cn || players[i]->ownernum == cn))
players[i]->ping = ping;
break;
}
case SV_TIMEUP:
{
int curgamemillis = getint(p);
int curgamelimit = getint(p);
timeupdate(curgamemillis, curgamelimit);
break;
}
case SV_WEAPCHANGE:
{
int cn = getint(p), gun = getint(p);
playerent *d = getclient(cn);
if (!d || gun < 0 || gun >= NUMGUNS) break;
d->zoomed = 0;
d->weaponswitch(d->weapons[gun]);
//if(!d->weaponchanging) d->selectweapon(gun);
break;
}
case SV_QUICKSWITCH:
{
int cn = getint(p);
playerent *d = getclient(cn);
if (!d) break;
d->weaponchanging = lastmillis - 1 - (SWITCHTIME(d->perk1 == PERK_TIME) / 2);
d->nextweaponsel = d->weaponsel = d->weapons[d->primary];
break;
}
case SV_SERVMSG:
getstring(text, p);
conoutf("%s", text);
break;
case SV_FLAGINFO:
{
int flag = getint(p);
if(flag<0 || flag>1)
{
neterr("invalid SV_FLAGINFO flag number");
break;
}
flaginfo &f = flaginfos[flag];
f.state = getint(p);
switch(f.state)
{
case CTFF_STOLEN:
flagstolen(flag, getint(p));
break;
case CTFF_DROPPED:
{
float x = getuint(p)/DMF;
float y = getuint(p)/DMF;
float z = getuint(p)/DMF;
flagdropped(flag, x, y, z);
break;
}
case CTFF_INBASE:
flaginbase(flag);
break;
case CTFF_IDLE:
flagidle(flag);
break;
}
break;
}
case SV_FLAGMSG:
{
int flag = getint(p);
int message = getint(p);
int actor = getint(p);
int flagtime = message == FA_KTFSCORE ? getint(p) : -1;
flagmsg(flag, message, actor, flagtime);
break;
}
case SV_FLAGSECURE:
{
const int ent = getint(p), team = getint(p), enemy = getint(p), overthrown = getint(p);
if (!ents.inrange(ent) || ents[ent].type != CTF_FLAG || (!team_isactive(team) && team != TEAM_SPECT) || ents[ent].attr2 < 2) break;
ents[ent].attr2 = 2 + team;
ents[ent].attr3 = enemy;
ents[ent].attr4 = overthrown;
break;
}
case SV_FLAGOVERLOAD:
{
const int team = getint(p), health = getint(p);
if (team < 0 || team >= 2) break;
flaginfos[team].flagent->attr3 = health;
break;
}
case SV_ARENAWIN:
{
int acn = getint(p);
playerent *alive = getclient(acn);
// check for multiple survivors
bool multi = false;
if (m_team(gamemode, mutators) && alive)
{
#define teammate(p) (p != alive && p->state == CS_ALIVE && isteam(p, alive))
if (teammate(player1)) multi = true;
else loopv(players) if (players[i] && teammate(players[i])){ multi = true; break; }
#undef teammate
}
conoutf(_("the round is over! next round in 5 seconds..."));
// no survivors
if (acn == -1) hudoutf(_("%c3everyone died; epic fail!"), CC);
// instead of waiting for bots to battle it out...
else if (acn == -2) hudoutf(_("the bots have won the round!"));
// should not happen? better safe than sorry
else if (!alive) hudoutf("unknown winner...?");
// Teams
else if (m_team(gamemode, mutators) && multi)
{
if (alive->team == player1->team)
hudoutf(_("your team is the victor!"));
else
hudoutf(_("your team was dominated!"));
}
// FFA or one team member
else if (alive == player1) hudoutf(_("you are the victor!"));
else hudoutf(_("%s is the victor!"), colorname(alive));
// set intermission time
arenaintermission = lastmillis;
break;
}
case SV_ZOMBIESWIN:
{
const int info = getint(p), round = (info >> 1) & 0x7F;
if (round > MAXZOMBIEROUND) hudoutf(_("%c0the humans have prevailed!"), CC);
else if (info & 1) hudoutf(_("%c2Get ready for wave %c1%d%c4; %c0the humans held off the zombies!"), CC, CC, round, CC, CC);
else hudoutf(_("%c2Get ready for wave %c1%d%c4; %c3the zombies have overrun the humans!"), CC, CC, round, CC, CC);
loopv(players)
{
playerent *p = players[i];
if (!p || team_isspect(p->team))
continue;
// Remove grenades
if (round == MAXZOMBIEROUND || p->team == TEAM_CLA)
removebounceents(p);
}
arenaintermission = lastmillis;
break;
}
case SV_CONVERTWIN:
{
hudoutf(_("%c1%cbeveryone has been converted!"), CC, CC);
arenaintermission = lastmillis;
break;
}
case SV_FORCEDEATH:
{
int cn = getint(p);
playerent *d = newclient(cn);
if(!d) break;
deathstate(d);
break;
}
case SV_CLAIMPRIV:
{
int cl = getint(p), r = getint(p), t = getint(p);
playerent *d = getclient(cl);
const char *n = (d == player1) ? "\f1you" : d ? colorname(d) : "\f2[a connecting admin]";
switch (t){
case 0:
case 1:
chatoutf(_("%s %s %s access"), n, t ? _("relinquished") : _("claimed"), privname(r));
break;
case 2:
if (!r) hudoutf(_("%c2this password is not privileged; it is a deban password!"), CC);
else if (d == player1) hudoutf(_("you already have %s access"), privname(r));
else hudoutf(_("there is already another %s (%s)"), privname(r), n);
break;
}
break;
}
case SV_SETPRIV:
{
int c = getint(p), priv = getint(p);
playerent *pl = newclient(c);
if(!pl) break;
pl->clientrole = priv;
break;
}
case SV_AUTH_ACR_REQ:
{
int sauthtoken = getint(p);
uchar buf[48+128];
// We choose the first 384 bits
loopi(48)
{
int num = randomMT();
buf[i++] = num;
buf[i++] = num >> 8;
buf[i++] = num >> 16;
buf[i] = num >> 24;
}
// Last 1024 bits are specified by the master-server
p.get(&buf[48], 128);
extern int authtoken;
if (sauthtoken != authtoken)
{
conoutf("server challenged incorrectly");
break;
}
authtoken = -1;
conoutf("server is challenging authentication details");
extern char *authkey;
uchar hash[32];
hmac_sha256(buf, sizeof(buf)/sizeof(*buf), (uchar *)authkey, strlen(authkey), hash);
/*
if (hash_failed)
{
conoutf("could not compute message digest");
break;
}
*/
uchar buf2[MAXTRANS];
ucharbuf p(buf2, MAXTRANS);
putint(p, SV_AUTH_ACR_CHAL);
p.put(buf, 48);
p.put(hash, 32);
addmsgraw(p);
break;
}
case SV_AUTH_ACR_CHAL:
{
switch(getint(p))
{
case 0:
conoutf("please wait, requesting credential match");
break;
case 1:
conoutf("waiting for previous attempt...");
break;
case 2:
conoutf("not connected to authentication server");
break;
case 3:
conoutf("authority request failed, please check your credentials");
break;
case 4:
conoutf("please wait, requesting authentication");
break;
case 5:
{
const int cn = getint(p), priv = getint(p);
getstring(text, p);
playerent *d = getclient(cn);
if (!d) break;
filtertext(text, text, 1, MAXNAMELEN);
d->build |= 0x02;
const char *privn, privc = privcolor(priv);
if(priv >= CR_DEFAULT && priv <= CR_MAX)
privn = privname(priv);
else
privn = "name-only";
conoutf("%s \f1identified \f0as \f2'\f9%s\f2' \f4for \f%c%s", d == player1 ? "you are" : colorname(d), text, privc, privn);
break;
}
case 6:
conoutf("please wait %.3f seconds to request another challenge", getint(p) / 1000.f);
break;
default:
conoutf("server sent undefined authority message");
break;
}
break;
}
case SV_TEAMDENY:
{
int t = getint(p);
if (t == 0x10) conoutf(_("%c3you were forced into this team by a vote and may not switch"), CC);
else if (t == 0x11) conoutf(_("%c3you may not switch teams in this mode!"), CC);
else if (t == 0x12) conoutf(_("%c3match team size is set -- cannot switch sides"), CC);
else conoutf(_("%cteam %s is full!"), CC, team_string(t & 0xF));
break;
}
case SV_SETTEAM:
{
int cn = getint(p), fnt = getint(p), ftr = fnt >> 4; fnt &= 0xf;
playerent *d = newclient(cn);
if (!d) break;
if (d->team == fnt)
{
// no change
switch (ftr){
case FTR_PLAYERWISH:
if (d == player1) hudoutf(_("%c1you %c2did not switch teams"), CC, CC);
else conoutf(_("%c2%s did not switch teams"), CC, colorname(d));
break;
case FTR_AUTO:
if (d == player1) hudoutf(_("%c1you %c2stay in team %s"), CC, CC, team_string(fnt));
else if (d->ownernum < 0) conoutf(_("%c2%s stays on team %s"), CC, colorname(d), team_string(fnt));
break;
}
}
else
{
switch (ftr)
{
case FTR_PLAYERWISH:
if (d == player1) hudoutf(_("%c1you %c2are now in team %s"), CC, CC, team_string(fnt));
else conoutf(_("%c2%s switched to team %s"), CC, colorname(d), team_string(fnt));
break;
case FTR_AUTO:
if (d == player1) hudoutf(_("%c2the server %c1forced you %c2to team %s"), CC, CC, CC, team_string(fnt));
else if(d->ownernum < 0) conoutf(_("%c2the server forced %s to team %s"), CC, colorname(d), team_string(fnt));
break;
}
d->team = fnt;
// client version of removeexplosives()
removebounceents(d);
}
break;
}
case SV_SERVERMODE:
{
int sm = getint(p);
servstate.autoteam = sm & 1;
servstate.mastermode = (sm >> 2) & MM_MASK;
servstate.matchteamsize = sm >> 4;
break;
}
case SV_CALLVOTE:
{
int cn = getint(p), type = getint(p), remain = getint(p);
float ratio = getint(p) / 32000.f;
playerent *d = getclient(cn);
if( type < 0 || type >= SA_NUM ) break;
// vote data storage
static votedata vote = votedata(text);
vote = votedata(text); // reset it
// vote parsing
switch(type)
{
case SA_BAN:
case SA_MAP:
vote.int2 = getint(p);
// fallthrough
case SA_KICK:
vote.int1 = getint(p);
// fallthrough
case SA_SERVERDESC:
getstring(text, p);
break;
case SA_FORCETEAM:
case SA_GIVEADMIN:
vote.int2 = getint(p);
// fallthrough
case SA_MASTERMODE:
case SA_AUTOTEAM:
case SA_RECORDDEMO:
case SA_CLEARDEMOS:
case SA_BOTBALANCE:
case SA_SUBDUE:
case SA_REVOKE:
vote.int1 = getint(p);
// fallthrough
case SA_STOPDEMO:
// compatibility
default:
case SA_REMBANS:
case SA_SHUFFLETEAMS:
break;
}
if(type < 0 || type >= SA_NUM) break;
votedisplayinfo *v = new votedisplayinfo(d, type, totalmillis + remain, votestring(type, vote), ratio);
displayvote(v);
onCallVote(type, v->owner->clientnum, vote);
break;
}
case SV_CALLVOTEERR:
{
int errn = getint(p);
callvoteerr(errn);
onChangeVote( 1, errn, -1 );
break;
}
case SV_VOTE:
{
const int cn = getint(p), vote = getint(p);
if (!curvote) break;
playerent *d = getclient(cn);
if (!d || vote < 0 || vote >= VOTE_NUM) break;
d->vote = vote;
if (vote == VOTE_NEUTRAL) break;
if ((/*voteid*/ true || d == player1) && (d != curvote->owner || curvote->millis + 100 < lastmillis))
conoutf("%s %c6(%d) %c2voted %s", (d == player1) ? "\f1you" : d->name, CC, cn, CC, vote == VOTE_NO ? "\f3no" : "\f0yes");
onChangeVote( 2, vote, cn );
break;
}
case SV_VOTESTATUS:
{
if (!curvote)
{
loopi(VOTE_NUM) getint(p);
break;
}
loopi(VOTE_NUM)
curvote->stats[i] = getint(p);
curvote->recompute();
break;
}
case SV_VOTERESULT:
{
int vres = getint(p), vetocn = getint(p);
playerent *d = getclient(vetocn);
curvote->veto = (d != NULL);
if (curvote && vres >= 0 && vres < VOTE_NUM)
{
curvote->result = vres;
curvote->millis = totalmillis + 5000;
if (d) conoutf("\f1%s vetoed the vote to %s", colorname(d), vres == VOTE_YES ? "\f0pass" : "\f3fail");
conoutf(vres == VOTE_YES ? _("vote %c0passed") : _("vote %c3failed"), CC);
audiomgr.playsound(vres == VOTE_YES ? S_VOTEPASS : S_VOTEFAIL, SP_HIGH);
if (identexists("onVoteEnd")) execute("onVoteEnd");
extern int votepending;
votepending = 0;
}
onChangeVote( 3, vres, vetocn );
break;
}
case SV_WHOIS:
{
const int cn = getint(p);
playerent *pl = getclient(cn);
if (cn == -1)
{
const int owner = getint(p), wants = getint(p);
pl = getclient(owner);
playerent *wanted = getclient(wants);
conoutf(_("%s requests whois on %s"), pl ? colorname(pl) : "someone", wanted ? colorname(wanted) : "someone");
}
else
{
uchar ip[16];
loopi(16) ip[i] = p.get();
const int mask = getint(p), port = getint(p);
getstring(text, p);
filtertext(text, text);
string cip;
copystring(cip, ip6toa(ip));
if (mask < 128) concatformatstring(cip, "/%d", mask);
conoutf(_("whois on %s returned [%s]:%d"), pl ? colorname(pl) : "unknown", cip, port);
if (text[0])
conoutf(_("this user is authed as '%s'"), text);
else
conoutf(_("this user is not authed"));
}
break;
}
case SV_LISTDEMOS:
{
int demos = getint(p);
if(!demos) conoutf(_("no demos available"));
else loopi(demos)
{
getstring(text, p);
conoutf("%d. %s", i+1, text);
}
break;
}
case SV_DEMOPLAYBACK:
{
string demofile;
extern char *curdemofile;
getstring(demofile, p, MAXSTRLEN);
watchingdemo = demoplayback = demofile[0] != '\0';
DELETEA(curdemofile);
if(demoplayback)
{
curdemofile = newstring(demofile);
player1->resetspec();
player1->state = CS_DEAD;
player1->team = TEAM_SPECT;
}
else
{
// cleanups
curdemofile = newstring("n/a");
loopv(players) zapplayer(players[i]);
clearvote();
player1->state = CS_ALIVE;
player1->resetspec();
}
player1->clientnum = getint(p);
break;
}
default:
neterr("type");
return;
}
}
#ifdef _DEBUG
protocoldebug(false);
#endif
}
void setDemoFilenameFormat(char *fmt)
{
extern string demofilenameformat;
if(fmt && fmt[0]!='\0')
{
copystring(demofilenameformat, fmt);
} else copystring(demofilenameformat, DEFDEMOFILEFMT); // reset to default if passed empty string - or should we output the current value in this case?
}
COMMANDN(demonameformat, setDemoFilenameFormat, "s");
void setDemoTimestampFormat(char *fmt)
{
extern string demotimestampformat;
if(fmt && fmt[0]!='\0')
{
copystring(demotimestampformat, fmt);
} else copystring(demotimestampformat, DEFDEMOTIMEFMT); // reset to default if passed empty string - or should we output the current value in this case?
}
COMMANDN(demotimeformat, setDemoTimestampFormat, "s");
void setDemoTimeLocal(int *truth)
{
extern int demotimelocal;
demotimelocal = *truth == 0 ? 0 : 1;
}
COMMANDN(demotimelocal, setDemoTimeLocal, "i");
void getdemonameformat() { extern string demofilenameformat; result(demofilenameformat); } COMMAND(getdemonameformat, "");
void getdemotimeformat() { extern string demotimestampformat; result(demotimestampformat); } COMMAND(getdemotimeformat, "");
void getdemotimelocal() { extern int demotimelocal; intret(demotimelocal); } COMMAND(getdemotimelocal, "");
const char *parseDemoFilename(char *srvfinfo)
{
int gmode = 0; //-314;
int gmuts = G_M_NONE;
int mplay = 0;
int mdrop = 0;
int stamp = 0;
string srvmap;
if(srvfinfo && srvfinfo[0])
{
int fip = 0;
char sep[] = ":";
char *pch;
pch = strtok (srvfinfo,sep);
while (pch != NULL && fip < 5)
{
fip++;
switch(fip)
{
case 1: gmode = atoi(pch); break;
case 5: gmuts = atoi(pch); break;
case 2: mplay = atoi(pch); break;
case 3: mdrop = atoi(pch); break;
case 4: stamp = atoi(pch); break;
default: break;
}
pch = strtok (NULL, sep);
}
copystring(srvmap, pch ? pch : "unknown_map");
}
extern const char *getDemoFilename(int gmode, int gmuts, int mplay, int mdrop, int tstamp, char *srvmap);
return getDemoFilename(gmode, gmuts, mplay, mdrop, stamp, srvmap);
}
void receivefile(uchar *data, int len)
{
static char text[MAXTRANS];
ucharbuf p(data, len);
int type = getint(p);
/*
data += p.length();
len -= p.length();
*/
switch(type)
{
case SV_GETDEMO:
{
getstring(text, p);
extern string demosubpath;
defformatstring(demofn)("%s", parseDemoFilename(text));
defformatstring(fname)("demos/%s%s.dmo", demosubpath, demofn);
copystring(demosubpath, "");
//data += strlen(text);
int demosize = getint(p);
if(p.remaining() < demosize)
{
p.forceoverread();
break;
}
path(fname);
stream *demo = openrawfile(fname, "wb");
if(!demo)
{
conoutf(_("failed writing to \"%s\""), fname);
return;
}
conoutf(_("received demo \"%s\""), fname);
demo->write(&p.buf[p.len], demosize);
delete demo;
break;
}
case SV_RECVMAP:
{
getstring(text, p);
conoutf(_("received map \"%s\" from server, reloading.."), text);
int mapsize = getint(p);
int cfgsize = getint(p);
int cfgsizegz = getint(p);
/* int revision = */ getint(p);
int size = mapsize + cfgsizegz;
if(MAXMAPSENDSIZE < mapsize + cfgsizegz || cfgsize > MAXCFGFILESIZE) { // sam's suggestion
conoutf(_("map %s is too large to receive"), text);
} else {
if(p.remaining() < size)
{
p.forceoverread();
break;
}
if(securemapcheck(text))
{
p.len += size;
break;
}
writemap(path(text), mapsize, &p.buf[p.len]);
p.len += mapsize;
writecfggz(path(text), cfgsize, cfgsizegz, &p.buf[p.len]);
p.len += cfgsizegz;
}
break;
}
default:
p.len = 0;
parsemessages(-1, NULL, p);
break;
}
}
void servertoclient(int chan, uchar *buf, int len, bool demo) // processes any updates from the server
{
ucharbuf p(buf, len);
switch(chan)
{
case 0: parsepositions(p); break;
case 1: parsemessages(-1, NULL, p, demo); break;
case 2: receivefile(p.buf, p.maxlen); break;
}
}
void localservertoclient(int chan, uchar *buf, int len, bool demo) // processes any updates from the server
{
// pktlogger.queue(enet_packet_create (buf, len, 0)); // log local & demo packets
servertoclient(chan, buf, len, demo);
}
Advertisement
153
pages
Clients2c.cpp
Advertisement