Attempt to support more of QuakeEx's network protocol changes, including a 'connectqe host' command to connect to QEx servers with the appropriate handshakes (requires manual PSK setup).

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@6162 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2022-01-16 18:41:44 +00:00
parent 2e627df7b5
commit 6c112c3368
20 changed files with 733 additions and 236 deletions

View file

@ -1774,6 +1774,7 @@ void CLNQ_ParseEntity(unsigned int bits)
packet_entities_t *pack; packet_entities_t *pack;
qboolean isnehahra = CPNQ_IS_BJP||(cls.protocol_nq == CPNQ_NEHAHRA); qboolean isnehahra = CPNQ_IS_BJP||(cls.protocol_nq == CPNQ_NEHAHRA);
qboolean floatcoords;
if (cls.signon == 4 - 1) if (cls.signon == 4 - 1)
{ // first update is the final signon stage { // first update is the final signon stage
@ -1837,6 +1838,8 @@ void CLNQ_ParseEntity(unsigned int bits)
state->dpflags = 0; state->dpflags = 0;
floatcoords = cls.qex && (bits & QE_U_FLOATCOORDS);
if (bits & NQU_MODEL) if (bits & NQU_MODEL)
{ {
if (CPNQ_IS_BJP) if (CPNQ_IS_BJP)
@ -1855,20 +1858,34 @@ void CLNQ_ParseEntity(unsigned int bits)
state->skinnum = MSG_ReadByte(); state->skinnum = MSG_ReadByte();
if (bits & NQU_EFFECTS) if (bits & NQU_EFFECTS)
state->effects = MSG_ReadByte(); {
i = MSG_ReadByte();
if (cls.qex)
{
unsigned fixed = i & ~(REEF_QUADLIGHT|REEF_PENTLIGHT|REEF_CANDLELIGHT);
if (i & REEF_QUADLIGHT)
fixed |= EF_BLUE;
if (i & REEF_PENTLIGHT)
fixed |= EF_RED;
if (i & REEF_CANDLELIGHT)
fixed |= 0; //tiny light
i = fixed;
}
state->effects = i;
}
if (bits & NQU_ORIGIN1) if (bits & NQU_ORIGIN1)
state->origin[0] = MSG_ReadCoord (); state->origin[0] = floatcoords?MSG_ReadFloat():MSG_ReadCoord ();
if (bits & NQU_ANGLE1) if (bits & NQU_ANGLE1)
state->angles[0] = MSG_ReadAngle(); state->angles[0] = MSG_ReadAngle();
if (bits & NQU_ORIGIN2) if (bits & NQU_ORIGIN2)
state->origin[1] = MSG_ReadCoord (); state->origin[1] = floatcoords?MSG_ReadFloat():MSG_ReadCoord ();
if (bits & NQU_ANGLE2) if (bits & NQU_ANGLE2)
state->angles[1] = MSG_ReadAngle(); state->angles[1] = MSG_ReadAngle();
if (bits & NQU_ORIGIN3) if (bits & NQU_ORIGIN3)
state->origin[2] = MSG_ReadCoord (); state->origin[2] = floatcoords?MSG_ReadFloat():MSG_ReadCoord ();
if (bits & NQU_ANGLE3) if (bits & NQU_ANGLE3)
state->angles[2] = MSG_ReadAngle(); state->angles[2] = MSG_ReadAngle();
@ -1907,6 +1924,20 @@ void CLNQ_ParseEntity(unsigned int bits)
if (bits & FITZU_LERPFINISH) if (bits & FITZU_LERPFINISH)
MSG_ReadByte(); MSG_ReadByte();
if (cls.qex)
{
if (bits & QE_U_SOLIDTYPE) /*state->solidsize =*/ MSG_ReadByte(); //needed for correct prediction
if (bits & QE_U_ENTFLAGS) /*state->entflags = */ MSG_ReadULEB128(); //for onground/etc state
if (bits & QE_U_HEALTH) /*state->health =*/ MSG_ReadSignedQEX(); //health... not really sure why, I suppose it changes player physics (they should have sent movetype instead though).
if (bits & QE_U_UNKNOWN26) /*unknown =*/MSG_ReadByte();
if (bits & QE_U_UNUSED27) Con_Printf(CON_WARNING"QE_U_UNUSED27: %u\n", MSG_ReadByte());
if (bits & QE_U_UNUSED28) Con_Printf(CON_WARNING"QE_U_UNUSED28: %u\n", MSG_ReadByte());
if (bits & QE_U_UNUSED29) Con_Printf(CON_WARNING"QE_U_UNUSED29: %u\n", MSG_ReadByte());
if (bits & QE_U_UNUSED30) Con_Printf(CON_WARNING"QE_U_UNUSED30: %u\n", MSG_ReadByte());
if (bits & QE_U_UNUSED31) Con_Printf(CON_WARNING"QE_U_UNUSED31: %u\n", MSG_ReadByte());
}
} }
else else
{ //dp tends to leak stuff, so parse as quakedp if the normal protocol doesn't define it as something better. { //dp tends to leak stuff, so parse as quakedp if the normal protocol doesn't define it as something better.

View file

@ -1362,6 +1362,12 @@ void CLNQ_SendMove (usercmd_t *cmd, int pnum, sizebuf_t *buf)
return; return;
} }
if (cls.qex)
{
MSG_WriteByte (buf, clc_delta);
MSG_WriteULEB128(buf, cl.movesequence);
}
MSG_WriteByte (buf, clc_move); MSG_WriteByte (buf, clc_move);
if (cls.protocol_nq >= CPNQ_DP7) if (cls.protocol_nq >= CPNQ_DP7)
@ -1376,6 +1382,9 @@ void CLNQ_SendMove (usercmd_t *cmd, int pnum, sizebuf_t *buf)
MSG_WriteFloat (buf, cmd->fservertime); // use latest time. because ping reports! MSG_WriteFloat (buf, cmd->fservertime); // use latest time. because ping reports!
if (cls.qex)
MSG_WriteByte(buf, 4);
for (i=0 ; i<3 ; i++) for (i=0 ; i<3 ; i++)
{ {
if (cls.protocol_nq == CPNQ_FITZ666 || (cls.proquake_angles_hack && buf->prim.anglesize <= 1)) if (cls.protocol_nq == CPNQ_FITZ666 || (cls.proquake_angles_hack && buf->prim.anglesize <= 1))

View file

@ -185,6 +185,9 @@ cvar_t cl_gunanglex = CVAR("cl_gunanglex", "0");
cvar_t cl_gunangley = CVAR("cl_gunangley", "0"); cvar_t cl_gunangley = CVAR("cl_gunangley", "0");
cvar_t cl_gunanglez = CVAR("cl_gunanglez", "0"); cvar_t cl_gunanglez = CVAR("cl_gunanglez", "0");
#ifdef HAVE_DTLS
extern cvar_t net_enable_dtls;
#endif
cvar_t cl_proxyaddr = CVAR("cl_proxyaddr", ""); cvar_t cl_proxyaddr = CVAR("cl_proxyaddr", "");
cvar_t cl_sendguid = CVARD("cl_sendguid", "", "Send a randomly generated 'globally unique' id to servers, which can be used by servers for score rankings and stuff. Different servers will see different guids. Delete the 'qkey' file in order to appear as a different user.\nIf set to 2, all servers will see the same guid. Be warned that this can show other people the guid that you're using."); cvar_t cl_sendguid = CVARD("cl_sendguid", "", "Send a randomly generated 'globally unique' id to servers, which can be used by servers for score rankings and stuff. Different servers will see different guids. Delete the 'qkey' file in order to appear as a different user.\nIf set to 2, all servers will see the same guid. Be warned that this can show other people the guid that you're using.");
cvar_t cl_downloads = CVARAFD("cl_downloads", "1", /*q3*/"cl_allowDownload", CVAR_NOTFROMSERVER, "Allows you to block all automatic downloads."); cvar_t cl_downloads = CVARAFD("cl_downloads", "1", /*q3*/"cl_allowDownload", CVAR_NOTFROMSERVER, "Allows you to block all automatic downloads.");
@ -286,11 +289,11 @@ static struct
netadr_t adr[8]; //addresses that we're trying to transfer to, one entry per dns result, eg both ::1 AND 127.0.0.1 netadr_t adr[8]; //addresses that we're trying to transfer to, one entry per dns result, eg both ::1 AND 127.0.0.1
#ifdef HAVE_DTLS #ifdef HAVE_DTLS
enum enum
{ { //not relevant when given a direct dtls address.
DTLS_DISABLE, DTLS_DISABLE,
DTLS_TRY, DTLS_TRY,
DTLS_REQUIRE, DTLS_REQUIRE,
DTLS_ACTIVE DTLS_ACTIVE,
} dtlsupgrade; } dtlsupgrade;
#endif #endif
int mtu; int mtu;
@ -304,6 +307,12 @@ static struct
int challenge; //tracked as part of guesswork based upon what replies we get. int challenge; //tracked as part of guesswork based upon what replies we get.
double time; //for connection retransmits double time; //for connection retransmits
qboolean clogged; //ignore time... qboolean clogged; //ignore time...
enum coninfomode_e
{
CIM_DEFAULT, //sends both a qw getchallenge and nq connect (also with postfixed getchallenge so modified servers can force getchallenge)
CIM_NQONLY, //disables getchallenge (so fte servers treat us as an nq server). should not be used for dpp7 servers.
CIM_QEONLY, //forces dtls and uses a different nq netchan version
} mode;
int defaultport; int defaultport;
int tries; //increased each try, every fourth trys nq connect packets. int tries; //increased each try, every fourth trys nq connect packets.
unsigned char guid[64]; unsigned char guid[64];
@ -799,13 +808,14 @@ static void CL_ResolvedServer(void *vctx, void *data, size_t a, size_t b)
#ifdef HAVE_DTLS #ifdef HAVE_DTLS
for (i = 0; i < ctx->found; i++) for (i = 0; i < ctx->found; i++)
{ {
if (connectinfo.dtlsupgrade == DTLS_ACTIVE) if (connectinfo.dtlsupgrade == DTLS_ACTIVE || connectinfo.mode==CIM_QEONLY)
{ //if we've already established a dtls connection, stick with it { //if we've already established a dtls connection, stick with it
if (ctx->adr[i].prot == NP_DGRAM) if (ctx->adr[i].prot == NP_DGRAM)
ctx->adr[i].prot = NP_DTLS; ctx->adr[i].prot = NP_DTLS;
} }
else if (connectinfo.adr[i].prot == NP_DTLS) else if (connectinfo.adr[i].prot == NP_DTLS)
{ //dtls connections start out with regular udp, and upgrade to dtls once its established that the server supports it. { //dtls connections start out with regular udp, and upgrade to dtls once its established that the server supports it.
//FIXME: remove this block once our new netcode is better established.
connectinfo.dtlsupgrade = DTLS_REQUIRE; connectinfo.dtlsupgrade = DTLS_REQUIRE;
ctx->adr[i].prot = NP_DGRAM; ctx->adr[i].prot = NP_DGRAM;
} }
@ -966,6 +976,12 @@ void CL_CheckForResend (void)
connectinfo.protocol = CP_NETQUAKE; connectinfo.protocol = CP_NETQUAKE;
connectinfo.subprotocol = CPNQ_FITZ666; connectinfo.subprotocol = CPNQ_FITZ666;
} }
else if (!strcmp(lbp, "qe")||!strcmp(lbp, "qex")||!strcmp(lbp, "kex"))
{ //quake-ex has special quirks that cannot be defined by protocol numbers alone.
connectinfo.protocol = CP_NETQUAKE;
connectinfo.subprotocol = CPNQ_FITZ666;
connectinfo.mode = CIM_QEONLY;
}
else if (!strcmp(lbp, "bjp1") || !strcmp(lbp, "bjp2") || //placeholders only else if (!strcmp(lbp, "bjp1") || !strcmp(lbp, "bjp2") || //placeholders only
!strcmp(lbp, "bjp3") || !strcmp(lbp, "bjp")) !strcmp(lbp, "bjp3") || !strcmp(lbp, "bjp"))
{ {
@ -1094,7 +1110,14 @@ void CL_CheckForResend (void)
net_message.cursize = 0; net_message.cursize = 0;
MSG_BeginReading(net_message.prim); MSG_BeginReading(net_message.prim);
if (connectinfo.subprotocol == CPNQ_ID && !proquakeangles) if (connectinfo.mode == CIM_QEONLY)
{
net_from = connectinfo.adr[connectinfo.nextadr];
Cmd_TokenizeString (va("connect %i %i %i \"\\name\\unconnected\"", NQ_NETCHAN_VERSION_QEX, 0, SV_NewChallenge()), false, false);
SVC_DirectConnect(0);
}
else if (connectinfo.subprotocol == CPNQ_ID && !proquakeangles)
{ {
net_from = connectinfo.adr[connectinfo.nextadr]; net_from = connectinfo.adr[connectinfo.nextadr];
Cmd_TokenizeString (va("connect %i %i %i \"\\name\\unconnected\"", NQ_NETCHAN_VERSION, 0, SV_NewChallenge()), false, false); Cmd_TokenizeString (va("connect %i %i %i \"\\name\\unconnected\"", NQ_NETCHAN_VERSION, 0, SV_NewChallenge()), false, false);
@ -1239,11 +1262,16 @@ void CL_CheckForResend (void)
if (to->prot == NP_DGRAM) if (to->prot == NP_DGRAM)
connectinfo.nextadr++; //cycle hosts with each ping (if we got multiple). connectinfo.nextadr++; //cycle hosts with each ping (if we got multiple).
contype |= 1; /*always try qw type connections*/ if (connectinfo.mode==CIM_QEONLY || connectinfo.mode==CIM_NQONLY)
contype |= 2;
else
{
contype |= 1; /*always try qw type connections*/
#ifdef VM_UI #ifdef VM_UI
if (!UI_IsRunning()) //don't try to connect to nq servers when running a q3ui. I was getting annoying error messages from q3 servers due to this. if (!UI_IsRunning()) //don't try to connect to nq servers when running a q3ui. I was getting annoying error messages from q3 servers due to this.
#endif #endif
contype |= 2; /*try nq connections periodically (or if its the default nq port)*/ contype |= 2; /*try nq connections periodically (or if its the default nq port)*/
}
/*DP, QW, Q2, Q3*/ /*DP, QW, Q2, Q3*/
/*NOTE: ioq3 has <challenge> <gamename> args. yes, a challenge to get a challenge.*/ /*NOTE: ioq3 has <challenge> <gamename> args. yes, a challenge to get a challenge.*/
@ -1273,22 +1301,27 @@ void CL_CheckForResend (void)
MSG_WriteLong(&sb, LongSwap(NETFLAG_CTL | (strlen(NQ_NETCHAN_GAMENAME)+7))); MSG_WriteLong(&sb, LongSwap(NETFLAG_CTL | (strlen(NQ_NETCHAN_GAMENAME)+7)));
MSG_WriteByte(&sb, CCREQ_CONNECT); MSG_WriteByte(&sb, CCREQ_CONNECT);
MSG_WriteString(&sb, NQ_NETCHAN_GAMENAME); MSG_WriteString(&sb, NQ_NETCHAN_GAMENAME);
MSG_WriteByte(&sb, NQ_NETCHAN_VERSION); if (connectinfo.mode==CIM_QEONLY)
MSG_WriteByte(&sb, NQ_NETCHAN_VERSION_QEX);
else
{
MSG_WriteByte(&sb, NQ_NETCHAN_VERSION);
/*NQ engines have a few extra bits on the end*/ /*NQ engines have a few extra bits on the end*/
/*proquake servers wait for us to send them a packet before anything happens, /*proquake servers wait for us to send them a packet before anything happens,
which means it corrects for our public port if our nat uses different public ports for different remote ports which means it corrects for our public port if our nat uses different public ports for different remote ports
thus all nq engines claim to be proquake thus all nq engines claim to be proquake
*/ */
MSG_WriteByte(&sb, 1); /*'mod'*/ MSG_WriteByte(&sb, 1); /*'mod'*/
MSG_WriteByte(&sb, 34); /*'mod' version*/ MSG_WriteByte(&sb, 34); /*'mod' version*/
MSG_WriteByte(&sb, 0); /*flags*/ MSG_WriteByte(&sb, 0); /*flags*/
MSG_WriteLong(&sb, strtoul(password.string, NULL, 0)); /*password*/ MSG_WriteLong(&sb, strtoul(password.string, NULL, 0)); /*password*/
/*FTE servers will detect this string and treat it as a qw challenge instead (if it allows qw clients), so protocol choice is deterministic*/ /*FTE servers will detect this string and treat it as a qw challenge instead (if it allows qw clients), so protocol choice is deterministic*/
if (contype & 1) if (contype & 1)
MSG_WriteString(&sb, "getchallenge"); MSG_WriteString(&sb, "getchallenge");
}
*(int*)sb.data = LongSwap(NETFLAG_CTL | sb.cursize); *(int*)sb.data = LongSwap(NETFLAG_CTL | sb.cursize);
switch(NET_SendPacket (cls.sockets, sb.cursize, sb.data, to)) switch(NET_SendPacket (cls.sockets, sb.cursize, sb.data, to))
@ -1308,7 +1341,10 @@ void CL_CheckForResend (void)
if (!keeptrying) if (!keeptrying)
{ {
if (to->prot != NP_DGRAM && connectinfo.nextadr+1 < connectinfo.numadr) if (to->prot != NP_DGRAM && connectinfo.nextadr+1 < connectinfo.numadr)
connectinfo.nextadr++; //cycle hosts with each ping (if we got multiple). {
connectinfo.nextadr++; //cycle hosts with each connection failure (if we got multiple addresses).
connectinfo.tries = 0;
}
else else
{ {
Cvar_Set(&cl_disconnectreason, va("No route to \"%s\", giving up\n", cls.servername)); Cvar_Set(&cl_disconnectreason, va("No route to \"%s\", giving up\n", cls.servername));
@ -1319,7 +1355,7 @@ void CL_CheckForResend (void)
} }
} }
void CL_BeginServerConnect(const char *host, int port, qboolean noproxy) static void CL_BeginServerConnect(const char *host, int port, qboolean noproxy, enum coninfomode_e mode)
{ {
if (!strncmp(host, "localhost", 9)) if (!strncmp(host, "localhost", 9))
noproxy = true; //FIXME: resolve the address here or something so that we don't end up using a proxy for lan addresses. noproxy = true; //FIXME: resolve the address here or something so that we don't end up using a proxy for lan addresses.
@ -1341,6 +1377,16 @@ void CL_BeginServerConnect(const char *host, int port, qboolean noproxy)
connectinfo.trying = true; connectinfo.trying = true;
connectinfo.defaultport = port; connectinfo.defaultport = port;
connectinfo.protocol = CP_UNKNOWN; connectinfo.protocol = CP_UNKNOWN;
connectinfo.mode = mode;
#ifdef HAVE_DTLS
if (net_enable_dtls.ival >= 3)
connectinfo.dtlsupgrade = DTLS_REQUIRE;
else if (net_enable_dtls.ival >= 2)
connectinfo.dtlsupgrade = DTLS_TRY;
else
connectinfo.dtlsupgrade = DTLS_DISABLE;
#endif
SCR_SetLoadingStage(LS_CONNECTION); SCR_SetLoadingStage(LS_CONNECTION);
CL_CheckForResend(); CL_CheckForResend();
@ -1436,7 +1482,7 @@ void CL_Connect_f (void)
#endif #endif
CL_Disconnect_f (); CL_Disconnect_f ();
CL_BeginServerConnect(server, 0, false); CL_BeginServerConnect(server, 0, false, CIM_DEFAULT);
} }
#if defined(CL_MASTER) && defined(HAVE_PACKET) #if defined(CL_MASTER) && defined(HAVE_PACKET)
static void CL_ConnectBestRoute_f (void) static void CL_ConnectBestRoute_f (void)
@ -1471,7 +1517,7 @@ static void CL_ConnectBestRoute_f (void)
else else
#endif #endif
CL_Disconnect_f (); CL_Disconnect_f ();
CL_BeginServerConnect(server, 0, true); CL_BeginServerConnect(server, 0, true, CIM_DEFAULT);
} }
#endif #endif
@ -1500,7 +1546,7 @@ static void CL_Join_f (void)
Cvar_Set(&spectator, "0"); Cvar_Set(&spectator, "0");
CL_BeginServerConnect(server, 0, false); CL_BeginServerConnect(server, 0, false, CIM_DEFAULT);
} }
void CL_Observe_f (void) void CL_Observe_f (void)
@ -1528,13 +1574,14 @@ void CL_Observe_f (void)
Cvar_Set(&spectator, "1"); Cvar_Set(&spectator, "1");
CL_BeginServerConnect(server, 0, false); CL_BeginServerConnect(server, 0, false, CIM_DEFAULT);
} }
#ifdef NQPROT #ifdef NQPROT
void CLNQ_Connect_f (void) void CLNQ_Connect_f (void)
{ {
char *server; char *server;
enum coninfomode_e mode;
if (Cmd_Argc() != 2) if (Cmd_Argc() != 2)
{ {
@ -1542,12 +1589,17 @@ void CLNQ_Connect_f (void)
return; return;
} }
if (!strcmp(Cmd_Argv(0), "connectqe"))
mode = CIM_QEONLY;
else
mode = CIM_NQONLY;
server = Cmd_Argv (1); server = Cmd_Argv (1);
server = strcpy(alloca(strlen(server)+1), server); server = strcpy(alloca(strlen(server)+1), server);
CL_Disconnect_f (); CL_Disconnect_f ();
CL_BeginServerConnect(server, 26000, true); CL_BeginServerConnect(server, 26000, true, mode);
} }
#endif #endif
@ -3218,7 +3270,7 @@ void CL_ConnectionlessPacket (void)
} }
if (cls.demoplayback == DPB_NONE && net_from.type != NA_LOOPBACK) if (cls.demoplayback == DPB_NONE && net_from.type != NA_LOOPBACK)
Con_Printf ("%s: ", NET_AdrToString (adr, sizeof(adr), &net_from)); Con_Printf (S_COLOR_GRAY"%s: ", NET_AdrToString (adr, sizeof(adr), &net_from));
// Con_DPrintf ("%s", net_message.data + 4); // Con_DPrintf ("%s", net_message.data + 4);
if (c == 'f') //using 'f' as a prefix so that I don't need lots of hacks if (c == 'f') //using 'f' as a prefix so that I don't need lots of hacks
@ -3228,13 +3280,13 @@ void CL_ConnectionlessPacket (void)
{ {
netadr_t adr; netadr_t adr;
char *data = MSG_ReadStringLine(); char *data = MSG_ReadStringLine();
Con_TPrintf ("redirect to %s\n", data); Con_TPrintf (S_COLOR_GRAY"redirect to %s\n", data);
if (NET_StringToAdr(data, PORT_DEFAULTSERVER, &adr)) if (NET_StringToAdr(data, PORT_DEFAULTSERVER, &adr))
{ {
if (CL_IsPendingServerAddress(&net_from)) if (CL_IsPendingServerAddress(&net_from))
{ {
if (!NET_EnsureRoute(cls.sockets, "redir", cls.servername, &adr)) if (!NET_EnsureRoute(cls.sockets, "redir", cls.servername, &adr))
Con_Printf ("Unable to redirect to %s\n", data); Con_Printf (CON_ERROR"Unable to redirect to %s\n", data);
else else
{ {
connectinfo.istransfer = true; connectinfo.istransfer = true;
@ -3310,12 +3362,12 @@ void CL_ConnectionlessPacket (void)
} }
#endif #endif
Con_TPrintf ("challenge\n"); Con_TPrintf (S_COLOR_GRAY"challenge\n");
if (!CL_IsPendingServerAddress(&net_from)) if (!CL_IsPendingServerAddress(&net_from))
{ {
if (net_from.prot != NP_RTC_TCP && net_from.prot != NP_RTC_TLS) if (net_from.prot != NP_RTC_TCP && net_from.prot != NP_RTC_TLS)
Con_Printf("Challenge from wrong server, ignoring\n"); Con_Printf(CON_WARNING"Challenge from wrong server, ignoring\n");
return; return;
} }
connectinfo.numadr = 1; connectinfo.numadr = 1;
@ -3557,7 +3609,7 @@ void CL_ConnectionlessPacket (void)
if (!strcmp(s, "print")) if (!strcmp(s, "print"))
{ {
Con_TPrintf ("print\n"); Con_TPrintf (S_COLOR_GRAY"print\n");
s = MSG_ReadString (); s = MSG_ReadString ();
if (connectinfo.trying && CL_IsPendingServerBaseAddress(&net_from) == false) if (connectinfo.trying && CL_IsPendingServerBaseAddress(&net_from) == false)
@ -3602,7 +3654,7 @@ void CL_ConnectionlessPacket (void)
if (!strcmp(com_token, "ccept")) if (!strcmp(com_token, "ccept"))
{ {
/*this is a DP server... but we don't know which version nor nq protocol*/ /*this is a DP server... but we don't know which version nor nq protocol*/
Con_Printf ("accept\n"); Con_Printf (S_COLOR_GRAY"accept\n");
if (cls.state == ca_connected) if (cls.state == ca_connected)
return; //we're already connected. don't do it again! return; //we're already connected. don't do it again!
@ -3638,7 +3690,7 @@ void CL_ConnectionlessPacket (void)
{ {
if (!strncmp(net_message.data+4, "infoResponse\n", 13)) if (!strncmp(net_message.data+4, "infoResponse\n", 13))
{ {
Con_TPrintf ("infoResponse\n"); Con_TPrintf (S_COLOR_GRAY"infoResponse\n");
Info_Print(net_message.data+17, ""); Info_Print(net_message.data+17, "");
return; return;
} }
@ -3648,7 +3700,7 @@ void CL_ConnectionlessPacket (void)
if (!strncmp(net_message.data+4, "getserversResponse", 18)) if (!strncmp(net_message.data+4, "getserversResponse", 18))
{ {
qbyte *b = net_message.data+4+18; qbyte *b = net_message.data+4+18;
Con_TPrintf ("getserversResponse\n"); Con_TPrintf (S_COLOR_GRAY"getserversResponse\n");
while (b+7 <= net_message.data+net_message.cursize) while (b+7 <= net_message.data+net_message.cursize)
{ {
if (*b == '\\') if (*b == '\\')
@ -3686,7 +3738,7 @@ void CL_ConnectionlessPacket (void)
else if (!strcmp(com_token, "tlsopened")) else if (!strcmp(com_token, "tlsopened"))
{ //server is letting us know that its now listening for a dtls handshake. { //server is letting us know that its now listening for a dtls handshake.
#ifdef HAVE_DTLS #ifdef HAVE_DTLS
Con_Printf ("dtlsopened\n"); Con_Printf (S_COLOR_GRAY"dtlsopened\n");
if (!CL_IsPendingServerAddress(&net_from)) if (!CL_IsPendingServerAddress(&net_from))
return; return;
@ -3715,7 +3767,7 @@ void CL_ConnectionlessPacket (void)
} }
else else
{ {
Con_Printf ("d\n"); Con_Printf ("disconnect\n");
if (cls.demoplayback != DPB_NONE) if (cls.demoplayback != DPB_NONE)
{ {
Con_Printf("Disconnect\n"); Con_Printf("Disconnect\n");
@ -3743,7 +3795,7 @@ client_connect: //fixme: make function
} }
if (net_from.type != NA_LOOPBACK) if (net_from.type != NA_LOOPBACK)
{ {
Con_TPrintf ("connection\n"); Con_TPrintf (S_COLOR_GRAY"connection\n");
#ifdef HAVE_SERVER #ifdef HAVE_SERVER
if (sv.state && sv.state != ss_clustermode) if (sv.state && sv.state != ss_clustermode)
@ -3883,7 +3935,7 @@ client_connect: //fixme: make function
{ {
if (!strncmp(net_message.data+4, "print\n", 6)) if (!strncmp(net_message.data+4, "print\n", 6))
{ //quake2+quake3 send rejects this way { //quake2+quake3 send rejects this way
Con_TPrintf ("print\n"); Con_TPrintf (S_COLOR_GRAY"print\n");
Con_Printf ("%s", net_message.data+10); Con_Printf ("%s", net_message.data+10);
if (connectinfo.trying && CL_IsPendingServerBaseAddress(&net_from) == false) if (connectinfo.trying && CL_IsPendingServerBaseAddress(&net_from) == false)
@ -3893,7 +3945,7 @@ client_connect: //fixme: make function
} }
if (c == A2C_PRINT) if (c == A2C_PRINT)
{ //closest quakeworld has to a reject message { //closest quakeworld has to a reject message
Con_TPrintf ("print\n"); Con_TPrintf (S_COLOR_GRAY"print\n");
s = MSG_ReadString (); s = MSG_ReadString ();
Con_Printf ("%s", s); Con_Printf ("%s", s);
@ -3929,6 +3981,9 @@ void CLNQ_ConnectionlessPacket(void)
int length; int length;
unsigned short port; unsigned short port;
if (net_message.cursize < 5)
return; //not enough size to be meaningful (qe does not include a port number)
MSG_BeginReading (msg_nullnetprim); MSG_BeginReading (msg_nullnetprim);
length = LongSwap(MSG_ReadLong ()); length = LongSwap(MSG_ReadLong ());
if (!(length & NETFLAG_CTL)) if (!(length & NETFLAG_CTL))
@ -3947,30 +4002,40 @@ void CLNQ_ConnectionlessPacket(void)
Con_TPrintf ("Dup connect received. Ignored.\n"); Con_TPrintf ("Dup connect received. Ignored.\n");
return; return;
} }
port = htons((unsigned short)MSG_ReadLong());
//this is the port that we're meant to respond to.
if (port) if (length == 5 && net_from.prot == NP_DTLS)
{ {
char buf[256]; cls.proquake_angles_hack = false;
net_from.port = port; cls.protocol_nq = CPNQ_ID;
Con_DPrintf("redirecting to port %s\n", NET_AdrToString(buf, sizeof(buf), &net_from)); Con_DPrintf("QuakeEx server...\n");
} }
else
cls.proquake_angles_hack = false;
cls.protocol_nq = CPNQ_ID;
if (MSG_ReadByte() == 1) //a proquake server adds a little extra info
{ {
int ver = MSG_ReadByte(); port = htons((unsigned short)MSG_ReadLong());
Con_DPrintf("ProQuake server %i.%i\n", ver/10, ver%10); //this is the port that we're meant to respond to.
// if (ver >= 34) if (port)
cls.proquake_angles_hack = true;
if (MSG_ReadByte() == 1)
{ {
//its a 'pure' server. char buf[256];
Con_Printf("pure ProQuake server\n"); net_from.port = port;
return; Con_DPrintf("redirecting to port %s\n", NET_AdrToString(buf, sizeof(buf), &net_from));
}
cls.proquake_angles_hack = false;
cls.protocol_nq = CPNQ_ID;
if (MSG_ReadByte() == 1) //a proquake server adds a little extra info
{
int ver = MSG_ReadByte();
Con_DPrintf("ProQuake server %i.%i\n", ver/10, ver%10);
// if (ver >= 34)
cls.proquake_angles_hack = true;
if (MSG_ReadByte() == 1)
{
//its a 'pure' server.
Con_Printf("pure ProQuake server\n");
return;
}
} }
} }
@ -3985,7 +4050,11 @@ void CLNQ_ConnectionlessPacket(void)
cls.netchan.compresstable = NULL; cls.netchan.compresstable = NULL;
cls.protocol = CP_NETQUAKE; cls.protocol = CP_NETQUAKE;
cls.state = ca_connected; cls.state = ca_connected;
Con_TPrintf ("Connected.\n");
if (cls.netchan.remote_address.prot == NP_DTLS || cls.netchan.remote_address.prot == NP_TLS || cls.netchan.remote_address.prot == NP_WSS)
Con_TPrintf ("Connected (^[^2encrypted\\tip\\Any passwords will be sent securely, but may still be logged^]).\n");
else
Con_TPrintf ("Connected (^[^1plain-text\\tip\\"CON_WARNING"Do not type passwords as they can potentially be seen by network sniffers^]).\n");
total_loading_size = 100; total_loading_size = 100;
current_loading_size = 0; current_loading_size = 0;
@ -3995,9 +4064,14 @@ void CLNQ_ConnectionlessPacket(void)
allowremotecmd = false; // localid required now for remote cmds allowremotecmd = false; // localid required now for remote cmds
#endif #endif
//send a dummy packet. if (length == 5)
//this makes our local nat think we initialised the conversation, so that we can receive the. cls.qex = (connectinfo.mode==CIM_QEONLY);
Netchan_Transmit(&cls.netchan, 1, "\x01", 2500); else
{
//send a dummy packet.
//this makes our local nat think we initialised the conversation, so that we can receive the.
Netchan_Transmit(&cls.netchan, 1, "\x01", 2500);
}
return; return;
case CCREP_REJECT: case CCREP_REJECT:
@ -4056,16 +4130,6 @@ void CL_ReadPacket(void)
return; return;
} }
if (net_message.cursize < 6 && (cls.demoplayback != DPB_MVD && cls.demoplayback != DPB_EZTV)) //MVDs don't have the whole sequence header thing going on
{
char adr[MAX_ADR_SIZE];
if (net_message.cursize == 1 && net_message.data[0] == A2A_ACK)
Con_TPrintf ("%s: Ack (Pong)\n", NET_AdrToString(adr, sizeof(adr), &net_from));
else
Con_TPrintf ("%s: Runt packet\n", NET_AdrToString(adr, sizeof(adr), &net_from));
return;
}
if (cls.state == ca_disconnected) if (cls.state == ca_disconnected)
{ //connect to nq servers, but don't get confused with sequenced packets. { //connect to nq servers, but don't get confused with sequenced packets.
if (NET_WasSpecialPacket(cls.sockets)) if (NET_WasSpecialPacket(cls.sockets))
@ -4076,6 +4140,16 @@ void CL_ReadPacket(void)
return; //ignore it. We arn't connected. return; //ignore it. We arn't connected.
} }
if (net_message.cursize < 6 && (cls.demoplayback != DPB_MVD && cls.demoplayback != DPB_EZTV)) //MVDs don't have the whole sequence header thing going on
{
char adr[MAX_ADR_SIZE];
if (net_message.cursize == 1 && net_message.data[0] == A2A_ACK)
Con_TPrintf ("%s: Ack (Pong)\n", NET_AdrToString(adr, sizeof(adr), &net_from));
else
Con_TPrintf ("%s: Runt packet\n", NET_AdrToString(adr, sizeof(adr), &net_from));
return;
}
// //
// packet from server // packet from server
// //
@ -4098,11 +4172,17 @@ void CL_ReadPacket(void)
{ {
case CP_NETQUAKE: case CP_NETQUAKE:
#ifdef NQPROT #ifdef NQPROT
if(NQNetChan_Process(&cls.netchan)) switch(NQNetChan_Process(&cls.netchan))
{ {
case NQNC_IGNORED:
break;
case NQNC_ACK:
case NQNC_RELIABLE:
case NQNC_UNRELIABLE:
MSG_ChangePrimitives(cls.netchan.netprim); MSG_ChangePrimitives(cls.netchan.netprim);
CL_WriteDemoMessage (&net_message, msg_readcount); CL_WriteDemoMessage (&net_message, msg_readcount);
CLNQ_ParseServerMessage (); CLNQ_ParseServerMessage ();
break;
} }
#endif #endif
break; break;
@ -5148,13 +5228,14 @@ void CL_Init (void)
); );
Cmd_AddCommandD ("cl_transfer", CL_Transfer_f, "Connect to a different server, disconnecting from the current server only when the new server replies."); Cmd_AddCommandD ("cl_transfer", CL_Transfer_f, "Connect to a different server, disconnecting from the current server only when the new server replies.");
#ifdef TCPCONNECT #ifdef TCPCONNECT
Cmd_AddCommandAD ("tcpconnect", CL_TCPConnect_f, CL_Connect_c, "Connect to a server using the tcp:// prefix"); Cmd_AddCommandAD ("connecttcp", CL_TCPConnect_f, CL_Connect_c, "Connect to a server using the tcp:// prefix");
#endif #endif
#ifdef IRCCONNECT #ifdef IRCCONNECT
Cmd_AddCommand ("ircconnect", CL_IRCConnect_f); Cmd_AddCommand ("connectirc", CL_IRCConnect_f);
#endif #endif
#ifdef NQPROT #ifdef NQPROT
Cmd_AddCommandD ("nqconnect", CLNQ_Connect_f, "Connects to the specified server, defaulting to port "STRINGIFY(PORT_NQSERVER)". Otherwise identical to the connect command."); Cmd_AddCommandD ("connectnq", CLNQ_Connect_f, "Connects to the specified server, defaulting to port "STRINGIFY(PORT_NQSERVER)". Also disables QW/Q2/Q3/DP handshakes preventing them from being favoured, so should only be used when you actually want NQ protocols specifically.");
Cmd_AddCommandD ("connectqe", CLNQ_Connect_f, "Connects to the specified server, defaulting to port "STRINGIFY(PORT_NQSERVER)". Also forces the use of DTLS and QE-specific handshakes. You will also need to ensure the dtls_psk_* cvars are set properly or the server will refuse the connection.");
#endif #endif
Cmd_AddCommand ("reconnect", CL_Reconnect_f); Cmd_AddCommand ("reconnect", CL_Reconnect_f);
Cmd_AddCommandAD ("join", CL_Join_f, CL_Connect_c, "Switches away from spectator mode, optionally connecting to a different server."); Cmd_AddCommandAD ("join", CL_Join_f, CL_Connect_c, "Switches away from spectator mode, optionally connecting to a different server.");

View file

@ -201,13 +201,13 @@ static const char *svc_nqstrings[] =
"nqsvc_time", // [float] server time "nqsvc_time", // [float] server time
"nqsvc_print", // [string] null terminated string "nqsvc_print", // [string] null terminated string
"nqsvc_stufftext", // [string] stuffed into client's console buffer "nqsvc_stufftext", // [string] stuffed into client's console buffer
// the string should be \n terminated // the string should be \n terminated
"nqsvc_setangle", // [vec3] set the view angle to this absolute value "nqsvc_setangle", // [vec3] set the view angle to this absolute value
"nqsvc_serverinfo", // [long] version "nqsvc_serverinfo", // [long] version
// [string] signon string // [string] signon string
// [string]..[0]model cache [string]...[0]sounds cache // [string]..[0]model cache [string]...[0]sounds cache
// [string]..[0]item cache // [string]..[0]item cache
"nqsvc_lightstyle", // [qbyte] [string] "nqsvc_lightstyle", // [qbyte] [string]
"nqsvc_updatename", // [qbyte] [string] "nqsvc_updatename", // [qbyte] [string]
"nqsvc_updatefrags", // [qbyte] [short] "nqsvc_updatefrags", // [qbyte] [short]
@ -218,10 +218,10 @@ static const char *svc_nqstrings[] =
"nqsvc_damage", // [qbyte] impact [qbyte] blood [vec3] from "nqsvc_damage", // [qbyte] impact [qbyte] blood [vec3] from
"nqsvc_spawnstatic", "nqsvc_spawnstatic",
"nqsvcfte_spawnstatic2(21)", "ftenq_spawnstatic2(21)",
"nqsvc_spawnbaseline", "nqsvc_spawnbaseline",
"nqsvc_temp_entity", // <variable> "nqsvc_temp_entity", // <variable>
"nqsvc_setpause", "nqsvc_setpause",
"nqsvc_signonnum", "nqsvc_signonnum",
"nqsvc_centerprint", "nqsvc_centerprint",
@ -230,42 +230,42 @@ static const char *svc_nqstrings[] =
"nqsvc_spawnstaticsound", "nqsvc_spawnstaticsound",
"nqsvc_intermission", "nqsvc_intermission",
"nqsvc_finale", // [string] music [string] text "nqsvc_finale", // [string] music [string] text
"nqsvc_cdtrack", // [qbyte] track [qbyte] looptrack "nqsvc_cdtrack", // [qbyte] track [qbyte] looptrack
"nqsvc_sellscreen", "nqsvc_sellscreen",
"nqsvc_cutscene", //34 "nqsvc_cutscene", //34
"NEW PROTOCOL", //35 "NEW PROTOCOL", //35
"NEW PROTOCOL", //36 "NEW PROTOCOL", //36
"fitzsvc_skybox", //37 "fitz_skybox", //37
"NEW PROTOCOL", //38 "NEW PROTOCOL", //38
"NEW PROTOCOL", //39 "NEW PROTOCOL", //39
"fitzsvc_bf", //40 "fitz_bf", //40
"fitzsvc_fog", //41 "fitz_fog", //41
"fitzsvc_spawnbaseline2", //42 "fitz_spawnbaseline2", //42
"fitzsvc_spawnstatic2", //43 "fitz_spawnstatic2", //43
"fitzsvc_spawnstaticsound2", //44 "fitz_spawnstaticsound2", //44
"NEW PROTOCOL", //45 "NEW PROTOCOL", //45
"NEW PROTOCOL", //46 "qex_updateping", //46
"NEW PROTOCOL", //47 "qex_updatesocial", //47
"NEW PROTOCOL", //48 "qex_updateplinfo", //48
"NEW PROTOCOL", //49 "qex_print", //49
"dpsvc_downloaddata", //50 "dp_downloaddata / neh_skyboxsize / qex_servervars", //50
"dpsvc_updatestatubyte", //51 "dp_updatestatubyte / neh_fog / qex_seq", //51
"dpsvc_effect", //52 "dp_effect / qex_achievement", //52
"dpsvc_effect2", //53 "dp_effect2", //53
"dp6svc_precache/dp5svc_sound2", //54 "dp6_precache / dp5_sound2", //54
"dpsvc_spawnbaseline2", //55 "dp_spawnbaseline2", //55
"dpsvc_spawnstatic2", //56 obsolete "dp_spawnstatic2", //56 obsolete
"dpsvc_entities", //57 "dp_entities", //57
"dpsvc_csqcentities", //58 "dp_csqcentities", //58
"dpsvc_spawnstaticsound2", //59 "dp_spawnstaticsound2", //59
"dpsvc_trailparticles", //60 "dp_trailparticles", //60
"dpsvc_pointparticles", //61 "dp_pointparticles", //61
"dpsvc_pointparticles1", //62 "dp_pointparticles1", //62
"NEW PROTOCOL(63)", //63 "NEW PROTOCOL(63)", //63
"NEW PROTOCOL(64)", //64 "NEW PROTOCOL(64)", //64
"NEW PROTOCOL(65)", //65 "NEW PROTOCOL(65)", //65
"ftenqsvc_spawnbaseline2", //66 "ftenq_spawnbaseline2", //66
"NEW PROTOCOL(67)", //67 "NEW PROTOCOL(67)", //67
"NEW PROTOCOL(68)", //68 "NEW PROTOCOL(68)", //68
"NEW PROTOCOL(69)", //69 "NEW PROTOCOL(69)", //69
@ -277,21 +277,21 @@ static const char *svc_nqstrings[] =
"NEW PROTOCOL(75)", //75 "NEW PROTOCOL(75)", //75
"NEW PROTOCOL(76)", //76 "NEW PROTOCOL(76)", //76
"NEW PROTOCOL(77)", //77 "NEW PROTOCOL(77)", //77
"nqsvcfte_updatestatstring", //78 "ftenq_updatestatstring", //78
"nqsvcfte_updatestatfloat", //79 "ftenq_updatestatfloat", //79
"NEW PROTOCOL(80)", //80 "NEW PROTOCOL(80)", //80
"NEW PROTOCOL(81)", //81 "NEW PROTOCOL(81)", //81
"NEW PROTOCOL(82)", //82 "NEW PROTOCOL(82)", //82
"nqsvcfte_cgamepacket", //83 "ftenq_cgamepacket", //83
"nqsvcfte_voicechat", //84 "ftenq_voicechat", //84
"nqsvcfte_setangledelta", //85 "ftenq_setangledelta", //85
"nqsvcfte_updateentities", //86 "ftenq_updateentities", //86
"NEW PROTOCOL(87)", //87 "NEW PROTOCOL(87)", //87
"NEW PROTOCOL(88)", //88 "NEW PROTOCOL(88)", //88
"svcfte_setinfoblob", //89 "ftenq_setinfoblob", //89
"svcfte_cgamepacket_sized", //90 "ftenq_cgamepacket_sized", //90
"svcfte_temp_entity_sized", //91 "ftenq_temp_entity_sized", //91
"svcfte_csqcentities_sized", //92 "ftenq_csqcentities_sized", //92
}; };
#endif #endif
@ -3712,6 +3712,7 @@ static void CLQ2_ParseServerData (void)
void CL_ParseEstablished(void) void CL_ParseEstablished(void)
{ {
#ifdef NQPROT #ifdef NQPROT
cls.qex = false;
Z_Free(cl_dp_packagenames); Z_Free(cl_dp_packagenames);
cl_dp_packagenames = NULL; cl_dp_packagenames = NULL;
cl_dp_serverextension_download = false; cl_dp_serverextension_download = false;
@ -3880,7 +3881,7 @@ void CL_KeepaliveMessage(void){}
static void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caution. static void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caution.
{ {
int nummodels, numsounds; int nummodels, numsounds;
char *str; char *str = NULL;
int gametype; int gametype;
Con_DPrintf ("Serverdata packet %s.\n", cls.demoplayback?"read":"received"); Con_DPrintf ("Serverdata packet %s.\n", cls.demoplayback?"read":"received");
SCR_SetLoadingStage(LS_CLIENT); SCR_SetLoadingStage(LS_CLIENT);
@ -3892,9 +3893,19 @@ static void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caut
CLNQ_ParseProtoVersion(); CLNQ_ParseProtoVersion();
if (cls.fteprotocolextensions2 & PEXT2_PREDINFO) if (cls.qex)
{ {
cl.allocated_client_slots = MSG_ReadByte();
str = MSG_ReadString(); str = MSG_ReadString();
}
else
{
if (cls.fteprotocolextensions2 & PEXT2_PREDINFO)
str = MSG_ReadString();
cl.allocated_client_slots = MSG_ReadByte();
}
if (str)
{
#ifndef CLIENTONLY #ifndef CLIENTONLY
if (!sv.state) if (!sv.state)
#endif #endif
@ -3906,8 +3917,6 @@ static void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caut
#endif #endif
} }
} }
cl.allocated_client_slots = MSG_ReadByte();
if (cl.allocated_client_slots > MAX_CLIENTS) if (cl.allocated_client_slots > MAX_CLIENTS)
{ {
cl.allocated_client_slots = MAX_CLIENTS; cl.allocated_client_slots = MAX_CLIENTS;
@ -3916,6 +3925,7 @@ static void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caut
cl.splitclients = 1; cl.splitclients = 1;
gametype = MSG_ReadByte (); gametype = MSG_ReadByte ();
str = MSG_ReadString (); str = MSG_ReadString ();
@ -4063,6 +4073,55 @@ static void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caut
CSQC_Shutdown(); CSQC_Shutdown();
#endif #endif
} }
static void CLQEX_ParseServerVars(void)
{
unsigned int bits = MSG_ReadULEB128();
if (bits & QEX_GV_DEATHMATCH)
cl.deathmatch = MSG_ReadByte ();
if (bits & QEX_GV_IDEALPITCHSCALE)
MSG_ReadFloat ();
if (bits & QEX_GV_FRICTION)
movevars.friction = MSG_ReadFloat ();
if (bits & QEX_GV_EDGEFRICTION)
movevars.edgefriction = MSG_ReadFloat ();
if (bits & QEX_GV_STOPSPEED)
movevars.stopspeed = MSG_ReadFloat ();
if (bits & QEX_GV_MAXVELOCITY)
/*movevars.maxvelocity =*/ MSG_ReadFloat ();
if (bits & QEX_GV_GRAVITY)
movevars.gravity = MSG_ReadFloat ();
if (bits & QEX_GV_NOSTEP)
/*movevars.nostep =*/ MSG_ReadByte ();
if (bits & QEX_GV_MAXSPEED)
movevars.maxspeed = MSG_ReadFloat ();
if (bits & QEX_GV_ACCELERATE)
movevars.accelerate = MSG_ReadFloat ();
if (bits & QEX_GV_CONTROLLERONLY)
/*cl.blockmouse =*/ MSG_ReadByte ();
if (bits & QEX_GV_TIMELIMIT)
InfoBuf_SetStarKey(&cl.serverinfo, "timelimit", va("%g", MSG_ReadFloat ()));
if (bits & QEX_GV_FRAGLIMIT)
InfoBuf_SetStarKey(&cl.serverinfo, "fraglimit", va("%g", MSG_ReadFloat ()));
}
static char *CLQEX_ReadStrings(void)
{
unsigned short count = MSG_ReadShort(), a;
const char *arg[8];
static char formatted[8192];
if (count > countof(arg))
Host_EndGame ("CLQEX_ReadStrings: too many strings (%u>%u)", count, (unsigned)countof(arg));
for (a = 0; a < count; a++)
{
char *s = alloca(8192);
arg[a] = s;
MSG_ReadStringBuffer(s, 8192);
}
TL_Reformat(formatted, sizeof(formatted), count, arg);
return formatted;
}
static void CLNQ_SendInitialUserInfo(void *ctx, const char *key, const char *value) static void CLNQ_SendInitialUserInfo(void *ctx, const char *key, const char *value)
{ {
InfoSync_Add(&cls.userinfosync, ctx, key); InfoSync_Add(&cls.userinfosync, ctx, key);
@ -4153,7 +4212,7 @@ static void CLNQ_ParseClientdata (void)
if (bits & (SU_VELOCITY1<<i)) if (bits & (SU_VELOCITY1<<i))
{ {
if (CPNQ_IS_DP) if (CPNQ_IS_DP || (cls.qex && (bits & QEX_SU_FLOATCOORDS)))
pl->velocity[i] = MSG_ReadFloat(); pl->velocity[i] = MSG_ReadFloat();
else else
pl->velocity[i] = MSG_ReadChar()*16; pl->velocity[i] = MSG_ReadChar()*16;
@ -4233,6 +4292,11 @@ static void CLNQ_ParseClientdata (void)
weaponframe |= MSG_ReadByte() << 8; weaponframe |= MSG_ReadByte() << 8;
if (bits & FITZSU_WEAPONALPHA) if (bits & FITZSU_WEAPONALPHA)
MSG_ReadByte(); MSG_ReadByte();
if (cls.qex)
{
if (bits & QEX_SU_ENTFLAGS) /*entflags =*/ MSG_ReadULEB128();
}
} }
CL_SetStatInt(0, STAT_WEAPONFRAME, weaponframe); CL_SetStatInt(0, STAT_WEAPONFRAME, weaponframe);
@ -4705,7 +4769,21 @@ static void CL_ParseBaseline (entity_state_t *es, int baselinetype2)
} }
es->trans = (bits & FITZ_B_ALPHA) ? MSG_ReadByte() : 255; es->trans = (bits & FITZ_B_ALPHA) ? MSG_ReadByte() : 255;
es->scale = (bits & RMQFITZ_B_SCALE) ? MSG_ReadByte() : 16; if (cls.qex)
{
if (bits & QEX_B_SOLID)
/*es->solidtype =*/ MSG_ReadByte();
if (bits & QEX_B_UNKNOWN4)
Con_Printf(CON_WARNING"QEX_B_UNKNOWN4: %x\n", MSG_ReadByte());
if (bits & QEX_B_UNKNOWN5)
Con_Printf(CON_WARNING"QEX_B_UNKNOWN5: %x\n", MSG_ReadByte());
if (bits & QEX_B_UNKNOWN6)
Con_DPrintf(CON_WARNING"QEX_B_UNKNOWN6: %x\n", MSG_ReadByte());
if (bits & QEX_B_UNKNOWN7)
Con_Printf(CON_WARNING"QEX_B_UNKNOWN7: %x\n", MSG_ReadByte());
}
else
es->scale = (bits & RMQFITZ_B_SCALE) ? MSG_ReadByte() : 16;
} }
static void CL_ParseBaselineDelta (void) static void CL_ParseBaselineDelta (void)
{ {
@ -7972,7 +8050,7 @@ static qboolean CLNQ_ParseNQPrints(char *s)
{ {
int i; int i;
char *start = s; char *start = s;
if (!strcmp(s, "Client ping times:\n")) if (!strcmp(s, "Client ping times:\n") && !cls.qex)
{ {
cl.nqparseprint = CLNQPP_PINGS; cl.nqparseprint = CLNQPP_PINGS;
return true; return true;
@ -8020,7 +8098,7 @@ static qboolean CLNQ_ParseNQPrints(char *s)
s = start; s = start;
} }
if (!strncmp(s, "host: ", 9)) if (!strncmp(s, "host: ", 9) && !cls.qex)
{ {
cl.nqparseprint = CLNQPP_STATUS; cl.nqparseprint = CLNQPP_STATUS;
return cls.nqexpectingstatusresponse; return cls.nqexpectingstatusresponse;
@ -8134,6 +8212,7 @@ void CLNQ_ParseServerMessage (void)
switch (cmd) switch (cmd)
{ {
default: default:
badsvc:
CL_DumpPacket(); CL_DumpPacket();
Host_EndGame ("CLNQ_ParseServerMessage: Illegible server message (%i@%i)", cmd, msg_readcount-1); Host_EndGame ("CLNQ_ParseServerMessage: Illegible server message (%i@%i)", cmd, msg_readcount-1);
return; return;
@ -8143,7 +8222,13 @@ void CLNQ_ParseServerMessage (void)
break; break;
case svc_print: case svc_print:
s = MSG_ReadString (); if(cls.qex && cls.protocol_nq != CPNQ_ID)
s = CLQEX_ReadStrings();
else
{
svcprint:
s = MSG_ReadString ();
}
if (*s == 1 || *s == 2) if (*s == 1 || *s == 2)
{ {
@ -8161,7 +8246,10 @@ void CLNQ_ParseServerMessage (void)
return; return;
case svc_centerprint: case svc_centerprint:
s = MSG_ReadString (); if (cls.qex && cls.protocol_nq != CPNQ_ID)
s = CLQEX_ReadStrings();
else
s = MSG_ReadString ();
#ifdef PLUGINS #ifdef PLUGINS
if (Plug_CenterPrintMessage(s, destsplit)) if (Plug_CenterPrintMessage(s, destsplit))
@ -8188,6 +8276,14 @@ void CLNQ_ParseServerMessage (void)
break; break;
case svcdp_precache: case svcdp_precache:
//also svcqex_levelcompleted
if (cls.qex)
{ //svcqex_levelcompleted
//not really sure why this even exists.
MSG_ReadSkip(10);
MSG_ReadString();
break;
}
CL_ParsePrecache(); CL_ParsePrecache();
break; break;
@ -8431,6 +8527,14 @@ void CLNQ_ParseServerMessage (void)
break; break;
case svcdp_updatestatbyte: case svcdp_updatestatbyte:
//case svcneh_fog: //case svcneh_fog:
//also svcqex_seq
if (cls.qex)
{ //svcqex_seq
unsigned seq = MSG_ReadULEB128();
if (!cls.demoplayback && 0)
CL_AckedInputFrame(cls.netchan.incoming_sequence, seq, true);
break;
}
if (CPNQ_IS_BJP || cls.protocol_nq == CPNQ_NEHAHRA) if (CPNQ_IS_BJP || cls.protocol_nq == CPNQ_NEHAHRA)
{ {
CL_ResetFog(FOGTYPE_AIR); CL_ResetFog(FOGTYPE_AIR);
@ -8599,9 +8703,28 @@ void CLNQ_ParseServerMessage (void)
case svcnq_effect: case svcnq_effect:
//also svcqex_achievement
if (cls.qex)
{ //svcqex_achievement
MSG_ReadString();
break;
}
CL_ParseEffect(false); CL_ParseEffect(false);
break; break;
case svcnq_effect2: case svcnq_effect2:
//also svcqex_chat
if (cls.qex)
{ //svcqex_chat
//in qex this text is in some small special chat box. which disappears quickly rendering its contents kinda unreadable. and its messagemode stuff seems broken too, so whatever. and it seems to have newline issues.
//FIXME: figure out the player index so we can kickban/mute/etc them.
qbyte plcolour = MSG_ReadByte();
qbyte chatcolour = MSG_ReadByte();
char *pcols[] = {S_COLOR_WHITE,S_COLOR_RED,S_COLOR_YELLOW, S_COLOR_CYAN};
char *ccols[] = {S_COLOR_WHITE,S_COLOR_CYAN};
Con_Printf("^[%s%s"/*"\\player\\%i"*/"^]: ", pcols[plcolour%countof(pcols)], MSG_ReadString());
Con_Printf("%s%s\n", ccols[chatcolour%countof(ccols)], MSG_ReadString());
break;
}
CL_ParseEffect(true); CL_ParseEffect(true);
break; break;
@ -8638,7 +8761,11 @@ void CLNQ_ParseServerMessage (void)
#endif #endif
case svcdp_downloaddata: case svcdp_downloaddata:
CLDP_ParseDownloadData(); //also svcqex_servervars:
if (cls.qex)
CLQEX_ParseServerVars();
else
CLDP_ParseDownloadData();
break; break;
case svcdp_trailparticles: case svcdp_trailparticles:
@ -8650,6 +8777,49 @@ void CLNQ_ParseServerMessage (void)
case svcdp_pointparticles1: case svcdp_pointparticles1:
CL_ParsePointParticles(true); CL_ParsePointParticles(true);
break; break;
case svcqex_updateping:
if (cls.qex)
{ //svcqex_updateping
int ping;
i = MSG_ReadByte();
ping = MSG_ReadSignedQEX();
if (i < MAX_CLIENTS)
cl.players[i].ping = ping;
break;
}
goto badsvc;
case svcqex_updatesocial:
if (cls.qex)
{ //svcqex_updatesocial
//both ints are -1 for lan/direct clients, and 0 for the host. I guess this is for chatting to people via steam.
/*slot =*/ MSG_ReadByte();
/*??? =*/ MSG_ReadLong();
/*??? =*/ MSG_ReadLong();
break;
}
goto badsvc;
case svcqex_updateplinfo:
if (cls.qex)
{ //svcqex_updateplinfo
unsigned int slot = MSG_ReadByte();
int health = MSG_ReadSignedQEX();
int armour = MSG_ReadSignedQEX();
if (slot < MAX_CLIENTS)
{
InfoBuf_SetValueForKey(&cl.players[slot].userinfo, "health", va("%i", health));
InfoBuf_SetValueForKey(&cl.players[slot].userinfo, "health", va("%i", armour));
}
break;
}
goto badsvc;
case svcqex_print:
if (cls.qex)
{ //svcqex_'raw'print
goto svcprint;
}
goto badsvc;
} }
} }

View file

@ -474,6 +474,9 @@ typedef struct
#define CPNQ_IS_DP (cls.protocol_nq >= CPNQ_DP5) #define CPNQ_IS_DP (cls.protocol_nq >= CPNQ_DP5)
#define CPNQ_IS_BJP (cls.protocol_nq >= CPNQ_BJP1 && cls.protocol_nq <= CPNQ_BJP3) #define CPNQ_IS_BJP (cls.protocol_nq >= CPNQ_BJP1 && cls.protocol_nq <= CPNQ_BJP3)
qboolean proquake_angles_hack; //angles are always 16bit qboolean proquake_angles_hack; //angles are always 16bit
#ifdef NQPROT
qboolean qex; //we're connected to a QuakeEx server, which means lots of special workarounds that are not controlled via the actual protocol version.
#endif
int protocol_q2; int protocol_q2;
@ -1144,7 +1147,6 @@ void CL_SaveInfo(vfsfile_t *f);
void CL_SetInfo (int pnum, const char *key, const char *value); void CL_SetInfo (int pnum, const char *key, const char *value);
void CL_SetInfoBlob (int pnum, const char *key, const char *value, size_t valuesize); void CL_SetInfoBlob (int pnum, const char *key, const char *value, size_t valuesize);
void CL_BeginServerConnect(const char *host, int port, qboolean noproxy);
char *CL_TryingToConnect(void); char *CL_TryingToConnect(void);
void CL_ExecInitialConfigs(char *defaultexec); void CL_ExecInitialConfigs(char *defaultexec);

View file

@ -6090,7 +6090,7 @@ static int PScript_ParticleTrail (vec3_t startpos, vec3_t end, int type, float t
// issues with entities in action during particle reconfiguration // issues with entities in action during particle reconfiguration
// but that shouldn't be happening too often // but that shouldn't be happening too often
trailstate_t* ts = P_FetchTrailstate(tk); trailstate_t* ts = P_FetchTrailstate(tk);
return fallback->ParticleTrail(startpos, end, type - FALLBACKBIAS, timeinterval, dlkey, axis, &(ts->fallbackkey)); return fallback->ParticleTrail(startpos, end, type - FALLBACKBIAS, timeinterval, dlkey, axis, ts?&(ts->fallbackkey):NULL);
} }
if (type < 0 || type >= numparticletypes) if (type < 0 || type >= numparticletypes)

View file

@ -3374,13 +3374,13 @@ void Sbar_DeathmatchOverlay (playerview_t *pv, int start)
cl.last_ping_request = realtime; cl.last_ping_request = realtime;
CL_SendClientCommand(true, "pings"); CL_SendClientCommand(true, "pings");
} }
else if (cls.protocol == CP_NETQUAKE) else if (cls.protocol == CP_NETQUAKE && !cls.qex)
{ {
cl.last_ping_request = realtime; cl.last_ping_request = realtime;
CL_SendClientCommand(true, "ping"); CL_SendClientCommand(true, "ping");
} }
} }
if (cls.protocol == CP_NETQUAKE) if (cls.protocol == CP_NETQUAKE && !cls.qex)
{ {
if (cl.nqplayernamechanged && cl.nqplayernamechanged < realtime) if (cl.nqplayernamechanged && cl.nqplayernamechanged < realtime)
{ {

View file

@ -3882,7 +3882,7 @@ void CL_Say (qboolean team, char *extra)
//messagemode always adds quotes. the console command never did. //messagemode always adds quotes. the console command never did.
//the server is expected to use Cmd_Args and to strip first+last chars if the first is a quote. this is annoying and clumsy for mods to parse. //the server is expected to use Cmd_Args and to strip first+last chars if the first is a quote. this is annoying and clumsy for mods to parse.
#ifdef HAVE_LEGACY #ifdef HAVE_LEGACY
if (!dpcompat_console.ival) if (!dpcompat_console.ival && !cls.qex)
CL_SendSeatClientCommand(true, split, "%s \"%s%s\"", team ? "say_team" : "say", extra?extra:"", sendtext); CL_SendSeatClientCommand(true, split, "%s \"%s%s\"", team ? "say_team" : "say", extra?extra:"", sendtext);
else else
#endif #endif

View file

@ -2032,6 +2032,14 @@ quint64_t MSG_ReadULEB128 (void)
} }
return r; return r;
} }
qint64_t MSG_ReadSignedQEX (void)
{ //this is not signed leb128 (which would normally just sign-extend)
quint64_t c = MSG_ReadULEB128();
if (c&1)
return -1-(qint64_t)(c>>1);
else
return (qint64_t)(c>>1);
}
quint64_t MSG_ReadUInt64 (void) quint64_t MSG_ReadUInt64 (void)
{ //0* 10*,*, 110*,*,* etc, up to 0xff followed by 8 continuation bytes { //0* 10*,*, 110*,*,* etc, up to 0xff followed by 8 continuation bytes
qbyte l=0x80, v, b = 0; qbyte l=0x80, v, b = 0;

View file

@ -356,6 +356,7 @@ qint64_t MSG_ReadInt64 (void);
quint64_t MSG_ReadUInt64 (void); quint64_t MSG_ReadUInt64 (void);
qint64_t MSG_ReadSLEB128 (void); qint64_t MSG_ReadSLEB128 (void);
quint64_t MSG_ReadULEB128 (void); quint64_t MSG_ReadULEB128 (void);
qint64_t MSG_ReadSignedQEX (void);
struct client_s; struct client_s;
unsigned int MSGSV_ReadEntity (struct client_s *fromclient); unsigned int MSGSV_ReadEntity (struct client_s *fromclient);
unsigned int MSGCL_ReadEntity (void); unsigned int MSGCL_ReadEntity (void);
@ -1002,11 +1003,11 @@ qboolean IPLog_Merge_File(const char *fname);
#endif #endif
enum certlog_problem_e enum certlog_problem_e
{ {
CERTLOG_WRONGHOST, CERTLOG_WRONGHOST =1<<0,
CERTLOG_EXPIRED, CERTLOG_EXPIRED =1<<1,
CERTLOG_MISSINGCA, CERTLOG_MISSINGCA =1<<2,
CERTLOG_UNKNOWN, CERTLOG_UNKNOWN =1<<3,
}; };
qboolean CertLog_ConnectOkay(const char *hostname, void *cert, size_t certsize, unsigned int certlogproblems); qboolean CertLog_ConnectOkay(const char *hostname, void *cert, size_t certsize, unsigned int certlogproblems);

View file

@ -293,6 +293,52 @@ vfsfile_t *FS_DecompressGZip(vfsfile_t *infile, vfsfile_t *outfile)
size_t ZLib_DecompressBuffer(qbyte *in, size_t insize, qbyte *out, size_t maxoutsize)
{
int ret;
z_stream strm = {
in,
insize,
0,
out,
maxoutsize,
0,
NULL,
NULL,
NULL,
NULL,
NULL,
Z_UNKNOWN,
0,
0
};
qinflateInit2(&strm, MAX_WBITS);
while ((ret=qinflate(&strm, Z_SYNC_FLUSH)) != Z_STREAM_END)
{
if (strm.avail_in == 0 || strm.avail_out == 0)
{
if (strm.avail_in == 0)
break; //reached the end of the input
//output not big enough, but max size is fixed.
break;
}
//doh, it terminated for no reason
if (ret != Z_STREAM_END)
break;
}
qinflateEnd(&strm);
return strm.total_out;
}

View file

@ -328,6 +328,7 @@ void Huff_EmitByte(int ch, qbyte *buffer, int *count);
#define NETFLAG_NAK 0x00040000 #define NETFLAG_NAK 0x00040000
#define NETFLAG_EOM 0x00080000 #define NETFLAG_EOM 0x00080000
#define NETFLAG_UNRELIABLE 0x00100000 #define NETFLAG_UNRELIABLE 0x00100000
#define NETFLAG_ZLIB 0x00200000 //QEx - payload contains the real (full) packet
#define NETFLAG_CTL 0x80000000 #define NETFLAG_CTL 0x80000000
#define NQ_NETCHAN_GAMENAME "QUAKE" #define NQ_NETCHAN_GAMENAME "QUAKE"

View file

@ -443,6 +443,7 @@ qboolean ServerPaused(void);
#endif #endif
#ifdef NQPROT #ifdef NQPROT
size_t ZLib_DecompressBuffer(qbyte *in, size_t insize, qbyte *out, size_t maxoutsize);
enum nqnc_packettype_e NQNetChan_Process(netchan_t *chan) enum nqnc_packettype_e NQNetChan_Process(netchan_t *chan)
{ {
int header; int header;
@ -453,12 +454,44 @@ enum nqnc_packettype_e NQNetChan_Process(netchan_t *chan)
MSG_BeginReading (chan->netprim); MSG_BeginReading (chan->netprim);
header = LongSwap(MSG_ReadLong()); header = LongSwap(MSG_ReadLong());
if (net_message.cursize != (header & NETFLAG_LENGTH_MASK))
return NQNC_IGNORED; //size was wrong, couldn't have been ours.
if (header & NETFLAG_CTL) if (header & NETFLAG_CTL)
return NQNC_IGNORED; //huh? return NQNC_IGNORED; //huh?
#ifdef HAVE_CLIENT
if (header & NETFLAG_ZLIB)
{ //note: qex gets the size header wrong here.
qbyte *tmp;
if (net_message.cursize <= PACKET_HEADER || net_message.cursize != PACKET_HEADER+(header & NETFLAG_LENGTH_MASK))
return NQNC_IGNORED; //huh?
/*redundantsequence =*/ MSG_ReadLong(); //wasting 4 bytes...
#ifdef AVAIL_ZLIB
tmp = alloca(0xffff);
//note: its zlib rather than raw deflate (wasting a further 6 bytes...).
net_message.cursize = ZLib_DecompressBuffer(net_message.data+8, net_message.cursize-8, tmp, 0xffff);
if (net_message.cursize < PACKET_HEADER)
#endif
{
if (chan->sock == NS_CLIENT)
{ //clients can just throw an error. the server will appear dead if we try to just ignore it.
Host_EndGame("QuakeEx netchan decompression error");
return NQNC_IGNORED;
}
else
{ //inject a disconnect request. clients shouldn't be sending this anyway.
net_message.data[8] = clc_disconnect;
net_message.cursize = 9;
return NQNC_RELIABLE;
}
}
memcpy(net_message.data, tmp, net_message.cursize);
MSG_BeginReading (chan->netprim);
header = LongSwap(MSG_ReadLong()); //re-read the now-decompressed copy of the header for the real flags
}
#endif
if (net_message.cursize != (header & NETFLAG_LENGTH_MASK))
return NQNC_IGNORED; //size was wrong, couldn't have been ours.
sequence = LongSwap(MSG_ReadLong()); sequence = LongSwap(MSG_ReadLong());
if (header & NETFLAG_ACK) if (header & NETFLAG_ACK)

View file

@ -348,7 +348,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// 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) // 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 // however we still need some special case svcs
// (there's also some problematic c2s differences too) // (there's also some problematic c2s differences too)
#define svcqex_updateping 46 #define svcqex_updateping 46 // [byte] slot, [signed_qe] ping
#define svcqex_updatesocial 47 // [byte] slot, 8 bytes of unknown
#define svcqex_updateplinfo 48 // [byte] slot, [leb128] health, [leb128] armour
#define svcqex_print 49 // identical to svc_print, except not broken by qex.
#define svcqex_servervars 50 // [leb128] changedvalues, [???] value... #define svcqex_servervars 50 // [leb128] changedvalues, [???] value...
#define svcqex_seq 51 // [leb128] input sequence ack #define svcqex_seq 51 // [leb128] input sequence ack
#define svcqex_achievement 52 // [string] codename #define svcqex_achievement 52 // [string] codename
@ -791,6 +794,10 @@ enum {
#define FITZSU_UNUSED30 (1<<30) #define FITZSU_UNUSED30 (1<<30)
#define SU_EXTEND3 (1<<31) // another byte to follow, future expansion #define SU_EXTEND3 (1<<31) // another byte to follow, future expansion
//builds on top of fitz
#define QEX_SU_FLOATCOORDS (1<<8)
#define QEX_SU_ENTFLAGS (1<<26) // ULEB128 copy of the player's .flags field
// first extend byte // first extend byte
#define DPSU_PUNCHVEC1 (1<<16) #define DPSU_PUNCHVEC1 (1<<16)
#define DPSU_PUNCHVEC2 (1<<17) #define DPSU_PUNCHVEC2 (1<<17)
@ -852,11 +859,23 @@ enum {
#define DPU_UNUSED30 (1<<30) // future expansion #define DPU_UNUSED30 (1<<30) // future expansion
#define DPU_EXTEND3 (1<<31) // another byte to follow, future expansion #define DPU_EXTEND3 (1<<31) // another byte to follow, future expansion
#define FITZU_ALPHA (1<<16) #define FITZU_ALPHA (1<<16)
#define FITZU_FRAME2 (1<<17) #define FITZU_FRAME2 (1<<17)
#define FITZU_MODEL2 (1<<18) #define FITZU_MODEL2 (1<<18)
#define FITZU_LERPFINISH (1<<19) #define FITZU_LERPFINISH (1<<19)
#define RMQU_SCALE (1<<20) #define RMQU_SCALE (1<<20)
#define QE_U_FLOATCOORDS (1<<21) //set on the local player entity, to boost precision for prediction.
#define QE_U_SOLIDTYPE (1<<22) //for prediction I suppose.
//#define QE_U_EXTEND (1<<23)
#define QE_U_ENTFLAGS (1<<24) //not sure why this needs to be networked, oh well. redundant with clientdata
#define QE_U_HEALTH (1<<25) //not sure why this needs to be networked, oh well.
#define QE_U_UNKNOWN26 (1<<26) //seems to be some sort of nodraw flag (presumably for solid bmodels that need a modelindex for collisions).
#define QE_U_UNUSED27 (1<<27)
#define QE_U_UNUSED28 (1<<28)
#define QE_U_UNUSED29 (1<<29)
#define QE_U_UNUSED30 (1<<30)
#define QE_U_UNUSED31 (1u<<31)
#endif #endif
@ -903,6 +922,22 @@ enum {
#define Q2UX_UNUSED (Q2UX_UNUSED1|Q2UX_UNUSED2|Q2UX_UNUSED3|Q2UX_UNUSED4) #define Q2UX_UNUSED (Q2UX_UNUSED1|Q2UX_UNUSED2|Q2UX_UNUSED3|Q2UX_UNUSED4)
//QuakeEx-specific stuff
//gamevar info
#define QEX_GV_DEATHMATCH (1<<0)
#define QEX_GV_IDEALPITCHSCALE (1<<1)
#define QEX_GV_FRICTION (1<<2)
#define QEX_GV_EDGEFRICTION (1<<3)
#define QEX_GV_STOPSPEED (1<<4)
#define QEX_GV_MAXVELOCITY (1<<5)
#define QEX_GV_GRAVITY (1<<6)
#define QEX_GV_NOSTEP (1<<7)
#define QEX_GV_MAXSPEED (1<<8)
#define QEX_GV_ACCELERATE (1<<9)
#define QEX_GV_CONTROLLERONLY (1<<10)
#define QEX_GV_TIMELIMIT (1<<11)
#define QEX_GV_FRAGLIMIT (1<<12)
#define QEX_GV_ALL ((1<<13)-1)
//============================================== //==============================================
//obsolete demo players info //obsolete demo players info
@ -955,6 +990,12 @@ enum {
#define FITZ_B_ALPHA (1<<2) #define FITZ_B_ALPHA (1<<2)
#define RMQFITZ_B_SCALE (1<<3) #define RMQFITZ_B_SCALE (1<<3)
#define QEX_B_SOLID (1<<3)
#define QEX_B_UNKNOWN4 (1<<4)
#define QEX_B_UNKNOWN5 (1<<5)
#define QEX_B_UNKNOWN6 (1<<6)
#define QEX_B_UNKNOWN7 (1<<7)
#define DEFAULT_VIEWHEIGHT 22 #define DEFAULT_VIEWHEIGHT 22

View file

@ -698,7 +698,7 @@ typedef struct client_s
enum serverprotocols_e protocol; enum serverprotocols_e protocol;
unsigned int supportedprotocols; unsigned int supportedprotocols;
qboolean proquake_angles_hack; //expect 16bit client->server angles . qboolean proquake_angles_hack; //expect 16bit client->server angles .
qboolean qex_input_hack; //qex sends strange clc inputs and needs workarounds for its prediction. qboolean qex; //qex sends strange clc inputs and needs workarounds for its prediction. it also favours fitzquake's protocol but violates parts of it.
unsigned int lastruncmd; //for non-qw physics. timestamp they were last run, so switching between physics modes isn't a (significant) cheat 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 //speed cheat testing

View file

@ -2246,7 +2246,7 @@ static void SV_Status_f (void)
#define C_DROP COLUMN(6, "drop", Con_Printf("%4.1f ", 100.0*cl->netchan.drop_count / cl->netchan.incoming_sequence)) #define C_DROP COLUMN(6, "drop", Con_Printf("%4.1f ", 100.0*cl->netchan.drop_count / cl->netchan.incoming_sequence))
#define C_DLP COLUMN(7, "dl ", if (!cl->download)Con_Printf(" ");else Con_Printf("%3g ", (cl->downloadcount*100.0)/cl->downloadsize)) #define C_DLP COLUMN(7, "dl ", if (!cl->download)Con_Printf(" ");else Con_Printf("%3g ", (cl->downloadcount*100.0)/cl->downloadsize))
#define C_DLS COLUMN(8, "dls", if (!cl->download)Con_Printf(" ");else Con_Printf("%3u ", (unsigned int)(cl->downloadsize/1024))) #define C_DLS COLUMN(8, "dls", if (!cl->download)Con_Printf(" ");else Con_Printf("%3u ", (unsigned int)(cl->downloadsize/1024)))
#define C_PROT COLUMN(9, "prot", Con_Printf("%-5.5s", p)) #define C_PROT COLUMN(9, "prot ", Con_Printf("%-6.6s", p))
#define C_ADDRESS2 COLUMN(10, "address ", Con_Printf("%s", s)) #define C_ADDRESS2 COLUMN(10, "address ", Con_Printf("%s", s))
int columns = (1<<6)-1; int columns = (1<<6)-1;
@ -2305,12 +2305,12 @@ static void SV_Status_f (void)
case SCP_BAD: case SCP_BAD:
p = ""; p = "";
break; break;
case SCP_QUAKEWORLD: p = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?"fteq":"qw"; break; case SCP_QUAKEWORLD: p = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?"fteqw":"qw"; break;
case SCP_QUAKE2: p = "q2"; break; case SCP_QUAKE2: p = "q2"; break;
case SCP_QUAKE3: p = "q3"; break; case SCP_QUAKE3: p = "q3"; break;
case SCP_NETQUAKE: p = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?"ften":(cl->qex_input_hack?"qe15":"nq"); break; case SCP_NETQUAKE: p = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?"ftenq":(cl->qex?"qe15":"nq"); break;
case SCP_BJP3: p = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?"ften":"bjp3"; break; case SCP_BJP3: p = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?"ftenq":"bjp3"; break;
case SCP_FITZ666: p = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?"ften":"fitz"; break; case SCP_FITZ666: p = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?"ftenq":(cl->qex?"qe666":"fitz"); break;
case SCP_DARKPLACES6: p = "dpp6"; break; case SCP_DARKPLACES6: p = "dpp6"; break;
case SCP_DARKPLACES7: p = "dpp7"; break; case SCP_DARKPLACES7: p = "dpp7"; break;
} }

View file

@ -3042,6 +3042,23 @@ int glowsize=0, glowcolour=0, colourmod=0;
if (baseline->dpflags & RENDER_STEP) if (baseline->dpflags & RENDER_STEP)
bits |= FITZU_LERPFINISH; bits |= FITZU_LERPFINISH;
if (host_client->qex)
{
if (host_client->edict == ed)
{ //only send some bloated things to yourself.
bits |= QE_U_FLOATCOORDS; //when predicting, you'll need more precision to avoid errors
if (ed->v->flags)
bits |= QE_U_ENTFLAGS;
}
if (ed->v->solid)
bits |= QE_U_SOLIDTYPE;
if (ed->v->health)
bits |= QE_U_HEALTH;
//bits |= QE_U_UNKNOWN26;
}
} }
else if (host_client->protocol == SCP_BJP3) else if (host_client->protocol == SCP_BJP3)
{ {
@ -3126,12 +3143,24 @@ int glowsize=0, glowcolour=0, colourmod=0;
if (bits & NQU_COLORMAP) MSG_WriteByte (msg, ent->colormap & 0xff); if (bits & NQU_COLORMAP) MSG_WriteByte (msg, ent->colormap & 0xff);
if (bits & NQU_SKIN) MSG_WriteByte (msg, ent->skinnum & 0xff); if (bits & NQU_SKIN) MSG_WriteByte (msg, ent->skinnum & 0xff);
if (bits & NQU_EFFECTS) MSG_WriteByte (msg, eff & 0x00ff); if (bits & NQU_EFFECTS) MSG_WriteByte (msg, eff & 0x00ff);
if (bits & NQU_ORIGIN1) MSG_WriteCoord (msg, ent->origin[0]); if (host_client->qex && (bits & QE_U_FLOATCOORDS))
if (bits & NQU_ANGLE1) MSG_WriteAngle(msg, ent->angles[0]); {
if (bits & NQU_ORIGIN2) MSG_WriteCoord (msg, ent->origin[1]); if (bits & NQU_ORIGIN1) MSG_WriteFloat (msg, ent->origin[0]);
if (bits & NQU_ANGLE2) MSG_WriteAngle(msg, ent->angles[1]); if (bits & NQU_ANGLE1) MSG_WriteAngle (msg, ent->angles[0]);
if (bits & NQU_ORIGIN3) MSG_WriteCoord (msg, ent->origin[2]); if (bits & NQU_ORIGIN2) MSG_WriteFloat (msg, ent->origin[1]);
if (bits & NQU_ANGLE3) MSG_WriteAngle(msg, ent->angles[2]); if (bits & NQU_ANGLE2) MSG_WriteAngle (msg, ent->angles[1]);
if (bits & NQU_ORIGIN3) MSG_WriteFloat (msg, ent->origin[2]);
if (bits & NQU_ANGLE3) MSG_WriteAngle (msg, ent->angles[2]);
}
else
{
if (bits & NQU_ORIGIN1) MSG_WriteCoord (msg, ent->origin[0]);
if (bits & NQU_ANGLE1) MSG_WriteAngle (msg, ent->angles[0]);
if (bits & NQU_ORIGIN2) MSG_WriteCoord (msg, ent->origin[1]);
if (bits & NQU_ANGLE2) MSG_WriteAngle (msg, ent->angles[1]);
if (bits & NQU_ORIGIN3) MSG_WriteCoord (msg, ent->origin[2]);
if (bits & NQU_ANGLE3) MSG_WriteAngle (msg, ent->angles[2]);
}
if (host_client->protocol == SCP_FITZ666) if (host_client->protocol == SCP_FITZ666)
{ {
@ -3140,6 +3169,14 @@ int glowsize=0, glowcolour=0, colourmod=0;
if (bits & FITZU_FRAME2) MSG_WriteByte(msg, ent->frame>>8); if (bits & FITZU_FRAME2) MSG_WriteByte(msg, ent->frame>>8);
if (bits & FITZU_MODEL2) MSG_WriteByte(msg, ent->modelindex>>8); if (bits & FITZU_MODEL2) MSG_WriteByte(msg, ent->modelindex>>8);
if (bits & FITZU_LERPFINISH)MSG_WriteByte(msg, bound(0, (int)((ed->v->nextthink - sv.world.physicstime) * 255), 255)); if (bits & FITZU_LERPFINISH)MSG_WriteByte(msg, bound(0, (int)((ed->v->nextthink - sv.world.physicstime) * 255), 255));
if (host_client->qex)
{
if (bits & QE_U_SOLIDTYPE) MSG_WriteByte(msg, ed->v->solid);
if (bits & QE_U_ENTFLAGS) MSG_WriteULEB128(msg, ed->v->flags);
if (bits & QE_U_HEALTH) MSG_WriteSignedQEX(msg, ed->v->health);
if (bits & QE_U_UNKNOWN26) MSG_WriteByte(msg, 0);
}
} }
else if (host_client->protocol == SCP_BJP3) else if (host_client->protocol == SCP_BJP3)
{ {

View file

@ -446,17 +446,35 @@ void SV_FinalMessage (char *message)
buf.data = bufdata; buf.data = bufdata;
buf.maxsize = sizeof(bufdata); buf.maxsize = sizeof(bufdata);
SZ_Clear (&buf);
MSG_WriteByte (&buf, svc_print);
MSG_WriteByte (&buf, PRINT_HIGH);
MSG_WriteString (&buf, message);
MSG_WriteByte (&buf, svc_disconnect);
for (i=0, cl = svs.clients ; i<svs.allocated_client_slots ; i++, cl++) for (i=0, cl = svs.clients ; i<svs.allocated_client_slots ; i++, cl++)
if (cl->state >= cs_spawned && !cl->controlled) if (cl->state >= cs_spawned && !cl->controlled)
if (ISNQCLIENT(cl) || ISQWCLIENT(cl)) {
Netchan_Transmit (&cl->netchan, buf.cursize if (ISQWCLIENT(cl))
, buf.data, 10000); {
SZ_Clear (&buf);
MSG_WriteByte (&buf, svc_print);
MSG_WriteByte (&buf, PRINT_HIGH);
MSG_WriteString (&buf, message);
MSG_WriteByte (&buf, svc_disconnect);
}
#ifdef NQPROT
else if (ISNQCLIENT(cl))
{
SZ_Clear (&buf);
if (cl->qex && cl->protocol != SCP_NETQUAKE)
MSG_WriteByte (&buf, svcqex_print); //urgh, ffs.
else
MSG_WriteByte (&buf, svc_print);
MSG_WriteString (&buf, message);
MSG_WriteByte (&buf, svc_disconnect);
}
#endif
else
continue;
Netchan_Transmit (&cl->netchan, buf.cursize
, buf.data, 10000);
}
} }
@ -1883,13 +1901,18 @@ void SV_AcceptMessage(client_t *newcl)
SZ_Clear(&sb); SZ_Clear(&sb);
MSG_WriteLong(&sb, 0); MSG_WriteLong(&sb, 0);
MSG_WriteByte(&sb, CCREP_ACCEPT); MSG_WriteByte(&sb, CCREP_ACCEPT);
NET_LocalAddressForRemote(svs.sockets, &net_from, &localaddr, 0); if (newcl->qex)
MSG_WriteLong(&sb, ShortSwap(localaddr.port)); ; //skip any port info (as well as any proquake ident stuff.
if (newcl->proquake_angles_hack) else
{ {
MSG_WriteByte(&sb, MOD_PROQUAKE); NET_LocalAddressForRemote(svs.sockets, &net_from, &localaddr, 0);
MSG_WriteByte(&sb, MOD_PROQUAKE_VERSION); MSG_WriteLong(&sb, ShortSwap(localaddr.port));
MSG_WriteByte(&sb, 0/*flags*/); if (newcl->proquake_angles_hack)
{
MSG_WriteByte(&sb, MOD_PROQUAKE);
MSG_WriteByte(&sb, MOD_PROQUAKE_VERSION);
MSG_WriteByte(&sb, 0/*flags*/);
}
} }
*(int*)sb.data = BigLong(NETFLAG_CTL|sb.cursize); *(int*)sb.data = BigLong(NETFLAG_CTL|sb.cursize);
NET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from); NET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);
@ -2110,7 +2133,7 @@ void SV_ClientProtocolExtensionsChanged(client_t *client)
client->datagram.maxsize = sizeof(host_client->datagram_buf); client->datagram.maxsize = sizeof(host_client->datagram_buf);
} }
else if (client->qex_input_hack) else if (client->qex)
{ {
client->max_net_clients = NQMAX_CLIENTS; client->max_net_clients = NQMAX_CLIENTS;
client->datagram.maxsize = sizeof(host_client->datagram_buf); client->datagram.maxsize = sizeof(host_client->datagram_buf);
@ -2570,7 +2593,7 @@ void SV_DoDirectConnect(svconnectinfo_t *fte_restrict info)
} }
newcl->supportedprotocols = info->supportedprotocols; newcl->supportedprotocols = info->supportedprotocols;
newcl->proquake_angles_hack = info->proquakeanglehack; newcl->proquake_angles_hack = info->proquakeanglehack;
newcl->qex_input_hack = info->isqex; newcl->qex = info->isqex;
#endif #endif
newcl->userid = ++nextuserid; newcl->userid = ++nextuserid;
@ -3404,7 +3427,7 @@ void SVC_DirectConnect(int expectedreliablesequence)
} }
else if (version == NQ_NETCHAN_VERSION_QEX) else if (version == NQ_NETCHAN_VERSION_QEX)
{ //rerelease... { //rerelease...
info.protocol = SCP_NETQUAKE; info.protocol = SCP_FITZ666;//NETQUAKE;
info.isqex = true; info.isqex = true;
} }
#endif #endif
@ -4031,7 +4054,7 @@ qboolean SV_ConnectionlessPacket (void)
c = Cmd_Argv(0); c = Cmd_Argv(0);
if (sv_showconnectionlessmessages.ival) if (sv_showconnectionlessmessages.ival)
Con_Printf("%s: %s\n", NET_AdrToString (adr, sizeof(adr), &net_from), s); Con_Printf(S_COLOR_GRAY"%s: %s\n", NET_AdrToString (adr, sizeof(adr), &net_from), s);
if (!strcmp(c, "ping") || ( c[0] == A2A_PING && (c[1] == 0 || c[1] == '\n')) ) if (!strcmp(c, "ping") || ( c[0] == A2A_PING && (c[1] == 0 || c[1] == '\n')) )
{ //only continue respond to these if we're actually public. qwfwd likes spamming us endlessly even if we stop heartbeating (which leaves us discoverable to others, too). { //only continue respond to these if we're actually public. qwfwd likes spamming us endlessly even if we stop heartbeating (which leaves us discoverable to others, too).
@ -4253,7 +4276,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
if (msg_readcount+17 <= net_message.cursize && !strncmp("challengeconnect ", &net_message.data[msg_readcount], 17)) if (msg_readcount+17 <= net_message.cursize && !strncmp("challengeconnect ", &net_message.data[msg_readcount], 17))
{ {
if (sv_showconnectionlessmessages.ival) if (sv_showconnectionlessmessages.ival)
Con_Printf("%s: CCREQ_CONNECT_COOKIE\n", NET_AdrToString (com_token, sizeof(com_token), &net_from)); Con_Printf(S_COLOR_GRAY"%s: CCREQ_CONNECT_COOKIE\n", NET_AdrToString (com_token, sizeof(com_token), &net_from));
Cmd_TokenizeString(MSG_ReadStringLine(), false, false); Cmd_TokenizeString(MSG_ReadStringLine(), false, false);
/*okay, so this is a reliable packet from a client, containing a 'cmd challengeconnect $challenge' response*/ /*okay, so this is a reliable packet from a client, containing a 'cmd challengeconnect $challenge' response*/
str = va("connect %i %i %s \"\\name\\unconnected\\mod\\%s\\modver\\%s\\flags\\%s\\password\\%s\"", NQ_NETCHAN_VERSION, 0, Cmd_Argv(1), Cmd_Argv(2), Cmd_Argv(3), Cmd_Argv(4), Cmd_Argv(5)); str = va("connect %i %i %s \"\\name\\unconnected\\mod\\%s\\modver\\%s\\flags\\%s\\password\\%s\"", NQ_NETCHAN_VERSION, 0, Cmd_Argv(1), Cmd_Argv(2), Cmd_Argv(3), Cmd_Argv(4), Cmd_Argv(5));
@ -4323,7 +4346,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
protver = MSG_ReadByte(); protver = MSG_ReadByte();
if (sv_showconnectionlessmessages.ival) if (sv_showconnectionlessmessages.ival)
Con_Printf("%s: CCREQ_CONNECT (\"%s\" %i)\n", NET_AdrToString (com_token, sizeof(com_token), &net_from), str, protver); Con_Printf(S_COLOR_GRAY"%s: CCREQ_CONNECT (\"%s\" %i)\n", NET_AdrToString (com_token, sizeof(com_token), &net_from), str, protver);
sb.maxsize = sizeof(buffer); sb.maxsize = sizeof(buffer);
sb.data = buffer; sb.data = buffer;
@ -4420,7 +4443,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
return true; return true;
case CCREQ_SERVER_INFO: case CCREQ_SERVER_INFO:
if (sv_showconnectionlessmessages.ival) if (sv_showconnectionlessmessages.ival)
Con_Printf("%s: CCREQ_SERVER_INFO\n", NET_AdrToString (com_token, sizeof(com_token), &net_from)); Con_Printf(S_COLOR_GRAY"%s: CCREQ_SERVER_INFO\n", NET_AdrToString (com_token, sizeof(com_token), &net_from));
if (sv_public.ival < 0) if (sv_public.ival < 0)
return false; return false;
if (SV_BannedReason (&net_from)) if (SV_BannedReason (&net_from))
@ -4452,7 +4475,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
return true; return true;
case CCREQ_PLAYER_INFO: case CCREQ_PLAYER_INFO:
if (sv_showconnectionlessmessages.ival) if (sv_showconnectionlessmessages.ival)
Con_Printf("%s: CCREQ_PLAYER_INFO\n", NET_AdrToString (com_token, sizeof(com_token), &net_from)); Con_Printf(S_COLOR_GRAY"%s: CCREQ_PLAYER_INFO\n", NET_AdrToString (com_token, sizeof(com_token), &net_from));
if (sv_public.ival < 0) if (sv_public.ival < 0)
return false; return false;
if (SV_BannedReason (&net_from)) if (SV_BannedReason (&net_from))
@ -4486,7 +4509,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
return true; return true;
case CCREQ_RULE_INFO: case CCREQ_RULE_INFO:
if (sv_showconnectionlessmessages.ival) if (sv_showconnectionlessmessages.ival)
Con_Printf("%s: CCREQ_RULE_INFO\n", NET_AdrToString (com_token, sizeof(com_token), &net_from)); Con_Printf(S_COLOR_GRAY"%s: CCREQ_RULE_INFO\n", NET_AdrToString (com_token, sizeof(com_token), &net_from));
if (sv_public.ival < 0) if (sv_public.ival < 0)
return false; return false;
if (SV_BannedReason (&net_from)) if (SV_BannedReason (&net_from))
@ -4781,7 +4804,7 @@ dominping:
// packet is not from a known client // packet is not from a known client
if (sv_showconnectionlessmessages.ival) if (sv_showconnectionlessmessages.ival)
Con_Printf ("%s:sequenced packet without connection\n", NET_AdrToString (com_token, sizeof(com_token), &net_from)); //hack: com_token cos we need some random temp buffer. Con_Printf (S_COLOR_GRAY "%s:sequenced packet without connection\n", NET_AdrToString (com_token, sizeof(com_token), &net_from)); //hack: com_token cos we need some random temp buffer.
} }
/* /*

View file

@ -184,7 +184,10 @@ void SV_PrintToClient(client_t *cl, int level, const char *string)
case SCP_BJP3: case SCP_BJP3:
case SCP_FITZ666: case SCP_FITZ666:
#ifdef NQPROT #ifdef NQPROT
ClientReliableWrite_Begin (cl, svc_print, strlen(string)+3); if (cl->qex && cl->protocol != SCP_NETQUAKE)
ClientReliableWrite_Begin (cl, svcqex_print, strlen(string)+3); //urgh!
else
ClientReliableWrite_Begin (cl, svc_print, strlen(string)+3);
if (level == PRINT_CHAT) if (level == PRINT_CHAT)
ClientReliableWrite_Byte (cl, 1); ClientReliableWrite_Byte (cl, 1);
ClientReliableWrite_String (cl, string); ClientReliableWrite_String (cl, string);
@ -1594,7 +1597,13 @@ void SV_WriteCenterPrint(client_t *cl, char *s)
} }
else else
{ {
ClientReliableWrite_Begin (cl, svc_centerprint, 2 + strlen(s)); if (cl->qex && cl->protocol != SCP_NETQUAKE)
{
ClientReliableWrite_Begin (cl, svc_centerprint, 4 + strlen(s));
ClientReliableWrite_Short (cl, 1);
}
else
ClientReliableWrite_Begin (cl, svc_centerprint, 2 + strlen(s));
} }
ClientReliableWrite_String (cl, s); ClientReliableWrite_String (cl, s);
@ -1691,7 +1700,7 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg)
if (client->fteprotocolextensions2 & PEXT2_PREDINFO) if (client->fteprotocolextensions2 & PEXT2_PREDINFO)
MSG_WriteShort(msg, client->last_sequence); MSG_WriteShort(msg, client->last_sequence);
else if (client->qex_input_hack && client->last_sequence) else if (client->qex && client->last_sequence)
{ {
MSG_WriteByte(msg, svcqex_seq); MSG_WriteByte(msg, svcqex_seq);
MSG_WriteULEB128(msg, client->last_sequence); MSG_WriteULEB128(msg, client->last_sequence);
@ -1779,6 +1788,14 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg)
bits |= FITZSU_WEAPONFRAME2; bits |= FITZSU_WEAPONFRAME2;
if (ent->xv->alpha && ent->xv->alpha < 1) if (ent->xv->alpha && ent->xv->alpha < 1)
bits |= FITZSU_WEAPONALPHA; bits |= FITZSU_WEAPONALPHA;
if (client->qex)
{
if (ent->v->flags)
bits |= QEX_SU_ENTFLAGS;
if (bits & (SU_VELOCITY1|SU_VELOCITY2|SU_VELOCITY3))
bits |= QEX_SU_FLOATCOORDS;
}
} }
} }
@ -1818,8 +1835,10 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg)
// Msg_WriteCoord(msg, ent->xv->punchvector); // Msg_WriteCoord(msg, ent->xv->punchvector);
if (bits & (SU_VELOCITY1<<i)) if (bits & (SU_VELOCITY1<<i))
{ {
if (client->protocol == SCP_DARKPLACES6 || client->protocol == SCP_DARKPLACES7) if (client->qex && (bits & QEX_SU_ENTFLAGS))
MSG_WriteCoord(msg, ent->v->velocity[i]); MSG_WriteFloat(msg, ent->v->velocity[i]);
else if (client->protocol == SCP_DARKPLACES6 || client->protocol == SCP_DARKPLACES7)
MSG_WriteFloat(msg, ent->v->velocity[i]);
else else
MSG_WriteChar (msg, bound(-128, ent->v->velocity[i]/16, 127)); MSG_WriteChar (msg, bound(-128, ent->v->velocity[i]/16, 127));
} }
@ -1898,6 +1917,11 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg)
if (bits & FITZSU_CELLS2) MSG_WriteByte (msg, (int)ent->v->ammo_cells >> 8); if (bits & FITZSU_CELLS2) MSG_WriteByte (msg, (int)ent->v->ammo_cells >> 8);
if (bits & FITZSU_WEAPONFRAME2) MSG_WriteByte (msg, (int)ent->v->weaponframe >> 8); if (bits & FITZSU_WEAPONFRAME2) MSG_WriteByte (msg, (int)ent->v->weaponframe >> 8);
if (bits & FITZSU_WEAPONALPHA) MSG_WriteByte (msg, ent->xv->alpha*255); if (bits & FITZSU_WEAPONALPHA) MSG_WriteByte (msg, ent->xv->alpha*255);
if (client->qex)
{
if (bits & QEX_SU_ENTFLAGS) MSG_WriteULEB128 (msg, ent->v->flags);
}
} }
#endif #endif
} }
@ -3049,7 +3073,7 @@ void SV_UpdateToReliableMessages (void)
SV_FullClientUpdate (host_client, NULL); SV_FullClientUpdate (host_client, NULL);
} }
if (host_client->qex_input_hack && sendpings) if (host_client->qex && sendpings)
{ {
sizebuf_t *m; sizebuf_t *m;
for (j=0, client = svs.clients ; j<svs.allocated_client_slots && j < host_client->max_net_clients; j++, client++) for (j=0, client = svs.clients ; j<svs.allocated_client_slots && j < host_client->max_net_clients; j++, client++)
@ -3062,6 +3086,16 @@ void SV_UpdateToReliableMessages (void)
MSG_WriteByte(m, j); MSG_WriteByte(m, j);
MSG_WriteSignedQEX(m, SV_CalcPing(client, false)); MSG_WriteSignedQEX(m, SV_CalcPing(client, false));
ClientReliable_FinishWrite(host_client); ClientReliable_FinishWrite(host_client);
if (coop.ival)
{
m = ClientReliable_StartWrite(host_client, 64);
MSG_WriteByte(m, svcqex_updateplinfo);
MSG_WriteByte(m, j);
MSG_WriteSignedQEX(m, client->edict->v->health);
MSG_WriteSignedQEX(m, client->edict->v->armorvalue);
ClientReliable_FinishWrite(host_client);
}
} }
} }

View file

@ -173,9 +173,7 @@ qboolean SV_CheckRealIP(client_t *client, qboolean force)
return true; //we know that the ip is authentic return true; //we know that the ip is authentic
if (client->realip_status == 2) if (client->realip_status == 2)
{ {
ClientReliableWrite_Begin(client, svc_print, 256); SV_PrintToClient(client, PRINT_HIGH, "Couldn't verify your real ip\n");
ClientReliableWrite_Byte(client, PRINT_HIGH);
ClientReliableWrite_String(client, "Couldn't verify your real ip\n");
return true; //client doesn't support certainty. return true; //client doesn't support certainty.
} }
if (client->realip_status == -1) if (client->realip_status == -1)
@ -183,12 +181,10 @@ qboolean SV_CheckRealIP(client_t *client, qboolean force)
if (realtime - client->connection_started > sv_realip_timeout.value) if (realtime - client->connection_started > sv_realip_timeout.value)
{ {
ClientReliableWrite_Begin(client, svc_print, 256);
ClientReliableWrite_Byte(client, PRINT_HIGH);
if (client->realip_status > 0) if (client->realip_status > 0)
ClientReliableWrite_String(client, "Couldn't verify your real ip\n"); SV_PrintToClient(client, PRINT_HIGH, "Couldn't verify your real ip\n");
else else
ClientReliableWrite_String(client, "Couldn't determine your real ip\n"); SV_PrintToClient(client, PRINT_HIGH, "Couldn't determine your real ip\n");
if (sv_realip_kick.value > host_client->realip_status) if (sv_realip_kick.value > host_client->realip_status)
{ {
client->drop = true; client->drop = true;
@ -529,7 +525,7 @@ void SVNQ_New_f (void)
if (host_client->drop) if (host_client->drop)
return; return;
if (!host_client->pextknown && sv_listen_nq.ival != 1) //1 acts as a legacy mode, used for clients that can't cope with cmd before serverdata (either because they crash out or because they refuse to send reliables until after they got the first serverdata) if (!host_client->pextknown && sv_listen_nq.ival != 1 && !host_client->qex) //1 acts as a legacy mode, used for clients that can't cope with cmd before serverdata (either because they crash out or because they refuse to send reliables until after they got the first serverdata)
{ {
if (!host_client->supportedprotocols && host_client->netchan.remote_address.type != NA_LOOPBACK) if (!host_client->supportedprotocols && host_client->netchan.remote_address.type != NA_LOOPBACK)
{ //don't override cl_loopbackprotocol's choice { //don't override cl_loopbackprotocol's choice
@ -544,6 +540,8 @@ void SVNQ_New_f (void)
} }
return; return;
} }
else
host_client->pextknown = true; //just in case.
if (dpcompat_nopreparse.ival && progstype == PROG_QW) if (dpcompat_nopreparse.ival && progstype == PROG_QW)
{ {
@ -629,12 +627,12 @@ void SVNQ_New_f (void)
{ {
protext1 &= ~PEXT_FLOATCOORDS; //never report floatcoords when using rmq protocol, as the base protocol allows us to be more specific anyway. protext1 &= ~PEXT_FLOATCOORDS; //never report floatcoords when using rmq protocol, as the base protocol allows us to be more specific anyway.
protmain = PROTOCOL_VERSION_RMQ; protmain = PROTOCOL_VERSION_RMQ;
protoname = "RMQ"; protoname = host_client->qex?"QE999":"RMQ";
} }
else else
{ {
protmain = PROTOCOL_VERSION_FITZ; protmain = PROTOCOL_VERSION_FITZ;
protoname = "666"; protoname = host_client->qex?"QE666":"666";
} }
} }
else if (host_client->protocol == SCP_BJP3) else if (host_client->protocol == SCP_BJP3)
@ -658,14 +656,13 @@ void SVNQ_New_f (void)
"!!! EXPECT MISSING MODELS, SOUNDS, OR ENTITIES\n"); "!!! EXPECT MISSING MODELS, SOUNDS, OR ENTITIES\n");
//if you're reading this message to try to avoid your client being described as shitty, implement support for 'cmd protocol' and maybe 'cmd pext' stuffcmds. //if you're reading this message to try to avoid your client being described as shitty, implement support for 'cmd protocol' and maybe 'cmd pext' stuffcmds.
//simply put, I can't use 666 if I don't know that its safe to do so. //simply put, I can't use 666 if I don't know that its safe to do so.
MSG_WriteByte (&host_client->netchan.message, svc_print); SV_PrintToClient(host_client, PRINT_HIGH, message);
MSG_WriteString (&host_client->netchan.message,message);
} }
} }
host_client->protocol = SCP_NETQUAKE; //identical other than the client->server angles host_client->protocol = SCP_NETQUAKE; //identical other than the client->server angles
protmain = PROTOCOL_VERSION_NQ; protmain = PROTOCOL_VERSION_NQ;
protoname = "NQ"; protoname = host_client->qex?"QE15":"NQ";
} }
break; break;
case SCP_DARKPLACES6: case SCP_DARKPLACES6:
@ -728,8 +725,7 @@ void SVNQ_New_f (void)
protoname,(protext1||(protext2&~PEXT2_VOICECHAT))?"+":"",(protext2&PEXT2_VOICECHAT)?"Voip":"", protoname,(protext1||(protext2&~PEXT2_VOICECHAT))?"+":"",(protext2&PEXT2_VOICECHAT)?"Voip":"",
build,gamedir, mapname); build,gamedir, mapname);
} }
MSG_WriteByte (&host_client->netchan.message, svc_print); SV_PrintToClient(host_client, PRINT_HIGH, message);
MSG_WriteString (&host_client->netchan.message,message);
} }
if (host_client->protocol == SCP_DARKPLACES6 || host_client->protocol == SCP_DARKPLACES7) if (host_client->protocol == SCP_DARKPLACES6 || host_client->protocol == SCP_DARKPLACES7)
@ -797,8 +793,9 @@ void SVNQ_New_f (void)
if (protext2 & PEXT2_PREDINFO) if (protext2 & PEXT2_PREDINFO)
MSG_WriteString(&host_client->netchan.message, gamedir); MSG_WriteString(&host_client->netchan.message, gamedir);
MSG_WriteByte (&host_client->netchan.message, (sv.allocated_client_slots>host_client->max_net_clients)?host_client->max_net_clients:sv.allocated_client_slots); MSG_WriteByte (&host_client->netchan.message, (sv.allocated_client_slots>host_client->max_net_clients)?host_client->max_net_clients:sv.allocated_client_slots);
if (host_client->qex)
MSG_WriteString(&host_client->netchan.message, gamedir);
if (!coop.value && deathmatch.value) if (!coop.value && deathmatch.value)
MSG_WriteByte (&host_client->netchan.message, GAME_DEATHMATCH); MSG_WriteByte (&host_client->netchan.message, GAME_DEATHMATCH);
@ -828,29 +825,11 @@ void SVNQ_New_f (void)
MSG_WriteByte (&host_client->netchan.message, 0); MSG_WriteByte (&host_client->netchan.message, 0);
} }
if (host_client->qex_input_hack) if (host_client->qex)
{ { //FIXME: these may change mid-map.
extern cvar_t sv_friction, sv_stopspeed, sv_maxvelocity, sv_accelerate, sv_gravity; extern cvar_t sv_friction, sv_stopspeed, sv_maxvelocity, sv_accelerate, sv_gravity;
enum unsigned int bits = QEX_GV_ALL;
{
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_WriteByte (&host_client->netchan.message, svcqex_servervars);
MSG_WriteULEB128 (&host_client->netchan.message, bits); MSG_WriteULEB128 (&host_client->netchan.message, bits);
if (bits & QEX_GV_DEATHMATCH) if (bits & QEX_GV_DEATHMATCH)
@ -6026,13 +6005,13 @@ static void SVNQ_PreSpawn_f (void)
default: default:
break; break;
case SCP_NETQUAKE: case SCP_NETQUAKE:
prot = " (nq)"; prot = host_client->qex?" (qe15)":" (nq)";
break; break;
case SCP_BJP3: case SCP_BJP3:
prot = " (bjp3)"; prot = " (bjp3)";
break; break;
case SCP_FITZ666: case SCP_FITZ666:
prot = " (fitz)"; prot = host_client->qex?" (qe666)":" (fitz)";
break; break;
case SCP_DARKPLACES6: case SCP_DARKPLACES6:
prot = " (dpp6)"; prot = " (dpp6)";
@ -8722,7 +8701,10 @@ void SVNQ_ReadClientMove (qboolean forceangle16, qboolean quakeex)
if (quakeex) if (quakeex)
{ //I'm guessing this has something to do with splitscreen. { //I'm guessing this has something to do with splitscreen.
if (MSG_ReadByte() != 1) if (MSG_ReadByte() != 1)
{
Con_Printf("Unknown byte wasn't 1\n");
msg_badread = true; msg_badread = true;
}
} }
@ -8873,7 +8855,7 @@ void SVNQ_ReadClientMove (qboolean forceangle16, qboolean quakeex)
} }
else else
{ {
if (!host_client->qex_input_hack) if (!host_client->qex)
host_client->last_sequence = 0; //let the client know that prediction is fucked, by not acking any input frames. host_client->last_sequence = 0; //let the client know that prediction is fucked, by not acking any input frames.
if (cmd.impulse) if (cmd.impulse)
host_client->edict->v->impulse = cmd.impulse; host_client->edict->v->impulse = cmd.impulse;
@ -8889,7 +8871,6 @@ void SVNQ_ExecuteClientMessage (client_t *cl)
char *s; char *s;
// client_frame_t *frame; // client_frame_t *frame;
qboolean forceangle16; qboolean forceangle16;
qboolean qex = false;
cl->netchan.outgoing_sequence++; cl->netchan.outgoing_sequence++;
cl->netchan.incoming_acknowledged = cl->netchan.outgoing_sequence-1; cl->netchan.incoming_acknowledged = cl->netchan.outgoing_sequence-1;
@ -9020,7 +9001,7 @@ void SVNQ_ExecuteClientMessage (client_t *cl)
break; break;
} }
SVNQ_ReadClientMove (forceangle16, qex); SVNQ_ReadClientMove (forceangle16, cl->qex);
// cmd = host_client->lastcmd; // cmd = host_client->lastcmd;
// SV_ClientThink(); // SV_ClientThink();
break; break;
@ -9075,7 +9056,6 @@ void SVNQ_ExecuteClientMessage (client_t *cl)
case clc_delta://clcqex_sequence: case clc_delta://clcqex_sequence:
host_client->last_sequence = MSG_ReadULEB128(); host_client->last_sequence = MSG_ReadULEB128();
qex = true;
break; break;
case clc_tmove://clcqex_auth case clc_tmove://clcqex_auth