This way, nowhere else autorelease pools need to be managed. FossilOrigin-Name: 489124a92fd2a7e6d543b58ce50e454f2cb1647c81b4ba637d6c252404012ddd
400 lines
8.6 KiB
Text
400 lines
8.6 KiB
Text
// client processing of the incoming network stream
|
|
|
|
#include "cube.h"
|
|
|
|
#import "DynamicEntity.h"
|
|
|
|
extern int clientnum;
|
|
extern bool c2sinit, senditemstoserver;
|
|
extern OFString *toservermap;
|
|
extern OFString *clientpassword;
|
|
|
|
void
|
|
neterr(OFString *s)
|
|
{
|
|
conoutf(@"illegal network message (%@)", s);
|
|
disconnect();
|
|
}
|
|
|
|
void
|
|
changemapserv(OFString *name, int mode) // forced map change from the server
|
|
{
|
|
gamemode = mode;
|
|
load_world(name);
|
|
}
|
|
|
|
void
|
|
changemap(OFString *name) // request map change, server may ignore
|
|
{
|
|
toservermap = name;
|
|
}
|
|
|
|
// 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(DynamicEntity *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)
|
|
// push aside
|
|
d.o = OFMakeVector3D(d.o.x,
|
|
d.o.y + (dy < 0 ? r - fy : -(r - fy)), d.o.z);
|
|
else
|
|
d.o = OFMakeVector3D(
|
|
d.o.x + (dx < 0 ? r - fx : -(r - fx)), d.o.y,
|
|
d.o.z);
|
|
}
|
|
int lagtime = lastmillis - d.lastupdate;
|
|
if (lagtime) {
|
|
d.plag = (d.plag * 5 + lagtime) / 6;
|
|
d.lastupdate = lastmillis;
|
|
}
|
|
}
|
|
|
|
// processes any updates from the server
|
|
void
|
|
localservertoclient(uchar *buf, int len)
|
|
{
|
|
if (ENET_NET_TO_HOST_16(*(ushort *)buf) != len)
|
|
neterr(@"packet length");
|
|
incomingdemodata(buf, len);
|
|
|
|
uchar *end = buf + len;
|
|
uchar *p = buf + 2;
|
|
char text[MAXTRANS];
|
|
int cn = -1, type;
|
|
DynamicEntity *d = nil;
|
|
bool mapchanged = false;
|
|
|
|
while (p < end)
|
|
switch (type = getint(p)) {
|
|
case SV_INITS2C: // welcome messsage from the server
|
|
{
|
|
cn = getint(p);
|
|
int prot = getint(p);
|
|
if (prot != PROTOCOL_VERSION) {
|
|
conoutf(@"you are using a different game "
|
|
@"protocol (you: %d, server: %d)",
|
|
PROTOCOL_VERSION, prot);
|
|
disconnect();
|
|
return;
|
|
}
|
|
toservermap = @"";
|
|
clientnum = cn; // we are now fully connected
|
|
if (!getint(p))
|
|
// we are the first client on this server, set
|
|
// map
|
|
toservermap = getclientmap();
|
|
sgetstr();
|
|
if (text[0] &&
|
|
strcmp(text, clientpassword.UTF8String)) {
|
|
conoutf(@"you need to set the correct password "
|
|
@"to join this server!");
|
|
disconnect();
|
|
return;
|
|
}
|
|
if (getint(p) == 1)
|
|
conoutf(@"server is FULL, disconnecting..");
|
|
break;
|
|
}
|
|
|
|
case SV_POS: {
|
|
// position of another client
|
|
cn = getint(p);
|
|
d = getclient(cn);
|
|
if (d == nil)
|
|
return;
|
|
d.o = OFMakeVector3D(
|
|
getint(p) / DMF, getint(p) / DMF, getint(p) / DMF);
|
|
d.yaw = getint(p) / DAF;
|
|
d.pitch = getint(p) / DAF;
|
|
d.roll = getint(p) / DAF;
|
|
d.vel = OFMakeVector3D(
|
|
getint(p) / DVF, getint(p) / DVF, getint(p) / DVF);
|
|
int f = getint(p);
|
|
d.strafe = (f & 3) == 3 ? -1 : f & 3;
|
|
f >>= 2;
|
|
d.move = (f & 3) == 3 ? -1 : f & 3;
|
|
d.onfloor = (f >> 2) & 1;
|
|
int state = f >> 3;
|
|
if (state == CS_DEAD && d.state != CS_DEAD)
|
|
d.lastaction = lastmillis;
|
|
d.state = state;
|
|
if (!demoplayback)
|
|
updatepos(d);
|
|
break;
|
|
}
|
|
|
|
case SV_SOUND: {
|
|
OFVector3D loc = d.o;
|
|
playsound(getint(p), &loc);
|
|
break;
|
|
}
|
|
|
|
case SV_TEXT:
|
|
sgetstr();
|
|
conoutf(@"%@:\f %s", d.name, text);
|
|
break;
|
|
|
|
case SV_MAPCHANGE:
|
|
sgetstr();
|
|
changemapserv(@(text), getint(p));
|
|
mapchanged = true;
|
|
break;
|
|
|
|
case SV_ITEMLIST: {
|
|
int n;
|
|
if (mapchanged) {
|
|
senditemstoserver = false;
|
|
resetspawns();
|
|
}
|
|
while ((n = getint(p)) != -1)
|
|
if (mapchanged)
|
|
setspawn(n, true);
|
|
break;
|
|
}
|
|
|
|
case SV_MAPRELOAD: // server requests next map
|
|
{
|
|
getint(p);
|
|
OFString *nextmapalias = [OFString
|
|
stringWithFormat:@"nextmap_%@", getclientmap()];
|
|
OFString *map =
|
|
getalias(nextmapalias); // look up map in the cycle
|
|
changemap(map != nil ? map : getclientmap());
|
|
break;
|
|
}
|
|
|
|
// another client either connected or changed name/team
|
|
case SV_INITC2S: {
|
|
sgetstr();
|
|
if (d.name.length > 0) {
|
|
// already connected
|
|
if (![d.name isEqual:@(text)])
|
|
conoutf(@"%@ is now known as %s",
|
|
d.name, text);
|
|
} else {
|
|
// new client
|
|
|
|
// send new players my info again
|
|
c2sinit = false;
|
|
conoutf(@"connected: %s", text);
|
|
}
|
|
d.name = @(text);
|
|
sgetstr();
|
|
d.team = @(text);
|
|
d.lifesequence = getint(p);
|
|
break;
|
|
}
|
|
|
|
case SV_CDIS:
|
|
cn = getint(p);
|
|
if ((d = getclient(cn)) == nil)
|
|
break;
|
|
conoutf(@"player %@ disconnected",
|
|
d.name.length ? d.name : @"[incompatible client]");
|
|
players[cn] = [OFNull null];
|
|
break;
|
|
|
|
case SV_SHOT: {
|
|
int gun = getint(p);
|
|
OFVector3D s, e;
|
|
s.x = getint(p) / DMF;
|
|
s.y = getint(p) / DMF;
|
|
s.z = getint(p) / DMF;
|
|
e.x = getint(p) / DMF;
|
|
e.y = getint(p) / DMF;
|
|
e.z = getint(p) / DMF;
|
|
if (gun == GUN_SG)
|
|
createrays(s, e);
|
|
shootv(gun, s, e, d);
|
|
break;
|
|
}
|
|
|
|
case SV_DAMAGE: {
|
|
int target = getint(p);
|
|
int damage = getint(p);
|
|
int ls = getint(p);
|
|
if (target == clientnum) {
|
|
if (ls == player1.lifesequence)
|
|
selfdamage(damage, cn, d);
|
|
} else {
|
|
OFVector3D loc = getclient(target).o;
|
|
playsound(S_PAIN1 + rnd(5), &loc);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SV_DIED: {
|
|
int actor = getint(p);
|
|
if (actor == cn) {
|
|
conoutf(@"%@ suicided", d.name);
|
|
} else if (actor == clientnum) {
|
|
int frags;
|
|
if (isteam(player1.team, d.team)) {
|
|
frags = -1;
|
|
conoutf(@"you fragged a teammate (%@)",
|
|
d.name);
|
|
} else {
|
|
frags = 1;
|
|
conoutf(@"you fragged %@", d.name);
|
|
}
|
|
addmsg(
|
|
1, 2, SV_FRAGS, (player1.frags += frags));
|
|
} else {
|
|
DynamicEntity *a = getclient(actor);
|
|
if (a != nil) {
|
|
if (isteam(a.team, d.name))
|
|
conoutf(@"%@ fragged his "
|
|
@"teammate (%@)",
|
|
a.name, d.name);
|
|
else
|
|
conoutf(@"%@ fragged %@",
|
|
a.name, d.name);
|
|
}
|
|
}
|
|
OFVector3D loc = d.o;
|
|
playsound(S_DIE1 + rnd(2), &loc);
|
|
d.lifesequence++;
|
|
break;
|
|
}
|
|
|
|
case SV_FRAGS:
|
|
[players[cn] setFrags:getint(p)];
|
|
break;
|
|
|
|
case SV_ITEMPICKUP:
|
|
setspawn(getint(p), false);
|
|
getint(p);
|
|
break;
|
|
|
|
case SV_ITEMSPAWN: {
|
|
uint i = getint(p);
|
|
setspawn(i, true);
|
|
if (i >= (uint)ents.length())
|
|
break;
|
|
OFVector3D v =
|
|
OFMakeVector3D(ents[i].x, ents[i].y, ents[i].z);
|
|
playsound(S_ITEMSPAWN, &v);
|
|
break;
|
|
}
|
|
|
|
case SV_ITEMACC: // server acknowledges that I picked up this
|
|
// item
|
|
realpickup(getint(p), player1);
|
|
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_EDITENT: // coop edit of ent
|
|
{
|
|
uint i = getint(p);
|
|
while ((uint)ents.length() <= i)
|
|
ents.add().type = NOTUSED;
|
|
int to = ents[i].type;
|
|
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();
|
|
break;
|
|
}
|
|
|
|
case SV_PING:
|
|
getint(p);
|
|
break;
|
|
|
|
case SV_PONG:
|
|
addmsg(0, 2, SV_CLIENTPING,
|
|
player1.ping =
|
|
(player1.ping * 5 + lastmillis - getint(p)) /
|
|
6);
|
|
break;
|
|
|
|
case SV_CLIENTPING:
|
|
[players[cn] setPing:getint(p)];
|
|
break;
|
|
|
|
case SV_GAMEMODE:
|
|
nextmode = getint(p);
|
|
break;
|
|
|
|
case SV_TIMEUP:
|
|
timeupdate(getint(p));
|
|
break;
|
|
|
|
case SV_RECVMAP: {
|
|
sgetstr();
|
|
conoutf(@"received map \"%s\" from server, reloading..",
|
|
text);
|
|
int mapsize = getint(p);
|
|
OFString *string = @(text);
|
|
writemap(string, mapsize, p);
|
|
p += mapsize;
|
|
changemapserv(string, gamemode);
|
|
break;
|
|
}
|
|
|
|
case SV_SERVMSG:
|
|
sgetstr();
|
|
conoutf(@"%s", text);
|
|
break;
|
|
|
|
case SV_EXT: // so we can messages without breaking previous
|
|
// clients/servers, if necessary
|
|
{
|
|
for (int n = getint(p); n; n--)
|
|
getint(p);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
neterr(@"type");
|
|
return;
|
|
}
|
|
}
|