2020-04-16 18:28:40 +00:00
|
|
|
#include "game.hh"
|
2020-04-15 16:39:17 +00:00
|
|
|
|
|
|
|
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(gameent *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();
|
|
|
|
gle::defvertex(2);
|
|
|
|
gle::deftexcoord0();
|
|
|
|
gle::begin(GL_TRIANGLE_FAN);
|
|
|
|
loopi(16)
|
|
|
|
{
|
|
|
|
vec v = vec(0, -1, 0).rotate_around_z(i/16.0f*2*M_PI);
|
|
|
|
gle::attribf(x + 0.5f*s*(1.0f + v.x), y + 0.5f*s*(1.0f + v.y));
|
|
|
|
vec tc = vec(dir).rotate_around_z(i/16.0f*2*M_PI);
|
|
|
|
gle::attribf(1.0f - (pos.x + tc.x*scale*minimapscale.x), pos.y + tc.y*scale*minimapscale.y);
|
|
|
|
}
|
|
|
|
gle::end();
|
|
|
|
}
|
|
|
|
|
|
|
|
void setradartex()
|
|
|
|
{
|
|
|
|
settexture("media/interface/radar/radar.png", 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
clientmode *cmode = NULL;
|
|
|
|
|
|
|
|
void setclientmode()
|
|
|
|
{
|
2020-04-16 18:43:52 +00:00
|
|
|
cmode = NULL;
|
2020-04-15 16:39:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2020-04-16 21:18:05 +00:00
|
|
|
int sessionid = 0, gamespeed = 100;
|
|
|
|
string servdesc = "", servauth = "";
|
2020-04-15 16:39:17 +00:00
|
|
|
|
|
|
|
VARP(deadpush, 1, 2, 20);
|
|
|
|
|
|
|
|
void sendmapinfo()
|
|
|
|
{
|
|
|
|
if(!connected) return;
|
|
|
|
sendcrc = true;
|
2020-04-16 21:18:05 +00:00
|
|
|
senditemstoserver = true;
|
2020-04-15 16:39:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void writeclientinfo(stream *f)
|
|
|
|
{
|
|
|
|
f->printf("name %s\n", escapestring(player1->name));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool allowedittoggle()
|
|
|
|
{
|
|
|
|
if(editmode) return true;
|
|
|
|
if(isconnected() && multiplayer(false) && !m_edit)
|
|
|
|
{
|
|
|
|
conoutf(CON_ERROR, "editing in multiplayer requires edit mode");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return execidentbool("allowedittoggle", true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void edittoggled(bool on)
|
|
|
|
{
|
|
|
|
addmsg(N_EDITMODE, "ri", on ? 1 : 0);
|
|
|
|
if(player1->state==CS_DEAD) deathstate(player1, true);
|
|
|
|
disablezoom();
|
2020-04-17 18:19:42 +00:00
|
|
|
player1->respawned = -2;
|
2020-04-15 16:39:17 +00:00
|
|
|
checkfollow();
|
|
|
|
}
|
|
|
|
|
|
|
|
string clientmap = "";
|
|
|
|
|
|
|
|
void changemapserv(const char *name, int mode) // forced map change from the server
|
|
|
|
{
|
|
|
|
if(editmode) toggleedit();
|
|
|
|
if((m_edit && !name[0]) || !load_world(name))
|
|
|
|
{
|
|
|
|
emptymap(0, true, name);
|
|
|
|
senditemstoserver = false;
|
|
|
|
}
|
|
|
|
startgame();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void changemap(const char *name, int mode) // request map change, server may ignore
|
|
|
|
{
|
2020-04-16 21:18:05 +00:00
|
|
|
server::forcemap(name, mode);
|
|
|
|
if(!isconnected()) localconnect();
|
2020-04-15 16:39:17 +00:00
|
|
|
}
|
|
|
|
void changemap(const char *name)
|
|
|
|
{
|
2020-04-16 21:18:05 +00:00
|
|
|
changemap(name, 0);
|
2020-04-15 16:39:17 +00:00
|
|
|
}
|
|
|
|
ICOMMAND(map, "s", (char *name), changemap(name));
|
|
|
|
|
|
|
|
void forceedit(const char *name)
|
|
|
|
{
|
|
|
|
changemap(name, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
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, const VSlot *vs)
|
|
|
|
{
|
|
|
|
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:
|
|
|
|
{
|
|
|
|
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_TEX:
|
|
|
|
{
|
|
|
|
int tex1 = shouldpacktex(arg1);
|
|
|
|
if(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,
|
|
|
|
tex1 ? tex1 : arg1, arg2))
|
|
|
|
{
|
|
|
|
messages.pad(2);
|
|
|
|
int offset = messages.length();
|
|
|
|
if(tex1) packvslot(messages, arg1);
|
|
|
|
*(ushort *)&messages[offset-2] = lilswap(ushort(messages.length() - offset));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case EDIT_REPLACE:
|
|
|
|
{
|
|
|
|
int tex1 = shouldpacktex(arg1), tex2 = shouldpacktex(arg2);
|
|
|
|
if(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,
|
|
|
|
tex1 ? tex1 : arg1, tex2 ? tex2 : arg2, arg3))
|
|
|
|
{
|
|
|
|
messages.pad(2);
|
|
|
|
int offset = messages.length();
|
|
|
|
if(tex1) packvslot(messages, arg1);
|
|
|
|
if(tex2) packvslot(messages, arg2);
|
|
|
|
*(ushort *)&messages[offset-2] = lilswap(ushort(messages.length() - offset));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case EDIT_CALCLIGHT:
|
|
|
|
case EDIT_REMIP:
|
|
|
|
{
|
|
|
|
addmsg(N_EDITF + op, "r");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case EDIT_VSLOT:
|
|
|
|
{
|
|
|
|
if(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))
|
|
|
|
{
|
|
|
|
messages.pad(2);
|
|
|
|
int offset = messages.length();
|
|
|
|
packvslot(messages, vs);
|
|
|
|
*(ushort *)&messages[offset-2] = lilswap(ushort(messages.length() - offset));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case EDIT_UNDO:
|
|
|
|
case EDIT_REDO:
|
|
|
|
{
|
|
|
|
uchar *outbuf = NULL;
|
|
|
|
int inlen = 0, outlen = 0;
|
|
|
|
if(packundo(op, inlen, outbuf, outlen))
|
|
|
|
{
|
|
|
|
if(addmsg(N_EDITF + op, "ri2", inlen, outlen)) messages.put(outbuf, outlen);
|
|
|
|
delete[] outbuf;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void printvar(gameent *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);
|
|
|
|
});
|
|
|
|
ICOMMAND(prettygamespeed, "i", (), result(tempformatstring("%d.%02dx", gamespeed/100, gamespeed%100)));
|
|
|
|
|
|
|
|
int scaletime(int t) { return t*gamespeed; }
|
|
|
|
|
|
|
|
// collect c2s messages conveniently
|
|
|
|
vector<uchar> messages;
|
|
|
|
int messagecn = -1, messagereliable = false;
|
|
|
|
|
|
|
|
bool addmsg(int type, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
if(!connected) return false;
|
|
|
|
static uchar buf[MAXTRANS];
|
|
|
|
ucharbuf p(buf, sizeof(buf));
|
|
|
|
putint(p, type);
|
2020-04-16 21:18:05 +00:00
|
|
|
int numi = 1, numf = 0, nums = 0;
|
2020-04-15 16:39:17 +00:00
|
|
|
bool reliable = false;
|
|
|
|
if(fmt)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
|
|
while(*fmt) switch(*fmt++)
|
|
|
|
{
|
|
|
|
case 'r': reliable = true; break;
|
|
|
|
case 'c':
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
messages.put(buf, p.length());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void connectattempt(const char *name, const char *password, const ENetAddress &address)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void connectfail()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void gameconnect(bool _remote)
|
|
|
|
{
|
|
|
|
remote = _remote;
|
|
|
|
}
|
|
|
|
|
|
|
|
void gamedisconnect(bool cleanup)
|
|
|
|
{
|
|
|
|
if(remote) stopfollowing();
|
|
|
|
connected = remote = false;
|
|
|
|
player1->clientnum = -1;
|
|
|
|
if(editmode) toggleedit();
|
|
|
|
sessionid = 0;
|
|
|
|
messages.setsize(0);
|
|
|
|
messagereliable = false;
|
|
|
|
messagecn = -1;
|
|
|
|
player1->respawn();
|
|
|
|
player1->lifesequence = 0;
|
|
|
|
player1->state = CS_ALIVE;
|
|
|
|
sendcrc = senditemstoserver = false;
|
|
|
|
demoplayback = false;
|
|
|
|
gamepaused = false;
|
|
|
|
gamespeed = 100;
|
|
|
|
clearclients(false);
|
|
|
|
if(cleanup)
|
|
|
|
{
|
|
|
|
clientmap[0] = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-17 18:19:42 +00:00
|
|
|
const char *chatcolorname(gameent *d) { return colorname(d); }
|
2020-04-15 16:39:17 +00:00
|
|
|
|
2020-04-17 18:19:42 +00:00
|
|
|
void toserver(char *text) { conoutf(CON_CHAT, "%s: %s", chatcolorname(player1), text); addmsg(N_TEXT, "rcs", player1, text); }
|
2020-04-15 16:39:17 +00:00
|
|
|
COMMANDN(say, toserver, "C");
|
|
|
|
|
|
|
|
ICOMMAND(servcmd, "C", (char *cmd), addmsg(N_SERVCMD, "rs", cmd));
|
|
|
|
|
|
|
|
static void sendposition(gameent *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, 1 bit crouching
|
|
|
|
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;
|
|
|
|
if(d->crouching < 0) flags |= 1<<8;
|
|
|
|
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(gameent *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)
|
|
|
|
{
|
|
|
|
gameent *d = players[i];
|
2020-04-16 21:18:05 +00:00
|
|
|
if((d == player1) && (d->state == CS_ALIVE || d->state == CS_EDITING))
|
2020-04-15 16:39:17 +00:00
|
|
|
{
|
|
|
|
packetbuf q(100);
|
|
|
|
sendposition(d, q);
|
|
|
|
for(int j = i+1; j < players.length(); j++)
|
|
|
|
{
|
|
|
|
gameent *d = players[j];
|
2020-04-16 21:18:05 +00:00
|
|
|
if((d == player1) && (d->state == CS_ALIVE || d->state == CS_EDITING))
|
2020-04-15 16:39:17 +00:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
p.reliable();
|
|
|
|
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 < 40 && !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);
|
|
|
|
putint(p, player1->playercolor);
|
|
|
|
sendclientpacket(p.finalize(), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void updatepos(gameent *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;
|
|
|
|
while(p.remaining()) switch(type = getint(p))
|
|
|
|
{
|
|
|
|
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 |= ~0U<<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;
|
|
|
|
gameent *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;
|
|
|
|
d->crouching = (flags&(1<<8))!=0 ? -1 : abs(d->crouching);
|
|
|
|
vec oldpos(d->o);
|
|
|
|
d->o = o;
|
|
|
|
d->o.z += d->eyeheight;
|
|
|
|
d->vel = vel;
|
|
|
|
d->falling = falling;
|
|
|
|
d->physstate = physstate&7;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
neterr("type");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void parsestate(gameent *d, ucharbuf &p, bool resume = false)
|
|
|
|
{
|
|
|
|
if(!d) { static gameent dummy; d = &dummy; }
|
|
|
|
if(resume)
|
|
|
|
{
|
|
|
|
if(d==player1) getint(p);
|
|
|
|
else d->state = getint(p);
|
|
|
|
}
|
|
|
|
d->lifesequence = getint(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
void parsemessages(int cn, gameent *d, ucharbuf &p)
|
|
|
|
{
|
|
|
|
static char text[MAXTRANS];
|
|
|
|
int type;
|
|
|
|
|
|
|
|
while(p.remaining()) switch(type = getint(p))
|
|
|
|
{
|
|
|
|
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
|
|
|
|
sendintro();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case N_WELCOME:
|
|
|
|
{
|
|
|
|
connected = true;
|
|
|
|
notifywelcome();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case N_PAUSEGAME:
|
|
|
|
{
|
|
|
|
bool val = getint(p) > 0;
|
|
|
|
int cn = getint(p);
|
|
|
|
gameent *a = cn >= 0 ? getclient(cn) : NULL;
|
2020-04-16 21:18:05 +00:00
|
|
|
gamepaused = val;
|
2020-04-15 16:39:17 +00:00
|
|
|
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);
|
|
|
|
gameent *a = cn >= 0 ? getclient(cn) : NULL;
|
2020-04-16 21:18:05 +00:00
|
|
|
gamespeed = val;
|
2020-04-15 16:39:17 +00:00
|
|
|
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, true, true);
|
|
|
|
if(d->state!=CS_DEAD && d->state!=CS_SPECTATOR)
|
|
|
|
particle_textcopy(d->abovehead(), text, PART_TEXT, 2000, 0x32FF64, 4.0f, -8);
|
2020-04-17 18:19:42 +00:00
|
|
|
conoutf(CON_CHAT, "%s: %s", chatcolorname(d), text);
|
2020-04-15 16:39:17 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case N_MAPCHANGE:
|
|
|
|
getstring(text, p);
|
|
|
|
changemapserv(text, getint(p));
|
|
|
|
if(getint(p)) entities::spawnitems();
|
|
|
|
else senditemstoserver = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case N_INITCLIENT: // another client either connected or changed name/team
|
|
|
|
{
|
|
|
|
int cn = getint(p);
|
|
|
|
gameent *d = newclient(cn);
|
|
|
|
if(!d)
|
|
|
|
{
|
|
|
|
getstring(text, p);
|
|
|
|
getstring(text, p);
|
|
|
|
getint(p);
|
|
|
|
getint(p);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
getstring(text, p);
|
|
|
|
filtertext(text, text, false, false, MAXNAMELEN);
|
|
|
|
if(!text[0]) copystring(text, "unnamed");
|
|
|
|
if(d->name[0]) // already connected
|
|
|
|
{
|
2020-04-16 21:18:05 +00:00
|
|
|
if(strcmp(d->name, text))
|
2020-04-15 16:39:17 +00:00
|
|
|
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);
|
2020-04-17 18:19:42 +00:00
|
|
|
d->playermodel = 0;
|
|
|
|
d->playercolor = 0;
|
2020-04-15 16:39:17 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case N_SWITCHNAME:
|
|
|
|
getstring(text, p);
|
|
|
|
if(d)
|
|
|
|
{
|
|
|
|
filtertext(text, text, false, false, MAXNAMELEN);
|
|
|
|
if(!text[0]) copystring(text, "unnamed");
|
|
|
|
if(strcmp(text, d->name))
|
|
|
|
{
|
2020-04-16 21:18:05 +00:00
|
|
|
conoutf("%s is now known as %s", colorname(d), colorname(d, text));
|
2020-04-15 16:39:17 +00:00
|
|
|
copystring(d->name, text, MAXNAMELEN+1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case N_CDIS:
|
|
|
|
clientdisconnected(getint(p));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case N_SPAWN:
|
|
|
|
{
|
|
|
|
if(d)
|
|
|
|
{
|
|
|
|
d->respawn();
|
|
|
|
}
|
|
|
|
parsestate(d, p);
|
|
|
|
if(!d) break;
|
|
|
|
d->state = CS_SPAWNING;
|
|
|
|
if(d == followingplayer()) lasthit = 0;
|
|
|
|
checkfollow();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case N_SPAWNSTATE:
|
|
|
|
{
|
|
|
|
int scn = getint(p);
|
|
|
|
gameent *s = getclient(scn);
|
|
|
|
if(!s) { parsestate(NULL, p); break; }
|
|
|
|
if(s==player1)
|
|
|
|
{
|
|
|
|
if(editmode) toggleedit();
|
|
|
|
}
|
|
|
|
s->respawn();
|
|
|
|
parsestate(s, p);
|
|
|
|
s->state = CS_ALIVE;
|
|
|
|
if(cmode) cmode->pickspawn(s);
|
2020-04-17 18:19:42 +00:00
|
|
|
else findplayerspawn(s, -1, 0);
|
2020-04-15 16:39:17 +00:00
|
|
|
if(s == player1)
|
|
|
|
{
|
|
|
|
lasthit = 0;
|
|
|
|
}
|
|
|
|
if(cmode) cmode->respawned(s);
|
|
|
|
checkfollow();
|
2020-04-17 18:19:42 +00:00
|
|
|
addmsg(N_SPAWN, "rci", s, s->lifesequence);
|
2020-04-15 16:39:17 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case N_RESUME:
|
|
|
|
{
|
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
int cn = getint(p);
|
|
|
|
if(p.overread() || cn<0) break;
|
|
|
|
gameent *d = (cn == player1->clientnum ? player1 : newclient(cn));
|
|
|
|
parsestate(d, p, true);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case N_CLIPBOARD:
|
|
|
|
{
|
|
|
|
int cn = getint(p), unpacklen = getint(p), packlen = getint(p);
|
|
|
|
gameent *d = getclient(cn);
|
|
|
|
ucharbuf q = p.subbuf(max(packlen, 0));
|
|
|
|
if(d) unpackeditinfo(d->edit, q.buf, q.maxlen, unpacklen);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case N_UNDO:
|
|
|
|
case N_REDO:
|
|
|
|
{
|
|
|
|
int cn = getint(p), unpacklen = getint(p), packlen = getint(p);
|
|
|
|
gameent *d = getclient(cn);
|
|
|
|
ucharbuf q = p.subbuf(max(packlen, 0));
|
|
|
|
if(d) unpackundo(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:
|
|
|
|
case N_EDITVSLOT:
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
switch(type)
|
|
|
|
{
|
|
|
|
case N_EDITF: { int dir = getint(p), mode = getint(p); if(sel.validate()) mpeditface(dir, mode, sel, false); break; }
|
|
|
|
case N_EDITT:
|
|
|
|
{
|
|
|
|
int tex = getint(p),
|
|
|
|
allfaces = getint(p);
|
|
|
|
if(p.remaining() < 2) return;
|
|
|
|
int extra = lilswap(*(const ushort *)p.pad(2));
|
|
|
|
if(p.remaining() < extra) return;
|
|
|
|
ucharbuf ebuf = p.subbuf(extra);
|
|
|
|
if(sel.validate()) mpedittex(tex, allfaces, sel, ebuf);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case N_EDITM: { int 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: { int dir = getint(p); if(sel.validate()) mprotate(dir, sel, false); break; }
|
|
|
|
case N_REPLACE:
|
|
|
|
{
|
|
|
|
int oldtex = getint(p),
|
|
|
|
newtex = getint(p),
|
|
|
|
insel = getint(p);
|
|
|
|
if(p.remaining() < 2) return;
|
|
|
|
int extra = lilswap(*(const ushort *)p.pad(2));
|
|
|
|
if(p.remaining() < extra) return;
|
|
|
|
ucharbuf ebuf = p.subbuf(extra);
|
|
|
|
if(sel.validate()) mpreplacetex(oldtex, newtex, insel>0, sel, ebuf);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case N_DELCUBE: if(sel.validate()) mpdelcube(sel, false); break;
|
|
|
|
case N_EDITVSLOT:
|
|
|
|
{
|
|
|
|
int delta = getint(p),
|
|
|
|
allfaces = getint(p);
|
|
|
|
if(p.remaining() < 2) return;
|
|
|
|
int extra = lilswap(*(const ushort *)p.pad(2));
|
|
|
|
if(p.remaining() < extra) return;
|
|
|
|
ucharbuf ebuf = p.subbuf(extra);
|
|
|
|
if(sel.validate()) mpeditvslot(delta, allfaces, sel, ebuf);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case N_REMIP:
|
|
|
|
if(!d) return;
|
|
|
|
conoutf("%s remipped", colorname(d));
|
|
|
|
mpremip(false);
|
|
|
|
break;
|
|
|
|
case N_CALCLIGHT:
|
|
|
|
if(!d) return;
|
|
|
|
conoutf("%s calced lights", colorname(d));
|
|
|
|
mpcalclight(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);
|
|
|
|
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_SERVMSG:
|
|
|
|
getstring(text, p);
|
|
|
|
conoutf("%s", text);
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
checkfollow();
|
|
|
|
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_SERVCMD:
|
|
|
|
getstring(text, p);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
neterr("type", cn < 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void receivefile(packetbuf &p)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|