implement connectbr command, along with providing best-route info from the server browser.

does not use proxies by default. this is the first pass at this, and I'm not sure the ping values are reliable enough to have any faith in the routes selected.
fix a couple of other issues too.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4933 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2015-07-07 02:03:31 +00:00
parent d789a2c81a
commit 6f5f8c3e34
8 changed files with 327 additions and 38 deletions

View file

@ -147,6 +147,7 @@ cvar_t cl_gunanglex = CVAR("cl_gunanglex", "0");
cvar_t cl_gunangley = CVAR("cl_gunangley", "0");
cvar_t cl_gunanglez = CVAR("cl_gunanglez", "0");
cvar_t cl_proxyaddr = CVAR("cl_proxyaddr", "");
cvar_t cl_sendguid = CVARD("cl_sendguid", "0", "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 = CVARFD("cl_downloads", "1", CVAR_NOTFROMSERVER, "Allows you to block all automatic downloads.");
cvar_t cl_download_csprogs = CVARFD("cl_download_csprogs", "1", CVAR_NOTFROMSERVER, "Download updated client gamecode if available. Warning: If you clear this to avoid downloading vm code, you should also clear cl_download_packages.");
@ -505,6 +506,7 @@ void CL_SendConnectPacket (netadr_t *to, int mtu,
#endif
int clients;
int c;
char *a;
// JACK: Fixed bug where DNS lookups would cause two connects real fast
// Now, adds lookup time to the connect time.
@ -610,11 +612,21 @@ void CL_SendConnectPacket (netadr_t *to, int mtu,
Q_strncatz(data, va(" %i %i", cls.qport, connectinfo.challenge), sizeof(data));
//userinfo 0 + zquake extension info.
if (connectinfo.protocol == CP_QUAKEWORLD)
Q_strncatz(data, va(" \"%s\\*z_ext\\%i\"", cls.userinfo[0], SUPPORTED_Z_EXTENSIONS), sizeof(data));
else
Q_strncatz(data, va(" \"%s\"", cls.userinfo[0]), sizeof(data));
//userinfo0 has some twiddles for extensions from other qw engines.
Q_strncatz(data, " \"", sizeof(data));
//qwfwd proxy routing
if ((a = strrchr(cls.servername, '@')))
{
*a = 0;
Q_strncatz(data, va("\\prx\\%s", cls.servername), sizeof(data));
*a = '@';
}
//the info itself
Q_strncatz(data, cls.userinfo[0], sizeof(data));
if (connectinfo.protocol == CP_QUAKEWORLD) //zquake extension info.
Q_strncatz(data, va("\\*z_ext\\%i", cls.userinfo[0], SUPPORTED_Z_EXTENSIONS), sizeof(data));
Q_strncatz(data, "\"", sizeof(data));
for (c = 1; c < clients; c++)
{
Q_strncatz(data, va(" \"%s\"", cls.userinfo[c]), sizeof(data));
@ -690,6 +702,7 @@ void CL_CheckForResend (void)
double t1, t2;
int contype = 0;
qboolean keeptrying = true;
char *host;
#ifndef CLIENTONLY
if (!cls.state && (!connectinfo.trying || sv.state != ss_clustermode) && sv.state)
@ -872,7 +885,13 @@ void CL_CheckForResend (void)
t1 = Sys_DoubleTime ();
if (!connectinfo.istransfer)
{
if (!NET_StringToAdr (cls.servername, connectinfo.defaultport, &connectinfo.adr))
host = strrchr(cls.servername, '@');
if (host)
host++;
else
host = cls.servername;
if (!NET_StringToAdr (host, connectinfo.defaultport, &connectinfo.adr))
{
Con_TPrintf ("Bad server address\n");
connectinfo.trying = false;
@ -971,8 +990,13 @@ void CL_CheckForResend (void)
}
}
void CL_BeginServerConnect(int port)
void CL_BeginServerConnect(const char *host, int port, qboolean noproxy)
{
if (strstr(host, "://") || !*cl_proxyaddr.string || noproxy)
Q_strncpyz (cls.servername, host, sizeof(cls.servername));
else
Q_snprintfz(cls.servername, sizeof(cls.servername), "%s@%s", host, cl_proxyaddr.string);
if (!port)
port = cl_defaultport.value;
memset(&connectinfo, 0, sizeof(connectinfo));
@ -1060,11 +1084,44 @@ void CL_Connect_f (void)
#endif
CL_Disconnect_f ();
Q_strncpyz (cls.servername, server, sizeof(cls.servername));
CL_BeginServerConnect(0);
CL_BeginServerConnect(server, 0, false);
}
static void CL_ConnectBestRoute_f (void)
{
char server[1024];
int proxies;
int directcost, chainedcost;
if (Cmd_Argc() != 2)
{
Con_TPrintf ("usage: connectbr <server>\n");
return;
}
proxies = Master_FindBestRoute(Cmd_Argv(1), server, sizeof(server), &directcost, &chainedcost);
if (!*server)
{
Con_TPrintf ("Unable to route to server\n");
return;
}
else if (proxies < 0)
Con_TPrintf ("Routing database is not initialised, connecting directly\n");
else if (!proxies)
Con_TPrintf ("Routing table favours a direct connection\n");
else if (proxies == 1)
Con_TPrintf ("Routing table favours a single proxy (%ims vs %ims)\n", chainedcost, directcost);
else
Con_TPrintf ("Routing table favours chaining through %i proxies (%ims vs %ims)\n", proxies, chainedcost, directcost);
#ifndef CLIENTONLY
if (sv.state == ss_clustermode)
CL_Disconnect ();
else
#endif
CL_Disconnect_f ();
CL_BeginServerConnect(server, 0, true);
}
void CL_Join_f (void)
static void CL_Join_f (void)
{
char *server;
@ -1088,8 +1145,7 @@ void CL_Join_f (void)
Cvar_Set(&spectator, "0");
Q_strncpyz (cls.servername, server, sizeof(cls.servername));
CL_BeginServerConnect(0);
CL_BeginServerConnect(server, 0, false);
}
void CL_Observe_f (void)
@ -1116,8 +1172,7 @@ void CL_Observe_f (void)
Cvar_Set(&spectator, "1");
Q_strncpyz (cls.servername, server, sizeof(cls.servername));
CL_BeginServerConnect(0);
CL_BeginServerConnect(server, 0, false);
}
#ifdef NQPROT
@ -1135,8 +1190,7 @@ void CLNQ_Connect_f (void)
CL_Disconnect_f ();
Q_strncpyz (cls.servername, server, sizeof(cls.servername));
CL_BeginServerConnect(26000);
CL_BeginServerConnect(server, 26000, true);
}
#endif
@ -1150,9 +1204,7 @@ void CL_IRCConnect_f (void)
char *server;
server = Cmd_Argv (1);
strcpy(cls.servername, "irc://");
Q_strncpyz (cls.servername+6, server, sizeof(cls.servername)-6);
CL_BeginServerConnect(0);
CL_BeginServerConnect(va("irc://%s", server), 0, true);
}
}
#endif
@ -3613,6 +3665,7 @@ void CL_Init (void)
Cvar_Register (&cfg_save_name, cl_controlgroup);
Cvar_Register (&cl_proxyaddr, cl_controlgroup);
Cvar_Register (&cl_sendguid, cl_controlgroup);
Cvar_Register (&cl_defaultport, cl_controlgroup);
Cvar_Register (&cl_servername, cl_controlgroup);
@ -3789,7 +3842,14 @@ void CL_Init (void)
Cmd_AddCommand ("cl_status", CL_Status_f);
Cmd_AddCommandD ("quit", CL_Quit_f, "Use this command when you get angry. Does not save any cvars. Use cfg_save to save settings, or use the menu for a prompt.");
Cmd_AddCommandD ("connect", CL_Connect_f, "connect scheme://address:port\nConnect to a server. Use a scheme of tcp:// or tls:// to connect via non-udp protocols."
Cmd_AddCommandD ("connectbr", CL_ConnectBestRoute_f, "connect address:port\nConnect to a qw server using the best route we can detect.");
Cmd_AddCommandD ("connect", CL_Connect_f, "connect scheme://address:port\nConnect to a server. "
#if defined(FTE_TARGET_WEB)
"Use a scheme of ws:// or wss:// to connect via using websockets."
#else
"Use a scheme of tcp:// or tls:// to connect via non-udp protocols."
#endif
#if defined(NQPROT) || defined(Q2CLIENT) || defined(Q3CLIENT)
"\nDefault port is port "STRINGIFY(PORT_QWSERVER)"."
#ifdef NQPROT

View file

@ -143,6 +143,15 @@ typedef struct serverinfo_s
serverdetailedinfo_t *moreinfo;
struct serverinfo_s *prevpeer;
unsigned short cost;
unsigned short numpeers;
struct peers_s
{
struct serverinfo_s *peer;
unsigned short ping;
} *peers;
struct serverinfo_s *next;
} serverinfo_t;
@ -216,3 +225,4 @@ void Master_ClearMasks(void);
serverinfo_t *Master_SortedServer(int idx);
void Master_SetMaskString(qboolean or, hostcachekey_t field, const char *param, slist_test_t testop);
void Master_SetMaskInteger(qboolean or, hostcachekey_t field, int param, slist_test_t testop);
serverinfo_t *Master_FindRoute(netadr_t target);

View file

@ -971,7 +971,7 @@ qboolean CL_DemoBehind(void);
void CL_SaveInfo(vfsfile_t *f);
void CL_SetInfo (int pnum, char *key, char *value);
void CL_BeginServerConnect(int port);
void CL_BeginServerConnect(const char *host, int port, qboolean noproxy);
char *CL_TryingToConnect(void);
void CL_ExecInitialConfigs(char *defaultexec);
@ -1045,6 +1045,7 @@ int CL_ReadFromServer (void);
void CL_WriteToServer (usercmd_t *cmd);
void CL_BaseMove (usercmd_t *cmd, int pnum, float extra, float wantfps);
int Master_FindBestRoute(char *server, char *out, size_t outsize, int *directcost, int *chainedcost);
float CL_KeyState (kbutton_t *key, int pnum, qboolean noslowstart);
char *Key_KeynumToString (int keynum);

View file

@ -28,7 +28,7 @@ static cvar_t sb_alpha = CVARF("sb_alpha", "0.7", CVAR_ARCHIVE);
vrect_t joinbutton;
static float refreshedtime;
static int isrefreshing;
static qboolean serverpreview;
static int serverpreview;
extern cvar_t slist_writeserverstxt;
extern cvar_t slist_cacheinfo;
@ -411,6 +411,7 @@ static void SL_PostDraw (menu_t *menu)
"rmb: cancel",
"j: join",
"o: observe",
"b: join with automatic best route",
"v: say server info",
"ctrl-v: say_team server info",
"c: copy server info to clipboard",
@ -438,7 +439,15 @@ static void SL_PostDraw (menu_t *menu)
if (server && server->moreinfo)
{
int lx, x, y, i;
if (serverpreview == 3)
if (serverpreview == 4)
{
//count the number of proxies the best route will need
serverinfo_t *prox;
for (h = 1, prox = server; prox; h++, prox = prox->prevpeer)
;
w += 120;
}
else if (serverpreview == 3)
h = countof(helpstrings);
else if (serverpreview == 2)
{
@ -474,7 +483,18 @@ static void SL_PostDraw (menu_t *menu)
Draw_FunStringWidth (x, y, "^Ue01d^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01f", w, 2, false);
y+=8;
if (serverpreview == 3)
if (serverpreview == 4)
{
serverinfo_t *prox;
for (prox = server; prox; prox = prox->prevpeer)
{
Draw_FunStringWidth (x, y, va("%i", prox->cost), 32-8, true, false);
Draw_FunStringWidth (x + 32, y, NET_AdrToString(buf, sizeof(buf), &prox->adr), w/2 - 8 - 32, true, false);
Draw_FunStringWidth (x + w/2, y, prox->name, w/2, false, false);
y += 8;
}
}
else if (serverpreview == 3)
{
x = lx;
for (i = 0; i < countof(helpstrings); i++)
@ -653,16 +673,27 @@ static qboolean SL_Key (int key, menu_t *menu)
else if (key == K_LEFTARROW)
{
if (--serverpreview < 1)
serverpreview = 3;
serverpreview = 4;
if (serverpreview == 4)
Master_FindRoute(server->adr);
return true;
}
else if (key == K_RIGHTARROW)
{
if (++serverpreview > 3)
if (++serverpreview > 4)
serverpreview = 1;
if (serverpreview == 4)
Master_FindRoute(server->adr);
return true;
}
else if (key == 'o' || key == 'j' || key == K_ENTER || key == K_KP_ENTER) //join
else if (key == 'b' && serverpreview != 4)
{
Master_FindRoute(server->adr);
serverpreview = 4;
}
else if (key == 'b' || key == 'o' || key == 'j' || key == K_ENTER || key == K_KP_ENTER) //join
{
if (key == 's' || key == 'o')
Cbuf_AddText("spectator 1\n", RESTRICT_LOCAL);
@ -672,10 +703,23 @@ doconnect:
Cbuf_AddText("spectator 0\n", RESTRICT_LOCAL);
}
//which connect command are we using?
if ((server->special & SS_PROTOCOLMASK) == SS_NETQUAKE)
Cbuf_AddText(va("nqconnect %s\n", NET_AdrToString(buf, sizeof(buf), &server->adr)), RESTRICT_LOCAL);
Cbuf_AddText("nqconnect ", RESTRICT_LOCAL);
else
Cbuf_AddText(va("connect %s\n", NET_AdrToString(buf, sizeof(buf), &server->adr)), RESTRICT_LOCAL);
Cbuf_AddText("connect ", RESTRICT_LOCAL);
//output the server's address
Cbuf_AddText(va("%s", NET_AdrToString(buf, sizeof(buf), &server->adr)), RESTRICT_LOCAL);
if (serverpreview == 4 || key == 'b')
{ //and postfix it with routing info if we're going for a proxied route.
if (serverpreview != 4)
Master_FindRoute(server->adr);
for (server = server->prevpeer; server; server = server->prevpeer)
Cbuf_AddText(va("@%s", NET_AdrToString(buf, sizeof(buf), &server->adr)), RESTRICT_LOCAL);
}
Cbuf_AddText("\n", RESTRICT_LOCAL);
M_RemoveAllMenus();
return true;
@ -754,6 +798,9 @@ doconnect:
{
selectedserver.inuse = true;
SListOptionChanged(server);
if (serverpreview == 4)
Master_FindRoute(server->adr);
}
}

View file

@ -1225,6 +1225,121 @@ int Master_KeyForName(const char *keyname)
}
}
static void Master_FloodRoute(serverinfo_t *node)
{
unsigned int i;
struct peers_s *peer = node->peers;
for (i = 0; i < node->numpeers; i++, peer++)
{
if (peer->ping)
if ((unsigned int)(peer->peer->cost) > (unsigned int)(node->cost + peer->ping))
{ //we found a shorter route. flood into it.
peer->peer->prevpeer = node;
peer->peer->cost = node->cost + peer->ping;
Master_FloodRoute(peer->peer);
}
}
}
serverinfo_t *Master_FindRoute(netadr_t target)
{
serverinfo_t *info, *targ, *prox;
extern cvar_t cl_proxyaddr;
targ = Master_InfoForServer(&target);
if (!targ) //you wot?
return NULL;
//never flood into a peer if its just going to be more expensive than a direct connection
if (*cl_proxyaddr.string)
{
//fixme: we don't handle chained proxies properly, as we assume we can directly hop to the named final proxy.
//fixme: we'll find the same route, we just won't display the correct expected ping.
netadr_t pa;
char *chain = strchr(cl_proxyaddr.string, '@');
if (chain)
*chain = 0;
NET_StringToAdr(cl_proxyaddr.string, 0, &pa);
prox = Master_InfoForServer(&pa);
if (chain)
*chain = '@';
}
else
prox = NULL;
if (prox)
{
for (info = firstserver; info; info = info->next)
{
info->cost = info->ping;
info->prevpeer = prox;
}
prox->cost = prox->ping;
prox->prevpeer = NULL;
Master_FloodRoute(prox);
}
else
{
for (info = firstserver; info; info = info->next)
{
info->cost = info->ping;
info->prevpeer = NULL;
}
//flood through all proxies
for (info = firstserver; info; info = info->next)
Master_FloodRoute(info);
}
if (targ->prevpeer)
return targ;
return NULL;
}
int Master_FindBestRoute(char *server, char *out, size_t outsize, int *directcost, int *chainedcost)
{
serverinfo_t *route;
netadr_t adr;
int ret = 0;
char buf[256];
*out = 0;
*directcost = 0;
*chainedcost = 0;
if (!NET_StringToAdr(server, 0, &adr))
return -1;
if (!firstserver)
{ //routing isn't initialised. you do actually need to refresh the serverbrowser for this junk
Q_strncpyz(out, server, outsize);
return -1;
}
route = Master_FindRoute(adr);
if (!route)
{ //routing didn't find anything, just go directly.
Q_strncpyz(out, server, outsize);
return 0;
}
*directcost = route->ping;
*chainedcost = route->cost;
Q_strncatz(out, NET_AdrToString(buf, sizeof(buf), &route->adr), outsize);
for (ret = 0, route = route->prevpeer; route; route = route->prevpeer, ret++)
Q_strncatz(out, va("@%s", NET_AdrToString(buf, sizeof(buf), &route->adr)), outsize);
return ret;
}
//main thread
void CLMaster_AddMaster_Worker_Resolved(void *ctx, void *data, size_t a, size_t b)
{
@ -1365,6 +1480,8 @@ void MasterInfo_Shutdown(void)
{
sv = firstserver;
firstserver = sv->next;
if (sv->peers)
Z_Free(sv->peers);
Z_Free(sv);
}
while(master)
@ -2633,8 +2750,6 @@ int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favor
}
else
{
MasterInfo_RemovePlayers(&info->adr);
//determine the ping
if (info->refreshtime)
{
@ -2653,6 +2768,59 @@ int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favor
*nl = '\0';
nl++;
}
if (info->special & SS_PROXY)
{
if (!*Info_ValueForKey(msg, "hostname"))
{ //qq, you suck
//this is a proxy peer list, not an actual serverinfo update.
unsigned char *ptr = net_message.data + 5;
int remaining = net_message.cursize - 5;
struct peers_s *peer;
netadr_t pa;
memset(&pa, 0, sizeof(pa));
remaining /= 8;
NET_AdrToString(adr, sizeof(adr), &info->adr);
Z_Free(info->peers);
info->numpeers = remaining;
peer = info->peers = Z_Malloc(sizeof(*peer)*info->numpeers);
while (remaining --> 0)
{
pa.type = NA_IP;
pa.address.ip[0] = *ptr++;
pa.address.ip[1] = *ptr++;
pa.address.ip[2] = *ptr++;
pa.address.ip[3] = *ptr++;
pa.port = *ptr++<<8;
pa.port |= *ptr++;
peer->ping = *ptr++;
peer->ping |= *ptr++<<8;
peer->peer = Master_InfoForServer(&pa);
if (!peer->peer)
{
//generate some lame peer node that we can use.
peer->peer = Z_Malloc(sizeof(serverinfo_t));
peer->peer->adr = pa;
peer->peer->sends = 1;
peer->peer->special = 0;
peer->peer->refreshtime = 0;
peer->peer->ping = 0xffff;
peer->peer->next = firstserver;
firstserver = peer->peer;
}
peer++;
}
return false;
}
}
MasterInfo_RemovePlayers(&info->adr);
name = Info_ValueForKey(msg, "hostname");
if (!*name)
name = Info_ValueForKey(msg, "sv_hostname");
@ -2717,7 +2885,12 @@ int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favor
if (!strcmp(Info_ValueForKey(msg, "*progs"), "666") && !strcmp(Info_ValueForKey(msg, "*version"), "2.91"))
info->special |= SS_PROXY; //qizmo
if (!Q_strncmp(Info_ValueForKey(msg, "*version"), "qwfwd", 5))
{
char *msg = "\xff\xff\xff\xffpingstatus";
NET_SendPollPacket(strlen(msg), msg, info->adr);
info->special |= SS_PROXY; //qwfwd
}
if (!Q_strncasecmp(Info_ValueForKey(msg, "*version"), "qtv ", 4))
info->special |= SS_PROXY; //eztv

View file

@ -2381,7 +2381,7 @@ static IShellLinkW *CreateShellLink(char *command, char *target, char *title, ch
if (FAILED(hr))
return NULL;
GetModuleFileNameW(NULL, buf, sizeof(buf)/sizeof(wchar_t)-1);
GetModuleFileNameW(NULL, buf, countof(buf)-1);
IShellLinkW_SetIconLocation(link, buf, 0); /*grab the first icon from our exe*/
IShellLinkW_SetPath(link, buf); /*program to run*/
@ -2394,10 +2394,8 @@ static IShellLinkW *CreateShellLink(char *command, char *target, char *title, ch
else
*s = tolower(*s);
}
_snwprintf(buf, sizeof(buf), L"%ls \"%ls\" -basedir \"%ls\"", command, target, tmp);
IShellLinkW_SetArguments(link, buf); /*args*/
_snwprintf(buf, sizeof(buf), L"%ls", desc);
IShellLinkW_SetDescription(link, buf); /*tooltip*/
IShellLinkW_SetArguments(link, widen(buf, sizeof(buf), va("%s \"%s\" -basedir \"%s\"", command, target, tmp))); /*args*/
IShellLinkW_SetDescription(link, widen(buf, sizeof(buf), desc)); /*tooltip*/
hr = IShellLinkW_QueryInterface(link, &qIID_IPropertyStore, (void**)&prop_store);

View file

@ -5095,7 +5095,7 @@ void NET_PrintAddresses(ftenet_connections_t *collection)
}
if (warn)
Con_Printf("net address (%s): no addresses\n", con[i]->name);
Con_Printf("net address: no addresses\n");
}
//=============================================================================

View file

@ -118,7 +118,7 @@ int main (int argc, char **argv)
fprintf(logfile, " %s", argv[i]);
}
fprintf(logfile, "\n");
sucess = CompileParams(&funcs, true, argc, argv);
sucess = CompileParams(&funcs, NULL, argc, argv);
qccClearHunk();
if (logfile)
fclose(logfile);