Cube  clients2c.mm at [b81e2948d7]

File src/clients2c.mm artifact ef18048f8b part of check-in b81e2948d7


// 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();
			@autoreleasepool {
				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();
			@autoreleasepool {
				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);
			@autoreleasepool {
				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;
		}
}