yquake2remaster/src/client/cl_network.c

703 lines
13 KiB
C
Raw Normal View History

/*
* Copyright (C) 1997-2001 Id Software, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* This file implements generic network functions
*
* =======================================================================
2012-04-29 13:57:33 +00:00
*/
#include "header/client.h"
2012-07-22 13:34:45 +00:00
void CL_ParseStatusMessage(void);
2012-07-22 13:34:45 +00:00
extern cvar_t *msg;
extern cvar_t *rcon_client_password;
extern cvar_t *rcon_address;
extern cvar_t *cl_timeout;
/*
* adds the current command line as a clc_stringcmd to the client
* message. things like godmode, noclip, etc, are commands directed to
* the server, so when they are typed in at the console, they will need
* to be forwarded.
*/
2012-07-22 13:34:45 +00:00
void
Cmd_ForwardToServer(void)
{
2012-07-22 13:34:45 +00:00
char *cmd;
cmd = Cmd_Argv(0);
2012-07-22 13:34:45 +00:00
if ((cls.state <= ca_connected) || (*cmd == '-') || (*cmd == '+'))
{
2012-07-22 13:34:45 +00:00
Com_Printf("Unknown command \"%s\"\n", cmd);
return;
}
2012-07-22 13:34:45 +00:00
MSG_WriteByte(&cls.netchan.message, clc_stringcmd);
2012-07-22 13:34:45 +00:00
SZ_Print(&cls.netchan.message, cmd);
if (Cmd_Argc() > 1)
{
2012-07-22 13:34:45 +00:00
SZ_Print(&cls.netchan.message, " ");
SZ_Print(&cls.netchan.message, Cmd_Args());
}
}
2012-04-29 13:57:33 +00:00
2012-07-22 13:34:45 +00:00
void
CL_ForwardToServer_f(void)
{
2012-07-22 13:34:45 +00:00
if ((cls.state != ca_connected) && (cls.state != ca_active))
{
2012-07-22 13:34:45 +00:00
Com_Printf("Can't \"%s\", not connected\n", Cmd_Argv(0));
return;
}
/* don't forward the first argument */
if (Cmd_Argc() > 1)
{
2012-07-22 13:34:45 +00:00
MSG_WriteByte(&cls.netchan.message, clc_stringcmd);
SZ_Print(&cls.netchan.message, Cmd_Args());
}
}
/*
* Called after an ERR_DROP was thrown
*/
2012-07-22 13:34:45 +00:00
void
CL_Drop(void)
{
if (cls.state == ca_uninitialized)
2012-07-22 13:34:45 +00:00
{
return;
2012-07-22 13:34:45 +00:00
}
if (cls.state == ca_disconnected)
2012-07-22 13:34:45 +00:00
{
return;
2012-07-22 13:34:45 +00:00
}
2012-07-22 13:34:45 +00:00
CL_Disconnect();
/* drop loading plaque unless this is the initial game start */
if (cls.disable_servercount != -1)
2012-07-22 13:34:45 +00:00
{
SCR_EndLoadingPlaque(); /* get rid of loading plaque */
}
}
/*
* We have gotten a challenge from the server, so try and
* connect.
*/
2012-07-22 13:34:45 +00:00
void
CL_SendConnectPacket(void)
{
2012-07-22 13:34:45 +00:00
netadr_t adr;
int port;
memset(&adr, 0, sizeof(adr));
2012-07-22 13:34:45 +00:00
if (!NET_StringToAdr(cls.servername, &adr))
{
2012-07-22 13:34:45 +00:00
Com_Printf("Bad server address\n");
cls.connect_time = 0;
return;
}
if (adr.port == 0)
2012-07-22 13:34:45 +00:00
{
adr.port = BigShort(PORT_SERVER);
}
2012-07-22 13:34:45 +00:00
port = Cvar_VariableValue("qport");
userinfo_modified = false;
2012-07-22 13:34:45 +00:00
Netchan_OutOfBandPrint(NS_CLIENT, adr, "connect %i %i %i \"%s\"\n",
PROTOCOL_VERSION, port, cls.challenge, Cvar_Userinfo());
}
/*
* Resend a connect message if the last one has timed out
*/
2012-07-22 13:34:45 +00:00
void
CL_CheckForResend(void)
{
2012-07-22 13:34:45 +00:00
netadr_t adr;
/* if the local server is running and we aren't just connect */
2012-07-22 13:34:45 +00:00
if ((cls.state == ca_disconnected) && Com_ServerState())
{
cls.state = ca_connecting;
Q_strlcpy(cls.servername, "localhost", sizeof(cls.servername));
/* we don't need a challenge on the localhost */
2012-07-22 13:34:45 +00:00
CL_SendConnectPacket();
return;
}
/* resend if we haven't gotten a reply yet */
if (cls.state != ca_connecting)
2012-07-22 13:34:45 +00:00
{
return;
2012-07-22 13:34:45 +00:00
}
if (cls.realtime - cls.connect_time < 3000)
2012-07-22 13:34:45 +00:00
{
return;
2012-07-22 13:34:45 +00:00
}
2012-07-22 13:34:45 +00:00
if (!NET_StringToAdr(cls.servername, &adr))
{
2012-07-22 13:34:45 +00:00
Com_Printf("Bad server address\n");
cls.state = ca_disconnected;
return;
}
if (adr.port == 0)
2012-07-22 13:34:45 +00:00
{
adr.port = BigShort(PORT_SERVER);
}
cls.connect_time = cls.realtime;
2012-07-22 13:34:45 +00:00
Com_Printf("Connecting to %s...\n", cls.servername);
2012-07-22 13:34:45 +00:00
Netchan_OutOfBandPrint(NS_CLIENT, adr, "getchallenge\n");
}
2012-07-22 13:34:45 +00:00
void
CL_Connect_f(void)
{
2012-07-22 13:34:45 +00:00
char *server;
if (Cmd_Argc() != 2)
{
2012-07-22 13:34:45 +00:00
Com_Printf("usage: connect <server>\n");
return;
}
2012-07-22 13:34:45 +00:00
if (Com_ServerState())
{
/* if running a local server, kill it and reissue
2012-07-22 13:34:45 +00:00
note: this is connect with the save game system */
SV_Shutdown(va("Server quit\n", msg), false);
}
else
{
2012-07-22 13:34:45 +00:00
CL_Disconnect();
}
2012-07-22 13:34:45 +00:00
server = Cmd_Argv(1);
2012-07-22 13:34:45 +00:00
NET_Config(true); /* allow remote */
2012-07-22 13:34:45 +00:00
CL_Disconnect();
cls.state = ca_connecting;
Q_strlcpy(cls.servername, server, sizeof(cls.servername));
cls.connect_time = -99999; /* HACK: CL_CheckForResend() will fire immediately */
}
/*
* Send the rest of the command line over as
* an unconnected command.
*/
2012-07-22 13:34:45 +00:00
void
CL_Rcon_f(void)
{
2012-07-22 13:34:45 +00:00
char message[1024];
int i;
netadr_t to;
if (!rcon_client_password->string)
{
2012-07-22 13:34:45 +00:00
Com_Printf("You must set 'rcon_password' before\n"
"issuing an rcon command.\n");
return;
}
memset(&to, 0, sizeof(to));
message[0] = (char)255;
message[1] = (char)255;
message[2] = (char)255;
message[3] = (char)255;
message[4] = 0;
2012-07-22 13:34:45 +00:00
NET_Config(true); /* allow remote */
strcat(message, "rcon ");
strcat(message, rcon_client_password->string);
strcat(message, " ");
2012-07-22 13:34:45 +00:00
for (i = 1; i < Cmd_Argc(); i++)
{
strcat(message, Cmd_Argv(i));
strcat(message, " ");
}
if (cls.state >= ca_connected)
2012-07-22 13:34:45 +00:00
{
to = cls.netchan.remote_address;
2012-07-22 13:34:45 +00:00
}
else
{
if (!strlen(rcon_address->string))
{
2012-07-22 13:34:45 +00:00
Com_Printf("You must either be connected,\n"
"or set the 'rcon_address' cvar\n"
"to issue rcon commands\n");
return;
}
2012-07-22 13:34:45 +00:00
NET_StringToAdr(rcon_address->string, &to);
if (to.port == 0)
2012-07-22 13:34:45 +00:00
{
to.port = BigShort(PORT_SERVER);
}
}
2012-07-22 13:34:45 +00:00
NET_SendPacket(NS_CLIENT, strlen(message) + 1, message, to);
}
2012-07-22 13:34:45 +00:00
/*
* Goes from a connected state to full screen
2012-07-22 13:34:45 +00:00
* console state Sends a disconnect message to
* the server This is also called on Com_Error, so
* it shouldn't cause any errors
*/
2012-07-22 13:34:45 +00:00
void
CL_Disconnect(void)
{
2012-07-22 13:34:45 +00:00
byte final[32];
if (cls.state == ca_disconnected)
2012-07-22 13:34:45 +00:00
{
return;
2012-07-22 13:34:45 +00:00
}
if (cl_timedemo && cl_timedemo->value)
{
2012-07-22 13:34:45 +00:00
int time;
2012-07-22 13:34:45 +00:00
time = Sys_Milliseconds() - cl.timedemo_start;
if (time > 0)
2012-07-22 13:34:45 +00:00
{
Com_Printf("%i frames, %3.1f seconds: %3.1f fps\n",
cl.timedemo_frames, time / 1000.0,
cl.timedemo_frames * 1000.0 / time);
}
}
2012-07-22 13:34:45 +00:00
VectorClear(cl.refdef.blend);
R_SetPalette(NULL);
2012-07-22 13:34:45 +00:00
M_ForceMenuOff();
cls.connect_time = 0;
2012-07-22 13:34:45 +00:00
SCR_StopCinematic();
2012-04-25 08:24:38 +00:00
#ifdef OGG
2010-10-14 07:33:20 +00:00
OGG_Stop();
2012-04-25 08:24:38 +00:00
#endif
2012-04-25 08:53:25 +00:00
#ifdef CDA
2010-10-14 07:33:20 +00:00
CDAudio_Stop();
2012-04-25 08:53:25 +00:00
#endif
if (cls.demorecording)
2012-07-22 13:34:45 +00:00
{
CL_Stop_f();
}
/* send a disconnect message to the server */
final[0] = clc_stringcmd;
2012-07-22 13:34:45 +00:00
strcpy((char *)final + 1, "disconnect");
2012-07-22 13:34:45 +00:00
Netchan_Transmit(&cls.netchan, strlen((const char *)final), final);
Netchan_Transmit(&cls.netchan, strlen((const char *)final), final);
Netchan_Transmit(&cls.netchan, strlen((const char *)final), final);
2012-07-22 13:34:45 +00:00
CL_ClearState();
/* stop file download */
if (cls.download)
{
fclose(cls.download);
cls.download = NULL;
}
cls.state = ca_disconnected;
}
2012-07-22 13:34:45 +00:00
void
CL_Disconnect_f(void)
{
2012-07-22 13:34:45 +00:00
Com_Error(ERR_DROP, "Disconnected from server");
}
/*
* packet <destination> <contents>
*
* Contents allows \n escape character
*/
2012-07-22 13:34:45 +00:00
void
CL_Packet_f(void)
{
2012-07-22 13:34:45 +00:00
char send[2048];
int i, l;
char *in, *out;
netadr_t adr;
if (Cmd_Argc() != 3)
{
2012-07-22 13:34:45 +00:00
Com_Printf("packet <destination> <contents>\n");
return;
}
2012-07-22 13:34:45 +00:00
NET_Config(true); /* allow remote */
2012-07-22 13:34:45 +00:00
if (!NET_StringToAdr(Cmd_Argv(1), &adr))
{
2012-07-22 13:34:45 +00:00
Com_Printf("Bad address\n");
return;
}
if (!adr.port)
2012-07-22 13:34:45 +00:00
{
adr.port = BigShort(PORT_SERVER);
}
in = Cmd_Argv(2);
2012-07-22 13:34:45 +00:00
out = send + 4;
send[0] = send[1] = send[2] = send[3] = (char)0xff;
2012-07-22 13:34:45 +00:00
l = strlen(in);
2012-07-22 13:34:45 +00:00
for (i = 0; i < l; i++)
{
2012-07-22 13:34:45 +00:00
if ((in[i] == '\\') && (in[i + 1] == 'n'))
{
*out++ = '\n';
i++;
}
else
2012-07-22 13:34:45 +00:00
{
*out++ = in[i];
2012-07-22 13:34:45 +00:00
}
}
*out = 0;
2012-07-22 13:34:45 +00:00
NET_SendPacket(NS_CLIENT, out - send, send, adr);
}
/*
* Just sent as a hint to the client that they should
* drop to full console
*/
2012-07-22 13:34:45 +00:00
void
CL_Changing_f(void)
{
/* if we are downloading, we don't change!
This so we don't suddenly stop downloading a map */
if (cls.download)
2012-07-22 13:34:45 +00:00
{
return;
2012-07-22 13:34:45 +00:00
}
2012-07-22 13:34:45 +00:00
SCR_BeginLoadingPlaque();
cls.state = ca_connected; /* not active anymore, but not disconnected */
2012-07-22 13:34:45 +00:00
Com_Printf("\nChanging map...\n");
}
/*
* The server is changing levels
*/
2012-07-22 13:34:45 +00:00
void
CL_Reconnect_f(void)
{
/* if we are downloading, we don't change!
This so we don't suddenly stop downloading a map */
if (cls.download)
2012-07-22 13:34:45 +00:00
{
return;
2012-07-22 13:34:45 +00:00
}
2012-07-22 13:34:45 +00:00
S_StopAllSounds();
if (cls.state == ca_connected)
{
2012-07-22 13:34:45 +00:00
Com_Printf("reconnecting...\n");
cls.state = ca_connected;
2012-07-22 13:34:45 +00:00
MSG_WriteChar(&cls.netchan.message, clc_stringcmd);
MSG_WriteString(&cls.netchan.message, "new");
return;
}
if (*cls.servername)
{
if (cls.state >= ca_connected)
{
CL_Disconnect();
cls.connect_time = cls.realtime - 1500;
}
else
2012-07-22 13:34:45 +00:00
{
cls.connect_time = -99999; /* Hack: fire immediately */
2012-07-22 13:34:45 +00:00
}
cls.state = ca_connecting;
2012-07-22 13:34:45 +00:00
Com_Printf("reconnecting...\n");
}
}
2012-07-22 13:34:45 +00:00
void
CL_PingServers_f(void)
{
2012-07-22 13:34:45 +00:00
int i;
netadr_t adr;
char name[32];
char *adrstring;
cvar_t *noudp;
cvar_t *noipx;
2012-07-22 13:34:45 +00:00
NET_Config(true); /* allow remote but do we even need lokal pings? */
/* send a broadcast packet */
2012-07-22 13:34:45 +00:00
Com_Printf("pinging broadcast...\n");
2012-07-22 13:34:45 +00:00
noudp = Cvar_Get("noudp", "0", CVAR_NOSET);
if (!noudp->value)
{
adr.type = NA_BROADCAST;
adr.port = BigShort(PORT_SERVER);
2012-07-22 13:34:45 +00:00
Netchan_OutOfBandPrint(NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION));
2011-10-15 16:18:26 +00:00
2012-07-22 13:34:45 +00:00
Com_Printf("pinging multicast...\n");
2011-10-15 16:18:26 +00:00
adr.type = NA_MULTICAST6;
adr.port = BigShort(PORT_SERVER);
2012-07-22 13:34:45 +00:00
Netchan_OutOfBandPrint(NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION));
}
2012-07-22 13:34:45 +00:00
noipx = Cvar_Get("noipx", "0", CVAR_NOSET);
if (!noipx->value)
{
adr.type = NA_BROADCAST_IPX;
adr.port = BigShort(PORT_SERVER);
2012-07-22 13:34:45 +00:00
Netchan_OutOfBandPrint(NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION));
}
/* send a packet to each address book entry */
2012-07-22 13:34:45 +00:00
for (i = 0; i < 16; i++)
{
2012-07-22 13:34:45 +00:00
Com_sprintf(name, sizeof(name), "adr%i", i);
adrstring = (char *)Cvar_VariableString(name);
if (!adrstring || !adrstring[0])
2012-07-22 13:34:45 +00:00
{
continue;
2012-07-22 13:34:45 +00:00
}
2012-07-22 13:34:45 +00:00
Com_Printf("pinging %s...\n", adrstring);
2012-07-22 13:34:45 +00:00
if (!NET_StringToAdr(adrstring, &adr))
{
2012-07-22 13:34:45 +00:00
Com_Printf("Bad address: %s\n", adrstring);
continue;
}
if (!adr.port)
2012-07-22 13:34:45 +00:00
{
adr.port = BigShort(PORT_SERVER);
2012-07-22 13:34:45 +00:00
}
2012-07-22 13:34:45 +00:00
Netchan_OutOfBandPrint(NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION));
}
}
/*
* Responses to broadcasts, etc
*/
2012-07-22 13:34:45 +00:00
void
CL_ConnectionlessPacket(void)
{
2012-07-22 13:34:45 +00:00
char *s;
char *c;
2012-07-22 13:34:45 +00:00
MSG_BeginReading(&net_message);
MSG_ReadLong(&net_message); /* skip the -1 */
2012-07-22 13:34:45 +00:00
s = MSG_ReadStringLine(&net_message);
2012-07-22 13:34:45 +00:00
Cmd_TokenizeString(s, false);
c = Cmd_Argv(0);
2012-07-22 13:34:45 +00:00
Com_Printf("%s: %s\n", NET_AdrToString(net_from), c);
/* server connection */
if (!strcmp(c, "client_connect"))
{
if (cls.state == ca_connected)
{
2012-07-22 13:34:45 +00:00
Com_Printf("Dup connect received. Ignored.\n");
return;
}
2012-07-22 13:34:45 +00:00
Netchan_Setup(NS_CLIENT, &cls.netchan, net_from, cls.quakePort);
2012-07-22 13:34:45 +00:00
MSG_WriteChar(&cls.netchan.message, clc_stringcmd);
MSG_WriteString(&cls.netchan.message, "new");
cls.state = ca_connected;
return;
}
/* server responding to a status broadcast */
if (!strcmp(c, "info"))
{
2012-07-22 13:34:45 +00:00
CL_ParseStatusMessage();
return;
}
/* remote command from gui front end */
if (!strcmp(c, "cmd"))
{
if (!NET_IsLocalAddress(net_from))
{
2012-07-22 13:34:45 +00:00
Com_Printf("Command packet from remote host. Ignored.\n");
return;
}
2012-07-22 13:34:45 +00:00
s = MSG_ReadString(&net_message);
Cbuf_AddText(s);
Cbuf_AddText("\n");
return;
}
/* print command from somewhere */
if (!strcmp(c, "print"))
{
2012-07-22 13:34:45 +00:00
s = MSG_ReadString(&net_message);
Com_Printf("%s", s);
return;
}
/* ping from somewhere */
if (!strcmp(c, "ping"))
{
2012-07-22 13:34:45 +00:00
Netchan_OutOfBandPrint(NS_CLIENT, net_from, "ack");
return;
}
/* challenge from the server we are connecting to */
if (!strcmp(c, "challenge"))
{
2012-06-02 07:31:27 +00:00
cls.challenge = (int)strtol(Cmd_Argv(1), (char **)NULL, 10);
2012-07-22 13:34:45 +00:00
CL_SendConnectPacket();
return;
}
/* echo request from server */
if (!strcmp(c, "echo"))
{
2012-07-22 13:34:45 +00:00
Netchan_OutOfBandPrint(NS_CLIENT, net_from, "%s", Cmd_Argv(1));
return;
}
2012-07-22 13:34:45 +00:00
Com_Printf("Unknown command.\n");
}
2012-07-22 13:34:45 +00:00
void
CL_ReadPackets(void)
{
2012-07-22 13:34:45 +00:00
while (NET_GetPacket(NS_CLIENT, &net_from, &net_message))
{
/* remote command packet */
if (*(int *)net_message.data == -1)
{
2012-07-22 13:34:45 +00:00
CL_ConnectionlessPacket();
continue;
}
2012-07-22 13:34:45 +00:00
if ((cls.state == ca_disconnected) || (cls.state == ca_connecting))
{
continue; /* dump it if not connected */
2012-07-22 13:34:45 +00:00
}
if (net_message.cursize < 8)
{
2012-07-22 13:34:45 +00:00
Com_Printf("%s: Runt packet\n", NET_AdrToString(net_from));
continue;
}
/* packet from server */
2012-07-22 13:34:45 +00:00
if (!NET_CompareAdr(net_from, cls.netchan.remote_address))
{
2012-07-22 13:34:45 +00:00
Com_DPrintf("%s:sequenced packet without connection\n",
NET_AdrToString(net_from));
continue;
}
if (!Netchan_Process(&cls.netchan, &net_message))
2012-07-22 13:34:45 +00:00
{
continue; /* wasn't accepted for some reason */
2012-07-22 13:34:45 +00:00
}
2012-07-22 13:34:45 +00:00
CL_ParseServerMessage();
}
/* check timeout */
2012-07-22 13:34:45 +00:00
if ((cls.state >= ca_connected) &&
(cls.realtime - cls.netchan.last_received > cl_timeout->value * 1000))
{
if (++cl.timeoutcount > 5)
{
2012-07-22 13:34:45 +00:00
Com_Printf("\nServer connection timed out.\n");
CL_Disconnect();
return;
}
}
else
2012-07-22 13:34:45 +00:00
{
cl.timeoutcount = 0;
2012-07-22 13:34:45 +00:00
}
}
2012-07-22 13:34:45 +00:00