From: havoc Date: Tue, 24 May 2005 21:37:32 +0000 (+0000) Subject: rewrote protocol version system (including splitting PROTOCOL_QUAKE into PROTOCOL_QUA... X-Git-Tag: xonotic-v0.1.0preview~4855 X-Git-Url: https://git.xonotic.org/?a=commitdiff_plain;h=31bfe80dd809721f0665fbe58f8d4eb3160aeb14;p=xonotic%2Fdarkplaces.git rewrote protocol version system (including splitting PROTOCOL_QUAKE into PROTOCOL_QUAKE, PROTOCOL_QUAKEDP, and PROTOCOL_NEHAHRAMOVIE) made server able to host PROTOCOL_NEHAHRAMOVIE protocol for completeness added PROTOCOL_DARKPLACES7 protocol (still in development, not enabled by default) which allows players to use QW-style movement messages (makes prediction rock solid but has speedcheat/lagaport potential) git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@5326 d7cf8633-e32d-0410-b094-e92efae38249 --- diff --git a/cl_input.c b/cl_input.c index b46bb142..f926dd38 100644 --- a/cl_input.c +++ b/cl_input.c @@ -498,7 +498,6 @@ void CL_ClientMovement(qboolean buttonjump, qboolean buttoncrouch) int crouch; int onground; double edgefriction; - double simulatedtime; double frametime; double t; vec_t wishspeed; @@ -524,16 +523,25 @@ void CL_ClientMovement(qboolean buttonjump, qboolean buttoncrouch) // remove stale queue items n = cl.movement_numqueue; cl.movement_numqueue = 0; - // calculate time to execute for - simulatedtime = cl.mtime[0] + cl_movement_latency.value / 1000.0; - for (i = 0;i < n;i++) - if (cl.movement_queue[i].time >= cl.mtime[0] && cl.movement_queue[i].time <= simulatedtime) - cl.movement_queue[cl.movement_numqueue++] = cl.movement_queue[i]; + if (cl.servermovesequence) + { + for (i = 0;i < n;i++) + if (cl.movement_queue[i].sequence > cl.servermovesequence) + cl.movement_queue[cl.movement_numqueue++] = cl.movement_queue[i]; + } + else + { + double simulatedtime = cl.mtime[0] + cl_movement_latency.value / 1000.0; + for (i = 0;i < n;i++) + if (cl.movement_queue[i].time >= cl.mtime[0] && cl.movement_queue[i].time <= simulatedtime) + cl.movement_queue[cl.movement_numqueue++] = cl.movement_queue[i]; + } // add to input queue if there is room - if (cl.movement_numqueue < sizeof(cl.movement_queue)/sizeof(cl.movement_queue[0]) && cl.mtime[0] > cl.mtime[1]) + if (cl_movement.integer && cl.movement_numqueue < sizeof(cl.movement_queue)/sizeof(cl.movement_queue[0]) && cl.mtime[0] > cl.mtime[1]) { // add to input queue - cl.movement_queue[cl.movement_numqueue].time = simulatedtime; + cl.movement_queue[cl.movement_numqueue].sequence = cl.movesequence; + cl.movement_queue[cl.movement_numqueue].time = cl.mtime[0] + cl_movement_latency.value / 1000.0; cl.movement_queue[cl.movement_numqueue].frametime = cl.mtime[0] - cl.mtime[1]; VectorCopy(cl.viewangles, cl.movement_queue[cl.movement_numqueue].viewangles); cl.movement_queue[cl.movement_numqueue].move[0] = cl.cmd.forwardmove; @@ -560,7 +568,7 @@ void CL_ClientMovement(qboolean buttonjump, qboolean buttoncrouch) // replay input queue, and remove any stale queue items // note: this relies on the fact there's always one queue item at the end // abort if client movement is disabled - cl.movement = cl_movement.integer && cl.stats[STAT_HEALTH] > 0 && !cls.demoplayback; + cl.movement = /*cl_movement.integer && */cl.stats[STAT_HEALTH] > 0 && !cls.demoplayback; if (!cl.movement) cl.movement_numqueue = 0; for (i = 0;i < cl.movement_numqueue;i++) @@ -815,18 +823,79 @@ void CL_SendMove(void) if (++cl.movemessages >= 2) { // send the movement message - // PROTOCOL_QUAKE clc_move = 16 bytes total - // PROTOCOL_DARKPLACES1 clc_move = 19 bytes total - // PROTOCOL_DARKPLACES2 clc_move = 25 bytes total - // PROTOCOL_DARKPLACES3 clc_move = 25 bytes total - // PROTOCOL_DARKPLACES4 clc_move = 19 bytes total - // PROTOCOL_DARKPLACES5 clc_move = 19 bytes total - // PROTOCOL_DARKPLACES6 clc_move = 52 bytes total - // 5 bytes - MSG_WriteByte (&buf, clc_move); - MSG_WriteFloat (&buf, cl.mtime[0]); // so server can get ping times - if (cl.protocol == PROTOCOL_DARKPLACES6) + // PROTOCOL_QUAKE clc_move = 16 bytes total + // PROTOCOL_QUAKEDP clc_move = 16 bytes total + // PROTOCOL_NEHAHRAMOVIE clc_move = 16 bytes total + // PROTOCOL_DARKPLACES1 clc_move = 19 bytes total + // PROTOCOL_DARKPLACES2 clc_move = 25 bytes total + // PROTOCOL_DARKPLACES3 clc_move = 25 bytes total + // PROTOCOL_DARKPLACES4 clc_move = 19 bytes total + // PROTOCOL_DARKPLACES5 clc_move = 19 bytes total + // PROTOCOL_DARKPLACES6 clc_move = 52 bytes total + // PROTOCOL_DARKPLACES7 clc_move = 56 bytes total + if (cl.protocol == PROTOCOL_QUAKE || cl.protocol == PROTOCOL_QUAKEDP || cl.protocol == PROTOCOL_NEHAHRAMOVIE) + { + // 5 bytes + MSG_WriteByte (&buf, clc_move); + MSG_WriteFloat (&buf, cl.mtime[0]); // so server can get ping times + // 3 bytes + for (i = 0;i < 3;i++) + MSG_WriteAngle8i (&buf, cl.viewangles[i]); + // 6 bytes + MSG_WriteCoord16i (&buf, forwardmove); + MSG_WriteCoord16i (&buf, sidemove); + MSG_WriteCoord16i (&buf, upmove); + // 2 bytes + MSG_WriteByte (&buf, bits); + MSG_WriteByte (&buf, in_impulse); + } + else if (cl.protocol == PROTOCOL_DARKPLACES2 || cl.protocol == PROTOCOL_DARKPLACES3) + { + // 5 bytes + MSG_WriteByte (&buf, clc_move); + MSG_WriteFloat (&buf, cl.mtime[0]); // so server can get ping times + // 12 bytes + for (i = 0;i < 3;i++) + MSG_WriteAngle32f (&buf, cl.viewangles[i]); + // 6 bytes + MSG_WriteCoord16i (&buf, forwardmove); + MSG_WriteCoord16i (&buf, sidemove); + MSG_WriteCoord16i (&buf, upmove); + // 2 bytes + MSG_WriteByte (&buf, bits); + MSG_WriteByte (&buf, in_impulse); + } + else if (cl.protocol == PROTOCOL_DARKPLACES1 || cl.protocol == PROTOCOL_DARKPLACES4 || cl.protocol == PROTOCOL_DARKPLACES5) + { + // 5 bytes + MSG_WriteByte (&buf, clc_move); + MSG_WriteFloat (&buf, cl.mtime[0]); // so server can get ping times + // 6 bytes + for (i = 0;i < 3;i++) + MSG_WriteAngle16i (&buf, cl.viewangles[i]); + // 6 bytes + MSG_WriteCoord16i (&buf, forwardmove); + MSG_WriteCoord16i (&buf, sidemove); + MSG_WriteCoord16i (&buf, upmove); + // 2 bytes + MSG_WriteByte (&buf, bits); + MSG_WriteByte (&buf, in_impulse); + } + else { + // 5 bytes + MSG_WriteByte (&buf, clc_move); + if (cl.protocol != PROTOCOL_DARKPLACES6) + { + if (cl_movement.integer) + { + cl.movesequence++; + MSG_WriteLong (&buf, cl.movesequence); + } + else + MSG_WriteLong (&buf, 0); + } + MSG_WriteFloat (&buf, cl.mtime[0]); // so server can get ping times // 6 bytes for (i = 0;i < 3;i++) MSG_WriteAngle16i (&buf, cl.viewangles[i]); @@ -849,36 +918,6 @@ void CL_SendMove(void) MSG_WriteFloat (&buf, cl.cmd.cursor_impact[2]); MSG_WriteShort (&buf, cl.cmd.cursor_entitynumber); } - else - { - if (cl.protocol == PROTOCOL_QUAKE || cl.protocol == PROTOCOL_NEHAHRAMOVIE) - { - // 3 bytes - for (i = 0;i < 3;i++) - MSG_WriteAngle8i (&buf, cl.viewangles[i]); - } - else if (cl.protocol == PROTOCOL_DARKPLACES2 || cl.protocol == PROTOCOL_DARKPLACES3) - { - // 12 bytes - for (i = 0;i < 3;i++) - MSG_WriteAngle32f (&buf, cl.viewangles[i]); - } - else if (cl.protocol == PROTOCOL_DARKPLACES1 || cl.protocol == PROTOCOL_DARKPLACES4 || cl.protocol == PROTOCOL_DARKPLACES5) - { - // 6 bytes - for (i = 0;i < 3;i++) - MSG_WriteAngle16i (&buf, cl.viewangles[i]); - } - else - Host_Error("CL_SendMove: unknown cl.protocol %i\n", cl.protocol); - // 6 bytes - MSG_WriteCoord16i (&buf, forwardmove); - MSG_WriteCoord16i (&buf, sidemove); - MSG_WriteCoord16i (&buf, upmove); - // 2 bytes - MSG_WriteByte (&buf, bits); - MSG_WriteByte (&buf, in_impulse); - } } #if MOVEAVERAGING @@ -901,6 +940,7 @@ void CL_SendMove(void) } // PROTOCOL_DARKPLACES6 = 67 bytes per packet + // PROTOCOL_DARKPLACES7 = 71 bytes per packet // deliver the message if (cls.demoplayback) diff --git a/cl_parse.c b/cl_parse.c index 50a1e133..232ae69a 100644 --- a/cl_parse.c +++ b/cl_parse.c @@ -331,6 +331,7 @@ void CL_ParseServerInfo (void) { char *str; int i; + protocolversion_t protocol; int nummodels, numsounds; entity_t *ent; @@ -346,16 +347,17 @@ void CL_ParseServerInfo (void) // parse protocol version number i = MSG_ReadLong (); - // hack for unmarked Nehahra movie demos which had a custom protocol - if (i == PROTOCOL_QUAKE && cls.demoplayback && demo_nehahra.integer) - i = PROTOCOL_NEHAHRAMOVIE; - if (i != PROTOCOL_QUAKE && i != PROTOCOL_DARKPLACES1 && i != PROTOCOL_DARKPLACES2 && i != PROTOCOL_DARKPLACES3 && i != PROTOCOL_DARKPLACES4 && i != PROTOCOL_DARKPLACES5 && i != PROTOCOL_DARKPLACES6 && i != PROTOCOL_NEHAHRAMOVIE) + protocol = Protocol_EnumForNumber(i); + if (protocol == PROTOCOL_UNKNOWN) { - Host_Error("CL_ParseServerInfo: Server is protocol %i, not %i (Quake), %i (DP1), %i (DP2), %i (DP3), %i (DP4), %i (DP5), %i (DP6), or %i (Nehahra movie)", i, PROTOCOL_QUAKE, PROTOCOL_DARKPLACES1, PROTOCOL_DARKPLACES2, PROTOCOL_DARKPLACES3, PROTOCOL_DARKPLACES4, PROTOCOL_DARKPLACES5, PROTOCOL_DARKPLACES6, PROTOCOL_NEHAHRAMOVIE); + Host_Error("CL_ParseServerInfo: Server is unrecognized protocol number (%i)\n", i); return; } - cl.protocol = i; - Con_DPrintf("Server protocol is %i\n", cl.protocol); + // hack for unmarked Nehahra movie demos which had a custom protocol + if (protocol == PROTOCOL_QUAKEDP && cls.demoplayback && demo_nehahra.integer) + protocol = PROTOCOL_NEHAHRAMOVIE; + cl.protocol = protocol; + Con_DPrintf("Server protocol is %s\n", Protocol_NameForEnum(cl.protocol)); // parse maxclients cl.maxclients = MSG_ReadByte (); @@ -618,7 +620,7 @@ void CL_ParseClientdata (void) VectorCopy (cl.mvelocity[0], cl.mvelocity[1]); cl.mviewzoom[1] = cl.mviewzoom[0]; - if (cl.protocol != PROTOCOL_DARKPLACES6) + if (cl.protocol == PROTOCOL_QUAKE || cl.protocol == PROTOCOL_QUAKEDP || cl.protocol == PROTOCOL_NEHAHRAMOVIE || cl.protocol == PROTOCOL_DARKPLACES1 || cl.protocol == PROTOCOL_DARKPLACES2 || cl.protocol == PROTOCOL_DARKPLACES3 || cl.protocol == PROTOCOL_DARKPLACES4 || cl.protocol == PROTOCOL_DARKPLACES5) { cl.stats[STAT_VIEWHEIGHT] = DEFAULT_VIEWHEIGHT; cl.stats[STAT_ITEMS] = 0; @@ -652,44 +654,35 @@ void CL_ParseClientdata (void) { if (bits & (SU_PUNCH1<frametime = sv.frametime; - // set the time and clear the general datagram SV_ClearDatagram(); diff --git a/host_cmd.c b/host_cmd.c index 702d1496..d68b2518 100644 --- a/host_cmd.c +++ b/host_cmd.c @@ -45,7 +45,6 @@ Host_Status_f */ void Host_Status_f (void) { - const char *protocolname; client_t *client; int seconds, minutes, hours = 0, j, players; void (*print) (const char *fmt, ...); @@ -67,18 +66,7 @@ void Host_Status_f (void) players++; print ("host: %s\n", Cvar_VariableString ("hostname")); print ("version: %s build %s\n", gamename, buildstring); - switch(sv.protocol) - { - case PROTOCOL_QUAKE: protocolname = sv.netquakecompatible ? "QUAKE" : "QUAKEDP";break; - case PROTOCOL_DARKPLACES1: protocolname = "PROTOCOL_DARKPLACES1";break; - case PROTOCOL_DARKPLACES2: protocolname = "PROTOCOL_DARKPLACES2";break; - case PROTOCOL_DARKPLACES3: protocolname = "PROTOCOL_DARKPLACES3";break; - case PROTOCOL_DARKPLACES4: protocolname = "PROTOCOL_DARKPLACES4";break; - case PROTOCOL_DARKPLACES5: protocolname = "PROTOCOL_DARKPLACES5";break; - case PROTOCOL_DARKPLACES6: protocolname = "PROTOCOL_DARKPLACES6";break; - default: protocolname = "PROTOCOL_UNKNOWN";break; - } - print ("protocol: %i (%s)\n", sv.protocol, protocolname); + print ("protocol: %i (%s)\n", Protocol_NumberForEnum(sv.protocol), Protocol_NameForEnum(sv.protocol)); print ("map: %s\n", sv.name); print ("players: %i active (%i max)\n\n", players, svs.maxclients); for (j = 0, client = svs.clients;j < svs.maxclients;j++, client++) diff --git a/netconn.h b/netconn.h index d958db4d..72c257d8 100755 --- a/netconn.h +++ b/netconn.h @@ -116,7 +116,7 @@ typedef struct netconn_s lhnetsocket_t *mysocket; lhnetaddress_t peeraddress; - + // this is mostly identical to qsocket_t from quake // if this time is reached, kick off peer @@ -155,7 +155,7 @@ extern int playercolor; #define SERVERLIST_ANDMASKCOUNT 5 #define SERVERLIST_ORMASKCOUNT 5 -typedef enum +typedef enum { // SLMO_CONTAINS is the default for strings // SLMO_GREATEREQUAL is the default for numbers (also used when OP == CONTAINS or NOTCONTAINS @@ -193,7 +193,7 @@ typedef struct int protocol; } serverlist_info_t; -typedef enum +typedef enum { SLIF_CNAME, SLIF_PING, @@ -210,12 +210,12 @@ typedef enum typedef struct { // used to determine whether this entry should be included into the final view - qboolean finished; + qboolean finished; // used to calculate ping when update comes in double querytime; serverlist_info_t info; - + // legacy stuff char line1[128]; char line2[128]; @@ -237,7 +237,7 @@ extern qboolean serverlist_sortdescending; extern int serverlist_viewcount; extern serverlist_entry_t *serverlist_viewlist[SERVERLIST_VIEWLISTSIZE]; -extern int serverlist_cachecount; +extern int serverlist_cachecount; extern qboolean serverlist_consoleoutput; diff --git a/progs.h b/progs.h index e4ea1410..2a2605c9 100644 --- a/progs.h +++ b/progs.h @@ -57,7 +57,7 @@ typedef struct edict_engineprivate_s // we should avoid extensive checking on entities already encountered int areagridmarknumber; - // PROTOCOL_QUAKE + // PROTOCOL_QUAKE, PROTOCOL_QUAKEDP, PROTOCOL_NEHAHRAMOVIE // baseline values entity_state_t baseline; diff --git a/protocol.c b/protocol.c index eafd3e74..cb7fda68 100644 --- a/protocol.c +++ b/protocol.c @@ -35,6 +35,71 @@ entity_state_t defaultstate = {0,0}//unsigned char unused[2]; // ! }; +// LordHavoc: I own protocol ranges 96, 97, 3500-3599 + +struct +{ + int number; + const char *name; +} +protocolversioninfo[] = +{ + {0, "UNKNOWN"}, + {15, "QUAKE"}, + {15, "QUAKEDP"}, + {250, "NEHAHRAMOVIE"}, + {96, "DARKPLACES1"}, + {97, "DARKPLACES2"}, + {3500, "DARKPLACES3"}, + {3501, "DARKPLACES4"}, + {3502, "DARKPLACES5"}, + {3503, "DARKPLACES6"}, + {3504, "DARKPLACES7"}, + {0, NULL} +}; + +protocolversion_t Protocol_EnumForName(const char *s) +{ + int i; + for (i = 1;protocolversioninfo[i].name;i++) + if (!strcasecmp(s, protocolversioninfo[i].name)) + return i; + return PROTOCOL_UNKNOWN; +} + +const char *Protocol_NameForEnum(protocolversion_t p) +{ + return protocolversioninfo[p].name; +} + +protocolversion_t Protocol_EnumForNumber(int n) +{ + int i; + for (i = 1;protocolversioninfo[i].name;i++) + if (protocolversioninfo[i].number == n) + return i; + return PROTOCOL_UNKNOWN; +} + +int Protocol_NumberForEnum(protocolversion_t p) +{ + return protocolversioninfo[p].number; +} + +void Protocol_Names(char *buffer, size_t buffersize) +{ + int i; + if (buffersize < 1) + return; + buffer[0] = 0; + for (i = 1;protocolversioninfo[i].name;i++) + { + if (i > 1) + strlcat(buffer, " ", sizeof(buffer)); + strlcat(buffer, protocolversioninfo[i].name, sizeof(buffer)); + } +} + // keep track of quake entities because they need to be killed if they get stale int cl_lastquakeentity = 0; qbyte cl_isquakeentity[MAX_EDICTS]; @@ -250,8 +315,11 @@ void EntityFrameQuake_WriteFrame(sizebuf_t *msg, int numstates, const entity_sta bits |= U_GLOWCOLOR; // if extensions are disabled, clear the relevant update flags - if (sv.netquakecompatible) + if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_NEHAHRAMOVIE) bits &= 0x7FFF; + if (sv.protocol == PROTOCOL_NEHAHRAMOVIE) + if (s->alpha != 255 || s->effects & EF_FULLBRIGHT) + bits |= U_EXTEND1; // write the message if (bits >= 16777216) @@ -289,6 +357,22 @@ void EntityFrameQuake_WriteFrame(sizebuf_t *msg, int numstates, const entity_sta if (bits & U_FRAME2) MSG_WriteByte(&buf, s->frame >> 8); if (bits & U_MODEL2) MSG_WriteByte(&buf, s->modelindex >> 8); + // the nasty protocol + if ((bits & U_EXTEND1) && sv.protocol == PROTOCOL_NEHAHRAMOVIE) + { + if (s->effects & EF_FULLBRIGHT) + { + MSG_WriteFloat(&buf, 2); // QSG protocol version + MSG_WriteFloat(&buf, s->alpha <= 0 ? 0 : (s->alpha >= 255 ? 1 : s->alpha * (1.0f / 255.0f))); // alpha + MSG_WriteFloat(&buf, 1); // fullbright + } + else + { + MSG_WriteFloat(&buf, 1); // QSG protocol version + MSG_WriteFloat(&buf, s->alpha <= 0 ? 0 : (s->alpha >= 255 ? 1 : s->alpha * (1.0f / 255.0f))); // alpha + } + } + // if the commit is full, we're done this frame if (msg->cursize + buf.cursize > msg->maxsize) { @@ -392,7 +476,7 @@ void EntityState_WriteFields(const entity_state_t *ent, sizebuf_t *msg, unsigned if (bits & E_ORIGIN3) MSG_WriteCoord16i(msg, ent->origin[2]); } - else if (sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5 || sv.protocol == PROTOCOL_DARKPLACES6) + else { // LordHavoc: have to write flags first, as they can modify protocol if (bits & E_FLAGS) @@ -416,23 +500,23 @@ void EntityState_WriteFields(const entity_state_t *ent, sizebuf_t *msg, unsigned MSG_WriteCoord32f(msg, ent->origin[2]); } } - if ((sv.protocol == PROTOCOL_DARKPLACES5 || sv.protocol == PROTOCOL_DARKPLACES6) && !(ent->flags & RENDER_LOWPRECISION)) + if ((sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4) && (ent->flags & RENDER_LOWPRECISION)) { if (bits & E_ANGLE1) - MSG_WriteAngle16i(msg, ent->angles[0]); + MSG_WriteAngle8i(msg, ent->angles[0]); if (bits & E_ANGLE2) - MSG_WriteAngle16i(msg, ent->angles[1]); + MSG_WriteAngle8i(msg, ent->angles[1]); if (bits & E_ANGLE3) - MSG_WriteAngle16i(msg, ent->angles[2]); + MSG_WriteAngle8i(msg, ent->angles[2]); } else { if (bits & E_ANGLE1) - MSG_WriteAngle8i(msg, ent->angles[0]); + MSG_WriteAngle16i(msg, ent->angles[0]); if (bits & E_ANGLE2) - MSG_WriteAngle8i(msg, ent->angles[1]); + MSG_WriteAngle16i(msg, ent->angles[1]); if (bits & E_ANGLE3) - MSG_WriteAngle8i(msg, ent->angles[2]); + MSG_WriteAngle16i(msg, ent->angles[2]); } if (bits & E_MODEL1) MSG_WriteByte(msg, ent->modelindex & 0xFF); @@ -532,7 +616,7 @@ void EntityState_ReadFields(entity_state_t *e, unsigned int bits) if (bits & E_ORIGIN3) e->origin[2] = MSG_ReadCoord16i(); } - else if (cl.protocol == PROTOCOL_DARKPLACES1 || cl.protocol == PROTOCOL_DARKPLACES3 || cl.protocol == PROTOCOL_DARKPLACES4 || cl.protocol == PROTOCOL_DARKPLACES5 || cl.protocol == PROTOCOL_DARKPLACES6) + else { if (bits & E_FLAGS) e->flags = MSG_ReadByte(); @@ -555,25 +639,23 @@ void EntityState_ReadFields(entity_state_t *e, unsigned int bits) e->origin[2] = MSG_ReadCoord32f(); } } - else - Host_Error("EntityState_ReadFields: unknown cl.protocol %i\n", cl.protocol); - if ((cl.protocol == PROTOCOL_DARKPLACES5 || cl.protocol == PROTOCOL_DARKPLACES6) && !(e->flags & RENDER_LOWPRECISION)) + if ((cl.protocol == PROTOCOL_DARKPLACES1 || cl.protocol == PROTOCOL_DARKPLACES2 || cl.protocol == PROTOCOL_DARKPLACES3 || cl.protocol == PROTOCOL_DARKPLACES4) && (e->flags & RENDER_LOWPRECISION)) { if (bits & E_ANGLE1) - e->angles[0] = MSG_ReadAngle16i(); + e->angles[0] = MSG_ReadAngle8i(); if (bits & E_ANGLE2) - e->angles[1] = MSG_ReadAngle16i(); + e->angles[1] = MSG_ReadAngle8i(); if (bits & E_ANGLE3) - e->angles[2] = MSG_ReadAngle16i(); + e->angles[2] = MSG_ReadAngle8i(); } else { if (bits & E_ANGLE1) - e->angles[0] = MSG_ReadAngle8i(); + e->angles[0] = MSG_ReadAngle16i(); if (bits & E_ANGLE2) - e->angles[1] = MSG_ReadAngle8i(); + e->angles[1] = MSG_ReadAngle16i(); if (bits & E_ANGLE3) - e->angles[2] = MSG_ReadAngle8i(); + e->angles[2] = MSG_ReadAngle16i(); } if (bits & E_MODEL1) e->modelindex = (e->modelindex & 0xFF00) | (unsigned int) MSG_ReadByte(); @@ -1815,6 +1897,8 @@ void EntityFrame5_CL_ReadFrame(void) for (i = 0;i < LATESTFRAMENUMS-1;i++) cl.latestframenums[i] = cl.latestframenums[i+1]; cl.latestframenums[LATESTFRAMENUMS-1] = MSG_ReadLong(); + if (cl.protocol != PROTOCOL_QUAKE && cl.protocol != PROTOCOL_QUAKEDP && cl.protocol != PROTOCOL_NEHAHRAMOVIE && cl.protocol != PROTOCOL_DARKPLACES1 && cl.protocol != PROTOCOL_DARKPLACES2 && cl.protocol != PROTOCOL_DARKPLACES3 && cl.protocol != PROTOCOL_DARKPLACES4 && cl.protocol != PROTOCOL_DARKPLACES5 && cl.protocol != PROTOCOL_DARKPLACES6) + cl.servermovesequence = MSG_ReadLong(); // read entity numbers until we find a 0x8000 // (which would be remove world entity, but is actually a terminator) while ((n = (unsigned short)MSG_ReadShort()) != 0x8000 && !msg_badread) @@ -1933,7 +2017,7 @@ void EntityFrame5_AckFrame(entityframe5_database_t *d, int framenum) int entityframe5_prioritychaincounts[E5_PROTOCOL_PRIORITYLEVELS]; unsigned short entityframe5_prioritychains[E5_PROTOCOL_PRIORITYLEVELS][ENTITYFRAME5_MAXSTATES]; -void EntityFrame5_WriteFrame(sizebuf_t *msg, entityframe5_database_t *d, int numstates, const entity_state_t *states, int viewentnum, int *stats) +void EntityFrame5_WriteFrame(sizebuf_t *msg, entityframe5_database_t *d, int numstates, const entity_state_t *states, int viewentnum, int *stats, int movesequence) { const entity_state_t *n; int i, num, l, framenum, packetlognumber, priority; @@ -2038,7 +2122,7 @@ void EntityFrame5_WriteFrame(sizebuf_t *msg, entityframe5_database_t *d, int num packetlog->packetnumber = framenum; packetlog->numstates = 0; // write stat updates - if (sv.protocol == PROTOCOL_DARKPLACES6) + if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE && sv.protocol != PROTOCOL_DARKPLACES1 && sv.protocol != PROTOCOL_DARKPLACES2 && sv.protocol != PROTOCOL_DARKPLACES3 && sv.protocol != PROTOCOL_DARKPLACES4 && sv.protocol != PROTOCOL_DARKPLACES5) { for (i = 0;i < MAX_CL_STATS;i++) { @@ -2065,6 +2149,8 @@ void EntityFrame5_WriteFrame(sizebuf_t *msg, entityframe5_database_t *d, int num d->latestframenum = framenum; MSG_WriteByte(msg, svc_entities); MSG_WriteLong(msg, framenum); + if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE && sv.protocol != PROTOCOL_DARKPLACES1 && sv.protocol != PROTOCOL_DARKPLACES2 && sv.protocol != PROTOCOL_DARKPLACES3 && sv.protocol != PROTOCOL_DARKPLACES4 && sv.protocol != PROTOCOL_DARKPLACES5 && sv.protocol != PROTOCOL_DARKPLACES6) + MSG_WriteLong(msg, movesequence); for (priority = E5_PROTOCOL_PRIORITYLEVELS - 1;priority >= 0 && packetlog->numstates < ENTITYFRAME5_MAXSTATES;priority--) { for (i = 0;i < entityframe5_prioritychaincounts[priority] && packetlog->numstates < ENTITYFRAME5_MAXSTATES;i++) diff --git a/protocol.h b/protocol.h index b0ff854d..b749fe6a 100644 --- a/protocol.h +++ b/protocol.h @@ -22,27 +22,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #ifndef PROTOCOL_H #define PROTOCOL_H -// LordHavoc: I own protocol ranges 96, 97, 3500-3599 +// protocolversion_t is defined in common.h -// quake or darkplaces extended quake entity protocol -// (still used by TomazQuake and others) -#define PROTOCOL_QUAKE 15 - -// neh_gl entity protocol -// (failed QSG protocol, used only by nehahra movie) -#define PROTOCOL_NEHAHRAMOVIE 250 - -// entityframe protocol -#define PROTOCOL_DARKPLACES1 96 -#define PROTOCOL_DARKPLACES2 97 - -// entityframe4 protocol -#define PROTOCOL_DARKPLACES3 3500 -#define PROTOCOL_DARKPLACES4 3501 - -// entityframe5 protocol -#define PROTOCOL_DARKPLACES5 3502 -#define PROTOCOL_DARKPLACES6 3503 +protocolversion_t Protocol_EnumForName(const char *s); +const char *Protocol_NameForEnum(protocolversion_t p); +protocolversion_t Protocol_EnumForNumber(int n); +int Protocol_NumberForEnum(protocolversion_t p); +void Protocol_Names(char *buffer, size_t buffersize); // model effects #define EF_ROCKET 1 // leave a trail @@ -767,7 +753,7 @@ int EntityState5_DeltaBitsForState(entity_state_t *o, entity_state_t *n); void EntityFrame5_CL_ReadFrame(void); void EntityFrame5_LostFrame(entityframe5_database_t *d, int framenum); void EntityFrame5_AckFrame(entityframe5_database_t *d, int framenum); -void EntityFrame5_WriteFrame(sizebuf_t *msg, entityframe5_database_t *d, int numstates, const entity_state_t *states, int viewentnum, int *stats); +void EntityFrame5_WriteFrame(sizebuf_t *msg, entityframe5_database_t *d, int numstates, const entity_state_t *states, int viewentnum, int *stats, int movesequence); extern cvar_t developer_networkentities; diff --git a/server.h b/server.h index a62603c7..a35b42cd 100644 --- a/server.h +++ b/server.h @@ -48,9 +48,7 @@ typedef struct qboolean loadgame; // one of the PROTOCOL_ values - int protocol; - // this disables extensions when using PROTOCOL_QUAKE - qboolean netquakecompatible; + protocolversion_t protocol; // used for running multiple steps in one frame, etc double timer; @@ -134,6 +132,7 @@ typedef struct client_s // communications handle netconn_t *netconnection; + int movesequence; // movement usercmd_t cmd; // intended motion calced from cmd diff --git a/sv_main.c b/sv_main.c index 74d9da50..d1aeb0b2 100644 --- a/sv_main.c +++ b/sv_main.c @@ -21,9 +21,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "quakedef.h" -// select which protocol to host, by name -// this is named the same as PROTOCOL_DARKPLACES6 for example, minus the PROTOCOL_ prefix -cvar_t sv_protocolname = {0, "sv_protocolname", "DARKPLACES6"}; +// select which protocol to host, this is fed to Protocol_EnumForName +cvar_t sv_protocolname = {0, "sv_protocolname", "DARKPLACES7"}; cvar_t sv_ratelimitlocalplayer = {0, "sv_ratelimitlocalplayer", "0"}; cvar_t sv_maxrate = {CVAR_SAVE | CVAR_NOTIFY, "sv_maxrate", "10000"}; @@ -310,12 +309,15 @@ void SV_SendServerinfo (client_t *client) if (client->entitydatabase5) EntityFrame5_FreeDatabase(client->entitydatabase5); - if (sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3) - client->entitydatabase = EntityFrame_AllocDatabase(sv_mempool); - if (sv.protocol == PROTOCOL_DARKPLACES4) - client->entitydatabase4 = EntityFrame4_AllocDatabase(sv_mempool); - if (sv.protocol == PROTOCOL_DARKPLACES5 || sv.protocol == PROTOCOL_DARKPLACES6) - client->entitydatabase5 = EntityFrame5_AllocDatabase(sv_mempool); + if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE) + { + if (sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3) + client->entitydatabase = EntityFrame_AllocDatabase(sv_mempool); + else if (sv.protocol == PROTOCOL_DARKPLACES4) + client->entitydatabase4 = EntityFrame4_AllocDatabase(sv_mempool); + else + client->entitydatabase5 = EntityFrame5_AllocDatabase(sv_mempool); + } SZ_Clear (&client->message); MSG_WriteByte (&client->message, svc_print); @@ -323,7 +325,7 @@ void SV_SendServerinfo (client_t *client) MSG_WriteString (&client->message,message); MSG_WriteByte (&client->message, svc_serverinfo); - MSG_WriteLong (&client->message, sv.protocol); + MSG_WriteLong (&client->message, Protocol_NumberForEnum(sv.protocol)); MSG_WriteByte (&client->message, svs.maxclients); if (!coop.integer && deathmatch.integer) @@ -668,7 +670,7 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s) return; // always send world submodels, they don't generate much traffic // except in PROTOCOL_QUAKE where they hog bandwidth like crazy - else if (!(s->effects & EF_NODEPTHTEST) && (!(isbmodel = (model = sv.models[s->modelindex]) != NULL && model->name[0] == '*') || sv.protocol == PROTOCOL_QUAKE)) + else if (!(s->effects & EF_NODEPTHTEST) && (!(isbmodel = (model = sv.models[s->modelindex]) != NULL && model->name[0] == '*') || (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE))) { Mod_CheckLoaded(model); // entity has survived every check so far, check if visible @@ -811,7 +813,7 @@ void SV_WriteEntitiesToClient(client_t *client, edict_t *clent, sizebuf_t *msg, Con_Printf("client \"%s\" entities: %d total, %d visible, %d culled by: %d pvs %d trace\n", client->name, sv_writeentitiestoclient_totalentities, sv_writeentitiestoclient_visibleentities, sv_writeentitiestoclient_culled_pvs + sv_writeentitiestoclient_culled_trace, sv_writeentitiestoclient_culled_pvs, sv_writeentitiestoclient_culled_trace); if (client->entitydatabase5) - EntityFrame5_WriteFrame(msg, client->entitydatabase5, numsendstates, sendstates, client - svs.clients + 1, stats); + EntityFrame5_WriteFrame(msg, client->entitydatabase5, numsendstates, sendstates, client - svs.clients + 1, stats, client->movesequence); else if (client->entitydatabase4) EntityFrame4_WriteFrame(msg, client->entitydatabase4, numsendstates, sendstates); else if (client->entitydatabase) @@ -916,7 +918,7 @@ void SV_WriteClientdataToMessage (client_t *client, edict_t *ent, sizebuf_t *msg { if (ent->v->punchangle[i]) bits |= (SU_PUNCH1<v->velocity[i]) @@ -944,7 +946,7 @@ void SV_WriteClientdataToMessage (client_t *client, edict_t *ent, sizebuf_t *msg //stats[STAT_SECRETS] = pr_global_struct->found_secrets; //stats[STAT_MONSTERS] = pr_global_struct->killed_monsters; - if (sv.protocol != PROTOCOL_DARKPLACES6) + if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5) { if (stats[STAT_VIEWHEIGHT] != DEFAULT_VIEWHEIGHT) bits |= SU_VIEWHEIGHT; bits |= SU_ITEMS; @@ -952,7 +954,7 @@ void SV_WriteClientdataToMessage (client_t *client, edict_t *ent, sizebuf_t *msg if (stats[STAT_ARMOR]) bits |= SU_ARMOR; bits |= SU_WEAPON; // FIXME: which protocols support this? does PROTOCOL_DARKPLACES3 support viewzoom? - if (sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5) + if (sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5) if (viewzoom != 255) bits |= SU_VIEWZOOM; } @@ -980,26 +982,23 @@ void SV_WriteClientdataToMessage (client_t *client, edict_t *ent, sizebuf_t *msg { if (bits & (SU_PUNCH1<v->punchangle[i]); - else if (sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5 || sv.protocol == PROTOCOL_DARKPLACES6) + else MSG_WriteAngle16i(msg, ent->v->punchangle[i]); } - if (sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5 || sv.protocol == PROTOCOL_DARKPLACES6) + if (bits & (SU_PUNCHVEC1<v->velocity[i] * (1.0f / 16.0f)); - else if (sv.protocol == PROTOCOL_DARKPLACES5 || sv.protocol == PROTOCOL_DARKPLACES6) + else MSG_WriteCoord32f(msg, ent->v->velocity[i]); } } @@ -1025,7 +1024,7 @@ void SV_WriteClientdataToMessage (client_t *client, edict_t *ent, sizebuf_t *msg if (bits & SU_VIEWZOOM) MSG_WriteShort (msg, min(stats[STAT_VIEWZOOM], 65535)); } - else if (sv.protocol != PROTOCOL_DARKPLACES6) + else if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4) { if (bits & SU_WEAPONFRAME) MSG_WriteByte (msg, stats[STAT_WEAPONFRAME]); @@ -1050,9 +1049,9 @@ void SV_WriteClientdataToMessage (client_t *client, edict_t *ent, sizebuf_t *msg MSG_WriteByte (msg, stats[STAT_WEAPON]); if (bits & SU_VIEWZOOM) { - if (sv.protocol == PROTOCOL_DARKPLACES4) + if (sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4) MSG_WriteByte (msg, min(stats[STAT_VIEWZOOM], 255)); - else if (sv.protocol == PROTOCOL_DARKPLACES5 || sv.protocol == PROTOCOL_DARKPLACES6) + else MSG_WriteShort (msg, min(stats[STAT_VIEWZOOM], 65535)); } } @@ -1076,9 +1075,17 @@ qboolean SV_SendClientDatagram (client_t *client) maxsize = sizeof(sv_sendclientdatagram_buf); maxsize2 = sizeof(sv_sendclientdatagram_buf); } - else if (sv.protocol == PROTOCOL_DARKPLACES5 || sv.protocol == PROTOCOL_DARKPLACES6) + else if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4) { - // PROTOCOL_DARKPLACES5 supports packet size limiting of updates + // no rate limiting support on older protocols because dp protocols + // 1-4 kick the client off if they overflow, and quake protocol shows + // less than the full entity set if rate limited + maxsize = 1400; + maxsize2 = 1400; + } + else + { + // PROTOCOL_DARKPLACES5 and later support packet size limiting of updates maxrate = bound(NET_MINRATE, sv_maxrate.integer, NET_MAXRATE); if (sv_maxrate.integer != maxrate) Cvar_SetValueQuick(&sv_maxrate, maxrate); @@ -1088,14 +1095,6 @@ qboolean SV_SendClientDatagram (client_t *client) maxsize = bound(100, rate, 1400); maxsize2 = 1400; } - else - { - // no rate limiting support on older protocols because dp protocols - // 1-4 kick the client off if they overflow, and quake protocol shows - // less than the full entity set if rate limited - maxsize = 1400; - maxsize2 = 1400; - } msg.data = sv_sendclientdatagram_buf; msg.maxsize = maxsize; @@ -1336,7 +1335,7 @@ SV_ModelIndex */ int SV_ModelIndex(const char *s, int precachemode) { - int i, limit = (sv.protocol == PROTOCOL_QUAKE ? 256 : MAX_MODELS); + int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE) ? 256 : MAX_MODELS); char filename[MAX_QPATH]; if (!s || !*s) return 0; @@ -1350,7 +1349,7 @@ int SV_ModelIndex(const char *s, int precachemode) { if (precachemode) { - if (sv.state != ss_loading && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5)) + if (sv.state != ss_loading && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5)) { Con_Printf("SV_ModelIndex(\"%s\"): precache_model can only be done in spawn functions\n", filename); return 0; @@ -1359,7 +1358,7 @@ int SV_ModelIndex(const char *s, int precachemode) Con_Printf("SV_ModelIndex(\"%s\"): not precached (fix your code), precaching anyway\n", filename); strlcpy(sv.model_precache[i], filename, sizeof(sv.model_precache[i])); sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, false); - if (sv.protocol == PROTOCOL_DARKPLACES6 && sv.state != ss_loading) + if (sv.state != ss_loading) { MSG_WriteByte(&sv.reliable_datagram, svc_precache); MSG_WriteShort(&sv.reliable_datagram, i); @@ -1385,7 +1384,7 @@ SV_SoundIndex */ int SV_SoundIndex(const char *s, int precachemode) { - int i, limit = (sv.protocol == PROTOCOL_QUAKE ? 256 : MAX_SOUNDS); + int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE) ? 256 : MAX_SOUNDS); char filename[MAX_QPATH]; if (!s || !*s) return 0; @@ -1399,7 +1398,7 @@ int SV_SoundIndex(const char *s, int precachemode) { if (precachemode) { - if (sv.state != ss_loading && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5)) + if (sv.state != ss_loading && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5)) { Con_Printf("SV_SoundIndex(\"%s\"): precache_sound can only be done in spawn functions\n", filename); return 0; @@ -1407,7 +1406,7 @@ int SV_SoundIndex(const char *s, int precachemode) if (precachemode == 1) Con_Printf("SV_SoundIndex(\"%s\"): not precached (fix your code), precaching anyway\n", filename); strlcpy(sv.sound_precache[i], filename, sizeof(sv.sound_precache[i])); - if (sv.protocol == PROTOCOL_DARKPLACES6 && sv.state != ss_loading) + if (sv.state != ss_loading) { MSG_WriteByte(&sv.reliable_datagram, svc_precache); MSG_WriteShort(&sv.reliable_datagram, i + 32768); @@ -1661,30 +1660,13 @@ void SV_SpawnServer (const char *server) strlcpy (sv.name, server, sizeof (sv.name)); - sv.netquakecompatible = false; - if (!strcasecmp(sv_protocolname.string, "QUAKE")) + sv.protocol = Protocol_EnumForName(sv_protocolname.string); + if (sv.protocol == PROTOCOL_UNKNOWN) { + char buffer[1024]; + Protocol_Names(buffer, sizeof(buffer)); + Con_Printf("Unknown sv_protocolname \"%s\", valid values are:\n%s\n", sv_protocolname.string, buffer); sv.protocol = PROTOCOL_QUAKE; - sv.netquakecompatible = true; - } - else if (!strcasecmp(sv_protocolname.string, "QUAKEDP")) - sv.protocol = PROTOCOL_QUAKE; - else if (!strcasecmp(sv_protocolname.string, "DARKPLACES1")) - sv.protocol = PROTOCOL_DARKPLACES1; - else if (!strcasecmp(sv_protocolname.string, "DARKPLACES2")) - sv.protocol = PROTOCOL_DARKPLACES2; - else if (!strcasecmp(sv_protocolname.string, "DARKPLACES3")) - sv.protocol = PROTOCOL_DARKPLACES3; - else if (!strcasecmp(sv_protocolname.string, "DARKPLACES4")) - sv.protocol = PROTOCOL_DARKPLACES4; - else if (!strcasecmp(sv_protocolname.string, "DARKPLACES5")) - sv.protocol = PROTOCOL_DARKPLACES5; - else if (!strcasecmp(sv_protocolname.string, "DARKPLACES6")) - sv.protocol = PROTOCOL_DARKPLACES6; - else - { - sv.protocol = PROTOCOL_DARKPLACES6; - Con_Printf("Unknown sv_protocolname \"%s\", valid values are QUAKE, QUAKEDP, DARKPLACES1, DARKPLACES2, DARKPLACES3, DARKPLACES4, DARKPLACES5, DARKPLACES6, falling back to DARKPLACES6 protocol\n", sv_protocolname.string); } // load progs to get entity field count @@ -1803,14 +1785,14 @@ void SV_SpawnServer (const char *server) // run two frames to allow everything to settle for (i = 0;i < 2;i++) { - sv.frametime = pr_global_struct->frametime = host_frametime = 0.1; + sv.frametime = host_frametime = 0.1; SV_Physics (); } Mod_PurgeUnused(); // create a baseline for more efficient communications - if (sv.protocol == PROTOCOL_QUAKE) + if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE) SV_CreateBaseline (); // send serverinfo to all connected clients diff --git a/sv_phys.c b/sv_phys.c index 24513b06..f02cf84a 100644 --- a/sv_phys.c +++ b/sv_phys.c @@ -1340,6 +1340,121 @@ void SV_Physics_Step (edict_t *ent) //============================================================================ +void SV_Physics_Entity (edict_t *ent, qboolean runmove) +{ + int i = ent - sv.edicts; + if (i >= 1 && i <= svs.maxclients) + { + // apply the latest accepted move to the entity fields + SV_ApplyClientMove(); + // make sure the velocity is sane (not a NaN) + SV_CheckVelocity(ent); + // LordHavoc: QuakeC replacement for SV_ClientThink (player movement) + if (SV_PlayerPhysicsQC) + { + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(ent); + PR_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - pr_functions), "QC function SV_PlayerPhysics is missing"); + } + else + SV_ClientThink (); + // make sure the velocity is sane (not a NaN) + SV_CheckVelocity(ent); + // LordHavoc: a hack to ensure that the (rather silly) id1 quakec + // player_run/player_stand1 does not horribly malfunction if the + // velocity becomes a number that is both == 0 and != 0 + // (sounds to me like NaN but to be absolutely safe...) + if (DotProduct(ent->v->velocity, ent->v->velocity) < 0.0001) + VectorClear(ent->v->velocity); + // call standard client pre-think + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(ent); + PR_ExecuteProgram (pr_global_struct->PlayerPreThink, "QC function PlayerPreThink is missing"); + SV_CheckVelocity (ent); + } + + // LordHavoc: merged client and normal entity physics + switch ((int) ent->v->movetype) + { + case MOVETYPE_PUSH: + case MOVETYPE_FAKEPUSH: + SV_Physics_Pusher (ent); + break; + case MOVETYPE_NONE: + // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects + if (ent->v->nextthink > 0 && ent->v->nextthink <= sv.time + sv.frametime) + SV_RunThink (ent); + break; + case MOVETYPE_FOLLOW: + SV_Physics_Follow (ent); + break; + case MOVETYPE_NOCLIP: + if (SV_RunThink(ent)) + { + SV_CheckWater(ent); + VectorMA(ent->v->origin, sv.frametime, ent->v->velocity, ent->v->origin); + VectorMA(ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles); + } + // relink normal entities here, players always get relinked so don't relink twice + if (!(i > 0 && i <= svs.maxclients)) + SV_LinkEdict(ent, false); + break; + case MOVETYPE_STEP: + SV_Physics_Step (ent); + break; + case MOVETYPE_WALK: + if (SV_RunThink (ent)) + { + if (!SV_CheckWater (ent) && ! ((int)ent->v->flags & FL_WATERJUMP) ) + SV_AddGravity (ent); + SV_CheckStuck (ent); + SV_WalkMove (ent); + // relink normal entities here, players always get relinked so don't relink twice + if (!(i > 0 && i <= svs.maxclients)) + SV_LinkEdict (ent, true); + } + break; + case MOVETYPE_TOSS: + case MOVETYPE_BOUNCE: + case MOVETYPE_BOUNCEMISSILE: + case MOVETYPE_FLYMISSILE: + // regular thinking + if (SV_RunThink (ent) && runmove) + SV_Physics_Toss (ent); + break; + case MOVETYPE_FLY: + if (SV_RunThink (ent) && runmove) + { + if (i > 0 && i <= svs.maxclients) + { + SV_CheckWater (ent); + SV_WalkMove (ent); + } + else + SV_Physics_Toss (ent); + } + break; + default: + Host_Error ("SV_Physics: bad movetype %i", (int)ent->v->movetype); + break; + } + + if (i >= 1 && i <= svs.maxclients) + { + SV_CheckVelocity (ent); + + // call standard player post-think + SV_LinkEdict (ent, true); + + SV_CheckVelocity (ent); + + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(ent); + PR_ExecuteProgram (pr_global_struct->PlayerPostThink, "QC function PlayerPostThink is missing"); + } +} + + /* ================ SV_Physics @@ -1356,6 +1471,7 @@ void SV_Physics (void) pr_global_struct->self = EDICT_TO_PROG(sv.edicts); pr_global_struct->other = EDICT_TO_PROG(sv.edicts); pr_global_struct->time = sv.time; + pr_global_struct->frametime = sv.frametime; PR_ExecuteProgram (pr_global_struct->StartFrame, "QC function StartFrame is missing"); newnum_edicts = 0; @@ -1386,115 +1502,13 @@ void SV_Physics (void) continue; } // connected slot - // apply the latest accepted move to the entity fields - SV_ApplyClientMove(); - // make sure the velocity is sane (not a NaN) - SV_CheckVelocity(ent); - // LordHavoc: QuakeC replacement for SV_ClientThink (player movement) - if (SV_PlayerPhysicsQC) - { - pr_global_struct->time = sv.time; - pr_global_struct->self = EDICT_TO_PROG(ent); - PR_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - pr_functions), "QC function SV_PlayerPhysics is missing"); - } - else - SV_ClientThink (); - // make sure the velocity is sane (not a NaN) - SV_CheckVelocity(ent); - // LordHavoc: a hack to ensure that the (rather silly) id1 quakec - // player_run/player_stand1 does not horribly malfunction if the - // velocity becomes a number that is both == 0 and != 0 - // (sounds to me like NaN but to be absolutely safe...) - if (DotProduct(ent->v->velocity, ent->v->velocity) < 0.0001) - VectorClear(ent->v->velocity); - // call standard client pre-think - pr_global_struct->time = sv.time; - pr_global_struct->self = EDICT_TO_PROG(ent); - PR_ExecuteProgram (pr_global_struct->PlayerPreThink, "QC function PlayerPreThink is missing"); - SV_CheckVelocity (ent); + if (host_client->movesequence) + continue; // return if running asynchronously } else if (sv_freezenonclients.integer) continue; - // LordHavoc: merged client and normal entity physics - switch ((int) ent->v->movetype) - { - case MOVETYPE_PUSH: - case MOVETYPE_FAKEPUSH: - SV_Physics_Pusher (ent); - break; - case MOVETYPE_NONE: - // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects - if (ent->v->nextthink > 0 && ent->v->nextthink <= sv.time + sv.frametime) - SV_RunThink (ent); - break; - case MOVETYPE_FOLLOW: - SV_Physics_Follow (ent); - break; - case MOVETYPE_NOCLIP: - if (SV_RunThink(ent)) - { - SV_CheckWater(ent); - VectorMA(ent->v->origin, sv.frametime, ent->v->velocity, ent->v->origin); - VectorMA(ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles); - } - // relink normal entities here, players always get relinked so don't relink twice - if (!(i > 0 && i <= svs.maxclients)) - SV_LinkEdict(ent, false); - break; - case MOVETYPE_STEP: - SV_Physics_Step (ent); - break; - case MOVETYPE_WALK: - if (SV_RunThink (ent)) - { - if (!SV_CheckWater (ent) && ! ((int)ent->v->flags & FL_WATERJUMP) ) - SV_AddGravity (ent); - SV_CheckStuck (ent); - SV_WalkMove (ent); - // relink normal entities here, players always get relinked so don't relink twice - if (!(i > 0 && i <= svs.maxclients)) - SV_LinkEdict (ent, true); - } - break; - case MOVETYPE_TOSS: - case MOVETYPE_BOUNCE: - case MOVETYPE_BOUNCEMISSILE: - case MOVETYPE_FLYMISSILE: - // regular thinking - if (SV_RunThink (ent) && runmove[i]) - SV_Physics_Toss (ent); - break; - case MOVETYPE_FLY: - if (SV_RunThink (ent) && runmove[i]) - { - if (i > 0 && i <= svs.maxclients) - { - SV_CheckWater (ent); - SV_WalkMove (ent); - } - else - SV_Physics_Toss (ent); - } - break; - default: - Host_Error ("SV_Physics: bad movetype %i", (int)ent->v->movetype); - break; - } - - if (i >= 1 && i <= svs.maxclients) - { - SV_CheckVelocity (ent); - - // call standard player post-think - SV_LinkEdict (ent, true); - - SV_CheckVelocity (ent); - - pr_global_struct->time = sv.time; - pr_global_struct->self = EDICT_TO_PROG(ent); - PR_ExecuteProgram (pr_global_struct->PlayerPostThink, "QC function PlayerPostThink is missing"); - } + SV_Physics_Entity(ent, runmove[i]); } if (pr_global_struct->force_retouch > 0) diff --git a/sv_user.c b/sv_user.c index 96ed45f7..b9d44e9b 100644 --- a/sv_user.c +++ b/sv_user.c @@ -602,17 +602,21 @@ void SV_ClientThink(void) SV_ReadClientMove =================== */ -extern cvar_t cl_movement_latency; +extern void SV_Physics_Entity (edict_t *ent, qboolean runmove); void SV_ReadClientMove (void) { int i; + double oldmovetime; usercmd_t *move = &host_client->cmd; + oldmovetime = move->time; memset(move, 0, sizeof(usercmd_t)); if (msg_badread) Con_Printf("SV_ReadClientMessage: badread at %s:%i\n", __FILE__, __LINE__); // read ping time + if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE && sv.protocol != PROTOCOL_DARKPLACES1 && sv.protocol != PROTOCOL_DARKPLACES2 && sv.protocol != PROTOCOL_DARKPLACES3 && sv.protocol != PROTOCOL_DARKPLACES4 && sv.protocol != PROTOCOL_DARKPLACES5 && sv.protocol != PROTOCOL_DARKPLACES6) + move->sequence = MSG_ReadLong (); move->time = MSG_ReadFloat (); if (msg_badread) Con_Printf("SV_ReadClientMessage: badread at %s:%i\n", __FILE__, __LINE__); move->receivetime = sv.time; @@ -620,11 +624,13 @@ void SV_ReadClientMove (void) // read current angles for (i = 0;i < 3;i++) { - if (sv.protocol == PROTOCOL_QUAKE) + if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE) move->viewangles[i] = MSG_ReadAngle8i(); + else if (sv.protocol == PROTOCOL_DARKPLACES1) + move->viewangles[i] = MSG_ReadAngle16i(); else if (sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3) move->viewangles[i] = MSG_ReadAngle32f(); - else if (sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5 || sv.protocol == PROTOCOL_DARKPLACES6) + else move->viewangles[i] = MSG_ReadAngle16i(); } if (msg_badread) Con_Printf("SV_ReadClientMessage: badread at %s:%i\n", __FILE__, __LINE__); @@ -636,10 +642,10 @@ void SV_ReadClientMove (void) if (msg_badread) Con_Printf("SV_ReadClientMessage: badread at %s:%i\n", __FILE__, __LINE__); // read buttons - if (sv.protocol == PROTOCOL_DARKPLACES6) - move->buttons = MSG_ReadLong (); - else + if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5) move->buttons = MSG_ReadByte (); + else + move->buttons = MSG_ReadLong (); if (msg_badread) Con_Printf("SV_ReadClientMessage: badread at %s:%i\n", __FILE__, __LINE__); // read impulse @@ -649,7 +655,7 @@ void SV_ReadClientMove (void) if (msg_badread) Con_Printf("SV_ReadClientMessage: badread at %s:%i\n", __FILE__, __LINE__); // PRYDON_CLIENTCURSOR - if (sv.protocol == PROTOCOL_DARKPLACES6) + if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE && sv.protocol != PROTOCOL_DARKPLACES1 && sv.protocol != PROTOCOL_DARKPLACES2 && sv.protocol != PROTOCOL_DARKPLACES3 && sv.protocol != PROTOCOL_DARKPLACES4 && sv.protocol != PROTOCOL_DARKPLACES5) { // 30 bytes move->cursor_screen[0] = MSG_ReadShort() * (1.0f / 32767.0f); @@ -672,6 +678,23 @@ void SV_ReadClientMove (void) move->cursor_entitynumber = 0; if (msg_badread) Con_Printf("SV_ReadClientMessage: badread at %s:%i\n", __FILE__, __LINE__); } + + if (!host_client->spawned) + memset(move, 0, sizeof(*move)); + else + { + host_client->movesequence = move->sequence; + if (host_client->movesequence) + { + double frametime = move->time - oldmovetime; + double oldframetime = pr_global_struct->frametime; + if (frametime > 0.1) + frametime = 0.1; + pr_global_struct->frametime = frametime; + SV_Physics_Entity(host_client->edict, true); + pr_global_struct->frametime = oldframetime; + } + } } void SV_ApplyClientMove (void)