1
0
Fork 0
forked from fte/fteqw

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;
qboolean isnehahra = CPNQ_IS_BJP||(cls.protocol_nq == CPNQ_NEHAHRA);
qboolean floatcoords;
if (cls.signon == 4 - 1)
{ // first update is the final signon stage
@ -1837,6 +1838,8 @@ void CLNQ_ParseEntity(unsigned int bits)
state->dpflags = 0;
floatcoords = cls.qex && (bits & QE_U_FLOATCOORDS);
if (bits & NQU_MODEL)
{
if (CPNQ_IS_BJP)
@ -1855,20 +1858,34 @@ void CLNQ_ParseEntity(unsigned int bits)
state->skinnum = MSG_ReadByte();
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)
state->origin[0] = MSG_ReadCoord ();
state->origin[0] = floatcoords?MSG_ReadFloat():MSG_ReadCoord ();
if (bits & NQU_ANGLE1)
state->angles[0] = MSG_ReadAngle();
if (bits & NQU_ORIGIN2)
state->origin[1] = MSG_ReadCoord ();
state->origin[1] = floatcoords?MSG_ReadFloat():MSG_ReadCoord ();
if (bits & NQU_ANGLE2)
state->angles[1] = MSG_ReadAngle();
if (bits & NQU_ORIGIN3)
state->origin[2] = MSG_ReadCoord ();
state->origin[2] = floatcoords?MSG_ReadFloat():MSG_ReadCoord ();
if (bits & NQU_ANGLE3)
state->angles[2] = MSG_ReadAngle();
@ -1907,6 +1924,20 @@ void CLNQ_ParseEntity(unsigned int bits)
if (bits & FITZU_LERPFINISH)
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
{ //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;
}
if (cls.qex)
{
MSG_WriteByte (buf, clc_delta);
MSG_WriteULEB128(buf, cl.movesequence);
}
MSG_WriteByte (buf, clc_move);
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!
if (cls.qex)
MSG_WriteByte(buf, 4);
for (i=0 ; i<3 ; i++)
{
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_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_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.");
@ -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
#ifdef HAVE_DTLS
enum
{
{ //not relevant when given a direct dtls address.
DTLS_DISABLE,
DTLS_TRY,
DTLS_REQUIRE,
DTLS_ACTIVE
DTLS_ACTIVE,
} dtlsupgrade;
#endif
int mtu;
@ -304,6 +307,12 @@ static struct
int challenge; //tracked as part of guesswork based upon what replies we get.
double time; //for connection retransmits
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 tries; //increased each try, every fourth trys nq connect packets.
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
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 (ctx->adr[i].prot == NP_DGRAM)
ctx->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.
//FIXME: remove this block once our new netcode is better established.
connectinfo.dtlsupgrade = DTLS_REQUIRE;
ctx->adr[i].prot = NP_DGRAM;
}
@ -966,6 +976,12 @@ void CL_CheckForResend (void)
connectinfo.protocol = CP_NETQUAKE;
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
!strcmp(lbp, "bjp3") || !strcmp(lbp, "bjp"))
{
@ -1094,7 +1110,14 @@ void CL_CheckForResend (void)
net_message.cursize = 0;
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];
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)
connectinfo.nextadr++; //cycle hosts with each ping (if we got multiple).
if (connectinfo.mode==CIM_QEONLY || connectinfo.mode==CIM_NQONLY)
contype |= 2;
else
{
contype |= 1; /*always try qw type connections*/
#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.
#endif
contype |= 2; /*try nq connections periodically (or if its the default nq port)*/
}
/*DP, QW, Q2, Q3*/
/*NOTE: ioq3 has <challenge> <gamename> args. yes, a challenge to get a challenge.*/
@ -1273,6 +1301,10 @@ void CL_CheckForResend (void)
MSG_WriteLong(&sb, LongSwap(NETFLAG_CTL | (strlen(NQ_NETCHAN_GAMENAME)+7)));
MSG_WriteByte(&sb, CCREQ_CONNECT);
MSG_WriteString(&sb, NQ_NETCHAN_GAMENAME);
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*/
@ -1289,6 +1321,7 @@ void CL_CheckForResend (void)
/*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)
MSG_WriteString(&sb, "getchallenge");
}
*(int*)sb.data = LongSwap(NETFLAG_CTL | sb.cursize);
switch(NET_SendPacket (cls.sockets, sb.cursize, sb.data, to))
@ -1308,7 +1341,10 @@ void CL_CheckForResend (void)
if (!keeptrying)
{
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
{
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))
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.defaultport = port;
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);
CL_CheckForResend();
@ -1436,7 +1482,7 @@ void CL_Connect_f (void)
#endif
CL_Disconnect_f ();
CL_BeginServerConnect(server, 0, false);
CL_BeginServerConnect(server, 0, false, CIM_DEFAULT);
}
#if defined(CL_MASTER) && defined(HAVE_PACKET)
static void CL_ConnectBestRoute_f (void)
@ -1471,7 +1517,7 @@ static void CL_ConnectBestRoute_f (void)
else
#endif
CL_Disconnect_f ();
CL_BeginServerConnect(server, 0, true);
CL_BeginServerConnect(server, 0, true, CIM_DEFAULT);
}
#endif
@ -1500,7 +1546,7 @@ static void CL_Join_f (void)
Cvar_Set(&spectator, "0");
CL_BeginServerConnect(server, 0, false);
CL_BeginServerConnect(server, 0, false, CIM_DEFAULT);
}
void CL_Observe_f (void)
@ -1528,13 +1574,14 @@ void CL_Observe_f (void)
Cvar_Set(&spectator, "1");
CL_BeginServerConnect(server, 0, false);
CL_BeginServerConnect(server, 0, false, CIM_DEFAULT);
}
#ifdef NQPROT
void CLNQ_Connect_f (void)
{
char *server;
enum coninfomode_e mode;
if (Cmd_Argc() != 2)
{
@ -1542,12 +1589,17 @@ void CLNQ_Connect_f (void)
return;
}
if (!strcmp(Cmd_Argv(0), "connectqe"))
mode = CIM_QEONLY;
else
mode = CIM_NQONLY;
server = Cmd_Argv (1);
server = strcpy(alloca(strlen(server)+1), server);
CL_Disconnect_f ();
CL_BeginServerConnect(server, 26000, true);
CL_BeginServerConnect(server, 26000, true, mode);
}
#endif
@ -3218,7 +3270,7 @@ void CL_ConnectionlessPacket (void)
}
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);
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;
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 (CL_IsPendingServerAddress(&net_from))
{
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
{
connectinfo.istransfer = true;
@ -3310,12 +3362,12 @@ void CL_ConnectionlessPacket (void)
}
#endif
Con_TPrintf ("challenge\n");
Con_TPrintf (S_COLOR_GRAY"challenge\n");
if (!CL_IsPendingServerAddress(&net_from))
{
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;
}
connectinfo.numadr = 1;
@ -3557,7 +3609,7 @@ void CL_ConnectionlessPacket (void)
if (!strcmp(s, "print"))
{
Con_TPrintf ("print\n");
Con_TPrintf (S_COLOR_GRAY"print\n");
s = MSG_ReadString ();
if (connectinfo.trying && CL_IsPendingServerBaseAddress(&net_from) == false)
@ -3602,7 +3654,7 @@ void CL_ConnectionlessPacket (void)
if (!strcmp(com_token, "ccept"))
{
/*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)
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))
{
Con_TPrintf ("infoResponse\n");
Con_TPrintf (S_COLOR_GRAY"infoResponse\n");
Info_Print(net_message.data+17, "");
return;
}
@ -3648,7 +3700,7 @@ void CL_ConnectionlessPacket (void)
if (!strncmp(net_message.data+4, "getserversResponse", 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)
{
if (*b == '\\')
@ -3686,7 +3738,7 @@ void CL_ConnectionlessPacket (void)
else if (!strcmp(com_token, "tlsopened"))
{ //server is letting us know that its now listening for a dtls handshake.
#ifdef HAVE_DTLS
Con_Printf ("dtlsopened\n");
Con_Printf (S_COLOR_GRAY"dtlsopened\n");
if (!CL_IsPendingServerAddress(&net_from))
return;
@ -3715,7 +3767,7 @@ void CL_ConnectionlessPacket (void)
}
else
{
Con_Printf ("d\n");
Con_Printf ("disconnect\n");
if (cls.demoplayback != DPB_NONE)
{
Con_Printf("Disconnect\n");
@ -3743,7 +3795,7 @@ client_connect: //fixme: make function
}
if (net_from.type != NA_LOOPBACK)
{
Con_TPrintf ("connection\n");
Con_TPrintf (S_COLOR_GRAY"connection\n");
#ifdef HAVE_SERVER
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))
{ //quake2+quake3 send rejects this way
Con_TPrintf ("print\n");
Con_TPrintf (S_COLOR_GRAY"print\n");
Con_Printf ("%s", net_message.data+10);
if (connectinfo.trying && CL_IsPendingServerBaseAddress(&net_from) == false)
@ -3893,7 +3945,7 @@ client_connect: //fixme: make function
}
if (c == A2C_PRINT)
{ //closest quakeworld has to a reject message
Con_TPrintf ("print\n");
Con_TPrintf (S_COLOR_GRAY"print\n");
s = MSG_ReadString ();
Con_Printf ("%s", s);
@ -3929,6 +3981,9 @@ void CLNQ_ConnectionlessPacket(void)
int length;
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);
length = LongSwap(MSG_ReadLong ());
if (!(length & NETFLAG_CTL))
@ -3947,6 +4002,15 @@ void CLNQ_ConnectionlessPacket(void)
Con_TPrintf ("Dup connect received. Ignored.\n");
return;
}
if (length == 5 && net_from.prot == NP_DTLS)
{
cls.proquake_angles_hack = false;
cls.protocol_nq = CPNQ_ID;
Con_DPrintf("QuakeEx server...\n");
}
else
{
port = htons((unsigned short)MSG_ReadLong());
//this is the port that we're meant to respond to.
@ -3973,6 +4037,7 @@ void CLNQ_ConnectionlessPacket(void)
return;
}
}
}
Validation_Apply_Ruleset();
@ -3985,7 +4050,11 @@ void CLNQ_ConnectionlessPacket(void)
cls.netchan.compresstable = NULL;
cls.protocol = CP_NETQUAKE;
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;
current_loading_size = 0;
@ -3995,9 +4064,14 @@ void CLNQ_ConnectionlessPacket(void)
allowremotecmd = false; // localid required now for remote cmds
#endif
if (length == 5)
cls.qex = (connectinfo.mode==CIM_QEONLY);
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;
case CCREP_REJECT:
@ -4056,16 +4130,6 @@ void CL_ReadPacket(void)
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)
{ //connect to nq servers, but don't get confused with sequenced packets.
if (NET_WasSpecialPacket(cls.sockets))
@ -4076,6 +4140,16 @@ void CL_ReadPacket(void)
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
//
@ -4098,11 +4172,17 @@ void CL_ReadPacket(void)
{
case CP_NETQUAKE:
#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);
CL_WriteDemoMessage (&net_message, msg_readcount);
CLNQ_ParseServerMessage ();
break;
}
#endif
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.");
#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
#ifdef IRCCONNECT
Cmd_AddCommand ("ircconnect", CL_IRCConnect_f);
Cmd_AddCommand ("connectirc", CL_IRCConnect_f);
#endif
#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
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.");

View file

@ -218,7 +218,7 @@ static const char *svc_nqstrings[] =
"nqsvc_damage", // [qbyte] impact [qbyte] blood [vec3] from
"nqsvc_spawnstatic",
"nqsvcfte_spawnstatic2(21)",
"ftenq_spawnstatic2(21)",
"nqsvc_spawnbaseline",
"nqsvc_temp_entity", // <variable>
@ -236,36 +236,36 @@ static const char *svc_nqstrings[] =
"NEW PROTOCOL", //35
"NEW PROTOCOL", //36
"fitzsvc_skybox", //37
"fitz_skybox", //37
"NEW PROTOCOL", //38
"NEW PROTOCOL", //39
"fitzsvc_bf", //40
"fitzsvc_fog", //41
"fitzsvc_spawnbaseline2", //42
"fitzsvc_spawnstatic2", //43
"fitzsvc_spawnstaticsound2", //44
"fitz_bf", //40
"fitz_fog", //41
"fitz_spawnbaseline2", //42
"fitz_spawnstatic2", //43
"fitz_spawnstaticsound2", //44
"NEW PROTOCOL", //45
"NEW PROTOCOL", //46
"NEW PROTOCOL", //47
"NEW PROTOCOL", //48
"NEW PROTOCOL", //49
"dpsvc_downloaddata", //50
"dpsvc_updatestatubyte", //51
"dpsvc_effect", //52
"dpsvc_effect2", //53
"dp6svc_precache/dp5svc_sound2", //54
"dpsvc_spawnbaseline2", //55
"dpsvc_spawnstatic2", //56 obsolete
"dpsvc_entities", //57
"dpsvc_csqcentities", //58
"dpsvc_spawnstaticsound2", //59
"dpsvc_trailparticles", //60
"dpsvc_pointparticles", //61
"dpsvc_pointparticles1", //62
"qex_updateping", //46
"qex_updatesocial", //47
"qex_updateplinfo", //48
"qex_print", //49
"dp_downloaddata / neh_skyboxsize / qex_servervars", //50
"dp_updatestatubyte / neh_fog / qex_seq", //51
"dp_effect / qex_achievement", //52
"dp_effect2", //53
"dp6_precache / dp5_sound2", //54
"dp_spawnbaseline2", //55
"dp_spawnstatic2", //56 obsolete
"dp_entities", //57
"dp_csqcentities", //58
"dp_spawnstaticsound2", //59
"dp_trailparticles", //60
"dp_pointparticles", //61
"dp_pointparticles1", //62
"NEW PROTOCOL(63)", //63
"NEW PROTOCOL(64)", //64
"NEW PROTOCOL(65)", //65
"ftenqsvc_spawnbaseline2", //66
"ftenq_spawnbaseline2", //66
"NEW PROTOCOL(67)", //67
"NEW PROTOCOL(68)", //68
"NEW PROTOCOL(69)", //69
@ -277,21 +277,21 @@ static const char *svc_nqstrings[] =
"NEW PROTOCOL(75)", //75
"NEW PROTOCOL(76)", //76
"NEW PROTOCOL(77)", //77
"nqsvcfte_updatestatstring", //78
"nqsvcfte_updatestatfloat", //79
"ftenq_updatestatstring", //78
"ftenq_updatestatfloat", //79
"NEW PROTOCOL(80)", //80
"NEW PROTOCOL(81)", //81
"NEW PROTOCOL(82)", //82
"nqsvcfte_cgamepacket", //83
"nqsvcfte_voicechat", //84
"nqsvcfte_setangledelta", //85
"nqsvcfte_updateentities", //86
"ftenq_cgamepacket", //83
"ftenq_voicechat", //84
"ftenq_setangledelta", //85
"ftenq_updateentities", //86
"NEW PROTOCOL(87)", //87
"NEW PROTOCOL(88)", //88
"svcfte_setinfoblob", //89
"svcfte_cgamepacket_sized", //90
"svcfte_temp_entity_sized", //91
"svcfte_csqcentities_sized", //92
"ftenq_setinfoblob", //89
"ftenq_cgamepacket_sized", //90
"ftenq_temp_entity_sized", //91
"ftenq_csqcentities_sized", //92
};
#endif
@ -3712,6 +3712,7 @@ static void CLQ2_ParseServerData (void)
void CL_ParseEstablished(void)
{
#ifdef NQPROT
cls.qex = false;
Z_Free(cl_dp_packagenames);
cl_dp_packagenames = NULL;
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.
{
int nummodels, numsounds;
char *str;
char *str = NULL;
int gametype;
Con_DPrintf ("Serverdata packet %s.\n", cls.demoplayback?"read":"received");
SCR_SetLoadingStage(LS_CLIENT);
@ -3892,9 +3893,19 @@ static void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caut
CLNQ_ParseProtoVersion();
if (cls.fteprotocolextensions2 & PEXT2_PREDINFO)
if (cls.qex)
{
cl.allocated_client_slots = MSG_ReadByte();
str = MSG_ReadString();
}
else
{
if (cls.fteprotocolextensions2 & PEXT2_PREDINFO)
str = MSG_ReadString();
cl.allocated_client_slots = MSG_ReadByte();
}
if (str)
{
#ifndef CLIENTONLY
if (!sv.state)
#endif
@ -3906,8 +3917,6 @@ static void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caut
#endif
}
}
cl.allocated_client_slots = MSG_ReadByte();
if (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;
gametype = MSG_ReadByte ();
str = MSG_ReadString ();
@ -4063,6 +4073,55 @@ static void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caut
CSQC_Shutdown();
#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)
{
InfoSync_Add(&cls.userinfosync, ctx, key);
@ -4153,7 +4212,7 @@ static void CLNQ_ParseClientdata (void)
if (bits & (SU_VELOCITY1<<i))
{
if (CPNQ_IS_DP)
if (CPNQ_IS_DP || (cls.qex && (bits & QEX_SU_FLOATCOORDS)))
pl->velocity[i] = MSG_ReadFloat();
else
pl->velocity[i] = MSG_ReadChar()*16;
@ -4233,6 +4292,11 @@ static void CLNQ_ParseClientdata (void)
weaponframe |= MSG_ReadByte() << 8;
if (bits & FITZSU_WEAPONALPHA)
MSG_ReadByte();
if (cls.qex)
{
if (bits & QEX_SU_ENTFLAGS) /*entflags =*/ MSG_ReadULEB128();
}
}
CL_SetStatInt(0, STAT_WEAPONFRAME, weaponframe);
@ -4705,6 +4769,20 @@ static void CL_ParseBaseline (entity_state_t *es, int baselinetype2)
}
es->trans = (bits & FITZ_B_ALPHA) ? MSG_ReadByte() : 255;
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)
@ -7972,7 +8050,7 @@ static qboolean CLNQ_ParseNQPrints(char *s)
{
int i;
char *start = s;
if (!strcmp(s, "Client ping times:\n"))
if (!strcmp(s, "Client ping times:\n") && !cls.qex)
{
cl.nqparseprint = CLNQPP_PINGS;
return true;
@ -8020,7 +8098,7 @@ static qboolean CLNQ_ParseNQPrints(char *s)
s = start;
}
if (!strncmp(s, "host: ", 9))
if (!strncmp(s, "host: ", 9) && !cls.qex)
{
cl.nqparseprint = CLNQPP_STATUS;
return cls.nqexpectingstatusresponse;
@ -8134,6 +8212,7 @@ void CLNQ_ParseServerMessage (void)
switch (cmd)
{
default:
badsvc:
CL_DumpPacket();
Host_EndGame ("CLNQ_ParseServerMessage: Illegible server message (%i@%i)", cmd, msg_readcount-1);
return;
@ -8143,7 +8222,13 @@ void CLNQ_ParseServerMessage (void)
break;
case svc_print:
if(cls.qex && cls.protocol_nq != CPNQ_ID)
s = CLQEX_ReadStrings();
else
{
svcprint:
s = MSG_ReadString ();
}
if (*s == 1 || *s == 2)
{
@ -8161,6 +8246,9 @@ void CLNQ_ParseServerMessage (void)
return;
case svc_centerprint:
if (cls.qex && cls.protocol_nq != CPNQ_ID)
s = CLQEX_ReadStrings();
else
s = MSG_ReadString ();
#ifdef PLUGINS
@ -8188,6 +8276,14 @@ void CLNQ_ParseServerMessage (void)
break;
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();
break;
@ -8431,6 +8527,14 @@ void CLNQ_ParseServerMessage (void)
break;
case svcdp_updatestatbyte:
//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)
{
CL_ResetFog(FOGTYPE_AIR);
@ -8599,9 +8703,28 @@ void CLNQ_ParseServerMessage (void)
case svcnq_effect:
//also svcqex_achievement
if (cls.qex)
{ //svcqex_achievement
MSG_ReadString();
break;
}
CL_ParseEffect(false);
break;
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);
break;
@ -8638,6 +8761,10 @@ void CLNQ_ParseServerMessage (void)
#endif
case svcdp_downloaddata:
//also svcqex_servervars:
if (cls.qex)
CLQEX_ParseServerVars();
else
CLDP_ParseDownloadData();
break;
@ -8650,6 +8777,49 @@ void CLNQ_ParseServerMessage (void)
case svcdp_pointparticles1:
CL_ParsePointParticles(true);
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_BJP (cls.protocol_nq >= CPNQ_BJP1 && cls.protocol_nq <= CPNQ_BJP3)
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;
@ -1144,7 +1147,6 @@ void CL_SaveInfo(vfsfile_t *f);
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_BeginServerConnect(const char *host, int port, qboolean noproxy);
char *CL_TryingToConnect(void);
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
// but that shouldn't be happening too often
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)

View file

@ -3374,13 +3374,13 @@ void Sbar_DeathmatchOverlay (playerview_t *pv, int start)
cl.last_ping_request = realtime;
CL_SendClientCommand(true, "pings");
}
else if (cls.protocol == CP_NETQUAKE)
else if (cls.protocol == CP_NETQUAKE && !cls.qex)
{
cl.last_ping_request = realtime;
CL_SendClientCommand(true, "ping");
}
}
if (cls.protocol == CP_NETQUAKE)
if (cls.protocol == CP_NETQUAKE && !cls.qex)
{
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.
//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
if (!dpcompat_console.ival)
if (!dpcompat_console.ival && !cls.qex)
CL_SendSeatClientCommand(true, split, "%s \"%s%s\"", team ? "say_team" : "say", extra?extra:"", sendtext);
else
#endif

View file

@ -2032,6 +2032,14 @@ quint64_t MSG_ReadULEB128 (void)
}
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)
{ //0* 10*,*, 110*,*,* etc, up to 0xff followed by 8 continuation bytes
qbyte l=0x80, v, b = 0;

View file

@ -356,6 +356,7 @@ qint64_t MSG_ReadInt64 (void);
quint64_t MSG_ReadUInt64 (void);
qint64_t MSG_ReadSLEB128 (void);
quint64_t MSG_ReadULEB128 (void);
qint64_t MSG_ReadSignedQEX (void);
struct client_s;
unsigned int MSGSV_ReadEntity (struct client_s *fromclient);
unsigned int MSGCL_ReadEntity (void);
@ -1002,11 +1003,11 @@ qboolean IPLog_Merge_File(const char *fname);
#endif
enum certlog_problem_e
{
CERTLOG_WRONGHOST,
CERTLOG_EXPIRED,
CERTLOG_MISSINGCA,
CERTLOG_WRONGHOST =1<<0,
CERTLOG_EXPIRED =1<<1,
CERTLOG_MISSINGCA =1<<2,
CERTLOG_UNKNOWN,
CERTLOG_UNKNOWN =1<<3,
};
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_EOM 0x00080000
#define NETFLAG_UNRELIABLE 0x00100000
#define NETFLAG_ZLIB 0x00200000 //QEx - payload contains the real (full) packet
#define NETFLAG_CTL 0x80000000
#define NQ_NETCHAN_GAMENAME "QUAKE"

View file

@ -443,6 +443,7 @@ qboolean ServerPaused(void);
#endif
#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)
{
int header;
@ -453,12 +454,44 @@ enum nqnc_packettype_e NQNetChan_Process(netchan_t *chan)
MSG_BeginReading (chan->netprim);
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)
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());
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)
// however we still need some special case svcs
// (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_seq 51 // [leb128] input sequence ack
#define svcqex_achievement 52 // [string] codename
@ -791,6 +794,10 @@ enum {
#define FITZSU_UNUSED30 (1<<30)
#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
#define DPSU_PUNCHVEC1 (1<<16)
#define DPSU_PUNCHVEC2 (1<<17)
@ -858,6 +865,18 @@ enum {
#define FITZU_LERPFINISH (1<<19)
#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
@ -903,6 +922,22 @@ enum {
#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
@ -955,6 +990,12 @@ enum {
#define FITZ_B_ALPHA (1<<2)
#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

View file

@ -698,7 +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.
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
//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_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_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))
int columns = (1<<6)-1;
@ -2305,12 +2305,12 @@ static void SV_Status_f (void)
case SCP_BAD:
p = "";
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_QUAKE3: p = "q3"; 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_NETQUAKE: p = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?"ftenq":(cl->qex?"qe15":"nq"); break;
case SCP_BJP3: p = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?"ftenq":"bjp3"; break;
case SCP_FITZ666: p = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?"ftenq":(cl->qex?"qe666":"fitz"); break;
case SCP_DARKPLACES6: p = "dpp6"; 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)
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)
{
@ -3126,12 +3143,24 @@ int glowsize=0, glowcolour=0, colourmod=0;
if (bits & NQU_COLORMAP) MSG_WriteByte (msg, ent->colormap & 0xff);
if (bits & NQU_SKIN) MSG_WriteByte (msg, ent->skinnum & 0xff);
if (bits & NQU_EFFECTS) MSG_WriteByte (msg, eff & 0x00ff);
if (host_client->qex && (bits & QE_U_FLOATCOORDS))
{
if (bits & NQU_ORIGIN1) MSG_WriteFloat (msg, ent->origin[0]);
if (bits & NQU_ANGLE1) MSG_WriteAngle (msg, ent->angles[0]);
if (bits & NQU_ORIGIN2) MSG_WriteFloat (msg, ent->origin[1]);
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_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_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 (bits & NQU_ANGLE3) MSG_WriteAngle (msg, ent->angles[2]);
}
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_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 (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)
{

View file

@ -446,17 +446,35 @@ void SV_FinalMessage (char *message)
buf.data = bufdata;
buf.maxsize = sizeof(bufdata);
for (i=0, cl = svs.clients ; i<svs.allocated_client_slots ; i++, cl++)
if (cl->state >= cs_spawned && !cl->controlled)
{
if (ISQWCLIENT(cl))
{
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;
for (i=0, cl = svs.clients ; i<svs.allocated_client_slots ; i++, cl++)
if (cl->state >= cs_spawned && !cl->controlled)
if (ISNQCLIENT(cl) || ISQWCLIENT(cl))
Netchan_Transmit (&cl->netchan, buf.cursize
, buf.data, 10000);
}
}
@ -1883,6 +1901,10 @@ void SV_AcceptMessage(client_t *newcl)
SZ_Clear(&sb);
MSG_WriteLong(&sb, 0);
MSG_WriteByte(&sb, CCREP_ACCEPT);
if (newcl->qex)
; //skip any port info (as well as any proquake ident stuff.
else
{
NET_LocalAddressForRemote(svs.sockets, &net_from, &localaddr, 0);
MSG_WriteLong(&sb, ShortSwap(localaddr.port));
if (newcl->proquake_angles_hack)
@ -1891,6 +1913,7 @@ void SV_AcceptMessage(client_t *newcl)
MSG_WriteByte(&sb, MOD_PROQUAKE_VERSION);
MSG_WriteByte(&sb, 0/*flags*/);
}
}
*(int*)sb.data = BigLong(NETFLAG_CTL|sb.cursize);
NET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);
return;
@ -2110,7 +2133,7 @@ void SV_ClientProtocolExtensionsChanged(client_t *client)
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->datagram.maxsize = sizeof(host_client->datagram_buf);
@ -2570,7 +2593,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;
newcl->qex = info->isqex;
#endif
newcl->userid = ++nextuserid;
@ -3404,7 +3427,7 @@ void SVC_DirectConnect(int expectedreliablesequence)
}
else if (version == NQ_NETCHAN_VERSION_QEX)
{ //rerelease...
info.protocol = SCP_NETQUAKE;
info.protocol = SCP_FITZ666;//NETQUAKE;
info.isqex = true;
}
#endif
@ -4031,7 +4054,7 @@ qboolean SV_ConnectionlessPacket (void)
c = Cmd_Argv(0);
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')) )
{ //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 (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);
/*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));
@ -4323,7 +4346,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
protver = MSG_ReadByte();
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.data = buffer;
@ -4420,7 +4443,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
return true;
case CCREQ_SERVER_INFO:
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)
return false;
if (SV_BannedReason (&net_from))
@ -4452,7 +4475,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
return true;
case CCREQ_PLAYER_INFO:
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)
return false;
if (SV_BannedReason (&net_from))
@ -4486,7 +4509,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
return true;
case CCREQ_RULE_INFO:
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)
return false;
if (SV_BannedReason (&net_from))
@ -4781,7 +4804,7 @@ dominping:
// packet is not from a known client
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,6 +184,9 @@ void SV_PrintToClient(client_t *cl, int level, const char *string)
case SCP_BJP3:
case SCP_FITZ666:
#ifdef NQPROT
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)
ClientReliableWrite_Byte (cl, 1);
@ -1594,6 +1597,12 @@ void SV_WriteCenterPrint(client_t *cl, char *s)
}
else
{
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);
@ -1691,7 +1700,7 @@ 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)
else if (client->qex && client->last_sequence)
{
MSG_WriteByte(msg, svcqex_seq);
MSG_WriteULEB128(msg, client->last_sequence);
@ -1779,6 +1788,14 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg)
bits |= FITZSU_WEAPONFRAME2;
if (ent->xv->alpha && ent->xv->alpha < 1)
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);
if (bits & (SU_VELOCITY1<<i))
{
if (client->protocol == SCP_DARKPLACES6 || client->protocol == SCP_DARKPLACES7)
MSG_WriteCoord(msg, ent->v->velocity[i]);
if (client->qex && (bits & QEX_SU_ENTFLAGS))
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
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_WEAPONFRAME2) MSG_WriteByte (msg, (int)ent->v->weaponframe >> 8);
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
}
@ -3049,7 +3073,7 @@ void SV_UpdateToReliableMessages (void)
SV_FullClientUpdate (host_client, NULL);
}
if (host_client->qex_input_hack && sendpings)
if (host_client->qex && sendpings)
{
sizebuf_t *m;
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_WriteSignedQEX(m, SV_CalcPing(client, false));
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
if (client->realip_status == 2)
{
ClientReliableWrite_Begin(client, svc_print, 256);
ClientReliableWrite_Byte(client, PRINT_HIGH);
ClientReliableWrite_String(client, "Couldn't verify your real ip\n");
SV_PrintToClient(client, PRINT_HIGH, "Couldn't verify your real ip\n");
return true; //client doesn't support certainty.
}
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)
{
ClientReliableWrite_Begin(client, svc_print, 256);
ClientReliableWrite_Byte(client, PRINT_HIGH);
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
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)
{
client->drop = true;
@ -529,7 +525,7 @@ void SVNQ_New_f (void)
if (host_client->drop)
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)
{ //don't override cl_loopbackprotocol's choice
@ -544,6 +540,8 @@ void SVNQ_New_f (void)
}
return;
}
else
host_client->pextknown = true; //just in case.
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.
protmain = PROTOCOL_VERSION_RMQ;
protoname = "RMQ";
protoname = host_client->qex?"QE999":"RMQ";
}
else
{
protmain = PROTOCOL_VERSION_FITZ;
protoname = "666";
protoname = host_client->qex?"QE666":"666";
}
}
else if (host_client->protocol == SCP_BJP3)
@ -658,14 +656,13 @@ void SVNQ_New_f (void)
"!!! 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.
//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);
MSG_WriteString (&host_client->netchan.message,message);
SV_PrintToClient(host_client, PRINT_HIGH, message);
}
}
host_client->protocol = SCP_NETQUAKE; //identical other than the client->server angles
protmain = PROTOCOL_VERSION_NQ;
protoname = "NQ";
protoname = host_client->qex?"QE15":"NQ";
}
break;
case SCP_DARKPLACES6:
@ -728,8 +725,7 @@ void SVNQ_New_f (void)
protoname,(protext1||(protext2&~PEXT2_VOICECHAT))?"+":"",(protext2&PEXT2_VOICECHAT)?"Voip":"",
build,gamedir, mapname);
}
MSG_WriteByte (&host_client->netchan.message, svc_print);
MSG_WriteString (&host_client->netchan.message,message);
SV_PrintToClient(host_client, PRINT_HIGH, message);
}
if (host_client->protocol == SCP_DARKPLACES6 || host_client->protocol == SCP_DARKPLACES7)
@ -797,8 +793,9 @@ void SVNQ_New_f (void)
if (protext2 & PEXT2_PREDINFO)
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);
if (host_client->qex)
MSG_WriteString(&host_client->netchan.message, gamedir);
if (!coop.value && deathmatch.value)
MSG_WriteByte (&host_client->netchan.message, GAME_DEATHMATCH);
@ -828,29 +825,11 @@ void SVNQ_New_f (void)
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;
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,
unsigned int bits = QEX_GV_ALL;
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)
@ -6026,13 +6005,13 @@ static void SVNQ_PreSpawn_f (void)
default:
break;
case SCP_NETQUAKE:
prot = " (nq)";
prot = host_client->qex?" (qe15)":" (nq)";
break;
case SCP_BJP3:
prot = " (bjp3)";
break;
case SCP_FITZ666:
prot = " (fitz)";
prot = host_client->qex?" (qe666)":" (fitz)";
break;
case SCP_DARKPLACES6:
prot = " (dpp6)";
@ -8722,8 +8701,11 @@ void SVNQ_ReadClientMove (qboolean forceangle16, qboolean quakeex)
if (quakeex)
{ //I'm guessing this has something to do with splitscreen.
if (MSG_ReadByte() != 1)
{
Con_Printf("Unknown byte wasn't 1\n");
msg_badread = true;
}
}
//read angles
@ -8873,7 +8855,7 @@ void SVNQ_ReadClientMove (qboolean forceangle16, qboolean quakeex)
}
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.
if (cmd.impulse)
host_client->edict->v->impulse = cmd.impulse;
@ -8889,7 +8871,6 @@ 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;
@ -9020,7 +9001,7 @@ void SVNQ_ExecuteClientMessage (client_t *cl)
break;
}
SVNQ_ReadClientMove (forceangle16, qex);
SVNQ_ReadClientMove (forceangle16, cl->qex);
// cmd = host_client->lastcmd;
// SV_ClientThink();
break;
@ -9075,7 +9056,6 @@ void SVNQ_ExecuteClientMessage (client_t *cl)
case clc_delta://clcqex_sequence:
host_client->last_sequence = MSG_ReadULEB128();
qex = true;
break;
case clc_tmove://clcqex_auth