diff --git a/source/src/client.cpp b/source/src/client.cpp index 64faedcd..1d3b38e2 100644 --- a/source/src/client.cpp +++ b/source/src/client.cpp @@ -433,67 +433,51 @@ void c2sinfo(playerent *d) // send update to the server if(d->state==CS_ALIVE || d->state==CS_EDITING) { + ASSERT(!(d->crouching && d->onladder)); // onladder is never set while crouching - and we're going to rely on it ;) + ASSERT(!(d->move < -1 || d->move > 1 || d->strafe < -1 || d->strafe > 1)); packetbuf q(100); 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), - dxt = (int)(d->vel.x*DVELF), - dyt = (int)(d->vel.y*DVELF), - dzt = (int)(d->vel.z*DVELF), - dx = dxt - d->vel_t.i[0], - dy = dyt - d->vel_t.i[1], - dz = dzt - d->vel_t.i[2], - // 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), - g = (dx?1:0) | ((dy?1:0)<<1) | ((dz?1:0)<<2) | ((r?1:0)<<3) | (((int)d->scoping)<<4) | (((int)d->shoot)<<5); - d->vel_t.i[0] = dxt; - d->vel_t.i[1] = dyt; - d->vel_t.i[2] = dzt; - int usefactor = sfactor < 7 ? 7 : sfactor, sizexy = 1 << (usefactor + 4); + zsign = z < 0 ? 1 : 0, + ya = encodeyaw(d->yaw), + pi = encodepitch(d->pitch), + dx = (int)(d->vel.x*DVELF), + dy = (int)(d->vel.y*DVELF), + dz = (int)(d->vel.z*DVELF); + int f = (d->strafe + 4 + d->move * 3 + (d->onladder ? 9 : 0) + (d->crouching ? 18 : 0)) // pack 6 bit into 5 with ternary logic :) + | (((int)d->scoping)<<5) | ((d->lifesequence&1)<<6) | (((int)d->onfloor)<<7) | (((int)d->jumpd)<<8) | (((int)(dx||dy||dz))<<9) | (zsign << 10); // number of used bits: FLAGBITS + int usefactor = sfactor < 7 ? 7 : sfactor; + if(zsign) z = -z; if(cn >= 0 && cn < 32 && usefactor <= 7 + 3 && // map size 7..10 - x >= 0 && x < sizexy && - y >= 0 && y < sizexy && + !((x | y) & ~((1 << (usefactor + 4)) - 1)) && z >= -2047 && z <= 2047 && - ya >= 0 && ya < 512 && - pi >= -128 && pi <= 127 && - r >= -32 && r <= 31 && - dx >= -8 && dx <= 7 && // FIXME + dx >= -8 && dx <= 7 && dy >= -8 && dy <= 7 && dz >= -8 && dz <= 7) { // compact POS packet - bool noroll = !r, novel = !dx && !dy && !dz; bitbuf b(q); putint(q, SV_POSC); b.putbits(5, cn); b.putbits(2, usefactor - 7); b.putbits(usefactor + 4, x); b.putbits(usefactor + 4, y); - b.putbits(9, ya); - b.putbits(8, pi + 128); - b.putbits(1, noroll ? 1 : 0); - if(!noroll) b.putbits(6, r + 32); - b.putbits(1, novel ? 1 : 0); - if(!novel) + b.putbits(YAWBITS, ya); + b.putbits(PITCHBITS, pi); + b.putbits(FLAGBITS, f); + if(f & (1 << 9)) // hasvel { b.putbits(4, dx + 8); b.putbits(4, dy + 8); b.putbits(4, dz + 8); } - b.putbits(8, f); - b.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 = (b.rembits() - 1 + 8) % 8; + int s = (b.rembits() - 1 + 8) % 8; // z is encoded with 3..10 bits minimum (fitted to byte boundaries), or full 11 bits if necessary if(s < 3) s += 8; if(z >= (1 << s)) s = 11; b.putbits(1, s == 11 ? 1 : 0); b.putbits(s, z); - b.putbits(1, d->scoping ? 1 : 0); - b.putbits(1, d->shoot ? 1 : 0); } else { // classic POS packet @@ -502,17 +486,16 @@ void c2sinfo(playerent *d) // send update to the server putuint(q, x); putuint(q, y); putuint(q, z); - putuint(q, (int)d->yaw); - putint(q, (int)d->pitch); - putuint(q, g); - if (r) putint(q, (int)(125*d->roll/20)); - if (dx) putint(q, dx); - if (dy) putint(q, dy); - if (dz) putint(q, dz); - putuint(q, f); + putuintn(q, (ya << (uint64_t)(FLAGBITS + PITCHBITS)) | (pi << FLAGBITS) | f, (YAWBITS + PITCHBITS + FLAGBITS + 7) / 8); + if(f & (1<<9)) + { + putint(q, dx); + putint(q, dy); + putint(q, dz); + } } sendpackettoserv(0, q.finalize()); - d->shoot = false; + d->jumpd = false; } if(sendmapidenttoserver || messages.length() || totalmillis-lastping>250) diff --git a/source/src/clients2c.cpp b/source/src/clients2c.cpp index 0fee862a..223a5836 100644 --- a/source/src/clients2c.cpp +++ b/source/src/clients2c.cpp @@ -116,10 +116,8 @@ void parsepositions(ucharbuf &p) case SV_POS: // position of another client case SV_POSC: { - int cn, f, g; + int cn, f, yaw, pitch, z; vec o, vel; - float yaw, pitch = 0; - bool scoping;//, shoot; if(type == SV_POSC) { bitbuf q(p); @@ -127,77 +125,68 @@ void parsepositions(ucharbuf &p) int usefactor = q.getbits(2) + 7; o.x = q.getbits(usefactor + 4) / DMF; o.y = q.getbits(usefactor + 4) / DMF; - yaw = q.getbits(9) * 360.0f / 512; - pitch = (q.getbits(8) - 128) * 90.0f / 127; - if(!q.getbits(1)) q.getbits(6); - if(!q.getbits(1)) + yaw = q.getbits(YAWBITS); + pitch = q.getbits(PITCHBITS); + f = q.getbits(FLAGBITS); + if(f & (1 << 9)) { 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; - scoping = ( q.getbits(1) ? true : false ); - q.getbits(1);//shoot = ( q.getbits(1) ? true : false ); + z = q.getbits(s); } 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); - g = getuint(p); - if ((g>>3) & 1) getint(p); - if (g & 1) vel.x = getint(p)/DVELF; else vel.x = 0; - if ((g>>1) & 1) vel.y = getint(p)/DVELF; else vel.y = 0; - if ((g>>2) & 1) vel.z = getint(p)/DVELF; else vel.z = 0; - scoping = ( (g>>4) & 1 ? true : false ); - //shoot = ( (g>>5) & 1 ? true : false ); // we are not using this yet - f = getuint(p); + o.x = getuint(p)/DMF; + o.y = getuint(p)/DMF; + z = getuint(p); + uint64_t ff = getuintn(p, (YAWBITS + PITCHBITS + FLAGBITS + 7) / 8); + yaw = (ff >> (FLAGBITS + PITCHBITS)) & ((1 << YAWBITS) - 1); + pitch = (ff >> FLAGBITS) & ((1 << PITCHBITS) - 1); + f = ff & ((1 << FLAGBITS) - 1); + if(f & (1 << 9)) + { + vel.x = getint(p)/DVELF; + vel.y = getint(p)/DVELF; + vel.z = getint(p)/DVELF; + } + else vel.x = vel.y = vel.z = 0.0f; } + if(f & (1 << 10)) z = -z; + o.z = z / DMF; int seqcolor = (f>>6)&1; playerent *d = getclient(cn); if(!d || seqcolor!=(d->lifesequence&1)) continue; vec oldpos(d->o); float oldyaw = d->yaw, oldpitch = d->pitch; - loopi(3) - { - float dr = o.v[i] - d->o.v[i] + ( i == 2 ? d->eyeheight : 0); - if ( !dr ) d->vel.v[i] = 0.0f; - else if ( d->vel.v[i] ) d->vel.v[i] = dr * 0.05f + d->vel.v[i] * 0.95f; - d->vel.v[i] += vel.v[i]; - if ( i==2 && d->onfloor && d->vel.v[i] < 0.0f ) d->vel.v[i] = 0.0f; - } d->o = o; d->o.z += d->eyeheight; - d->yaw = yaw; - d->pitch = pitch; + d->yaw = decodeyaw(yaw); + d->pitch = decodepitch(pitch); if(d->weaponsel->type == GUN_SNIPER) { sniperrifle *sr = (sniperrifle *)d->weaponsel; - sr->scoped = d->scoping = scoping; + sr->scoped = d->scoping = (f & (1 << 5)) ? true : false; } - d->strafe = (f&3)==3 ? -1 : f&3; - f >>= 2; - d->move = (f&3)==3 ? -1 : f&3; - f >>= 2; - d->onfloor = f&1; - f >>= 1; - d->onladder = f&1; - f >>= 2; + d->roll = 0; + d->vel = vel; + int ft = f & 0x1f; + d->strafe = (ft % 3) - 1; + ft /= 3; + d->move = (ft % 3) - 1; + ft /= 3; + d->jumpd = (f & (1 << 8)) ? true : false; // FIXME + d->onfloor = f & (1 << 7) ? true : false; + d->onladder = ft == 1; d->last_pos = totalmillis; - updatecrouch(d, f&1); + updatecrouch(d, ft == 2); updateplayerpos(d); updatelagtime(d); extern int smoothmove, smoothdist; diff --git a/source/src/entity.h b/source/src/entity.h index 0614be06..5061ce4b 100644 --- a/source/src/entity.h +++ b/source/src/entity.h @@ -125,7 +125,7 @@ public: class physent : public worldobject { public: - vec o, vel, vel_t; // origin, velocity + vec o, vel; // origin, velocity vec deltapos, newpos; // movement interpolation float yaw, pitch, roll; // used as vec in one place float pitchvel; @@ -133,7 +133,7 @@ public: int timeinair; // used for fake gravity float radius, eyeheight, maxeyeheight, aboveeye; // bounding box size bool inwater; - bool onfloor, onladder, jumpnext, crouching, crouchedinair, trycrouch, cancollide, stuck, scoping, shoot; + bool onfloor, onladder, jumpnext, jumpd, crouching, crouchedinair, trycrouch, cancollide, stuck, scoping; int lastjump; float lastjumpheight; int lastsplash; @@ -143,7 +143,7 @@ public: int last_pos; physent() : o(0, 0, 0), deltapos(0, 0, 0), newpos(0, 0, 0), yaw(270), pitch(0), roll(0), pitchvel(0), - crouching(false), crouchedinair(false), trycrouch(false), cancollide(true), stuck(false), scoping(false), shoot(false), lastjump(0), lastjumpheight(200), lastsplash(0), state(CS_ALIVE), last_pos(0) + crouching(false), crouchedinair(false), trycrouch(false), cancollide(true), stuck(false), scoping(false), lastjump(0), lastjumpheight(200), lastsplash(0), state(CS_ALIVE), last_pos(0) { reset(); } @@ -158,10 +158,10 @@ public: void reset() { - vel.x = vel.y = vel.z = eyeheightvel = vel_t.x = vel_t.y = vel_t.z = 0.0f; + vel.x = vel.y = vel.z = eyeheightvel = 0.0f; move = strafe = 0; timeinair = lastjump = lastsplash = 0; - onfloor = onladder = inwater = jumpnext = crouching = crouchedinair = trycrouch = stuck = false; + onfloor = onladder = inwater = jumpnext = jumpd = crouching = crouchedinair = trycrouch = stuck = false; last_pos = 0; } diff --git a/source/src/physics.cpp b/source/src/physics.cpp index 9c1bb2db..d30e0540 100644 --- a/source/src/physics.cpp +++ b/source/src/physics.cpp @@ -517,6 +517,7 @@ void moveplayer(physent *pl, int moveres, bool local, int curtime) { if(pl->jumpnext) { + pl->jumpd = true; pl->jumpnext = false; bool doublejump = pl->lastjump && lastmillis - pl->lastjump < 250 && pl->strafe != 0 && pl->o.z - pl->eyeheight - pl->lastjumpheight > 0.2f; pl->lastjumpheight = pl->o.z - pl->eyeheight; @@ -898,7 +899,6 @@ COMMAND(crouch, "d"); void fixcamerarange(physent *cam) { - const float MAXPITCH = 90.0f; if(cam->pitch>MAXPITCH) cam->pitch = MAXPITCH; if(cam->pitch<-MAXPITCH) cam->pitch = -MAXPITCH; while(cam->yaw<0.0f) cam->yaw += 360.0f; diff --git a/source/src/protocol.cpp b/source/src/protocol.cpp index 12fd77c5..c090c599 100644 --- a/source/src/protocol.cpp +++ b/source/src/protocol.cpp @@ -168,6 +168,24 @@ enet_uint32 getip4(ucharbuf &p) return ip; } +template +static inline void putuintn_(T &p, uint64_t val, int n) +{ + DEBUGVAR(val); + loopi(n) p.put(val >> (8 * i)); +} +void putuintn(ucharbuf &p, uint64_t val, int n) { putuintn_(p, val, n); } +void putuintn(packetbuf &p, uint64_t val, int n) { putuintn_(p, val, n); } +void putuintn(vector &p, uint64_t val, int n) { putuintn_(p, val, n); } + +uint64_t getuintn(ucharbuf &p, int n) +{ + uint64_t val = 0; + loopi(n) val |= p.get() << (8 * i); + DEBUGVAR(val); + return val; +} + #define GZMSGBUFSIZE ((MAXGZMSGSIZE * 11) / 10) static uchar *gzbuf = new uchar[GZMSGBUFSIZE]; // not thread-safe, but nesting is no problem @@ -526,6 +544,40 @@ char *gmode_enum(int gm, char *buf) // convert mode bitmask to string with sorte return buf; } +int encodepitch(float p) // pitch value quantisation: use double resolution for -30°..30° and half resolution for -90°..-30° and 30°..90° +{ + const int thres = (1 << 24) / 3; + int r = (int)(p * (1 << 24)) / MAXPITCH; + if(r > thres) r = r / 4 + (1 << 22); + else if(r < -thres) r = r / 4 - (1 << 22); + r += (1 << 23) + (1 << (23 - PITCHBITS)); + if(r < 0) r = 0; + else if(r >= (1 << 24)) r = (1 << 24) - 1; + return r >> (24 - PITCHBITS); +} + +float decodepitch(int r) +{ + const int thres = (1 << 22) + (1 << 22) / 3; + r = (r << (24 - PITCHBITS)) - (1 << 23); + if(r > thres) r = r * 4 - thres * 3; + else if(r < -thres) r = r * 4 + thres * 3; + float p = float(r) * MAXPITCH / (1 << 24); + return clamp(p, -MAXPITCH, MAXPITCH); +} + +int encodeyaw(float y) // yaw value quantisation: simple rounded integer +{ + int r = (int)floorf(y * (1 << YAWBITS) / 360.0f + 0.5f); + return r & ((1 << YAWBITS) - 1); // modulo 360 degrees +} + +float decodeyaw(int r) +{ + r &= (1 << YAWBITS) - 1; + return float(r) / (1 << YAWBITS) * 360.0f; +} + static const int msgsizes[] = // size inclusive message token, 0 for variable or not-checked sizes { SV_SERVINFO, 1, SV_SERVINFO_RESPONSE, 0, SV_SERVINFO_CONTD, 0, SV_WELCOME, 2, SV_INITCLIENT, 0, SV_POS, 0, SV_POSC, 0, SV_POSN, 0, diff --git a/source/src/protocol.h b/source/src/protocol.h index d9d8f2bf..ad82efdc 100644 --- a/source/src/protocol.h +++ b/source/src/protocol.h @@ -76,6 +76,10 @@ enum { EDITXY_HEIGHT = 0, EDITXY_TEX, EDITXY_TYPE, EDITXY_VDELTA, EDITXY_EQUALIS #define DMF 16.0f #define DNF 100.0f #define DVELF 4.0f +#define MAXPITCH 90.0f +#define PITCHBITS 8 +#define YAWBITS 10 +#define FLAGBITS 11 enum { DISC_PROTOCOL = 0, DISC_EOP, DISC_CN, DISC_TAGT, DISC_DUP, DISC_OVERFLOW, DISC_RANDOM, DISC_VOODOO, DISC_SYNC, DISC_AUTH, // div. protocol errors DISC_REFUSED = 16, DISC_BANNED, DISC_BLACKLISTED, DISC_WRONGPW, DISC_MAXCLIENTS, DISC_PRIVATE, DISC_OVERLOAD, // connection refused diff --git a/source/src/protos.h b/source/src/protos.h index ec6ddc52..fa0a57ac 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -955,6 +955,10 @@ extern int defaultgamelimit(int gamemode); extern int gmode_possible(bool hasffaspawns, bool hasteamspawns, bool hasflags); extern int gmode_parse(const char *list); extern char *gmode_enum(int gm, char *buf); +extern int encodepitch(float p); +extern float decodepitch(int r); +extern int encodeyaw(float y); +extern float decodeyaw(int r); // crypto #define TIGERHASHSIZE 24 @@ -1070,6 +1074,10 @@ extern void putip4(ucharbuf &p, enet_uint32 ip); extern void putip4(packetbuf &p, enet_uint32 ip); extern void putip4(vector &p, enet_uint32 ip); extern enet_uint32 getip4(ucharbuf &p); +extern void putuintn(ucharbuf &p, uint64_t val, int n); +extern void putuintn(packetbuf &p, uint64_t val, int n); +extern void putuintn(vector &p, uint64_t val, int n); +extern uint64_t getuintn(ucharbuf &p, int n); extern void putgzbuf(vector &d, vector &s); // zips a vector into a stream, stored in another vector extern ucharbuf *getgzbuf(ucharbuf &p); // fetch a gzipped buffer; needs to be freed properly later extern void freegzbuf(ucharbuf *p); // free a ucharbuf created by getgzbuf() diff --git a/source/src/server.cpp b/source/src/server.cpp index 9f883f4a..636039eb 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -3825,12 +3825,17 @@ void process(ENetPacket *packet, int sender, int chan) #endif return; } - loopi(3) cl->state.o[i] = getuint(p)/DMF; - cl->y = getuint(p); - cl->p = getint(p); - cl->g = getuint(p); - loopi(4) if ( (cl->g >> i) & 1 ) getint(p); - cl->f = getuint(p); + cl->state.o.x = getuint(p)/DMF; + cl->state.o.y = getuint(p)/DMF; + int z = getuint(p); + uint64_t ff = getuintn(p, (YAWBITS + PITCHBITS + FLAGBITS + 7) / 8); + cl->yaw = (int)decodeyaw((ff >> (FLAGBITS + PITCHBITS)) & ((1 << YAWBITS) - 1)); + cl->pitch = (int)decodepitch((ff >> FLAGBITS) & ((1 << PITCHBITS) - 1)); + cl->f = ff & ((1 << FLAGBITS) - 1); + if(cl->f & (1 << 10)) z = -z; + cl->state.o.z = z/DMF; + if(cl->f & (1 << 9)) loopi(3) getint(p); + if(!cl->isonrightmap) break; if(cl->type==ST_TCPIP && (cl->state.state==CS_ALIVE || cl->state.state==CS_EDITING)) { @@ -3856,28 +3861,23 @@ void process(ENetPacket *packet, int sender, int chan) int usefactor = q.getbits(2) + 7; int xt = q.getbits(usefactor + 4); int yt = q.getbits(usefactor + 4); - cl->y = (q.getbits(9)*360)/512; - cl->p = ((q.getbits(8)-128)*90)/127; - if(!q.getbits(1)) q.getbits(6); - if(!q.getbits(1)) q.getbits(4 + 4 + 4); - cl->f = q.getbits(8); - int negz = q.getbits(1); + cl->yaw = (int)decodeyaw(q.getbits(YAWBITS)); + cl->pitch = (int)decodepitch(q.getbits(PITCHBITS)); + cl->f = q.getbits(FLAGBITS); + if(cl->f & (1 << 9)) q.getbits(4 + 4 + 4); int zfull = q.getbits(1); int s = q.rembits(); if(s < 3) s += 8; if(zfull) s = 11; int zt = q.getbits(s); - if(negz) zt = -zt; - int g1 = q.getbits(1); // scoping - int g2 = q.getbits(1); // shooting - cl->g = (g1<<4) | (g2<<5); + if(cl->f & (1 << 10)) zt = -zt; if(!sg->curmap) break; // no compact POS packets without loaded servermap if(!cl->isonrightmap || p.remaining() || p.overread()) { p.flags = 0; break; } if(((cl->f >> 6) & 1) != (cl->state.lifesequence & 1) || usefactor != (sg->curmap->sfactor < 7 ? 7 : sg->curmap->sfactor)) break; - cl->state.o[0] = xt / DMF; - cl->state.o[1] = yt / DMF; - cl->state.o[2] = zt / DMF; + cl->state.o.x = xt / DMF; + cl->state.o.y = yt / DMF; + cl->state.o.z = zt / DMF; if(cl->type==ST_TCPIP && (cl->state.state==CS_ALIVE || cl->state.state==CS_EDITING)) { cl->position.setsize(0); diff --git a/source/src/server.h b/source/src/server.h index ebddb468..65e3c946 100644 --- a/source/src/server.h +++ b/source/src/server.h @@ -298,7 +298,7 @@ struct client // server side version of "dynent" type vec spawnp; int nvotes; int input, inputmillis; - int ffire, wn, f, g, t, y, p; + int ffire, wn, f, t, yaw, pitch; int yb, pb, oy, op, lda, ldda, fam; int nt[10], np, lp, ls, lsm, ld, nd, nlt, lem, led; vec cp[10], dp[10], d0, lv, lt, le; @@ -339,7 +339,7 @@ struct client // server side version of "dynent" type spawnp = vec(-1e10f, -1e10f, -1e10f); lmillis = ldt = spj = 0; ffire = 0; - f = g = y = p = t = 0; + f = yaw = pitch = t = 0; yb = pb = oy = op = lda = ldda = fam = 0; np = lp = ls = lsm = ld = nd = nlt = lem = led = 0; d0 = lv = lt = le = vec(0,0,0); diff --git a/source/src/weapon.cpp b/source/src/weapon.cpp index f65bace0..ae14dbcc 100644 --- a/source/src/weapon.cpp +++ b/source/src/weapon.cpp @@ -934,7 +934,6 @@ int weapon::flashtime() const { return max((int)info.attackdelay, 120)/4; } void weapon::sendshoot(vec &from, vec &to, int millis) { if(owner!=player1) return; - owner->shoot = true; addmsg(SV_SHOOT, "ri2i3iv", millis, owner->weaponsel->type, (int)(to.x*DMF), (int)(to.y*DMF), (int)(to.z*DMF), hits.length(), hits.length()*sizeof(hitmsg)/sizeof(int), hits.getbuf());