Hack in some support for qex's clc differences. Add some extra qex-specific svc differences to try to work around its prediction issues.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@6157 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2022-01-08 10:01:15 +00:00
parent bf44769b4d
commit 80fcfa7938
10 changed files with 213 additions and 20 deletions

View file

@ -918,6 +918,39 @@ void MSG_WriteLong (sizebuf_t *sb, int c)
buf[2] = (c>>16)&0xff;
buf[3] = (c>>24)&0xff;
}
void MSG_WriteULEB128 (sizebuf_t *sb, quint64_t c)
{
qbyte b;
for(;;)
{
b = c&0x7f;
c>>=7;
if (!c)
break;
MSG_WriteByte(sb, b|0x80);
}
MSG_WriteByte(sb, b);
}
/*void MSG_WriteSLEB128 (sizebuf_t *sb, qint64_t c)
{
qbyte b;
for(;;)
{
b = c&0x7f;
c>>=7;
if ((c==0 && (b&64)==0) || (c==-1 && (b&64)!=0))
break;
MSG_WriteByte(sb, b|0x80);
}
MSG_WriteByte(sb, b);
}*/
void MSG_WriteSignedQEX (sizebuf_t *sb, qint64_t c)
{
if (c < 0)
MSG_WriteULEB128(sb, ((quint64_t)(-1-c)<<1)|1);
else
MSG_WriteULEB128(sb, c<<1);
}
void MSG_WriteUInt64 (sizebuf_t *sb, quint64_t c)
{ //0* 10*,*, 110*,*,* etc, up to 0xff followed by 8 continuation bytes
qbyte *buf;
@ -1985,6 +2018,20 @@ int MSG_ReadLong (void)
return c;
}
quint64_t MSG_ReadULEB128 (void)
{
quint64_t r = 0;
qbyte b, o=0;
while (!msg_badread)
{
b = MSG_ReadByte();
r |= (b&0x7f)<<o;
o+=7;
if (!(b & 0x80))
break;
}
return r;
}
quint64_t MSG_ReadUInt64 (void)
{ //0* 10*,*, 110*,*,* etc, up to 0xff followed by 8 continuation bytes
qbyte l=0x80, v, b = 0;

View file

@ -324,6 +324,8 @@ void MSG_WriteShort (sizebuf_t *sb, int c);
void MSG_WriteLong (sizebuf_t *sb, int c);
void MSG_WriteInt64 (sizebuf_t *sb, qint64_t c);
void MSG_WriteUInt64 (sizebuf_t *sb, quint64_t c);
void MSG_WriteULEB128 (sizebuf_t *sb, quint64_t c);
void MSG_WriteSignedQEX (sizebuf_t *sb, qint64_t c);
void MSG_WriteEntity (sizebuf_t *sb, unsigned int e);
void MSG_WriteFloat (sizebuf_t *sb, float f);
void MSG_WriteDouble (sizebuf_t *sb, double f);
@ -352,6 +354,8 @@ int MSG_ReadShort (void);
int MSG_ReadLong (void);
qint64_t MSG_ReadInt64 (void);
quint64_t MSG_ReadUInt64 (void);
qint64_t MSG_ReadSLEB128 (void);
quint64_t MSG_ReadULEB128 (void);
struct client_s;
unsigned int MSGSV_ReadEntity (struct client_s *fromclient);
unsigned int MSGCL_ReadEntity (void);

View file

@ -331,6 +331,7 @@ void Huff_EmitByte(int ch, qbyte *buffer, int *count);
#define NQ_NETCHAN_GAMENAME "QUAKE"
#define NQ_NETCHAN_VERSION 3
#define NQ_NETCHAN_VERSION_QEX 4 //the rerelease's id, used for nqish-over dtls.
#define CCREQ_CONNECT 0x01

View file

@ -344,6 +344,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define svcneh_skyboxsize 50 // [coord] size (default is 4096)
#define svcneh_fog 51 // [byte] enable <optional past this point, only included if enable is true> [float] density [byte] red [byte] green [byte] blue
//QuakeEx(aka: rerelease) svcs.
// these are not really documented anywhere. we're trying to stick with protocol 15 (because that's the only documented protocol it supports properly thanks to demos)
// however we still need some special case svcs
// (there's also some problematic c2s differences too)
#define svcqex_updateping 46
#define svcqex_servervars 50 // [leb128] changedvalues, [???] value...
#define svcqex_seq 51 // [leb128] input sequence ack
#define svcqex_achievement 52 // [string] codename
//DP extended svcs
#define svcdp_downloaddata 50
#define svcdp_updatestatbyte 51

View file

@ -601,8 +601,6 @@ static int te_515sevilhackworkaround;
#define svcdp_showlmp 35 // [string] slotname [string] lmpfilename [short] x [short] y
#define svcdp_hidelmp 36 // [string] slotname
#define svcrm_acheesement 52 // [string] codename
//#define TE_RAILTRAIL_NEH 15 // [vector] origin [coord] red [coord] green [coord] blue (fixme: ignored)
#define TE_EXPLOSION3_NEH 16 // [vector] origin [coord] red [coord] green [coord] blue (fixme: ignored)
#define TE_LIGHTNING4_NEH 17 // [string] model [entity] entity [vector] start [vector] end
@ -1140,7 +1138,7 @@ void NPP_NQWriteByte(int dest, qbyte data) //replacement write func (nq to qw)
data = svcqw_updatestatlong; //ho hum... let it through (should check size later.)
protocollen = 6;
break;
case svcrm_acheesement:
case svcqex_achievement:
ignoreprotocol = true;
nullterms = 1;
break;
@ -1401,7 +1399,7 @@ void NPP_NQWriteByte(int dest, qbyte data) //replacement write func (nq to qw)
break;
}
break;
case svcrm_acheesement:
case svcqex_achievement:
case svc_updatename:
case svc_stufftext:
case svc_centerprint:
@ -1419,7 +1417,7 @@ void NPP_NQWriteByte(int dest, qbyte data) //replacement write func (nq to qw)
case svc_updatename:
if (bufferlen < 2)
break; //don't truncate the name if the mod is sending the slot number
case svcrm_acheesement:
case svcqex_achievement:
case svc_stufftext:
case svc_centerprint:
case svc_cutscene:

View file

@ -698,6 +698,7 @@ typedef struct client_s
enum serverprotocols_e protocol;
unsigned int supportedprotocols;
qboolean proquake_angles_hack; //expect 16bit client->server angles .
qboolean qex_input_hack; //qex sends strange clc inputs and needs workarounds for its prediction.
unsigned int lastruncmd; //for non-qw physics. timestamp they were last run, so switching between physics modes isn't a (significant) cheat
//speed cheat testing
@ -1183,6 +1184,7 @@ typedef struct
enum serverprotocols_e protocol; //protocol used to talk to this client.
#ifdef NQPROT
qboolean proquakeanglehack; //specifies that the client will expect proquake angles if we give a proquake CCREP_ACCEPT response.
qboolean isqex; //yay quirks...
unsigned int expectedreliablesequence; //required for nq connection cookies (like tcp's syn cookies).
unsigned int supportedprotocols; //1<<SCP_* bitmask
#endif

View file

@ -2308,7 +2308,7 @@ static void SV_Status_f (void)
case SCP_QUAKEWORLD: p = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?"fteq":"qw"; break;
case SCP_QUAKE2: p = "q2"; break;
case SCP_QUAKE3: p = "q3"; break;
case SCP_NETQUAKE: p = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?"ften":"nq"; break;
case SCP_NETQUAKE: p = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?"ften":(cl->qex_input_hack?"qe15":"nq"); break;
case SCP_BJP3: p = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?"ften":"bjp3"; break;
case SCP_FITZ666: p = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?"ften":"fitz"; break;
case SCP_DARKPLACES6: p = "dpp6"; break;

View file

@ -129,7 +129,7 @@ cvar_t sv_public = CVARD("sv_public", "0", "-1: Fully blocks all inbound conne
cvar_t sv_guidhash = CVARD("sv_guidkey", "", "If set, clients will calculate their GUID values against this string instead of the server's IP address. This allows consistency between multiple servers (for stats tracking), but do NOT treat the client's GUID as something that is secure.");
cvar_t sv_serverip = CVARD("sv_serverip", "", "Set this cvar to the server's public ip address if the server is behind a firewall and cannot detect its own public address. Providing a port is required if the firewall/nat remaps it, but is otherwise optional.");
cvar_t sv_listen_qw = CVARAFD("sv_listen_qw", "1", "sv_listen", 0, "Specifies whether normal clients are allowed to connect.");
cvar_t sv_listen_nq = CVARD("sv_listen_nq", "2", "Allow new (net)quake clients to connect to the server.\n0 = don't let them in.\n1 = allow them in (WARNING: this allows 'qsmurf' DOS attacks).\n2 = accept (net)quake clients by emulating a challenge (as secure as QW/Q2 but does not fully conform to the NQ protocol).");
cvar_t sv_listen_nq = CVARD("sv_listen_nq", "2", "Allow new (net)quake clients to connect to the server.\n0 = don't let them in.\n1 = allow them in (WARNING: this allows 'qsmurf' DOS attacks).\n2 = accept (net)quake clients by emulating a challenge (as secure as QW/Q2 but does not fully conform to the NQ protocol).\nYou may also need to set net_enable_dtls if you wish for the rerelease's client to connect.");
cvar_t sv_listen_dp = CVARD("sv_listen_dp", "0", "Allows the server to respond with the DP-specific handshake protocol.\nWarning: this can potentially get confused with quake2, and results in race conditions with both vanilla netquake and quakeworld protocols.\nOn the plus side, DP clients can usually be identified correctly, enabling a model+sound limit boost.");
#ifdef QWOVERQ3
cvar_t sv_listen_q3 = CVAR("sv_listen_q3", "0");
@ -2111,6 +2111,13 @@ void SV_ClientProtocolExtensionsChanged(client_t *client)
client->datagram.maxsize = sizeof(host_client->datagram_buf);
}
else if (client->qex_input_hack)
{
client->max_net_clients = NQMAX_CLIENTS;
client->datagram.maxsize = sizeof(host_client->datagram_buf);
client->max_net_ents = bound(512, pr_maxedicts.ival, 32768);
client->max_net_staticents = 4096;
}
else
{
client->max_net_clients = NQMAX_CLIENTS;
@ -2564,6 +2571,7 @@ void SV_DoDirectConnect(svconnectinfo_t *fte_restrict info)
}
newcl->supportedprotocols = info->supportedprotocols;
newcl->proquake_angles_hack = info->proquakeanglehack;
newcl->qex_input_hack = info->isqex;
#endif
newcl->userid = ++nextuserid;
@ -3202,6 +3210,7 @@ void SVC_DirectConnect(int expectedreliablesequence)
#ifdef NQPROT
extern cvar_t sv_protocol_nq;
info.proquakeanglehack = false;
info.isqex = false;
info.supportedprotocols = 0;
info.expectedreliablesequence = expectedreliablesequence;
#endif
@ -3376,7 +3385,7 @@ void SVC_DirectConnect(int expectedreliablesequence)
if (version >= 31 && version <= 34)
info.protocol = SCP_QUAKE2;
#ifdef NQPROT
else if (version == 3)
else if (version == NQ_NETCHAN_VERSION)
{
info.protocol = SCP_NETQUAKE; //because we can
switch(atoi(Info_ValueForKey(Cmd_Argv(4), "mod")))
@ -3394,6 +3403,11 @@ void SVC_DirectConnect(int expectedreliablesequence)
break;
}
}
else if (version == NQ_NETCHAN_VERSION_QEX)
{ //rerelease...
info.protocol = SCP_NETQUAKE;
info.isqex = true;
}
#endif
else if (version == PROTOCOL_VERSION_QW)
info.protocol = SCP_QUAKEWORLD;
@ -4193,7 +4207,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
sizebuf_t sb;
int header;
int length;
int active, i;
int active, i, protver;
int mod, modver, flags;
unsigned int passwd;
char *str;
@ -4302,12 +4316,15 @@ qboolean SVNQ_ConnectionlessPacket(void)
switch(MSG_ReadByte())
{
case CCREQ_CONNECT:
str = MSG_ReadString();
protver = MSG_ReadByte();
if (sv_showconnectionlessmessages.ival)
Con_Printf("%s: CCREQ_CONNECT\n", NET_AdrToString (com_token, sizeof(com_token), &net_from));
Con_Printf("%s: CCREQ_CONNECT (\"%s\" %i)\n", NET_AdrToString (com_token, sizeof(com_token), &net_from), str, protver);
sb.maxsize = sizeof(buffer);
sb.data = buffer;
if (strcmp(MSG_ReadString(), NQ_NETCHAN_GAMENAME))
if (strcmp(str, NQ_NETCHAN_GAMENAME))
{
SZ_Clear(&sb);
MSG_WriteLong(&sb, 0);
@ -4317,7 +4334,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
NET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);
return false; //not our game.
}
if (MSG_ReadByte() != NQ_NETCHAN_VERSION)
if (protver != NQ_NETCHAN_VERSION && protver != NQ_NETCHAN_VERSION_QEX)
{
SZ_Clear(&sb);
MSG_WriteLong(&sb, 0);
@ -4340,6 +4357,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
return false; //not our version...
}
//proquake's extensions
mod = MSG_ReadByte();
modver = MSG_ReadByte();
flags = MSG_ReadByte();
@ -4364,7 +4382,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
NET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);
return false; //not our version...
}
if (sv_listen_nq.ival == 2)
if (sv_listen_nq.ival == 2 && net_from.prot == NP_DGRAM)
{
SZ_Clear(&sb);
MSG_WriteLong(&sb, 0);
@ -4390,7 +4408,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
}
else
{
str = va("connect %i %i %i \"\\name\\unconnected\\mod\\%i\\modver\\%i\\flags\\%i\\password\\%i\"", NQ_NETCHAN_VERSION, 0, SV_NewChallenge(), mod, modver, flags, passwd);
str = va("connect %i %i %i \"\\name\\unconnected\\mod\\%i\\modver\\%i\\flags\\%i\\password\\%i\"", protver, 0, SV_NewChallenge(), mod, modver, flags, passwd);
Cmd_TokenizeString (str, false, false);
SVC_DirectConnect(0);

View file

@ -1691,6 +1691,11 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg)
if (client->fteprotocolextensions2 & PEXT2_PREDINFO)
MSG_WriteShort(msg, client->last_sequence);
else if (client->qex_input_hack && client->last_sequence)
{
MSG_WriteByte(msg, svcqex_seq);
MSG_WriteULEB128(msg, client->last_sequence);
}
// Con_Printf("%f\n", sv.world.physicstime);
}
@ -2887,6 +2892,17 @@ void SV_UpdateToReliableMessages (void)
float curspeed;
int curfrags;
static double pingtimer, lasttime;
double t = Sys_DoubleTime();
qboolean sendpings = false;
pingtimer -= (t-lasttime);
lasttime = t;
if (pingtimer < 0)
{ //update about once every 5 secs.
sendpings = true;
pingtimer = 5;
}
// check for changes to be sent over the reliable streams to all clients
for (i=0, host_client = svs.clients ; i<svs.allocated_client_slots ; i++, host_client++)
{
@ -3032,6 +3048,23 @@ void SV_UpdateToReliableMessages (void)
host_client->sendinfo = false;
SV_FullClientUpdate (host_client, NULL);
}
if (host_client->qex_input_hack && sendpings)
{
sizebuf_t *m;
for (j=0, client = svs.clients ; j<svs.allocated_client_slots && j < host_client->max_net_clients; j++, client++)
{
if (client->state != cs_spawned)
continue;
m = ClientReliable_StartWrite(host_client, 64);
MSG_WriteByte(m, svcqex_updateping);
MSG_WriteByte(m, j);
MSG_WriteSignedQEX(m, SV_CalcPing(client, false));
ClientReliable_FinishWrite(host_client);
}
}
if (host_client->old_frags != curfrags)
{
for (j=0, client = svs.clients ; j<sv.allocated_client_slots ; j++, client++)

View file

@ -828,6 +828,59 @@ void SVNQ_New_f (void)
MSG_WriteByte (&host_client->netchan.message, 0);
}
if (host_client->qex_input_hack)
{
extern cvar_t sv_friction, sv_stopspeed, sv_maxvelocity, sv_accelerate, sv_gravity;
enum
{
QEX_GV_DEATHMATCH = 1<<0,
QEX_GV_IDEALPITCHSCALE = 1<<1,
QEX_GV_FRICTION = 1<<2,
QEX_GV_EDGEFRICTION = 1<<3,
QEX_GV_STOPSPEED = 1<<4,
QEX_GV_MAXVELOCITY = 1<<5,
QEX_GV_GRAVITY = 1<<6,
QEX_GV_NOSTEP = 1<<7,
QEX_GV_MAXSPEED = 1<<8,
QEX_GV_ACCELERATE = 1<<9,
QEX_GV_CONTROLLERONLY = 1<<10,
QEX_GV_TIMELIMIT = 1<<11,
QEX_GV_FRAGLIMIT = 1<<12,
QEX_GV_ALL =(1<<13)-1
} bits = QEX_GV_ALL;
bits = QEX_GV_ALL;
MSG_WriteByte (&host_client->netchan.message, svcqex_servervars);
MSG_WriteULEB128 (&host_client->netchan.message, bits);
if (bits & QEX_GV_DEATHMATCH)
MSG_WriteByte (&host_client->netchan.message, deathmatch.ival);
if (bits & QEX_GV_IDEALPITCHSCALE)
MSG_WriteFloat (&host_client->netchan.message, 0);
if (bits & QEX_GV_FRICTION)
MSG_WriteFloat (&host_client->netchan.message, sv_friction.value);
if (bits & QEX_GV_EDGEFRICTION)
MSG_WriteFloat (&host_client->netchan.message, *pm_edgefriction.string?pm_edgefriction.value:2);
if (bits & QEX_GV_STOPSPEED)
MSG_WriteFloat (&host_client->netchan.message, sv_stopspeed.value);
if (bits & QEX_GV_MAXVELOCITY)
MSG_WriteFloat (&host_client->netchan.message, sv_maxvelocity.value);
if (bits & QEX_GV_GRAVITY)
MSG_WriteFloat (&host_client->netchan.message, sv_gravity.value);
if (bits & QEX_GV_NOSTEP)
MSG_WriteByte (&host_client->netchan.message, false);
if (bits & QEX_GV_MAXSPEED)
MSG_WriteFloat (&host_client->netchan.message, sv_maxspeed.value);
if (bits & QEX_GV_ACCELERATE)
MSG_WriteFloat (&host_client->netchan.message, sv_accelerate.value);
if (bits & QEX_GV_CONTROLLERONLY)
MSG_WriteByte (&host_client->netchan.message, 0);
if (bits & QEX_GV_TIMELIMIT)
MSG_WriteFloat (&host_client->netchan.message, timelimit.value);
if (bits & QEX_GV_FRAGLIMIT)
MSG_WriteFloat (&host_client->netchan.message, fraglimit.value);
}
// set view
MSG_WriteByte (&host_client->netchan.message, svc_setview);
MSG_WriteEntity (&host_client->netchan.message, (host_client - svs.clients)+1);//NUM_FOR_EDICT(svprogfuncs, host_client->edict));
@ -1875,7 +1928,7 @@ void SVQW_PreSpawn_f (void)
{
char *msg;
SV_ClientTPrintf (host_client, PRINT_HIGH,
"Map model file does not match (%s), %i != %i/%i.\nYou may need a new version of the map, or the proper install files.\n",
"Map model file does not match (%s), %#X != %#X/%#X.\nYou may need a new version of the map, or the proper install files.\n",
sv.modelname, check, sv.world.worldmodel->checksum, sv.world.worldmodel->checksum2);
@ -8156,6 +8209,12 @@ void SV_ExecuteClientMessage (client_t *cl)
SV_DropClient (cl);
return;
}
if (cl->state < cs_connected)
{ //something went badly... just give up instead of crashing.
host_client = NULL;
sv_player = NULL;
return;
}
c = MSG_ReadByte ();
if (c == -1)
@ -8351,7 +8410,6 @@ void SV_ExecuteClientMessage (client_t *cl)
#ifdef NETPREPARSE
NPP_Flush(); //flush it just in case there was an error and we stopped preparsing. This is only really needed while debugging.
#endif
host_client = cl;
sv_player = cl->edict;
break;
@ -8622,7 +8680,7 @@ void SVQ2_ExecuteClientMessage (client_t *cl)
}
#endif
#ifdef NQPROT
void SVNQ_ReadClientMove (qboolean forceangle16)
void SVNQ_ReadClientMove (qboolean forceangle16, qboolean quakeex)
{
int i;
client_frame_t *frame;
@ -8633,7 +8691,9 @@ void SVNQ_ReadClientMove (qboolean forceangle16)
frame = &host_client->frameunion.frames[host_client->netchan.incoming_acknowledged & UPDATE_MASK];
if (host_client->protocol == SCP_DARKPLACES7)
if (quakeex)
;
else if (host_client->protocol == SCP_DARKPLACES7)
host_client->last_sequence = MSG_ReadLong ();
else if (host_client->fteprotocolextensions2 & PEXT2_PREDINFO)
{
@ -8659,6 +8719,13 @@ void SVNQ_ReadClientMove (qboolean forceangle16)
cmd.fservertime = sv.time - 2;
cmd.servertime = cmd.fservertime*1000;
if (quakeex)
{ //I'm guessing this has something to do with splitscreen.
if (MSG_ReadByte() != 1)
msg_badread = true;
}
//read angles
for (i=0 ; i<3 ; i++)
{
@ -8806,6 +8873,7 @@ void SVNQ_ReadClientMove (qboolean forceangle16)
}
else
{
if (!host_client->qex_input_hack)
host_client->last_sequence = 0; //let the client know that prediction is fucked, by not acking any input frames.
if (cmd.impulse)
host_client->edict->v->impulse = cmd.impulse;
@ -8821,6 +8889,7 @@ void SVNQ_ExecuteClientMessage (client_t *cl)
char *s;
// client_frame_t *frame;
qboolean forceangle16;
qboolean qex = false;
cl->netchan.outgoing_sequence++;
cl->netchan.incoming_acknowledged = cl->netchan.outgoing_sequence-1;
@ -8951,7 +9020,7 @@ void SVNQ_ExecuteClientMessage (client_t *cl)
break;
}
SVNQ_ReadClientMove (forceangle16);
SVNQ_ReadClientMove (forceangle16, qex);
// cmd = host_client->lastcmd;
// SV_ClientThink();
break;
@ -9004,6 +9073,18 @@ void SVNQ_ExecuteClientMessage (client_t *cl)
break;
#endif
case clc_delta://clcqex_sequence:
host_client->last_sequence = MSG_ReadULEB128();
qex = true;
break;
case clc_tmove://clcqex_auth
//This allows for the client's positions to be slightly wrong, with the client being authoritive instead of the server (within tolerances anyway).
host_client->last_sequence = MSG_ReadULEB128();
/*host_client->edict->v->origin[0] =*/ MSG_ReadFloat();
/*host_client->edict->v->origin[1] =*/ MSG_ReadFloat();
/*host_client->edict->v->origin[2] =*/ MSG_ReadFloat();
break;
safedefault:
Con_Printf ("SVNQ_ReadClientMessage: unknown command char %i\n", c);
SV_DropClient (cl);