diff --git a/source/src/client.cpp b/source/src/client.cpp index 61e3d9ce..788fdd04 100644 --- a/source/src/client.cpp +++ b/source/src/client.cpp @@ -339,23 +339,74 @@ void c2sinfo(playerent *d) // send update to the server if(d->state==CS_ALIVE || d->state==CS_EDITING) { ENetPacket *packet = enet_packet_create(NULL, 100, 0); - ucharbuf q(packet->data, packet->dataLength); - - putint(q, SV_POS); - putint(q, d->clientnum); - putuint(q, (int)(d->o.x*DMF)); // quantize coordinates to 1/16th of a cube, between 1 and 3 bytes - putuint(q, (int)(d->o.y*DMF)); - putuint(q, (int)((d->o.z - d->eyeheight)*DMF)); - putuint(q, (int)d->yaw); - putint(q, (int)d->pitch); - putint(q, (int)(125*d->roll/20)); - putint(q, (int)(d->vel.x*DVELF)); - putint(q, (int)(d->vel.y*DVELF)); - putint(q, (int)(d->vel.z*DVELF)); - // pack rest in 1 int: strafe:2, move:2, onfloor:1, onladder: 1 - putuint(q, (d->strafe&3) | ((d->move&3)<<2) | (((int)d->onfloor)<<4) | (((int)d->onladder)<<5) | ((d->lifesequence&1)<<6) | (((int)d->crouching)<<7)); - - enet_packet_resize(packet, q.length()); + int cn = d->clientnum, + x = (int)(d->o.x*DMF), // quantize coordinates to 1/16th of a cube, between 1 and 3 bytes + y = (int)(d->o.y*DMF), + z = (int)((d->o.z - d->eyeheight)*DMF), + ya = (int)((512 * d->yaw) / 360.0f), + pi = (int)((127 * d->pitch) / 90.0f), + r = (int)(31*d->roll/20), + dx = (int)(d->vel.x*DVELF), + dy = (int)(d->vel.y*DVELF), + dz = (int)(d->vel.z*DVELF), + // pack rest in 1 int: strafe:2, move:2, onfloor:1, onladder: 1 + f = (d->strafe&3) | ((d->move&3)<<2) | (((int)d->onfloor)<<4) | (((int)d->onladder)<<5) | ((d->lifesequence&1)<<6) | (((int)d->crouching)<<7); + int sizexy = 1 << (sfactor + 4); + if(cn >= 0 && cn < 32 && + x >= 0 && x < sizexy && + y >= 0 && y < sizexy && + z >= -2047 && z <= 2047 && + ya >= 0 && ya < 512 && + pi >= -128 && pi <= 127 && + r >= -32 && r <= 31 && + dx >= -8 && dx <= 7 && + dy >= -8 && dy <= 7 && + dz >= -8 && dz <= 7) + { // compact POS packet + bool noroll = !r, novel = !dx && !dy && !dz; + bitbuf q(packet->data, packet->dataLength); + putint(q, SV_POSC); + q.putbits(5, cn); + q.putbits(sfactor + 4, x); + q.putbits(sfactor + 4, y); + q.putbits(9, ya); + q.putbits(8, pi + 128); + q.putbits(1, noroll ? 1 : 0); + if(!noroll) q.putbits(6, r + 32); + q.putbits(1, novel ? 1 : 0); + if(!novel) + { + q.putbits(4, dx + 8); + q.putbits(4, dy + 8); + q.putbits(4, dz + 8); + } + q.putbits(8, f); + q.putbits(1, z < 0 ? 1 : 0); + if(z < 0) z = -z; // z is encoded with 3..10 bits minimum (fitted to byte boundaries), or full 11 bits if necessary + int s = (q.rembits() - 1 + 8) % 8; + if(s < 3) s += 8; + if(z >= (1 << s)) s = 11; + q.putbits(1, s == 11 ? 1 : 0); + q.putbits(s, z); + enet_packet_resize(packet, q.length()); + } + else + { // classic POS packet + ucharbuf q(packet->data, packet->dataLength); + putint(q, SV_POS); + putint(q, d->clientnum); + putuint(q, x); + putuint(q, y); + putuint(q, z); + putuint(q, (int)d->yaw); + putint(q, (int)d->pitch); + putint(q, (int)(125*d->roll/20)); + putint(q, dx); + putint(q, dy); + putint(q, dz); + putuint(q, f); + enet_packet_resize(packet, q.length()); + } sendpackettoserv(0, packet); } diff --git a/source/src/clients2c.cpp b/source/src/clients2c.cpp index 711859f8..f060e292 100644 --- a/source/src/clients2c.cpp +++ b/source/src/clients2c.cpp @@ -107,20 +107,53 @@ void parsepositions(ucharbuf &p) while(p.remaining()) switch(type = getint(p)) { case SV_POS: // position of another client + case SV_POSC: { - int cn = getint(p); + int cn, f; vec o, vel; float yaw, pitch, roll; - o.x = getuint(p)/DMF; - o.y = getuint(p)/DMF; - o.z = getuint(p)/DMF; - yaw = (float)getuint(p); - pitch = (float)getint(p); - roll = (float)(getint(p)*20.0f/125.0f); - vel.x = getint(p)/DVELF; - vel.y = getint(p)/DVELF; - vel.z = getint(p)/DVELF; - int f = getuint(p), seqcolor = (f>>6)&1; + if(type == SV_POSC) + { + bitbuf q(p.buf + p.length(), p.remaining()); + cn = q.getbits(5); + o.x = q.getbits(sfactor + 4) / DMF; + o.y = q.getbits(sfactor + 4) / DMF; + yaw = q.getbits(9) * 360.0f / 512; + pitch = (q.getbits(8) - 128) * 90.0f / 127; + roll = !q.getbits(1) ? (q.getbits(6) - 32) * 20.0f / 31 : 0.0f; + if(!q.getbits(1)) + { + vel.x = (q.getbits(4) - 8) / DVELF; + vel.y = (q.getbits(4) - 8) / DVELF; + vel.z = (q.getbits(4) - 8) / DVELF; + } + else vel.x = vel.y = vel.z = 0.0f; + f = q.getbits(8); + int negz = q.getbits(1); + int full = q.getbits(1); + int s = q.rembits(); + if(s < 3) s += 8; + if(full) s = 11; + int z = q.getbits(s); + if(negz) z = -z; + o.z = z / DMF; + p.len += q.length(); + } + else + { + cn = getint(p); + o.x = getuint(p)/DMF; + o.y = getuint(p)/DMF; + o.z = getuint(p)/DMF; + yaw = (float)getuint(p); + pitch = (float)getint(p); + roll = (float)(getint(p)*20.0f/125.0f); + vel.x = getint(p)/DVELF; + vel.y = getint(p)/DVELF; + vel.z = getint(p)/DVELF; + f = getuint(p); + } + int seqcolor = (f>>6)&1; playerent *d = getclient(cn); if(!d || seqcolor!=(d->lifesequence&1)) continue; vec oldpos(d->o); diff --git a/source/src/protocol.cpp b/source/src/protocol.cpp index a74dea07..39264013 100644 --- a/source/src/protocol.cpp +++ b/source/src/protocol.cpp @@ -283,7 +283,7 @@ const char *mmfullname(int n) { return (n>=0 && n < MM_NUM) ? mmfullnames[n] : " char msgsizesl[] = // size inclusive message token, 0 for variable or not-checked sizes { - SV_INITS2C, 5, SV_WELCOME, 2, SV_INITC2S, 0, SV_POS, 0, SV_TEXT, 0, SV_TEAMTEXT, 0, SV_TEXTME, 0, SV_TEAMTEXTME, 0, + SV_INITS2C, 5, SV_WELCOME, 2, SV_INITC2S, 0, SV_POS, 0, SV_POSC, 0, SV_POSN, 0, SV_TEXT, 0, SV_TEAMTEXT, 0, SV_TEXTME, 0, SV_TEAMTEXTME, 0, SV_SOUND, 2, SV_VOICECOM, 2, SV_VOICECOMTEAM, 2, SV_CDIS, 2, SV_SHOOT, 0, SV_EXPLODE, 0, SV_SUICIDE, 1, SV_AKIMBO, 2, SV_RELOAD, 3, SV_SCOPE, 3, SV_GIBDIED, 4, SV_DIED, 4, SV_GIBDAMAGE, 6, SV_DAMAGE, 6, SV_HITPUSH, 6, SV_SHOTFX, 9, SV_THROWNADE, 8, diff --git a/source/src/protocol.h b/source/src/protocol.h index d16e905d..6f5fd47b 100644 --- a/source/src/protocol.h +++ b/source/src/protocol.h @@ -18,9 +18,9 @@ extern bool modprotocol; // network messages codes, c2s, c2c, s2c enum { - SV_INITS2C = 0, SV_WELCOME, SV_INITC2S, SV_POS, SV_TEXT, SV_TEAMTEXT, SV_TEXTME, SV_TEAMTEXTME, //7 + SV_INITS2C = 0, SV_WELCOME, SV_INITC2S, SV_POS, SV_POSC, SV_POSN, SV_TEXT, SV_TEAMTEXT, SV_TEXTME, SV_TEAMTEXTME, SV_SOUND, SV_VOICECOM, SV_VOICECOMTEAM, SV_CDIS, - SV_SHOOT, SV_EXPLODE, SV_SUICIDE, SV_AKIMBO, SV_RELOAD, SV_SCOPE, //17 + SV_SHOOT, SV_EXPLODE, SV_SUICIDE, SV_AKIMBO, SV_RELOAD, SV_SCOPE, SV_GIBDIED, SV_DIED, SV_GIBDAMAGE, SV_DAMAGE, SV_HITPUSH, SV_SHOTFX, SV_THROWNADE, SV_TRYSPAWN, SV_SPAWNSTATE, SV_SPAWN, SV_SPAWNDENY, SV_FORCEDEATH, SV_RESUME, SV_DISCSCORES, SV_TIMEUP, SV_EDITENT, SV_ITEMACC, @@ -48,9 +48,9 @@ extern void protocoldebug(bool enable); // converts message code to char #ifdef SERVER_CPP -const static char *messagenames[] = +const char *messagenames[] = { - "SV_INITS2C", "SV_WELCOME", "SV_INITC2S", "SV_POS", "SV_TEXT", "SV_TEAMTEXT", "SV_TEXTME", "SV_TEAMTEXTME", + "SV_INITS2C", "SV_WELCOME", "SV_INITC2S", "SV_POS", "SV_POSC", "SV_POSN", "SV_TEXT", "SV_TEAMTEXT", "SV_TEXTME", "SV_TEAMTEXTME", "SV_SOUND", "SV_VOICECOM", "SV_VOICECOMTEAM", "SV_CDIS", "SV_SHOOT", "SV_EXPLODE", "SV_SUICIDE", "SV_AKIMBO", "SV_RELOAD", "SV_SCOPE", "SV_GIBDIED", "SV_DIED", "SV_GIBDAMAGE", "SV_DAMAGE", "SV_HITPUSH", "SV_SHOTFX", "SV_THROWNADE", @@ -74,7 +74,7 @@ const static char *messagenames[] = "SV_MAPIDENT" }; #else -extern const static char *messagenames[]; +extern const char *messagenames[]; #endif //SERVER_CPP #endif @@ -150,5 +150,6 @@ enum #define m_valid(mode) (((mode)>=0 && (mode)=0 && (mode)!=7 && (mode)!=8 && (mode)!=12) #define m_demo (gamemode==-1) +#define m_coop (gamemode==1) #define m_flags (m_ctf || m_htf || m_ktf) diff --git a/source/src/server.cpp b/source/src/server.cpp index 571756a5..e22162e1 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -262,8 +262,8 @@ void changemastermode(int newmode) } } else if(matchteamsize) changematchteamsize(matchteamsize); - } sendservermode(); + } } int findcnbyaddress(ENetAddress *address) @@ -1506,9 +1506,9 @@ bool updateclientteam(int cln, int newteam, int ftr) } } if(cl.team == newteam && ftr != FTR_AUTOTEAM) return true; // no change - if(team_isspect(newteam) || (team_isactive(newteam) && team_isactive(cl.team))) forcedeath(&cl); + if(ftr != FTR_INFO && (team_isspect(newteam) || (team_isactive(newteam) && team_isactive(cl.team)))) forcedeath(&cl); sendf(-1, 1, "riii", SV_SETTEAM, cln, newteam | ((ftr == FTR_SILENTFORCE ? FTR_INFO : ftr) << 4)); - if(!team_isspect(newteam) && team_isspect(cl.team) && ftr != FTR_INFO) sendspawn(&cl); + if(ftr != FTR_INFO && !team_isspect(newteam) && team_isspect(cl.team)) sendspawn(&cl); cl.team = newteam; return true; } @@ -1744,7 +1744,7 @@ void startgame(const char *newname, int newmode, int newtime, bool notify) else if(autoteam) refillteams(true, FTR_INFO); } - // send spawns + // prepare spawns; players will spawn, once they've loaded the correct map loopv(clients) if(clients[i]->type!=ST_EMPTY) { client *c = clients[i]; @@ -2137,6 +2137,20 @@ void forcedeath(client *cl) sendf(-1, 1, "rii", SV_FORCEDEATH, cl->clientnum); } +void checkclientpos(client *cl) +{ + vec &po = cl->state.o; + int ls = (1 << maplayout_factor) - 1; + if(po.x < 0 || po.y < 0 || po.x > ls || po.y > ls || maplayout[((int) po.x) + (((int) po.y) << maplayout_factor)] > po.z + 3) + { + if(gamemillis > 10000 && (servmillis - cl->connectmillis) > 10000) cl->mapcollisions++; + if((cl->mapcollisions % 25) == 1) + { + logline(ACLOG_INFO, "[%s] %s collides with the map (%d)", cl->hostname, cl->name, cl->mapcollisions); + } + } +} + int checktype(int type, client *cl) { if(cl && cl->type==ST_LOCAL) return type; @@ -2144,7 +2158,7 @@ int checktype(int type, client *cl) static int edittypes[] = { SV_EDITENT, SV_EDITH, SV_EDITT, SV_EDITS, SV_EDITD, SV_EDITE, SV_NEWMAP }; if(cl && smode!=GMODE_COOPEDIT) loopi(sizeof(edittypes)/sizeof(int)) if(type == edittypes[i]) return -1; // server only messages - static int servtypes[] = { SV_INITS2C, SV_WELCOME, SV_CDIS, SV_GIBDIED, SV_DIED, + static int servtypes[] = { SV_INITS2C, SV_WELCOME, SV_POSN, SV_CDIS, SV_GIBDIED, SV_DIED, SV_GIBDAMAGE, SV_DAMAGE, SV_HITPUSH, SV_SHOTFX, SV_SPAWNSTATE, SV_SPAWNDENY, SV_FORCEDEATH, SV_RESUME, SV_DISCSCORES, SV_TIMEUP, SV_ITEMACC, SV_MAPCHANGE, SV_ITEMSPAWN, SV_PONG, @@ -2300,7 +2314,7 @@ void process(ENetPacket *packet, int sender, int chan) type = checktype(getint(p), cl); #ifdef _DEBUG - if(type!=SV_POS && type!=SV_CLIENTPING && type!=SV_PING && type!=SV_CLIENT) + if(type!=SV_POS && type!=SV_POSC && type!=SV_CLIENTPING && type!=SV_PING && type!=SV_CLIENT) { DEBUGVAR(cl->name); ASSERT(type>=0 && typeposition.setsizenodelete(0); while(curmsgposition.add(p.buf[curmsg++]); } - if(maplayout && !m_demo) + if(maplayout && !m_demo && !m_coop) checkclientpos(cl); + break; + } + + case SV_POSC: + { + bitbuf q(p.buf + p.length(), p.remaining()); + int cn = q.getbits(5); + if(cn!=sender) { - vec &po = clients[cn]->state.o; - int ls = (1 << maplayout_factor) - 1; - if(po.x < 0 || po.y < 0 || po.x > ls || po.y > ls || maplayout[((int) po.x) + (((int) po.y) << maplayout_factor)] > po.z + 3) - { - if(gamemillis > 10000 && (servmillis - clients[cn]->connectmillis) > 10000) clients[cn]->mapcollisions++; // assume map to be loaded after 10 seconds: fixme - if((clients[cn]->mapcollisions % 25) == 1) - { - logline(ACLOG_INFO, "[%s] %s collides with the map (%d)", clients[cn]->hostname, clients[cn]->name, clients[cn]->mapcollisions); - } - } + disconnect_client(sender, DISC_CN); + #ifndef STANDALONE + conoutf("ERROR: invalid client (msg %i)", type); + #endif + return; } + loopi(2) cl->state.o[i] = q.getbits(smapstats.hdr.sfactor + 4)/DMF; + q.getbits(9 + 8); + if(!q.getbits(1)) q.getbits(6); + if(!q.getbits(1)) q.getbits(4 + 4 + 4); + q.getbits(8); + int negz = q.getbits(1); + int full = q.getbits(1); + int s = q.rembits(); + if(s < 3) s += 8; + if(full) s = 11; + int z = q.getbits(s); + if(negz) z = -z; + cl->state.o[2] = z/DMF; + p.len += q.length(); + if(!cl->isonrightmap) break; + if(cl->type==ST_TCPIP && (cl->state.state==CS_ALIVE || cl->state.state==CS_EDITING)) + { + cl->position.setsizenodelete(0); + while(curmsgposition.add(p.buf[curmsg++]); + } + if(maplayout && !m_demo && !m_coop) checkclientpos(cl); break; } diff --git a/source/src/tools.h b/source/src/tools.h index bf5b97ab..9c291847 100644 --- a/source/src/tools.h +++ b/source/src/tools.h @@ -193,6 +193,46 @@ struct databuf typedef databuf charbuf; typedef databuf ucharbuf; +struct bitbuf : ucharbuf +{ + int blen; + uchar *cur; + + bitbuf(unsigned char *buf, int maxlen) : ucharbuf(buf, maxlen), blen(0) {} + + int getbits(int n) + { + int res = 0, p = 0; + while(n > 0) + { + if(!blen) cur = (uchar *)&get(); + int r = 8 - blen; + if(r > n) r = n; + res |= ((*cur >> blen) & ((1 << r) - 1)) << p; + n -= r; p += r; + blen = (blen + r) % 8; + } + return res; + } + + void putbits(int n, int v) + { + if(8 * remaining() < n) { flags |= OVERWROTE; return; } + while(n > 0) + { + if(!blen) put(0); + cur = (uchar *)buf + len - 1; + int r = 8 - blen; + if(r > n) r = n; + *cur |= (v & ((1 << r) - 1)) << blen; + n -= r; v >>= r; + blen = (blen + r) % 8; + } + } + + int rembits() { return (8 - blen) % 8; } +}; + template struct vector { static const int MINSIZE = 8;