Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Client

Download as txt, pdf, or txt
Download as txt, pdf, or txt
You are on page 1of 37

#include "game.

h"

namespace game
{
VARP(minradarscale, 0, 384, 10000);
VARP(maxradarscale, 1, 1024, 10000);
VARP(radarteammates, 0, 1, 1);
FVARP(minimapalpha, 0, 1, 1);

float calcradarscale()
{
return clamp(max(minimapradius.x, minimapradius.y)/3, float(minradarscale),
float(maxradarscale));
}

void drawminimap(fpsent *d, float x, float y, float s)


{
vec pos = vec(d->o).sub(minimapcenter).mul(minimapscale).add(0.5f), dir;
vecfromyawpitch(camera1->yaw, 0, 1, 0, dir);
float scale = calcradarscale();
glBegin(GL_TRIANGLE_FAN);
loopi(16)
{
vec tc = vec(dir).rotate_around_z(i/16.0f*2*M_PI);
glTexCoord2f(pos.x + tc.x*scale*minimapscale.x, pos.y +
tc.y*scale*minimapscale.y);
vec v = vec(0, -1, 0).rotate_around_z(i/16.0f*2*M_PI);
glVertex2f(x + 0.5f*s*(1.0f + v.x), y + 0.5f*s*(1.0f + v.y));
}
glEnd();
}

void drawradar(float x, float y, float s)


{
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0.0f, 0.0f); glVertex2f(x, y);
glTexCoord2f(1.0f, 0.0f); glVertex2f(x+s, y);
glTexCoord2f(0.0f, 1.0f); glVertex2f(x, y+s);
glTexCoord2f(1.0f, 1.0f); glVertex2f(x+s, y+s);
glEnd();
}

void drawteammate(fpsent *d, float x, float y, float s, fpsent *o, float scale)
{
vec dir = d->o;
dir.sub(o->o).div(scale);
float dist = dir.magnitude2(), maxdist = 1 - 0.05f - 0.05f;
if(dist >= maxdist) dir.mul(maxdist/dist);
dir.rotate_around_z(-camera1->yaw*RAD);
float bs = 0.06f*s,
bx = x + s*0.5f*(1.0f + dir.x),
by = y + s*0.5f*(1.0f + dir.y);
vec v(-0.5f, -0.5f, 0);
v.rotate_around_z((90+o->yaw-camera1->yaw)*RAD);
glTexCoord2f(0.0f, 0.0f); glVertex2f(bx + bs*v.x, by + bs*v.y);
glTexCoord2f(1.0f, 0.0f); glVertex2f(bx + bs*v.y, by - bs*v.x);
glTexCoord2f(1.0f, 1.0f); glVertex2f(bx - bs*v.x, by - bs*v.y);
glTexCoord2f(0.0f, 1.0f); glVertex2f(bx - bs*v.y, by + bs*v.x);
}
void drawteammates(fpsent *d, float x, float y, float s)
{
if(!radarteammates) return;
float scale = calcradarscale();
int alive = 0, dead = 0;
loopv(players)
{
fpsent *o = players[i];
if(o != d && o->state == CS_ALIVE && isteam(o->team, d->team))
{
if(!alive++)
{
settexture(isteam(d->team, player1->team) ?
"packages/hud/blip_blue_alive.png" : "packages/hud/blip_red_alive.png");
glBegin(GL_QUADS);
}
drawteammate(d, x, y, s, o, scale);
}
}
if(alive) glEnd();
loopv(players)
{
fpsent *o = players[i];
if(o != d && o->state == CS_DEAD && isteam(o->team, d->team))
{
if(!dead++)
{
settexture(isteam(d->team, player1->team) ?
"packages/hud/blip_blue_dead.png" : "packages/hud/blip_red_dead.png");
glBegin(GL_QUADS);
}
drawteammate(d, x, y, s, o, scale);
}
}
if(dead) glEnd();
}

#include "capture.h"
#include "ctf.h"
#include "collect.h"

clientmode *cmode = NULL;


captureclientmode capturemode;
ctfclientmode ctfmode;
collectclientmode collectmode;

void setclientmode()
{
if(m_capture) cmode = &capturemode;
else if(m_ctf) cmode = &ctfmode;
else if(m_collect) cmode = &collectmode;
else cmode = NULL;
}

bool senditemstoserver = false, sendcrc = false; // after a map change, since


server doesn't have map data
int lastping = 0;
bool connected = false, remote = false, demoplayback = false, gamepaused =
false;
int sessionid = 0, mastermode = MM_OPEN, gamespeed = 100;
string servinfo = "", servauth = "", connectpass = "";

VARP(deadpush, 1, 2, 20);

void switchname(const char *name)


{
if(name[0])
{
filtertext(player1->name, name, false, MAXNAMELEN);
if(!player1->name[0]) copystring(player1->name, "unnamed");
addmsg(N_SWITCHNAME, "rs", player1->name);
}
else conoutf("your name is: %s", colorname(player1));
}
ICOMMAND(name, "s", (char *s), switchname(s));
ICOMMAND(getname, "", (), result(player1->name));

void switchteam(const char *team)


{
if(team[0])
{
if(player1->clientnum < 0) filtertext(player1->team, team, false,
MAXTEAMLEN);
else addmsg(N_SWITCHTEAM, "rs", team);
}
else conoutf("your team is: %s", player1->team);
}
ICOMMAND(team, "s", (char *s), switchteam(s));
ICOMMAND(getteam, "", (), result(player1->team));

void switchplayermodel(int playermodel)


{
player1->playermodel = playermodel;
addmsg(N_SWITCHMODEL, "ri", player1->playermodel);
}

struct authkey
{
char *name, *key, *desc;
int lastauth;

authkey(const char *name, const char *key, const char *desc)


: name(newstring(name)), key(newstring(key)), desc(newstring(desc)),
lastauth(0)
{
}

~authkey()
{
DELETEA(name);
DELETEA(key);
DELETEA(desc);
}
};
vector<authkey *> authkeys;
authkey *findauthkey(const char *desc = "")
{
loopv(authkeys) if(!strcmp(authkeys[i]->desc, desc) && !
strcasecmp(authkeys[i]->name, player1->name)) return authkeys[i];
loopv(authkeys) if(!strcmp(authkeys[i]->desc, desc)) return authkeys[i];
return NULL;
}

VARP(autoauth, 0, 1, 1);

void addauthkey(const char *name, const char *key, const char *desc)
{
loopvrev(authkeys) if(!strcmp(authkeys[i]->desc, desc) && !
strcmp(authkeys[i]->name, name)) delete authkeys.remove(i);
if(name[0] && key[0]) authkeys.add(new authkey(name, key, desc));
}
ICOMMAND(authkey, "sss", (char *name, char *key, char *desc), addauthkey(name,
key, desc));

bool hasauthkey(const char *name, const char *desc)


{
if(!name[0] && !desc[0]) return authkeys.length() > 0;
loopvrev(authkeys) if(!strcmp(authkeys[i]->desc, desc) && !
strcmp(authkeys[i]->name, name)) return true;
return false;
}

ICOMMAND(hasauthkey, "ss", (char *name, char *desc), intret(hasauthkey(name,


desc) ? 1 : 0));

void genauthkey(const char *secret)


{
if(!secret[0]) { conoutf(CON_ERROR, "you must specify a secret password");
return; }
vector<char> privkey, pubkey;
genprivkey(secret, privkey, pubkey);
conoutf("private key: %s", privkey.getbuf());
conoutf("public key: %s", pubkey.getbuf());
}
COMMAND(genauthkey, "s");

void saveauthkeys()
{
stream *f = openfile("auth.cfg", "w");
if(!f) { conoutf(CON_ERROR, "failed to open auth.cfg for writing"); return;
}
loopv(authkeys)
{
authkey *a = authkeys[i];
f->printf("authkey %s %s %s\n", escapestring(a->name), escapestring(a-
>key), escapestring(a->desc));
}
conoutf("saved authkeys to auth.cfg");
delete f;
}
COMMAND(saveauthkeys, "");

void sendmapinfo()
{
if(!connected) return;
sendcrc = true;
if(player1->state!=CS_SPECTATOR || player1->privilege || !remote)
senditemstoserver = true;
}

void writeclientinfo(stream *f)


{
f->printf("name %s\n", escapestring(player1->name));
}

bool allowedittoggle()
{
if(editmode) return true;
if(connected && multiplayer(false) && !m_edit)
{
conoutf(CON_ERROR, "editing in multiplayer requires coop edit mode
(1)");
return false;
}
if(identexists("allowedittoggle") && !execute("allowedittoggle"))
return false;
return true;
}

void edittoggled(bool on)


{
addmsg(N_EDITMODE, "ri", on ? 1 : 0);
if(player1->state==CS_DEAD) deathstate(player1, true);
else if(player1->state==CS_EDITING && player1->editstate==CS_DEAD)
showscores(false);
disablezoom();
player1->suicided = player1->respawned = -2;
}

const char *getclientname(int cn)


{
fpsent *d = getclient(cn);
return d ? d->name : "";
}
ICOMMAND(getclientname, "i", (int *cn), result(getclientname(*cn)));

const char *getclientteam(int cn)


{
fpsent *d = getclient(cn);
return d ? d->team : "";
}
ICOMMAND(getclientteam, "i", (int *cn), result(getclientteam(*cn)));

int getclientmodel(int cn)


{
fpsent *d = getclient(cn);
return d ? d->playermodel : -1;
}
ICOMMAND(getclientmodel, "i", (int *cn), intret(getclientmodel(*cn)));

const char *getclienticon(int cn)


{
fpsent *d = getclient(cn);
if(!d || d->state==CS_SPECTATOR) return "spectator";
const playermodelinfo &mdl = getplayermodelinfo(d);
return m_teammode ? (isteam(player1->team, d->team) ? mdl.blueicon :
mdl.redicon) : mdl.ffaicon;
}
ICOMMAND(getclienticon, "i", (int *cn), result(getclienticon(*cn)));

bool ismaster(int cn)


{
fpsent *d = getclient(cn);
return d && d->privilege >= PRIV_MASTER;
}
ICOMMAND(ismaster, "i", (int *cn), intret(ismaster(*cn) ? 1 : 0));

bool isauth(int cn)


{
fpsent *d = getclient(cn);
return d && d->privilege >= PRIV_AUTH;
}
ICOMMAND(isauth, "i", (int *cn), intret(isauth(*cn) ? 1 : 0));

bool isadmin(int cn)


{
fpsent *d = getclient(cn);
return d && d->privilege >= PRIV_ADMIN;
}
ICOMMAND(isadmin, "i", (int *cn), intret(isadmin(*cn) ? 1 : 0));

ICOMMAND(getmastermode, "", (), intret(mastermode));


ICOMMAND(mastermodename, "i", (int *mm), result(server::mastermodename(*mm,
"")));

bool isspectator(int cn)


{
fpsent *d = getclient(cn);
return d && d->state==CS_SPECTATOR;
}
ICOMMAND(isspectator, "i", (int *cn), intret(isspectator(*cn) ? 1 : 0));

bool isai(int cn, int type)


{
fpsent *d = getclient(cn);
int aitype = type > 0 && type < AI_MAX ? type : AI_BOT;
return d && d->aitype==aitype;
}
ICOMMAND(isai, "ii", (int *cn, int *type), intret(isai(*cn, *type) ? 1 : 0));

int parseplayer(const char *arg)


{
char *end;
int n = strtol(arg, &end, 10);
if(*arg && !*end)
{
if(n!=player1->clientnum && !clients.inrange(n)) return -1;
return n;
}
// try case sensitive first
loopv(players)
{
fpsent *o = players[i];
if(!strcmp(arg, o->name)) return o->clientnum;
}
// nothing found, try case insensitive
loopv(players)
{
fpsent *o = players[i];
if(!strcasecmp(arg, o->name)) return o->clientnum;
}
return -1;
}
ICOMMAND(getclientnum, "s", (char *name), intret(name[0] ? parseplayer(name) :
player1->clientnum));

void listclients(bool local, bool bots)


{
vector<char> buf;
string cn;
int numclients = 0;
if(local && connected)
{
formatstring(cn)("%d", player1->clientnum);
buf.put(cn, strlen(cn));
numclients++;
}
loopv(clients) if(clients[i] && (bots || clients[i]->aitype == AI_NONE))
{
formatstring(cn)("%d", clients[i]->clientnum);
if(numclients++) buf.add(' ');
buf.put(cn, strlen(cn));
}
buf.add('\0');
result(buf.getbuf());
}
ICOMMAND(listclients, "bb", (int *local, int *bots), listclients(*local>0,
*bots!=0));

void clearbans()
{
addmsg(N_CLEARBANS, "r");
}
COMMAND(clearbans, "");

void kick(const char *victim, const char *reason)


{
int vn = parseplayer(victim);
if(vn>=0 && vn!=player1->clientnum) addmsg(N_KICK, "ris", vn, reason);
}
COMMAND(kick, "ss");

void authkick(const char *desc, const char *victim, const char *reason)
{
authkey *a = findauthkey(desc);
int vn = parseplayer(victim);
if(a && vn>=0 && vn!=player1->clientnum)
{
a->lastauth = lastmillis;
addmsg(N_AUTHKICK, "rssis", a->desc, a->name, vn, reason);
}
}
ICOMMAND(authkick, "ss", (const char *victim, const char *reason), authkick("",
victim, reason));
ICOMMAND(sauthkick, "ss", (const char *victim, const char *reason),
if(servauth[0]) authkick(servauth, victim, reason));
ICOMMAND(dauthkick, "sss", (const char *desc, const char *victim, const char
*reason), if(desc[0]) authkick(desc, victim, reason));

vector<int> ignores;

void ignore(int cn)


{
fpsent *d = getclient(cn);
if(!d || d == player1) return;
conoutf("ignoring %s", d->name);
if(ignores.find(cn) < 0) ignores.add(cn);
}

void unignore(int cn)


{
if(ignores.find(cn) < 0) return;
fpsent *d = getclient(cn);
if(d) conoutf("stopped ignoring %s", d->name);
ignores.removeobj(cn);
}

bool isignored(int cn) { return ignores.find(cn) >= 0; }

ICOMMAND(ignore, "s", (char *arg), ignore(parseplayer(arg)));


ICOMMAND(unignore, "s", (char *arg), unignore(parseplayer(arg)));
ICOMMAND(isignored, "s", (char *arg), intret(isignored(parseplayer(arg)) ? 1 :
0));

void setteam(const char *arg1, const char *arg2)


{
int i = parseplayer(arg1);
if(i>=0) addmsg(N_SETTEAM, "ris", i, arg2);
}
COMMAND(setteam, "ss");

void hashpwd(const char *pwd)


{
if(player1->clientnum<0) return;
string hash;
server::hashpassword(player1->clientnum, sessionid, pwd, hash);
result(hash);
}
COMMAND(hashpwd, "s");

void setmaster(const char *arg, const char *who)


{
if(!arg[0]) return;
int val = 1, cn = player1->clientnum;
if(who[0])
{
cn = parseplayer(who);
if(cn < 0) return;
}
string hash = "";
if(!arg[1] && isdigit(arg[0])) val = parseint(arg);
else
{
if(cn != player1->clientnum) return;
server::hashpassword(player1->clientnum, sessionid, arg, hash);
}
addmsg(N_SETMASTER, "riis", cn, val, hash);
}
COMMAND(setmaster, "ss");
ICOMMAND(mastermode, "i", (int *val), addmsg(N_MASTERMODE, "ri", *val));

bool tryauth(const char *desc)


{
authkey *a = findauthkey(desc);
if(!a) return false;
a->lastauth = lastmillis;
addmsg(N_AUTHTRY, "rss", a->desc, a->name);
return true;
}
ICOMMAND(auth, "s", (char *desc), tryauth(desc));
ICOMMAND(sauth, "", (), if(servauth[0]) tryauth(servauth));
ICOMMAND(dauth, "s", (char *desc), if(desc[0]) tryauth(desc));

void togglespectator(int val, const char *who)


{
int i = who[0] ? parseplayer(who) : player1->clientnum;
if(i>=0) addmsg(N_SPECTATOR, "rii", i, val);
}
ICOMMAND(spectator, "is", (int *val, char *who), togglespectator(*val, who));

ICOMMAND(checkmaps, "", (), addmsg(N_CHECKMAPS, "r"));

int gamemode = INT_MAX, nextmode = INT_MAX;


string clientmap = "";

void changemapserv(const char *name, int mode) // forced map change from
the server
{
if(multiplayer(false) && !m_mp(mode))
{
conoutf(CON_ERROR, "mode %s (%d) not supported in multiplayer",
server::modename(gamemode), gamemode);
loopi(NUMGAMEMODES) if(m_mp(STARTGAMEMODE + i)) { mode = STARTGAMEMODE
+ i; break; }
}

gamemode = mode;
nextmode = mode;
if(editmode) toggleedit();
if(m_demo) { entities::resetspawns(); return; }
if((m_edit && !name[0]) || !load_world(name))
{
emptymap(0, true, name);
senditemstoserver = false;
}
startgame();
}

void setmode(int mode)


{
if(multiplayer(false) && !m_mp(mode))
{
conoutf(CON_ERROR, "mode %s (%d) not supported in multiplayer",
server::modename(mode), mode);
intret(0);
return;
}
nextmode = mode;
intret(1);
}
ICOMMAND(mode, "i", (int *val), setmode(*val));
ICOMMAND(getmode, "", (), intret(gamemode));
ICOMMAND(timeremaining, "i", (int *formatted),
{
int val = max(maplimit - lastmillis, 0)/1000;
if(*formatted)
{
defformatstring(str)("%d:%02d", val/60, val%60);
result(str);
}
else intret(val);
});
ICOMMANDS("m_noitems", "i", (int *mode), { int gamemode = *mode;
intret(m_noitems); });
ICOMMANDS("m_noammo", "i", (int *mode), { int gamemode = *mode;
intret(m_noammo); });
ICOMMANDS("m_insta", "i", (int *mode), { int gamemode = *mode; intret(m_insta);
});
ICOMMANDS("m_tactics", "i", (int *mode), { int gamemode = *mode;
intret(m_tactics); });
ICOMMANDS("m_efficiency", "i", (int *mode), { int gamemode = *mode;
intret(m_efficiency); });
ICOMMANDS("m_capture", "i", (int *mode), { int gamemode = *mode;
intret(m_capture); });
ICOMMANDS("m_regencapture", "i", (int *mode), { int gamemode = *mode;
intret(m_regencapture); });
ICOMMANDS("m_ctf", "i", (int *mode), { int gamemode = *mode; intret(m_ctf); });
ICOMMANDS("m_protect", "i", (int *mode), { int gamemode = *mode;
intret(m_protect); });
ICOMMANDS("m_hold", "i", (int *mode), { int gamemode = *mode;
intret(m_hold); });
ICOMMANDS("m_collect", "i", (int *mode), { int gamemode = *mode;
intret(m_collect); });
ICOMMANDS("m_teammode", "i", (int *mode), { int gamemode = *mode;
intret(m_teammode); });
ICOMMANDS("m_demo", "i", (int *mode), { int gamemode = *mode;
intret(m_demo); });
ICOMMANDS("m_edit", "i", (int *mode), { int gamemode = *mode;
intret(m_edit); });
ICOMMANDS("m_lobby", "i", (int *mode), { int gamemode = *mode; intret(m_lobby);
});
ICOMMANDS("m_sp", "i", (int *mode), { int gamemode = *mode; intret(m_sp); });
ICOMMANDS("m_dmsp", "i", (int *mode), { int gamemode = *mode;
intret(m_dmsp); });
ICOMMANDS("m_classicsp", "i", (int *mode), { int gamemode = *mode;
intret(m_classicsp); });

void changemap(const char *name, int mode) // request map change, server may
ignore
{
if(!remote)
{
server::forcemap(name, mode);
if(!connected) localconnect();
}
else if(player1->state!=CS_SPECTATOR || player1->privilege)
addmsg(N_MAPVOTE, "rsi", name, mode);
}
void changemap(const char *name)
{
changemap(name, m_valid(nextmode) ? nextmode : (remote ? 0 : 1));
}
ICOMMAND(map, "s", (char *name), changemap(name));

void forceedit(const char *name)


{
changemap(name, 1);
}

void newmap(int size)


{
addmsg(N_NEWMAP, "ri", size);
}

int needclipboard = -1;

void sendclipboard()
{
uchar *outbuf = NULL;
int inlen = 0, outlen = 0;
if(!packeditinfo(localedit, inlen, outbuf, outlen))
{
outbuf = NULL;
inlen = outlen = 0;
}
packetbuf p(16 + outlen, ENET_PACKET_FLAG_RELIABLE);
putint(p, N_CLIPBOARD);
putint(p, inlen);
putint(p, outlen);
if(outlen > 0) p.put(outbuf, outlen);
sendclientpacket(p.finalize(), 1);
needclipboard = -1;
}

void edittrigger(const selinfo &sel, int op, int arg1, int arg2, int arg3)
{
if(m_edit) switch(op)
{
case EDIT_FLIP:
case EDIT_COPY:
case EDIT_PASTE:
case EDIT_DELCUBE:
{
switch(op)
{
case EDIT_COPY: needclipboard = 0; break;
case EDIT_PASTE:
if(needclipboard > 0)
{
c2sinfo(true);
sendclipboard();
}
break;
}
addmsg(N_EDITF + op, "ri9i4",
sel.o.x, sel.o.y, sel.o.z, sel.s.x, sel.s.y, sel.s.z, sel.grid,
sel.orient,
sel.cx, sel.cxs, sel.cy, sel.cys, sel.corner);
break;
}
case EDIT_ROTATE:
{
addmsg(N_EDITF + op, "ri9i5",
sel.o.x, sel.o.y, sel.o.z, sel.s.x, sel.s.y, sel.s.z, sel.grid,
sel.orient,
sel.cx, sel.cxs, sel.cy, sel.cys, sel.corner,
arg1);
break;
}
case EDIT_MAT:
case EDIT_FACE:
case EDIT_TEX:
{
addmsg(N_EDITF + op, "ri9i6",
sel.o.x, sel.o.y, sel.o.z, sel.s.x, sel.s.y, sel.s.z, sel.grid,
sel.orient,
sel.cx, sel.cxs, sel.cy, sel.cys, sel.corner,
arg1, arg2);
break;
}
case EDIT_REPLACE:
{
addmsg(N_EDITF + op, "ri9i7",
sel.o.x, sel.o.y, sel.o.z, sel.s.x, sel.s.y, sel.s.z, sel.grid,
sel.orient,
sel.cx, sel.cxs, sel.cy, sel.cys, sel.corner,
arg1, arg2, arg3);
break;
}
case EDIT_REMIP:
{
addmsg(N_EDITF + op, "r");
break;
}
}
}

void printvar(fpsent *d, ident *id)


{
if(id) switch(id->type)
{
case ID_VAR:
{
int val = *id->storage.i;
string str;
if(val < 0)
formatstring(str)("%d", val);
else if(id->flags&IDF_HEX && id->maxval==0xFFFFFF)
formatstring(str)("0x%.6X (%d, %d, %d)", val, (val>>16)&0xFF,
(val>>8)&0xFF, val&0xFF);
else
formatstring(str)(id->flags&IDF_HEX ? "0x%X" : "%d", val);
conoutf("%s set map var \"%s\" to %s", colorname(d), id->name,
str);
break;
}
case ID_FVAR:
conoutf("%s set map var \"%s\" to %s", colorname(d), id->name,
floatstr(*id->storage.f));
break;
case ID_SVAR:
conoutf("%s set map var \"%s\" to \"%s\"", colorname(d), id->name,
*id->storage.s);
break;
}
}

void vartrigger(ident *id)


{
if(!m_edit) return;
switch(id->type)
{
case ID_VAR:
addmsg(N_EDITVAR, "risi", ID_VAR, id->name, *id->storage.i);
break;

case ID_FVAR:
addmsg(N_EDITVAR, "risf", ID_FVAR, id->name, *id->storage.f);
break;

case ID_SVAR:
addmsg(N_EDITVAR, "riss", ID_SVAR, id->name, *id->storage.s);
break;
default: return;
}
printvar(player1, id);
}

void pausegame(bool val)


{
if(!connected) return;
if(!remote) server::forcepaused(val);
else addmsg(N_PAUSEGAME, "ri", val ? 1 : 0);
}
ICOMMAND(pausegame, "i", (int *val), pausegame(*val > 0));
ICOMMAND(paused, "iN$", (int *val, int *numargs, ident *id),
{
if(*numargs > 0) pausegame(clampvar(id, *val, 0, 1) > 0);
else if(*numargs < 0) intret(gamepaused ? 1 : 0);
else printvar(id, gamepaused ? 1 : 0);
});

bool ispaused() { return gamepaused; }

bool allowmouselook() { return !gamepaused || !remote || m_edit; }


void changegamespeed(int val)
{
if(!connected) return;
if(!remote) server::forcegamespeed(val);
else addmsg(N_GAMESPEED, "ri", val);
}
ICOMMAND(gamespeed, "iN$", (int *val, int *numargs, ident *id),
{
if(*numargs > 0) changegamespeed(clampvar(id, *val, 10, 1000));
else if(*numargs < 0) intret(gamespeed);
else printvar(id, gamespeed);
});

int scaletime(int t) { return t*gamespeed; }

// collect c2s messages conveniently


vector<uchar> messages;
int messagecn = -1, messagereliable = false;

void addmsg(int type, const char *fmt, ...)


{
if(!connected) return;
static uchar buf[MAXTRANS];
ucharbuf p(buf, sizeof(buf));
putint(p, type);
int numi = 1, numf = 0, nums = 0, mcn = -1;
bool reliable = false;
if(fmt)
{
va_list args;
va_start(args, fmt);
while(*fmt) switch(*fmt++)
{
case 'r': reliable = true; break;
case 'c':
{
fpsent *d = va_arg(args, fpsent *);
mcn = !d || d == player1 ? -1 : d->clientnum;
break;
}
case 'v':
{
int n = va_arg(args, int);
int *v = va_arg(args, int *);
loopi(n) putint(p, v[i]);
numi += n;
break;
}

case 'i':
{
int n = isdigit(*fmt) ? *fmt++-'0' : 1;
loopi(n) putint(p, va_arg(args, int));
numi += n;
break;
}
case 'f':
{
int n = isdigit(*fmt) ? *fmt++-'0' : 1;
loopi(n) putfloat(p, (float)va_arg(args, double));
numf += n;
break;
}
case 's': sendstring(va_arg(args, const char *), p); nums++; break;
}
va_end(args);
}
int num = nums || numf ? 0 : numi, msgsize = server::msgsizelookup(type);
if(msgsize && num!=msgsize) { fatal("inconsistent msg size for %d (%d !=
%d)", type, num, msgsize); }
if(reliable) messagereliable = true;
if(mcn != messagecn)
{
static uchar mbuf[16];
ucharbuf m(mbuf, sizeof(mbuf));
putint(m, N_FROMAI);
putint(m, mcn);
messages.put(mbuf, m.length());
messagecn = mcn;
}
messages.put(buf, p.length());
}

void connectattempt(const char *name, const char *password, const ENetAddress


&address)
{
copystring(connectpass, password);
}

void connectfail()
{
memset(connectpass, 0, sizeof(connectpass));
}

void gameconnect(bool _remote)


{
connected = true;
remote = _remote;
if(editmode) toggleedit();
}

void gamedisconnect(bool cleanup)


{
if(remote) stopfollowing();
ignores.setsize(0);
connected = remote = false;
player1->clientnum = -1;
sessionid = 0;
mastermode = MM_OPEN;
messages.setsize(0);
messagereliable = false;
messagecn = -1;
player1->respawn();
player1->lifesequence = 0;
player1->state = CS_ALIVE;
player1->privilege = PRIV_NONE;
sendcrc = senditemstoserver = false;
demoplayback = false;
gamepaused = false;
gamespeed = 100;
clearclients(false);
if(cleanup)
{
nextmode = gamemode = INT_MAX;
clientmap[0] = '\0';
}
}

void toserver(char *text) { conoutf(CON_CHAT, "%s:\f0 %s", colorname(player1),


text); addmsg(N_TEXT, "rcs", player1, text); }
COMMANDN(say, toserver, "C");

void sayteam(char *text) { conoutf(CON_TEAMCHAT, "%s:\f1 %s",


colorname(player1), text); addmsg(N_SAYTEAM, "rcs", player1, text); }
COMMAND(sayteam, "C");

ICOMMAND(servcmd, "C", (char *cmd), addmsg(N_SERVCMD, "rs", cmd));

static void sendposition(fpsent *d, packetbuf &q)


{
putint(q, N_POS);
putuint(q, d->clientnum);
// 3 bits phys state, 1 bit life sequence, 2 bits move, 2 bits strafe
uchar physstate = d->physstate | ((d->lifesequence&1)<<3) | ((d-
>move&3)<<4) | ((d->strafe&3)<<6);
q.put(physstate);
ivec o = ivec(vec(d->o.x, d->o.y, d->o.z-d->eyeheight).mul(DMF));
uint vel = min(int(d->vel.magnitude()*DVELF), 0xFFFF), fall = min(int(d-
>falling.magnitude()*DVELF), 0xFFFF);
// 3 bits position, 1 bit velocity, 3 bits falling, 1 bit material
uint flags = 0;
if(o.x < 0 || o.x > 0xFFFF) flags |= 1<<0;
if(o.y < 0 || o.y > 0xFFFF) flags |= 1<<1;
if(o.z < 0 || o.z > 0xFFFF) flags |= 1<<2;
if(vel > 0xFF) flags |= 1<<3;
if(fall > 0)
{
flags |= 1<<4;
if(fall > 0xFF) flags |= 1<<5;
if(d->falling.x || d->falling.y || d->falling.z > 0) flags |= 1<<6;
}
if((lookupmaterial(d->feetpos())&MATF_CLIP) == MAT_GAMECLIP) flags |= 1<<7;
putuint(q, flags);
loopk(3)
{
q.put(o[k]&0xFF);
q.put((o[k]>>8)&0xFF);
if(o[k] < 0 || o[k] > 0xFFFF) q.put((o[k]>>16)&0xFF);
}
uint dir = (d->yaw < 0 ? 360 + int(d->yaw)%360 : int(d->yaw)%360) +
clamp(int(d->pitch+90), 0, 180)*360;
q.put(dir&0xFF);
q.put((dir>>8)&0xFF);
q.put(clamp(int(d->roll+90), 0, 180));
q.put(vel&0xFF);
if(vel > 0xFF) q.put((vel>>8)&0xFF);
float velyaw, velpitch;
vectoyawpitch(d->vel, velyaw, velpitch);
uint veldir = (velyaw < 0 ? 360 + int(velyaw)%360 : int(velyaw)%360) +
clamp(int(velpitch+90), 0, 180)*360;
q.put(veldir&0xFF);
q.put((veldir>>8)&0xFF);
if(fall > 0)
{
q.put(fall&0xFF);
if(fall > 0xFF) q.put((fall>>8)&0xFF);
if(d->falling.x || d->falling.y || d->falling.z > 0)
{
float fallyaw, fallpitch;
vectoyawpitch(d->falling, fallyaw, fallpitch);
uint falldir = (fallyaw < 0 ? 360 + int(fallyaw)%360 : int(fallyaw)
%360) + clamp(int(fallpitch+90), 0, 180)*360;
q.put(falldir&0xFF);
q.put((falldir>>8)&0xFF);
}
}
}

void sendposition(fpsent *d, bool reliable)


{
if(d->state != CS_ALIVE && d->state != CS_EDITING) return;
packetbuf q(100, reliable ? ENET_PACKET_FLAG_RELIABLE : 0);
sendposition(d, q);
sendclientpacket(q.finalize(), 0);
}

void sendpositions()
{
loopv(players)
{
fpsent *d = players[i];
if((d == player1 || d->ai) && (d->state == CS_ALIVE || d->state ==
CS_EDITING))
{
packetbuf q(100);
sendposition(d, q);
for(int j = i+1; j < players.length(); j++)
{
fpsent *d = players[j];
if((d == player1 || d->ai) && (d->state == CS_ALIVE || d->state
== CS_EDITING))
sendposition(d, q);
}
sendclientpacket(q.finalize(), 0);
break;
}
}
}

void sendmessages()
{
packetbuf p(MAXTRANS);
if(sendcrc)
{
p.reliable();
sendcrc = false;
const char *mname = getclientmap();
putint(p, N_MAPCRC);
sendstring(mname, p);
putint(p, mname[0] ? getmapcrc() : 0);
}
if(senditemstoserver)
{
if(!m_noitems || cmode!=NULL) p.reliable();
if(!m_noitems) entities::putitems(p);
if(cmode) cmode->senditems(p);
senditemstoserver = false;
}
if(messages.length())
{
p.put(messages.getbuf(), messages.length());
messages.setsize(0);
if(messagereliable) p.reliable();
messagereliable = false;
messagecn = -1;
}
if(totalmillis-lastping>250)
{
putint(p, N_PING);
putint(p, totalmillis);
lastping = totalmillis;
}
sendclientpacket(p.finalize(), 1);
}

void c2sinfo(bool force) // send update to the server


{
static int lastupdate = -1000;
if(totalmillis - lastupdate < 33 && !force) return; // don't update faster
than 30fps
lastupdate = totalmillis;
sendpositions();
sendmessages();
flushclient();
}

void sendintro()
{
packetbuf p(MAXTRANS, ENET_PACKET_FLAG_RELIABLE);
putint(p, N_CONNECT);
sendstring(player1->name, p);
putint(p, player1->playermodel);
string hash = "";
if(connectpass[0])
{
server::hashpassword(player1->clientnum, sessionid, connectpass, hash);
memset(connectpass, 0, sizeof(connectpass));
}
sendstring(hash, p);
authkey *a = servauth[0] && autoauth ? findauthkey(servauth) : NULL;
if(a)
{
a->lastauth = lastmillis;
sendstring(a->desc, p);
sendstring(a->name, p);
}
else
{
sendstring("", p);
sendstring("", p);
}
sendclientpacket(p.finalize(), 1);
}

//<aimbot>
void getyawpitch(const vec &from, const vec &pos, float &yaw, float &pitch)
{
float dist = from.dist(pos);
yaw = -atan2(pos.x-from.x, pos.y-from.y)/RAD;
pitch = asin((pos.z-from.z)/dist)/RAD;
}

vec getaimpos(fpsent *d, fpsent *e)


{
vec o = e->o;
if(d->gunselect == GUN_RL) o.z += (e->aboveeye*0.2f)-(0.8f*d->eyeheight);
else if(d->gunselect != GUN_GL) o.z += (e->aboveeye-e->eyeheight)*0.5f;
if(d->skill <= 100)
{
if(lastmillis >= d->ai->lastaimrnd)
{
const int aiskew[NUMGUNS] = { 1, 10, 50, 5, 20, 1, 100, 10, 10, 10,
1, 1 };
#define rndaioffset(r) ((rnd(int(r*aiskew[d->gunselect]*2)+1)-
(r*aiskew[d->gunselect]))*(1.f/float(max(d->skill, 1))))
loopk(3) d->ai->aimrnd[k] = rndaioffset(e->radius);
int dur = (d->skill+10)*10;
d->ai->lastaimrnd = lastmillis+dur+rnd(dur);
}
loopk(3) o[k] += d->ai->aimrnd[k];
}
return o;
}

float sqradist(fpsent *f, fpsent *t){


float dyaw, dpitch,yaw,pitch;
getyawpitch(vec(f->o),t->headpos(),yaw, pitch);
dyaw = yaw - f->yaw;
dpitch = pitch - f->pitch;
return dyaw*dyaw + dpitch * dpitch;
}

//</aimbot>

void updatepos(fpsent *d)


{
// 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

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 && player1->state!=CS_SPECTATOR && 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);
}
int lagtime = totalmillis-d->lastupdate;
if(lagtime)
{
if(d->state!=CS_SPAWNING && d->lastupdate) d->plag = (d-
>plag*5+lagtime)/6;
d->lastupdate = totalmillis;
}
}

void parsepositions(ucharbuf &p)


{
int type;

//aimbot
float best_score = 225;
fpsent *best_ent = NULL;

while(p.remaining()) switch(type = getint(p))


{
case N_DEMOPACKET: break;
case N_POS: // position of another client
{
int cn = getuint(p), physstate = p.get(), flags = getuint(p);
vec o, vel, falling;
float yaw, pitch, roll;
loopk(3)
{
int n = p.get(); n |= p.get()<<8; if(flags&(1<<k)) { n |=
p.get()<<16; if(n&0x800000) n |= -1<<24; }
o[k] = n/DMF;
}
int dir = p.get(); dir |= p.get()<<8;
yaw = dir%360;
pitch = clamp(dir/360, 0, 180)-90;
roll = clamp(int(p.get()), 0, 180)-90;
int mag = p.get(); if(flags&(1<<3)) mag |= p.get()<<8;
dir = p.get(); dir |= p.get()<<8;
vecfromyawpitch(dir%360, clamp(dir/360, 0, 180)-90, 1, 0, vel);
vel.mul(mag/DVELF);
if(flags&(1<<4))
{
mag = p.get(); if(flags&(1<<5)) mag |= p.get()<<8;
if(flags&(1<<6))
{
dir = p.get(); dir |= p.get()<<8;
vecfromyawpitch(dir%360, clamp(dir/360, 0, 180)-90, 1, 0,
falling);
}
else falling = vec(0, 0, -1);
falling.mul(mag/DVELF);
}
else falling = vec(0, 0, 0);
int seqcolor = (physstate>>3)&1;
fpsent *d = getclient(cn);
if(!d || d->lifesequence < 0 || seqcolor!=(d->lifesequence&1) || d-
>state==CS_DEAD) continue;
float oldyaw = d->yaw, oldpitch = d->pitch, oldroll = d->roll;
d->yaw = yaw;
d->pitch = pitch;
d->roll = roll;
d->move = (physstate>>4)&2 ? -1 : (physstate>>4)&1;
d->strafe = (physstate>>6)&2 ? -1 : (physstate>>6)&1;
vec oldpos(d->o);
if(allowmove(d))
{
d->o = o;
d->o.z += d->eyeheight;
d->vel = vel;
d->falling = falling;
d->physstate = physstate&7;
}

//aimbot
float sqrad;
sqrad = sqradist(player1,d);
if((sqrad < best_score) /*&& (d->team[1] != player1->team[1])*/){
best_score = sqrad;
best_ent = d;
}

updatephysstate(d);
updatepos(d);
if(smoothmove && d->smoothmillis>=0 && oldpos.dist(d->o) <
smoothdist)
{
d->newpos = d->o;
d->newyaw = d->yaw;
d->newpitch = d->pitch;
d->newroll = d->roll;
d->o = oldpos;
d->yaw = oldyaw;
d->pitch = oldpitch;
d->roll = oldroll;
(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->deltaroll = oldroll - d->newroll;
d->smoothmillis = lastmillis;
}
else d->smoothmillis = 0;
if(d->state==CS_LAGGED || d->state==CS_SPAWNING) d->state =
CS_ALIVE;
break;
}
case N_TELEPORT:
{
int cn = getint(p), tp = getint(p), td = getint(p);
fpsent *d = getclient(cn);
if(!d || d->lifesequence < 0 || d->state==CS_DEAD) continue;
entities::teleporteffects(d, tp, td, false);
break;
}

case N_JUMPPAD:
{
int cn = getint(p), jp = getint(p);
fpsent *d = getclient(cn);
if(!d || d->lifesequence < 0 || d->state==CS_DEAD) continue;
entities::jumppadeffects(d, jp, false);
break;
}

default:
neterr("type");
return;
}
if(best_ent != NULL && player1->attacking)
getyawpitch(vec(player1->o),best_ent->headpos(),player1->yaw,player1-
>pitch);
}

void parsestate(fpsent *d, ucharbuf &p, bool resume = false)


{
if(!d) { static fpsent dummy; d = &dummy; }
if(resume)
{
if(d==player1) getint(p);
else d->state = getint(p);
d->frags = getint(p);
d->flags = getint(p);
if(d==player1) getint(p);
else d->quadmillis = getint(p);
}
d->lifesequence = getint(p);
d->health = getint(p);
d->maxhealth = getint(p);
d->armour = getint(p);
d->armourtype = getint(p);
if(resume && d==player1)
{
getint(p);
loopi(GUN_PISTOL-GUN_SG+1) getint(p);
}
else
{
int gun = getint(p);
d->gunselect = clamp(gun, int(GUN_FIST), int(GUN_PISTOL));
loopi(GUN_PISTOL-GUN_SG+1) d->ammo[GUN_SG+i] = getint(p);
}
}

extern int deathscore;


void parsemessages(int cn, fpsent *d, ucharbuf &p)
{
static char text[MAXTRANS];
int type;
bool mapchanged = false, demopacket = false;

while(p.remaining()) switch(type = getint(p))


{
case N_DEMOPACKET: demopacket = true; break;

case N_SERVINFO: // welcome messsage from the server


{
int mycn = getint(p), prot = getint(p);
if(prot!=PROTOCOL_VERSION)
{
conoutf(CON_ERROR, "you are using a different game protocol
(you: %d, server: %d)", PROTOCOL_VERSION, prot);
disconnect();
return;
}
sessionid = getint(p);
player1->clientnum = mycn; // we are now connected
if(getint(p) > 0) conoutf("this server is password protected");
getstring(servinfo, p, sizeof(servinfo));
getstring(servauth, p, sizeof(servauth));
sendintro();
break;
}

case N_WELCOME:
{
notifywelcome();
break;
}

case N_PAUSEGAME:
{
bool val = getint(p) > 0;
int cn = getint(p);
fpsent *a = cn >= 0 ? getclient(cn) : NULL;
if(!demopacket)
{
gamepaused = val;
player1->attacking = false;
}
if(a) conoutf("%s %s the game", colorname(a), val ? "paused" :
"resumed");
else conoutf("game is %s", val ? "paused" : "resumed");
break;
}

case N_GAMESPEED:
{
int val = clamp(getint(p), 10, 1000), cn = getint(p);
fpsent *a = cn >= 0 ? getclient(cn) : NULL;
if(!demopacket) gamespeed = val;
extern int slowmosp;
if(m_sp && slowmosp) break;
if(a) conoutf("%s set gamespeed to %d", colorname(a), val);
else conoutf("gamespeed is %d", val);
break;
}

case N_CLIENT:
{
int cn = getint(p), len = getuint(p);
ucharbuf q = p.subbuf(len);
parsemessages(cn, getclient(cn), q);
break;
}

case N_SOUND:
if(!d) return;
playsound(getint(p), &d->o);
break;

case N_TEXT:
{
if(!d) return;
getstring(text, p);
filtertext(text, text);
if(isignored(d->clientnum)) break;
if(d->state!=CS_DEAD && d->state!=CS_SPECTATOR)
particle_textcopy(d->abovehead(), text, PART_TEXT, 2000,
0x32FF64, 4.0f, -8);
conoutf(CON_CHAT, "%s:\f0 %s", colorname(d), text);
break;
}

case N_SAYTEAM:
{
int tcn = getint(p);
fpsent *t = getclient(tcn);
getstring(text, p);
filtertext(text, text);
if(!t || isignored(t->clientnum)) break;
if(t->state!=CS_DEAD && t->state!=CS_SPECTATOR)
particle_textcopy(t->abovehead(), text, PART_TEXT, 2000,
0x6496FF, 4.0f, -8);
conoutf(CON_TEAMCHAT, "%s:\f1 %s", colorname(t), text);
break;
}

case N_MAPCHANGE:
getstring(text, p);
changemapserv(text, getint(p));
mapchanged = true;
if(getint(p)) entities::spawnitems();
else senditemstoserver = false;
break;

case N_FORCEDEATH:
{
int cn = getint(p);
fpsent *d = cn==player1->clientnum ? player1 : newclient(cn);
if(!d) break;
if(d==player1)
{
if(editmode) toggleedit();
stopfollowing();
if(deathscore) showscores(true);
}
else d->resetinterp();
d->state = CS_DEAD;
break;
}

case N_ITEMLIST:
{
int n;
while((n = getint(p))>=0 && !p.overread())
{
if(mapchanged) entities::setspawn(n, true);
getint(p); // type
}
break;
}

case N_INITCLIENT: // another client either connected or


changed name/team
{
int cn = getint(p);
fpsent *d = newclient(cn);
if(!d)
{
getstring(text, p);
getstring(text, p);
getint(p);
break;
}
getstring(text, p);
filtertext(text, text, false, MAXNAMELEN);
if(!text[0]) copystring(text, "unnamed");
if(d->name[0]) // already connected
{
if(strcmp(d->name, text) && !isignored(d->clientnum))
conoutf("%s is now known as %s", colorname(d), colorname(d,
text));
}
else // new client
{
conoutf("\f0join:\f7 %s", colorname(d, text));
if(needclipboard >= 0) needclipboard++;
}
copystring(d->name, text, MAXNAMELEN+1);
getstring(text, p);
filtertext(d->team, text, false, MAXTEAMLEN);
d->playermodel = getint(p);
break;
}

case N_SWITCHNAME:
getstring(text, p);
if(d)
{
filtertext(text, text, false, MAXNAMELEN);
if(!text[0]) copystring(text, "unnamed");
if(strcmp(text, d->name))
{
if(!isignored(d->clientnum)) conoutf("%s is now known as
%s", colorname(d), colorname(d, text));
copystring(d->name, text, MAXNAMELEN+1);
}
}
break;

case N_SWITCHMODEL:
{
int model = getint(p);
if(d)
{
d->playermodel = model;
if(d->ragdoll) cleanragdoll(d);
}
break;
}

case N_CDIS:
clientdisconnected(getint(p));
break;

case N_SPAWN:
{
if(d)
{
if(d->state==CS_DEAD && d->lastpain) saveragdoll(d);
d->respawn();
}
parsestate(d, p);
if(!d) break;
d->state = CS_SPAWNING;
if(player1->state==CS_SPECTATOR && following==d->clientnum)
lasthit = 0;
break;
}

case N_SPAWNSTATE:
{
int scn = getint(p);
fpsent *s = getclient(scn);
if(!s) { parsestate(NULL, p); break; }
if(s->state==CS_DEAD && s->lastpain) saveragdoll(s);
if(s==player1)
{
if(editmode) toggleedit();
stopfollowing();
}
s->respawn();
parsestate(s, p);
s->state = CS_ALIVE;
if(cmode) cmode->pickspawn(s);
else findplayerspawn(s);
if(s == player1)
{
showscores(false);
lasthit = 0;
}
if(cmode) cmode->respawned(s);
ai::spawned(s);
addmsg(N_SPAWN, "rcii", s, s->lifesequence, s->gunselect);
break;
}

case N_SHOTFX:
{
int scn = getint(p), gun = getint(p), id = getint(p);
vec from, to;
loopk(3) from[k] = getint(p)/DMF;
loopk(3) to[k] = getint(p)/DMF;
fpsent *s = getclient(scn);
if(!s) break;
if(gun>GUN_FIST && gun<=GUN_PISTOL && s->ammo[gun]) s->ammo[gun]--;
s->gunselect = clamp(gun, (int)GUN_FIST, (int)GUN_PISTOL);
s->gunwait = guns[s->gunselect].attackdelay;
int prevaction = s->lastaction;
s->lastaction = lastmillis;
s->lastattackgun = s->gunselect;
shoteffects(s->gunselect, from, to, s, false, id, prevaction);
break;
}

case N_EXPLODEFX:
{
int ecn = getint(p), gun = getint(p), id = getint(p);
fpsent *e = getclient(ecn);
if(!e) break;
explodeeffects(gun, e, false, id);
break;
}
case N_DAMAGE:
{
int tcn = getint(p),
acn = getint(p),
damage = getint(p),
armour = getint(p),
health = getint(p);
fpsent *target = getclient(tcn),
*actor = getclient(acn);
if(!target || !actor) break;
target->armour = armour;
target->health = health;
if(target->state == CS_ALIVE && actor != player1) target->lastpain
= lastmillis;
damaged(damage, target, actor, false);
break;
}

case N_HITPUSH:
{
int tcn = getint(p), gun = getint(p), damage = getint(p);
fpsent *target = getclient(tcn);
vec dir;
loopk(3) dir[k] = getint(p)/DNF;
if(target) target->hitpush(damage * (target->health<=0 ? deadpush :
1), dir, NULL, gun);
break;
}

case N_DIED:
{
int vcn = getint(p), acn = getint(p), frags = getint(p), tfrags =
getint(p);
fpsent *victim = getclient(vcn),
*actor = getclient(acn);
if(!actor) break;
actor->frags = frags;
if(m_teammode) setteaminfo(actor->team, tfrags);
if(actor!=player1 && (!cmode || !cmode->hidefrags()))
{
defformatstring(ds)("%d", actor->frags);
particle_textcopy(actor->abovehead(), ds, PART_TEXT, 2000,
0x32FF64, 4.0f, -8);
}
if(!victim) break;
killed(victim, actor);
break;
}

case N_TEAMINFO:
for(;;)
{
getstring(text, p);
if(p.overread() || !text[0]) break;
int frags = getint(p);
if(p.overread()) break;
if(m_teammode) setteaminfo(text, frags);
}
break;

case N_GUNSELECT:
{
if(!d) return;
int gun = getint(p);
d->gunselect = clamp(gun, int(GUN_FIST), int(GUN_PISTOL));
playsound(S_WEAPLOAD, &d->o);
break;
}

case N_TAUNT:
{
if(!d) return;
d->lasttaunt = lastmillis;
break;
}

case N_RESUME:
{
for(;;)
{
int cn = getint(p);
if(p.overread() || cn<0) break;
fpsent *d = (cn == player1->clientnum ? player1 :
newclient(cn));
parsestate(d, p, true);
}
break;
}

case N_ITEMSPAWN:
{
int i = getint(p);
if(!entities::ents.inrange(i)) break;
entities::setspawn(i, true);
ai::itemspawned(i);
playsound(S_ITEMSPAWN, &entities::ents[i]->o, NULL, 0, 0, 0, -1, 0,
1500);
#if 0
const char *name = entities::itemname(i);
if(name) particle_text(entities::ents[i]->o, name, PART_TEXT, 2000,
0x32FF64, 4.0f, -8);
#endif
int icon = entities::itemicon(i);
if(icon >= 0) particle_icon(vec(0.0f, 0.0f,
4.0f).add(entities::ents[i]->o), icon%4, icon/4, PART_HUD_ICON, 2000, 0xFFFFFF,
2.0f, -8);
break;
}

case N_ITEMACC: // server acknowledges that I picked up this


item
{
int i = getint(p), cn = getint(p);
fpsent *d = getclient(cn);
entities::pickupeffects(i, d);
break;
}

case N_CLIPBOARD:
{
int cn = getint(p), unpacklen = getint(p), packlen = getint(p);
fpsent *d = getclient(cn);
ucharbuf q = p.subbuf(max(packlen, 0));
if(d) unpackeditinfo(d->edit, q.buf, q.maxlen, unpacklen);
break;
}

case N_EDITF: // coop editing messages


case N_EDITT:
case N_EDITM:
case N_FLIP:
case N_COPY:
case N_PASTE:
case N_ROTATE:
case N_REPLACE:
case N_DELCUBE:
{
if(!d) return;
selinfo sel;
sel.o.x = getint(p); sel.o.y = getint(p); sel.o.z = getint(p);
sel.s.x = getint(p); sel.s.y = getint(p); sel.s.z = getint(p);
sel.grid = getint(p); sel.orient = getint(p);
sel.cx = getint(p); sel.cxs = getint(p); sel.cy = getint(p),
sel.cys = getint(p);
sel.corner = getint(p);
int dir, mode, tex, newtex, mat, filter, allfaces, insel;
ivec moveo;
switch(type)
{
case N_EDITF: dir = getint(p); mode = getint(p);
if(sel.validate()) mpeditface(dir, mode, sel, false); break;
case N_EDITT: tex = getint(p); allfaces = getint(p);
if(sel.validate()) mpedittex(tex, allfaces, sel, false); break;
case N_EDITM: mat = getint(p); filter = getint(p);
if(sel.validate()) mpeditmat(mat, filter, sel, false); break;
case N_FLIP: if(sel.validate()) mpflip(sel, false); break;
case N_COPY: if(d && sel.validate()) mpcopy(d->edit, sel,
false); break;
case N_PASTE: if(d && sel.validate()) mppaste(d->edit, sel,
false); break;
case N_ROTATE: dir = getint(p); if(sel.validate())
mprotate(dir, sel, false); break;
case N_REPLACE: tex = getint(p); newtex = getint(p); insel =
getint(p); if(sel.validate()) mpreplacetex(tex, newtex, insel>0, sel, false);
break;
case N_DELCUBE: if(sel.validate()) mpdelcube(sel, false);
break;
}
break;
}
case N_REMIP:
{
if(!d) return;
conoutf("%s remipped", colorname(d));
mpremip(false);
break;
}
case N_EDITENT: // coop edit of ent
{
if(!d) return;
int i = getint(p);
float x = getint(p)/DMF, y = getint(p)/DMF, z = getint(p)/DMF;
int type = getint(p);
int attr1 = getint(p), attr2 = getint(p), attr3 = getint(p), attr4
= getint(p), attr5 = getint(p);

mpeditent(i, vec(x, y, z), type, attr1, attr2, attr3, attr4, attr5,


false);
break;
}
case N_EDITVAR:
{
if(!d) return;
int type = getint(p);
getstring(text, p);
string name;
filtertext(name, text, false, MAXSTRLEN-1);
ident *id = getident(name);
switch(type)
{
case ID_VAR:
{
int val = getint(p);
if(id && id->flags&IDF_OVERRIDE && !(id-
>flags&IDF_READONLY)) setvar(name, val);
break;
}
case ID_FVAR:
{
float val = getfloat(p);
if(id && id->flags&IDF_OVERRIDE && !(id-
>flags&IDF_READONLY)) setfvar(name, val);
break;
}
case ID_SVAR:
{
getstring(text, p);
if(id && id->flags&IDF_OVERRIDE && !(id-
>flags&IDF_READONLY)) setsvar(name, text);
break;
}
}
printvar(d, id);
break;
}

case N_PONG:
addmsg(N_CLIENTPING, "i", player1->ping = (player1-
>ping*5+totalmillis-getint(p))/6);
break;

case N_CLIENTPING:
if(!d) return;
d->ping = getint(p);
break;

case N_TIMEUP:
timeupdate(getint(p));
break;

case N_SERVMSG:
getstring(text, p);
conoutf("%s", text);
break;

case N_SENDDEMOLIST:
{
int demos = getint(p);
if(demos <= 0) conoutf("no demos available");
else loopi(demos)
{
getstring(text, p);
if(p.overread()) break;
conoutf("%d. %s", i+1, text);
}
break;
}

case N_DEMOPLAYBACK:
{
int on = getint(p);
if(on) player1->state = CS_SPECTATOR;
else clearclients();
demoplayback = on!=0;
player1->clientnum = getint(p);
gamepaused = false;
const char *alias = on ? "demostart" : "demoend";
if(identexists(alias)) execute(alias);
break;
}

case N_CURRENTMASTER:
{
int mm = getint(p), mn;
loopv(players) players[i]->privilege = PRIV_NONE;
while((mn = getint(p))>=0 && !p.overread())
{
fpsent *m = mn==player1->clientnum ? player1 : newclient(mn);
int priv = getint(p);
if(m) m->privilege = priv;
}
if(mm != mastermode)
{
mastermode = mm;
conoutf("mastermode is %s (%d)",
server::mastermodename(mastermode), mastermode);
}
break;
}

case N_MASTERMODE:
{
mastermode = getint(p);
conoutf("mastermode is %s (%d)",
server::mastermodename(mastermode), mastermode);
break;
}

case N_EDITMODE:
{
int val = getint(p);
if(!d) break;
if(val)
{
d->editstate = d->state;
d->state = CS_EDITING;
}
else
{
d->state = d->editstate;
if(d->state==CS_DEAD) deathstate(d, true);
}
break;
}

case N_SPECTATOR:
{
int sn = getint(p), val = getint(p);
fpsent *s;
if(sn==player1->clientnum)
{
s = player1;
if(val && remote && !player1->privilege) senditemstoserver =
false;
}
else s = newclient(sn);
if(!s) return;
if(val)
{
if(s==player1)
{
if(editmode) toggleedit();
if(s->state==CS_DEAD) showscores(false);
disablezoom();
}
s->state = CS_SPECTATOR;
}
else if(s->state==CS_SPECTATOR)
{
if(s==player1) stopfollowing();
deathstate(s, true);
}
break;
}

case N_SETTEAM:
{
int wn = getint(p);
getstring(text, p);
int reason = getint(p);
fpsent *w = getclient(wn);
if(!w) return;
filtertext(w->team, text, false, MAXTEAMLEN);
static const char *fmt[2] = { "%s switched to team %s", "%s forced
to team %s"};
if(reason >= 0 && size_t(reason) < sizeof(fmt)/sizeof(fmt[0]))
conoutf(fmt[reason], colorname(w), w->team);
break;
}

#define PARSEMESSAGES 1
#include "capture.h"
#include "ctf.h"
#include "collect.h"
#undef PARSEMESSAGES

case N_ANNOUNCE:
{
int t = getint(p);
if (t==I_QUAD) { playsound(S_V_QUAD10, NULL, NULL, 0, 0, 0, -
1, 0, 3000); conoutf(CON_GAMEINFO, "\f2quad damage will spawn in 10 seconds!"); }
else if(t==I_BOOST) { playsound(S_V_BOOST10, NULL, NULL, 0, 0, 0, -
1, 0, 3000); conoutf(CON_GAMEINFO, "\f2+10 health will spawn in 10 seconds!"); }
break;
}

case N_NEWMAP:
{
int size = getint(p);
if(size>=0) emptymap(size, true, NULL);
else enlargemap(true);
if(d && d!=player1)
{
int newsize = 0;
while(1<<newsize < getworldsize()) newsize++;
conoutf(size>=0 ? "%s started a new map of size %d" : "%s
enlarged the map to size %d", colorname(d), newsize);
}
break;
}

case N_REQAUTH:
{
getstring(text, p);
if(autoauth && text[0] && tryauth(text)) conoutf("server requested
authkey \"%s\"", text);
break;
}

case N_AUTHCHAL:
{
getstring(text, p);
authkey *a = findauthkey(text);
uint id = (uint)getint(p);
getstring(text, p);
if(a && a->lastauth && lastmillis - a->lastauth < 60*1000)
{
vector<char> buf;
answerchallenge(a->key, text, buf);
//conoutf(CON_DEBUG, "answering %u, challenge %s with %s", id,
text, buf.getbuf());
addmsg(N_AUTHANS, "rsis", a->desc, id, buf.getbuf());
}
break;
}

case N_INITAI:
{
int bn = getint(p), on = getint(p), at = getint(p), sk =
clamp(getint(p), 1, 101), pm = getint(p);
string name, team;
getstring(text, p);
filtertext(name, text, false, MAXNAMELEN);
getstring(text, p);
filtertext(team, text, false, MAXTEAMLEN);
fpsent *b = newclient(bn);
if(!b) break;
ai::init(b, at, on, sk, bn, pm, name, team);
break;
}

case N_SERVCMD:
getstring(text, p);
break;

default:
neterr("type", cn < 0);
return;
}
}

void receivefile(packetbuf &p)


{
int type;
while(p.remaining()) switch(type = getint(p))
{
case N_DEMOPACKET: return;
case N_SENDDEMO:
{
defformatstring(fname)("%d.dmo", lastmillis);
stream *demo = openrawfile(fname, "wb");
if(!demo) return;
conoutf("received demo \"%s\"", fname);
ucharbuf b = p.subbuf(p.remaining());
demo->write(b.buf, b.maxlen);
delete demo;
break;
}

case N_SENDMAP:
{
if(!m_edit) return;
string oldname;
copystring(oldname, getclientmap());
defformatstring(mname)("getmap_%d", lastmillis);
defformatstring(fname)("packages/base/%s.ogz", mname);
stream *map = openrawfile(path(fname), "wb");
if(!map) return;
conoutf("received map");
ucharbuf b = p.subbuf(p.remaining());
map->write(b.buf, b.maxlen);
delete map;
if(load_world(mname, oldname[0] ? oldname : NULL))
entities::spawnitems(true);
remove(findfile(fname, "rb"));
break;
}
}
}

void parsepacketclient(int chan, packetbuf &p) // processes any updates from


the server
{
if(p.packet->flags&ENET_PACKET_FLAG_UNSEQUENCED) return;
switch(chan)
{
case 0:
parsepositions(p);
break;

case 1:
parsemessages(-1, NULL, p);
break;

case 2:
receivefile(p);
break;
}
}

void getmap()
{
if(!m_edit) { conoutf(CON_ERROR, "\"getmap\" only works in coop edit
mode"); return; }
conoutf("getting map...");
addmsg(N_GETMAP, "r");
}
COMMAND(getmap, "");

void stopdemo()
{
if(remote)
{
if(player1->privilege<PRIV_MASTER) return;
addmsg(N_STOPDEMO, "r");
}
else server::stopdemo();
}
COMMAND(stopdemo, "");

void recorddemo(int val)


{
if(remote && player1->privilege<PRIV_MASTER) return;
addmsg(N_RECORDDEMO, "ri", val);
}
ICOMMAND(recorddemo, "i", (int *val), recorddemo(*val));

void cleardemos(int val)


{
if(remote && player1->privilege<PRIV_MASTER) return;
addmsg(N_CLEARDEMOS, "ri", val);
}
ICOMMAND(cleardemos, "i", (int *val), cleardemos(*val));

void getdemo(int i)
{
if(i<=0) conoutf("getting demo...");
else conoutf("getting demo %d...", i);
addmsg(N_GETDEMO, "ri", i);
}
ICOMMAND(getdemo, "i", (int *val), getdemo(*val));

void listdemos()
{
conoutf("listing demos...");
addmsg(N_LISTDEMOS, "r");
}
COMMAND(listdemos, "");

void sendmap()
{
if(!m_edit || (player1->state==CS_SPECTATOR && remote && !player1-
>privilege)) { conoutf(CON_ERROR, "\"sendmap\" only works in coop edit mode");
return; }
conoutf("sending map...");
defformatstring(mname)("sendmap_%d", lastmillis);
save_world(mname, true);
defformatstring(fname)("packages/base/%s.ogz", mname);
stream *map = openrawfile(path(fname), "rb");
if(map)
{
stream::offset len = map->size();
if(len > 4*1024*1024) conoutf(CON_ERROR, "map is too large");
else if(len <= 0) conoutf(CON_ERROR, "could not read map");
else
{
sendfile(-1, 2, map);
if(needclipboard >= 0) needclipboard++;
}
delete map;
}
else conoutf(CON_ERROR, "could not read map");
remove(findfile(fname, "rb"));
}
COMMAND(sendmap, "");

void gotoplayer(const char *arg)


{
if(player1->state!=CS_SPECTATOR && player1->state!=CS_EDITING) return;
int i = parseplayer(arg);
if(i>=0)
{
fpsent *d = getclient(i);
if(!d || d==player1) return;
player1->o = d->o;
vec dir;
vecfromyawpitch(player1->yaw, player1->pitch, 1, 0, dir);
player1->o.add(dir.mul(-32));
player1->resetinterp();
}
}
COMMANDN(goto, gotoplayer, "s");

void gotosel()
{
if(player1->state!=CS_EDITING) return;
player1->o = getselpos();
vec dir;
vecfromyawpitch(player1->yaw, player1->pitch, 1, 0, dir);
player1->o.add(dir.mul(-32));
player1->resetinterp();
}
COMMAND(gotosel, "");
}

You might also like